From c2a7d3ade6fcb03a955a134db67fcd3c3b7bdf65 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 02:11:52 +0200 Subject: [PATCH 01/25] Add session/list --- nalgonda/models/session_config.py | 9 ++++++ .../repositories/session_firestore_storage.py | 23 +++++++++++++++ nalgonda/routers/v1/api/session.py | 12 ++++++-- .../functional/v1/api/test_agent_endpoints.py | 29 ++++++++++++------- .../v1/api/test_session_endpoints.py | 18 ++++++++++++ 5 files changed, 78 insertions(+), 13 deletions(-) create mode 100644 nalgonda/models/session_config.py create mode 100644 nalgonda/repositories/session_firestore_storage.py diff --git a/nalgonda/models/session_config.py b/nalgonda/models/session_config.py new file mode 100644 index 00000000..90f28360 --- /dev/null +++ b/nalgonda/models/session_config.py @@ -0,0 +1,9 @@ +from pydantic import BaseModel, Field + + +class SessionConfig(BaseModel): + """Session configuration model""" + + session_id: str = Field(..., description="Unique identifier for the session") + owner_id: str = Field(..., description="The user ID associated with the session") + agency_id: str = Field(..., description="Unique identifier for the agency") diff --git a/nalgonda/repositories/session_firestore_storage.py b/nalgonda/repositories/session_firestore_storage.py new file mode 100644 index 00000000..013716d7 --- /dev/null +++ b/nalgonda/repositories/session_firestore_storage.py @@ -0,0 +1,23 @@ +from firebase_admin import firestore +from google.cloud.firestore_v1 import FieldFilter + +from nalgonda.models.session_config import SessionConfig + + +class SessionConfigFirestoreStorage: + def __init__(self): + self.db = firestore.client() + self.collection = self.db.collection("session_configs") + + def load_by_owner_id(self, owner_id: str | None = None) -> list[SessionConfig]: + query = self.collection.where(filter=FieldFilter("owner_id", "==", owner_id)) + return [SessionConfig.model_validate(document_snapshot.to_dict()) for document_snapshot in query.stream()] + + def load_by_session_id(self, session_id: str) -> SessionConfig | None: + document_snapshot = self.collection.document(session_id).get() + if not document_snapshot.exists: + return None + return SessionConfig.model_validate(document_snapshot.to_dict()) + + def save(self, session_config: SessionConfig) -> None: + self.collection.document(session_config.session_id).set(session_config.model_dump()) diff --git a/nalgonda/routers/v1/api/session.py b/nalgonda/routers/v1/api/session.py index 0aca33cb..42b7c7fc 100644 --- a/nalgonda/routers/v1/api/session.py +++ b/nalgonda/routers/v1/api/session.py @@ -10,6 +10,7 @@ from nalgonda.models.auth import UserInDB from nalgonda.models.request_models import AgencyMessagePostRequest, ThreadPostRequest from nalgonda.repositories.agency_config_firestore_storage import AgencyConfigFirestoreStorage +from nalgonda.repositories.session_firestore_storage import SessionConfigFirestoreStorage from nalgonda.services.agency_manager import AgencyManager from nalgonda.services.thread_manager import ThreadManager @@ -20,9 +21,14 @@ ) -@session_router.get("/session") -async def get_sessions(): - ... +@session_router.get("/session/list") +async def get_session_list( + current_user: Annotated[UserInDB, Depends(get_current_active_user)], + storage: SessionConfigFirestoreStorage = Depends(SessionConfigFirestoreStorage), +): + """Return a list of all sessions for the current user.""" + session_configs = storage.load_by_owner_id(current_user.id) + return session_configs @session_router.post("/session") diff --git a/tests/functional/v1/api/test_agent_endpoints.py b/tests/functional/v1/api/test_agent_endpoints.py index 64d655a9..166d8988 100644 --- a/tests/functional/v1/api/test_agent_endpoints.py +++ b/tests/functional/v1/api/test_agent_endpoints.py @@ -8,7 +8,7 @@ @pytest.fixture -def agent_data(): +def agent_config_data(): return { "agent_id": AGENT_ID, "owner_id": TEST_USER_ID, @@ -21,35 +21,44 @@ def agent_data(): @pytest.mark.usefixtures("mock_get_current_active_user") -def test_get_agent_config(client, agent_data, mock_firestore_client): - mock_firestore_client.setup_mock_data("agent_configs", AGENT_ID, agent_data) +def test_get_agent_list(agent_config_data, client, mock_firestore_client): + mock_firestore_client.setup_mock_data("agent_configs", AGENT_ID, agent_config_data) + + response = client.get("/v1/api/agent/list") + assert response.status_code == 200 + assert response.json() == [agent_config_data] + + +@pytest.mark.usefixtures("mock_get_current_active_user") +def test_get_agent_config(client, agent_config_data, mock_firestore_client): + mock_firestore_client.setup_mock_data("agent_configs", AGENT_ID, agent_config_data) response = client.get(f"/v1/api/agent?agent_id={AGENT_ID}") assert response.status_code == 200 - assert response.json() == agent_data + assert response.json() == agent_config_data @pytest.mark.usefixtures("mock_get_current_active_user") -def test_update_agent_config_success(client, agent_data, mock_firestore_client): - mock_firestore_client.setup_mock_data("agent_configs", AGENT_ID, agent_data) +def test_update_agent_config_success(client, agent_config_data, mock_firestore_client): + mock_firestore_client.setup_mock_data("agent_configs", AGENT_ID, agent_config_data) with patch("nalgonda.services.agent_manager.AgentManager") as mock_agent_manager: mock_agent_manager.return_value = AsyncMock() mock_agent_manager.return_value.create_or_update_agent.return_value = AGENT_ID - response = client.put("/v1/api/agent", json=agent_data) + response = client.put("/v1/api/agent", json=agent_config_data) assert response.status_code == 200 assert response.json() == {"agent_id": AGENT_ID} @pytest.mark.usefixtures("mock_get_current_active_user") -def test_update_agent_config_owner_id_mismatch(client, agent_data, mock_firestore_client): - agent_data_db = agent_data.copy() +def test_update_agent_config_owner_id_mismatch(client, agent_config_data, mock_firestore_client): + agent_data_db = agent_config_data.copy() agent_data_db["owner_id"] = "other_user" mock_firestore_client.setup_mock_data("agent_configs", AGENT_ID, agent_data_db) - response = client.put("/v1/api/agent", json=agent_data) + response = client.put("/v1/api/agent", json=agent_config_data) assert response.status_code == 403 assert response.json() == {"detail": "Forbidden"} diff --git a/tests/functional/v1/api/test_session_endpoints.py b/tests/functional/v1/api/test_session_endpoints.py index 920c5716..0e78d224 100644 --- a/tests/functional/v1/api/test_session_endpoints.py +++ b/tests/functional/v1/api/test_session_endpoints.py @@ -9,6 +9,24 @@ from tests.test_utils import TEST_USER_ID +@pytest.fixture +def session_config_data(): + return { + "session_id": "test_session_id", + "owner_id": TEST_USER_ID, + "agency_id": "test_agency_id", + } + + +@pytest.mark.usefixtures("mock_get_current_active_user") +def test_get_session_list(session_config_data, client, mock_firestore_client): + mock_firestore_client.setup_mock_data("session_configs", "test_session_id", session_config_data) + + response = client.get("/v1/api/session/list") + assert response.status_code == 200 + assert response.json() == [session_config_data] + + @pytest.mark.usefixtures("mock_get_current_active_user") def test_create_session_success(client, mock_firestore_client): with patch.object( From 03c9dd7a5f8fba72751507711b1b0d9738f65ab1 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 00:44:36 +0000 Subject: [PATCH 02/25] Save sessions to Firestore --- nalgonda/dependencies/dependencies.py | 11 +++--- nalgonda/models/request_models.py | 6 ++-- nalgonda/models/session_config.py | 1 + .../agency_config_firestore_storage.py | 20 ++++++----- .../agent_config_firestore_storage.py | 13 ++++--- .../repositories/session_firestore_storage.py | 11 +++--- .../tool_config_firestore_storage.py | 13 ++++--- nalgonda/routers/v1/api/session.py | 32 ++++++++--------- nalgonda/routers/v1/websocket.py | 18 +++++----- nalgonda/services/agency_manager.py | 22 ++++++------ .../{thread_manager.py => session_manager.py} | 24 +++++++++++-- .../v1/api/test_session_endpoints.py | 36 ++++++++++++------- 12 files changed, 126 insertions(+), 81 deletions(-) rename nalgonda/services/{thread_manager.py => session_manager.py} (66%) diff --git a/nalgonda/dependencies/dependencies.py b/nalgonda/dependencies/dependencies.py index 62ef6604..d8dd7e5e 100644 --- a/nalgonda/dependencies/dependencies.py +++ b/nalgonda/dependencies/dependencies.py @@ -3,10 +3,11 @@ from nalgonda.repositories.agency_config_firestore_storage import AgencyConfigFirestoreStorage from nalgonda.repositories.agent_config_firestore_storage import AgentConfigFirestoreStorage +from nalgonda.repositories.session_firestore_storage import SessionConfigFirestoreStorage from nalgonda.services.agency_manager import AgencyManager from nalgonda.services.agent_manager import AgentManager from nalgonda.services.caching.redis_cache_manager import RedisCacheManager -from nalgonda.services.thread_manager import ThreadManager +from nalgonda.services.session_manager import SessionManager from nalgonda.settings import settings @@ -32,6 +33,8 @@ def get_agency_manager( return AgencyManager(cache_manager, agent_manager, agency_config_storage) -def get_thread_manager() -> ThreadManager: - """Returns a ThreadManager object""" - return ThreadManager() +def get_session_manager( + session_storage: SessionConfigFirestoreStorage = Depends(SessionConfigFirestoreStorage), +) -> SessionManager: + """Returns a SessionManager object""" + return SessionManager(session_storage=session_storage) diff --git a/nalgonda/models/request_models.py b/nalgonda/models/request_models.py index c7582a22..81e7e838 100644 --- a/nalgonda/models/request_models.py +++ b/nalgonda/models/request_models.py @@ -1,11 +1,11 @@ from pydantic import BaseModel, Field -class ThreadPostRequest(BaseModel): +class SessionPostRequest(BaseModel): agency_id: str = Field(..., description="The unique identifier for the agency.") -class AgencyMessagePostRequest(BaseModel): +class SessionMessagePostRequest(BaseModel): agency_id: str = Field(..., description="The unique identifier for the agency.") - thread_id: str = Field(..., description="The identifier for the conversational thread.") + session_id: str = Field(..., description="The identifier for the conversational thread.") message: str = Field(..., description="The message to be sent to the agency.") diff --git a/nalgonda/models/session_config.py b/nalgonda/models/session_config.py index 90f28360..62e31ffa 100644 --- a/nalgonda/models/session_config.py +++ b/nalgonda/models/session_config.py @@ -7,3 +7,4 @@ class SessionConfig(BaseModel): session_id: str = Field(..., description="Unique identifier for the session") owner_id: str = Field(..., description="The user ID associated with the session") agency_id: str = Field(..., description="Unique identifier for the agency") + created_at: int = Field(..., description="The timestamp at which the session was created") diff --git a/nalgonda/repositories/agency_config_firestore_storage.py b/nalgonda/repositories/agency_config_firestore_storage.py index 20039194..1754c0d6 100644 --- a/nalgonda/repositories/agency_config_firestore_storage.py +++ b/nalgonda/repositories/agency_config_firestore_storage.py @@ -7,27 +7,29 @@ class AgencyConfigFirestoreStorage: def __init__(self): self.db = firestore.client() - self.collection = self.db.collection("agency_configs") + self.collection_name = "agency_configs" def load_by_owner_id(self, owner_id: str | None = None) -> list[AgencyConfig]: - query = self.collection.where(filter=FieldFilter("owner_id", "==", owner_id)) + collection = self.db.collection(self.collection_name) + query = collection.where(filter=FieldFilter("owner_id", "==", owner_id)) return [AgencyConfig.model_validate(document_snapshot.to_dict()) for document_snapshot in query.stream()] def load_by_agency_id(self, agency_id: str) -> AgencyConfig | None: - document_ref = self.collection.document(agency_id) - agency_config_snapshot = document_ref.get() - if agency_config_snapshot.exists: - return AgencyConfig.model_validate(agency_config_snapshot.to_dict()) - return None + collection = self.db.collection(self.collection_name) + document_snapshot = collection.document(agency_id).get() + if not document_snapshot.exists: + return None + return AgencyConfig.model_validate(document_snapshot.to_dict()) def save(self, agency_config: AgencyConfig) -> str: """Save the agency configuration to the Firestore. If the agency_id is not set, it will create a new document and set the agency_id. Returns the agency_id.""" + collection = self.db.collection(self.collection_name) if agency_config.agency_id is None: # Create a new document and set the agency_id - document_reference = self.collection.add(agency_config.model_dump())[1] + document_reference = collection.add(agency_config.model_dump())[1] agency_config.agency_id = document_reference.id - self.collection.document(agency_config.agency_id).set(agency_config.model_dump()) + collection.document(agency_config.agency_id).set(agency_config.model_dump()) return agency_config.agency_id diff --git a/nalgonda/repositories/agent_config_firestore_storage.py b/nalgonda/repositories/agent_config_firestore_storage.py index f8318715..75944a51 100644 --- a/nalgonda/repositories/agent_config_firestore_storage.py +++ b/nalgonda/repositories/agent_config_firestore_storage.py @@ -7,14 +7,16 @@ class AgentConfigFirestoreStorage: def __init__(self): self.db = firestore.client() - self.collection = self.db.collection("agent_configs") + self.collection_name = "agent_configs" def load_by_owner_id(self, owner_id: str | None = None) -> list[AgentConfig]: - query = self.collection.where(filter=FieldFilter("owner_id", "==", owner_id)) + collection = self.db.collection(self.collection_name) + query = collection.where(filter=FieldFilter("owner_id", "==", owner_id)) return [AgentConfig.model_validate(document_snapshot.to_dict()) for document_snapshot in query.stream()] def load_by_agent_id(self, agent_id: str) -> AgentConfig | None: - document_snapshot = self.collection.document(agent_id).get() + collection = self.db.collection(self.collection_name) + document_snapshot = collection.document(agent_id).get() if not document_snapshot.exists: return None return AgentConfig.model_validate(document_snapshot.to_dict()) @@ -23,10 +25,11 @@ def save(self, agent_config: AgentConfig) -> str: """Save the agent configuration to the Firestore. If the agent_id is not set, it will create a new document and set the agent_id. Returns the agent_id.""" + collection = self.db.collection(self.collection_name) if agent_config.agent_id is None: # Create a new document and set the agent_id - document_reference = self.collection.add(agent_config.model_dump())[1] + document_reference = collection.add(agent_config.model_dump())[1] agent_config.agent_id = document_reference.id - self.collection.document(agent_config.agent_id).set(agent_config.model_dump()) + collection.document(agent_config.agent_id).set(agent_config.model_dump()) return agent_config.agent_id diff --git a/nalgonda/repositories/session_firestore_storage.py b/nalgonda/repositories/session_firestore_storage.py index 013716d7..2a729996 100644 --- a/nalgonda/repositories/session_firestore_storage.py +++ b/nalgonda/repositories/session_firestore_storage.py @@ -7,17 +7,20 @@ class SessionConfigFirestoreStorage: def __init__(self): self.db = firestore.client() - self.collection = self.db.collection("session_configs") + self.collection_name = "session_configs" def load_by_owner_id(self, owner_id: str | None = None) -> list[SessionConfig]: - query = self.collection.where(filter=FieldFilter("owner_id", "==", owner_id)) + collection = self.db.collection(self.collection_name) + query = collection.where(filter=FieldFilter("owner_id", "==", owner_id)) return [SessionConfig.model_validate(document_snapshot.to_dict()) for document_snapshot in query.stream()] def load_by_session_id(self, session_id: str) -> SessionConfig | None: - document_snapshot = self.collection.document(session_id).get() + collection = self.db.collection(self.collection_name) + document_snapshot = collection.document(session_id).get() if not document_snapshot.exists: return None return SessionConfig.model_validate(document_snapshot.to_dict()) def save(self, session_config: SessionConfig) -> None: - self.collection.document(session_config.session_id).set(session_config.model_dump()) + collection = self.db.collection(self.collection_name) + collection.document(session_config.session_id).set(session_config.model_dump()) diff --git a/nalgonda/repositories/tool_config_firestore_storage.py b/nalgonda/repositories/tool_config_firestore_storage.py index d683e720..75664d2b 100644 --- a/nalgonda/repositories/tool_config_firestore_storage.py +++ b/nalgonda/repositories/tool_config_firestore_storage.py @@ -7,23 +7,26 @@ class ToolConfigFirestoreStorage: def __init__(self): self.db = firestore.client() - self.collection = self.db.collection("tool_configs") + self.collection_name = "tool_configs" def load_by_owner_id(self, owner_id: str | None = None) -> list[ToolConfig]: - query = self.collection.where(filter=FieldFilter("owner_id", "==", owner_id)) + collection = self.db.collection(self.collection_name) + query = collection.where(filter=FieldFilter("owner_id", "==", owner_id)) return [ToolConfig.model_validate(document_snapshot.to_dict()) for document_snapshot in query.stream()] def load_by_tool_id(self, tool_id: str) -> ToolConfig | None: - document_snapshot = self.collection.document(tool_id).get() + collection = self.db.collection(self.collection_name) + document_snapshot = collection.document(tool_id).get() if not document_snapshot.exists: return None return ToolConfig.model_validate(document_snapshot.to_dict()) def save(self, tool_config: ToolConfig) -> tuple[str, int]: + collection = self.db.collection(self.collection_name) if tool_config.tool_id is None: # Create a new document and set the tool_id - document_reference = self.collection.add(tool_config.model_dump())[1] + document_reference = collection.add(tool_config.model_dump())[1] tool_config.tool_id = document_reference.id - self.collection.document(tool_config.tool_id).set(tool_config.model_dump()) + collection.document(tool_config.tool_id).set(tool_config.model_dump()) return tool_config.tool_id, tool_config.version diff --git a/nalgonda/routers/v1/api/session.py b/nalgonda/routers/v1/api/session.py index 42b7c7fc..01f37f61 100644 --- a/nalgonda/routers/v1/api/session.py +++ b/nalgonda/routers/v1/api/session.py @@ -6,13 +6,13 @@ from starlette.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND from nalgonda.dependencies.auth import get_current_active_user -from nalgonda.dependencies.dependencies import get_agency_manager, get_thread_manager +from nalgonda.dependencies.dependencies import get_agency_manager, get_session_manager from nalgonda.models.auth import UserInDB -from nalgonda.models.request_models import AgencyMessagePostRequest, ThreadPostRequest +from nalgonda.models.request_models import SessionMessagePostRequest, SessionPostRequest from nalgonda.repositories.agency_config_firestore_storage import AgencyConfigFirestoreStorage from nalgonda.repositories.session_firestore_storage import SessionConfigFirestoreStorage from nalgonda.services.agency_manager import AgencyManager -from nalgonda.services.thread_manager import ThreadManager +from nalgonda.services.session_manager import SessionManager logger = logging.getLogger(__name__) session_router = APIRouter( @@ -33,19 +33,19 @@ async def get_session_list( @session_router.post("/session") async def create_session( - request: ThreadPostRequest, + request: SessionPostRequest, current_user: Annotated[UserInDB, Depends(get_current_active_user)], agency_manager: AgencyManager = Depends(get_agency_manager), - thread_manager: ThreadManager = Depends(get_thread_manager), - storage: AgencyConfigFirestoreStorage = Depends(AgencyConfigFirestoreStorage), + agency_storage: AgencyConfigFirestoreStorage = Depends(AgencyConfigFirestoreStorage), + session_manager: SessionManager = Depends(get_session_manager), ) -> dict: """Create a new session for the given agency and return its id.""" agency_id = request.agency_id # check if the current_user has permissions to create a session for the agency - agency_config = storage.load_by_agency_id(agency_id) - if not agency_config: + agency_config_db = agency_storage.load_by_agency_id(agency_id) + if not agency_config_db: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Agency not found") - if agency_config.owner_id != current_user.id: + if agency_config_db.owner_id != current_user.id: raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Forbidden") logger.info(f"Creating a new session for the agency: {agency_id}, and user: {current_user.id}") @@ -54,16 +54,16 @@ async def create_session( if not agency: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Agency not found") - thread_id = thread_manager.create_threads(agency) + session_id = session_manager.create_session(agency, agency_id=agency_id, owner_id=current_user.id) - await agency_manager.cache_agency(agency, agency_id, thread_id) - return {"thread_id": thread_id} + await agency_manager.cache_agency(agency, agency_id, session_id) + return {"session_id": session_id} @session_router.post("/session/message") async def post_agency_message( current_user: Annotated[UserInDB, Depends(get_current_active_user)], - request: AgencyMessagePostRequest, + request: SessionMessagePostRequest, agency_manager: AgencyManager = Depends(get_agency_manager), storage: AgencyConfigFirestoreStorage = Depends(AgencyConfigFirestoreStorage), ) -> dict: @@ -77,11 +77,11 @@ async def post_agency_message( user_message = request.message agency_id = request.agency_id - thread_id = request.thread_id + session_id = request.session_id - logger.info(f"Received message: {user_message}, agency_id: {agency_id}, thread_id: {thread_id}") + logger.info(f"Received message: {user_message}, agency_id: {agency_id}, session_id: {session_id}") - agency = await agency_manager.get_agency(agency_id, thread_id) + agency = await agency_manager.get_agency(agency_id, session_id) if not agency: raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Agency not found") diff --git a/nalgonda/routers/v1/websocket.py b/nalgonda/routers/v1/websocket.py index 51cfe65d..00f0b016 100644 --- a/nalgonda/routers/v1/websocket.py +++ b/nalgonda/routers/v1/websocket.py @@ -18,22 +18,22 @@ ) -@ws_router.websocket("/ws/{agency_id}/{thread_id}") -async def websocket_thread_endpoint( +@ws_router.websocket("/ws/{agency_id}/{session_id}") +async def websocket_session_endpoint( websocket: WebSocket, agency_id: str, - thread_id: str, + session_id: str, agency_manager: AgencyManager = Depends(get_agency_manager), ): - """WebSocket endpoint for maintaining conversation with a specific thread. + """WebSocket endpoint for maintaining conversation with a specific session. Send messages to and from CEO of the given agency.""" # TODO: Add authentication: check if agency_id is valid for the given user await connection_manager.connect(websocket) - logger.info(f"WebSocket connected for agency_id: {agency_id}, thread_id: {thread_id}") + logger.info(f"WebSocket connected for agency_id: {agency_id}, session_id: {session_id}") - agency = await agency_manager.get_agency(agency_id, thread_id) + agency = await agency_manager.get_agency(agency_id, session_id) if not agency: await connection_manager.send_message("Agency not found", websocket) await connection_manager.disconnect(websocket) @@ -41,7 +41,7 @@ async def websocket_thread_endpoint( return try: - await websocket_receive_and_process_messages(websocket, agency_id, agency, thread_id) + await websocket_receive_and_process_messages(websocket, agency_id, agency, session_id) except (WebSocketDisconnect, ConnectionClosedOK): await connection_manager.disconnect(websocket) logger.info(f"WebSocket disconnected for agency_id: {agency_id}") @@ -51,7 +51,7 @@ async def websocket_receive_and_process_messages( websocket: WebSocket, agency_id: str, agency: Agency, - thread_id: str, + session_id: str, ) -> None: """Receive messages from the websocket and process them.""" while True: @@ -67,7 +67,7 @@ async def websocket_receive_and_process_messages( except (WebSocketDisconnect, ConnectionClosedOK) as e: raise e except Exception: - logger.exception(f"Exception while processing message: agency_id: {agency_id}, thread_id: {thread_id}") + logger.exception(f"Exception while processing message: agency_id: {agency_id}, session_id: {session_id}") await connection_manager.send_message("Something went wrong. Please try again.", websocket) continue diff --git a/nalgonda/services/agency_manager.py b/nalgonda/services/agency_manager.py index eb6c10e4..91eb18ac 100644 --- a/nalgonda/services/agency_manager.py +++ b/nalgonda/services/agency_manager.py @@ -23,13 +23,13 @@ def __init__( self.cache_manager = cache_manager self.agent_manager = agent_manager - async def get_agency(self, agency_id: str, thread_id: str | None = None) -> Agency | None: - cache_key = self.get_cache_key(agency_id, thread_id) + async def get_agency(self, agency_id: str, session_id: str | None = None) -> Agency | None: + cache_key = self.get_cache_key(agency_id, session_id) agency = await self.cache_manager.get(cache_key) if not agency: # If agency is not found in the cache, re-populate the cache - agency = await self.repopulate_cache_and_update_assistants(agency_id, thread_id) + agency = await self.repopulate_cache_and_update_assistants(agency_id, session_id) if not agency: logger.error(f"Agency configuration for {agency_id} could not be found in the Firestore database.") return None @@ -47,7 +47,7 @@ async def update_or_create_agency(self, agency_config: AgencyConfig) -> str: return agency_id async def repopulate_cache_and_update_assistants( - self, agency_id: str, thread_id: str | None = None + self, agency_id: str, session_id: str | None = None ) -> Agency | None: """Gets the agency config from the Firestore, constructs agents and agency (agency-swarm also updates assistants), and saves the Agency instance to Redis @@ -61,7 +61,7 @@ async def repopulate_cache_and_update_assistants( agents = await self.load_and_construct_agents(agency_config) agency = await asyncio.to_thread(self.construct_agency, agency_config, agents) - await self.cache_agency(agency, agency_id, thread_id) + await self.cache_agency(agency, agency_id, session_id) return agency async def load_and_construct_agents(self, agency_config: AgencyConfig) -> dict[str, Agent]: @@ -91,21 +91,21 @@ def construct_agency(agency_config: AgencyConfig, agents: dict[str, Agent]) -> A return Agency(agency_chart, shared_instructions=agency_config.shared_instructions) - async def cache_agency(self, agency: Agency, agency_id: str, thread_id: str | None) -> None: + async def cache_agency(self, agency: Agency, agency_id: str, session_id: str | None) -> None: """Cache the agency.""" - cache_key = self.get_cache_key(agency_id, thread_id) + cache_key = self.get_cache_key(agency_id, session_id) agency_clean = self._remove_client_objects(agency) await self.cache_manager.set(cache_key, agency_clean) - async def delete_agency_from_cache(self, agency_id: str, thread_id: str | None) -> None: + async def delete_agency_from_cache(self, agency_id: str, session_id: str | None) -> None: """Delete the agency from the cache.""" - cache_key = self.get_cache_key(agency_id, thread_id) + cache_key = self.get_cache_key(agency_id, session_id) await self.cache_manager.delete(cache_key) @staticmethod - def get_cache_key(agency_id: str, thread_id: str | None = None) -> str: - return f"{agency_id}/{thread_id}" if thread_id else agency_id + def get_cache_key(agency_id: str, session_id: str | None = None) -> str: + return f"{agency_id}/{session_id}" if session_id else agency_id @staticmethod def _remove_client_objects(agency: Agency) -> Agency: diff --git a/nalgonda/services/thread_manager.py b/nalgonda/services/session_manager.py similarity index 66% rename from nalgonda/services/thread_manager.py rename to nalgonda/services/session_manager.py index c398f9fe..8bd1cbd4 100644 --- a/nalgonda/services/thread_manager.py +++ b/nalgonda/services/session_manager.py @@ -1,9 +1,29 @@ +from datetime import datetime + from agency_swarm import Agency, Agent, get_openai_client from agency_swarm.threads import Thread +from nalgonda.models.session_config import SessionConfig +from nalgonda.repositories.session_firestore_storage import SessionConfigFirestoreStorage + + +class SessionManager: + def __init__(self, session_storage: SessionConfigFirestoreStorage): + self.session_storage = session_storage + + def create_session(self, agency: Agency, agency_id: str, owner_id: str) -> str: + """Create a new session for the given agency and return its id.""" + session_id = self._create_threads(agency) + session_config = SessionConfig( + session_id=session_id, + owner_id=owner_id, + agency_id=agency_id, + created_at=int(datetime.utcnow().timestamp()), + ) + self.session_storage.save(session_config) + return session_id -class ThreadManager: - def create_threads(self, agency: Agency) -> str: + def _create_threads(self, agency: Agency) -> str: """Create new threads for the given agency and return the thread ID of the main thread.""" client = get_openai_client() self._init_threads(agency, client) diff --git a/tests/functional/v1/api/test_session_endpoints.py b/tests/functional/v1/api/test_session_endpoints.py index 0e78d224..cd0f779b 100644 --- a/tests/functional/v1/api/test_session_endpoints.py +++ b/tests/functional/v1/api/test_session_endpoints.py @@ -1,11 +1,12 @@ +from unittest import mock from unittest.mock import AsyncMock, MagicMock, patch import pytest from fastapi import status -from nalgonda.models.request_models import ThreadPostRequest +from nalgonda.models.request_models import SessionPostRequest from nalgonda.services.agency_manager import AgencyManager -from nalgonda.services.thread_manager import ThreadManager +from nalgonda.services.session_manager import SessionManager from tests.test_utils import TEST_USER_ID @@ -15,6 +16,7 @@ def session_config_data(): "session_id": "test_session_id", "owner_id": TEST_USER_ID, "agency_id": "test_agency_id", + "created_at": 1234567890, } @@ -32,7 +34,7 @@ def test_create_session_success(client, mock_firestore_client): with patch.object( AgencyManager, "get_agency", AsyncMock(return_value=MagicMock()) ) as mock_get_agency, patch.object( - ThreadManager, "create_threads", MagicMock(return_value="new_thread_id") + SessionManager, "_create_threads", MagicMock(return_value="new_session_id") ) as mock_create_threads, patch.object(AgencyManager, "cache_agency", AsyncMock()) as mock_cache_agency: # mock Firestore to pass the security owner_id check mock_firestore_client.setup_mock_data( @@ -40,22 +42,30 @@ def test_create_session_success(client, mock_firestore_client): ) # Create request data - request_data = ThreadPostRequest(agency_id="test_agency_id") + request_data = SessionPostRequest(agency_id="test_agency_id") # Create a test client response = client.post("/v1/api/session", json=request_data.model_dump()) # Assertions assert response.status_code == 200 - assert response.json() == {"thread_id": "new_thread_id"} + assert response.json() == {"session_id": "new_session_id"} mock_get_agency.assert_awaited_once_with("test_agency_id", None) mock_create_threads.assert_called_once_with(mock_get_agency.return_value) - mock_cache_agency.assert_awaited_once_with(mock_get_agency.return_value, "test_agency_id", "new_thread_id") + mock_cache_agency.assert_awaited_once_with(mock_get_agency.return_value, "test_agency_id", "new_session_id") + + # Check if the session config was created + assert mock_firestore_client.collection("session_configs").to_dict() == { + "session_id": "new_session_id", + "owner_id": TEST_USER_ID, + "agency_id": "test_agency_id", + "created_at": mock.ANY, + } @pytest.mark.usefixtures("mock_get_current_active_user") def test_create_session_agency_not_found(client): with patch.object(AgencyManager, "get_agency", AsyncMock(return_value=None)): # Create request data - request_data = ThreadPostRequest(agency_id="test_agency_id") + request_data = SessionPostRequest(agency_id="test_agency_id") # Create a test client response = client.post("/v1/api/session", json=request_data.model_dump()) # Assertions @@ -77,21 +87,21 @@ def test_post_agency_message_success(client, mock_get_agency, mock_firestore_cli mock_firestore_client.setup_mock_data("agency_configs", "test_agency_id", agency_data) # Sending a message - message_data = {"agency_id": "test_agency_id", "thread_id": "test_thread_id", "message": "Hello, world!"} + message_data = {"agency_id": "test_agency_id", "session_id": "test_session_id", "message": "Hello, world!"} response = client.post("/v1/api/session/message", json=message_data) assert response.status_code == 200 # We will check for the actual message we set up to be sent assert response.json().get("response") == "Hello, world!" - mock_get_agency.assert_called_once_with("test_agency_id", "test_thread_id") + mock_get_agency.assert_called_once_with("test_agency_id", "test_session_id") # Agency configuration not found @pytest.mark.usefixtures("mock_get_current_active_user", "mock_firestore_client") def test_post_agency_message_agency_config_not_found(client, mock_get_agency): # Sending a message - message_data = {"agency_id": "test_agency", "thread_id": "test_thread", "message": "Hello, world!"} + message_data = {"agency_id": "test_agency", "session_id": "test_session_id", "message": "Hello, world!"} response = client.post("/v1/api/session/message", json=message_data) assert response.status_code == 404 @@ -106,7 +116,7 @@ def test_post_agency_message_unauthorized(client, mock_get_agency, mock_firestor mock_firestore_client.setup_mock_data("agency_configs", "test_agency", agency_data) # Sending a message - message_data = {"agency_id": "test_agency", "thread_id": "test_thread", "message": "Hello, world!"} + message_data = {"agency_id": "test_agency", "session_id": "test_session_id", "message": "Hello, world!"} response = client.post("/v1/api/session/message", json=message_data) assert response.status_code == 403 @@ -123,10 +133,10 @@ def test_post_agency_message_processing_failure(client, mock_get_agency, mock_fi mock_get_agency.return_value.get_completion.side_effect = Exception("Test exception") # Sending a message - message_data = {"agency_id": "test_agency", "thread_id": "test_thread", "message": "Hello, world!"} + message_data = {"agency_id": "test_agency", "session_id": "test_session_id", "message": "Hello, world!"} response = client.post("/v1/api/session/message", json=message_data) assert response.status_code == 500 assert response.json()["detail"] == "Something went wrong" - mock_get_agency.assert_called_once_with("test_agency", "test_thread") + mock_get_agency.assert_called_once_with("test_agency", "test_session_id") From 50e8fdc8f4a27dcf276015719ea4063660f1f3e4 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 01:35:35 +0000 Subject: [PATCH 03/25] Add GET /message endpoint --- .../components/views/playground/chatbox.tsx | 4 +- .../src/components/views/playground/ra.tsx | 2 +- nalgonda/routers/v1/api/__init__.py | 8 +- nalgonda/routers/v1/api/message.py | 76 +++++++++++++++++++ nalgonda/routers/v1/api/session.py | 38 +--------- nalgonda/services/agency_manager.py | 2 +- .../v1/api/test_session_endpoints.py | 8 +- tests/unit/services/test_agency_manager.py | 6 +- 8 files changed, 93 insertions(+), 51 deletions(-) create mode 100644 nalgonda/routers/v1/api/message.py diff --git a/frontend/src/components/views/playground/chatbox.tsx b/frontend/src/components/views/playground/chatbox.tsx index 39f2db54..0f5c7296 100644 --- a/frontend/src/components/views/playground/chatbox.tsx +++ b/frontend/src/components/views/playground/chatbox.tsx @@ -34,7 +34,7 @@ const ChatBox = ({ const { user } = React.useContext(appContext); const serverUrl = getServerUrl(); - const deleteMsgUrl = `${serverUrl}/messages/delete`; + const deleteMsgUrl = `${serverUrl}/message/delete`; const [loading, setLoading] = React.useState(false); const [text, setText] = React.useState(""); @@ -287,7 +287,7 @@ const ChatBox = ({ session_id: session?.id || "", }; - const textUrl = `${serverUrl}/agency/message`; + const textUrl = `${serverUrl}/message`; const postData = { method: "POST", headers: { diff --git a/frontend/src/components/views/playground/ra.tsx b/frontend/src/components/views/playground/ra.tsx index c4a6719a..e52790f5 100644 --- a/frontend/src/components/views/playground/ra.tsx +++ b/frontend/src/components/views/playground/ra.tsx @@ -35,7 +35,7 @@ const RAView = () => { const { user } = React.useContext(appContext); const serverUrl = getServerUrl(); - const fetchMessagesUrl = `${serverUrl}/messages?session_id=${session?.id}`; + const fetchMessagesUrl = `${serverUrl}/message?session_id=${session?.id}`; const workflowConfig = useConfigStore((state) => state.workflowConfig); const fetchMessages = () => { diff --git a/nalgonda/routers/v1/api/__init__.py b/nalgonda/routers/v1/api/__init__.py index 7d512839..d4254869 100644 --- a/nalgonda/routers/v1/api/__init__.py +++ b/nalgonda/routers/v1/api/__init__.py @@ -5,6 +5,7 @@ from .agency import agency_router from .agent import agent_router from .auth import auth_router +from .message import message_router from .session import session_router from .tool import tool_router @@ -13,8 +14,9 @@ responses={404: {"description": "Not found"}}, ) +api_router.include_router(auth_router) +api_router.include_router(tool_router) +api_router.include_router(agent_router) api_router.include_router(agency_router) api_router.include_router(session_router) -api_router.include_router(agent_router) -api_router.include_router(tool_router) -api_router.include_router(auth_router) +api_router.include_router(message_router) diff --git a/nalgonda/routers/v1/api/message.py b/nalgonda/routers/v1/api/message.py new file mode 100644 index 00000000..11e7bd12 --- /dev/null +++ b/nalgonda/routers/v1/api/message.py @@ -0,0 +1,76 @@ +import asyncio +import logging +from typing import Annotated + +from agency_swarm import get_openai_client +from fastapi import APIRouter, Depends, HTTPException +from starlette.status import HTTP_403_FORBIDDEN, HTTP_404_NOT_FOUND + +from nalgonda.dependencies.auth import get_current_active_user +from nalgonda.dependencies.dependencies import get_agency_manager +from nalgonda.models.auth import UserInDB +from nalgonda.models.request_models import SessionMessagePostRequest +from nalgonda.repositories.agency_config_firestore_storage import AgencyConfigFirestoreStorage +from nalgonda.repositories.session_firestore_storage import SessionConfigFirestoreStorage +from nalgonda.services.agency_manager import AgencyManager + +logger = logging.getLogger(__name__) +message_router = APIRouter( + responses={404: {"description": "Not found"}}, + tags=["message"], +) + + +@message_router.get("/message/list") +async def get_message_list( + current_user: Annotated[UserInDB, Depends(get_current_active_user)], + session_id: str, + session_storage: SessionConfigFirestoreStorage = Depends(SessionConfigFirestoreStorage), +): + """Return a list of last 20 messages for the given session.""" + # check if the current_user has permissions to send a message to the agency + session_config = session_storage.load_by_session_id(session_id) + if not session_config: + raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Session not found") + if session_config.owner_id != current_user.id: + raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Forbidden") + + # use OpenAI's Assistants API to get the messages by thread_id=session_id + client = get_openai_client() + messages = client.beta.threads.messages.list(thread_id=session_id) + return messages + + +@message_router.post("/message") +async def post_message( + current_user: Annotated[UserInDB, Depends(get_current_active_user)], + request: SessionMessagePostRequest, + agency_manager: AgencyManager = Depends(get_agency_manager), + storage: AgencyConfigFirestoreStorage = Depends(AgencyConfigFirestoreStorage), +) -> dict: + """Send a message to the User Proxy of the given agency.""" + # check if the current_user has permissions to send a message to the agency + agency_config = storage.load_by_agency_id(request.agency_id) + if not agency_config: + raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Agency not found") + if agency_config.owner_id != current_user.id: + raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Forbidden") + + user_message = request.message + agency_id = request.agency_id + session_id = request.session_id + + logger.info(f"Received message: {user_message}, agency_id: {agency_id}, session_id: {session_id}") + + agency = await agency_manager.get_agency(agency_id, session_id) + if not agency: + raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Agency not found") + + try: + response = await asyncio.to_thread( + agency.get_completion, message=user_message, yield_messages=False, message_files=None + ) + return {"response": response} + except Exception as e: + logger.exception(e) + raise HTTPException(status_code=500, detail="Something went wrong") from e diff --git a/nalgonda/routers/v1/api/session.py b/nalgonda/routers/v1/api/session.py index 01f37f61..c7ac1406 100644 --- a/nalgonda/routers/v1/api/session.py +++ b/nalgonda/routers/v1/api/session.py @@ -1,4 +1,3 @@ -import asyncio import logging from typing import Annotated @@ -8,7 +7,7 @@ from nalgonda.dependencies.auth import get_current_active_user from nalgonda.dependencies.dependencies import get_agency_manager, get_session_manager from nalgonda.models.auth import UserInDB -from nalgonda.models.request_models import SessionMessagePostRequest, SessionPostRequest +from nalgonda.models.request_models import SessionPostRequest from nalgonda.repositories.agency_config_firestore_storage import AgencyConfigFirestoreStorage from nalgonda.repositories.session_firestore_storage import SessionConfigFirestoreStorage from nalgonda.services.agency_manager import AgencyManager @@ -58,38 +57,3 @@ async def create_session( await agency_manager.cache_agency(agency, agency_id, session_id) return {"session_id": session_id} - - -@session_router.post("/session/message") -async def post_agency_message( - current_user: Annotated[UserInDB, Depends(get_current_active_user)], - request: SessionMessagePostRequest, - agency_manager: AgencyManager = Depends(get_agency_manager), - storage: AgencyConfigFirestoreStorage = Depends(AgencyConfigFirestoreStorage), -) -> dict: - """Send a message to the User Proxy of the given agency.""" - # check if the current_user has permissions to send a message to the agency - agency_config = storage.load_by_agency_id(request.agency_id) - if not agency_config: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Agency not found") - if agency_config.owner_id != current_user.id: - raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Forbidden") - - user_message = request.message - agency_id = request.agency_id - session_id = request.session_id - - logger.info(f"Received message: {user_message}, agency_id: {agency_id}, session_id: {session_id}") - - agency = await agency_manager.get_agency(agency_id, session_id) - if not agency: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Agency not found") - - try: - response = await asyncio.to_thread( - agency.get_completion, message=user_message, yield_messages=False, message_files=None - ) - return {"response": response} - except Exception as e: - logger.exception(e) - raise HTTPException(status_code=500, detail="Something went wrong") from e diff --git a/nalgonda/services/agency_manager.py b/nalgonda/services/agency_manager.py index 91eb18ac..680d1242 100644 --- a/nalgonda/services/agency_manager.py +++ b/nalgonda/services/agency_manager.py @@ -89,7 +89,7 @@ def construct_agency(agency_config: AgencyConfig, agents: dict[str, Agent]) -> A new_agency_chart = [[agents[name] for name in layer] for layer in agency_config.agency_chart] agency_chart.extend(new_agency_chart) - return Agency(agency_chart, shared_instructions=agency_config.shared_instructions) + return Agency(agency_chart, shared_instructions=agency_config.shared_instructions, async_mode="threading") async def cache_agency(self, agency: Agency, agency_id: str, session_id: str | None) -> None: """Cache the agency.""" diff --git a/tests/functional/v1/api/test_session_endpoints.py b/tests/functional/v1/api/test_session_endpoints.py index cd0f779b..df7cf4b6 100644 --- a/tests/functional/v1/api/test_session_endpoints.py +++ b/tests/functional/v1/api/test_session_endpoints.py @@ -89,7 +89,7 @@ def test_post_agency_message_success(client, mock_get_agency, mock_firestore_cli # Sending a message message_data = {"agency_id": "test_agency_id", "session_id": "test_session_id", "message": "Hello, world!"} - response = client.post("/v1/api/session/message", json=message_data) + response = client.post("/v1/api/message", json=message_data) assert response.status_code == 200 # We will check for the actual message we set up to be sent @@ -102,7 +102,7 @@ def test_post_agency_message_success(client, mock_get_agency, mock_firestore_cli def test_post_agency_message_agency_config_not_found(client, mock_get_agency): # Sending a message message_data = {"agency_id": "test_agency", "session_id": "test_session_id", "message": "Hello, world!"} - response = client.post("/v1/api/session/message", json=message_data) + response = client.post("/v1/api/message", json=message_data) assert response.status_code == 404 assert response.json()["detail"] == "Agency not found" @@ -117,7 +117,7 @@ def test_post_agency_message_unauthorized(client, mock_get_agency, mock_firestor # Sending a message message_data = {"agency_id": "test_agency", "session_id": "test_session_id", "message": "Hello, world!"} - response = client.post("/v1/api/session/message", json=message_data) + response = client.post("/v1/api/message", json=message_data) assert response.status_code == 403 assert response.json()["detail"] == "Forbidden" @@ -134,7 +134,7 @@ def test_post_agency_message_processing_failure(client, mock_get_agency, mock_fi # Sending a message message_data = {"agency_id": "test_agency", "session_id": "test_session_id", "message": "Hello, world!"} - response = client.post("/v1/api/session/message", json=message_data) + response = client.post("/v1/api/message", json=message_data) assert response.status_code == 500 assert response.json()["detail"] == "Something went wrong" diff --git a/tests/unit/services/test_agency_manager.py b/tests/unit/services/test_agency_manager.py index 4ec45d73..c206406f 100644 --- a/tests/unit/services/test_agency_manager.py +++ b/tests/unit/services/test_agency_manager.py @@ -20,7 +20,7 @@ def agency_manager(): @pytest.mark.asyncio async def test_get_agency_from_cache(agency_manager): with patch.object(agency_manager.cache_manager, "get", new_callable=AsyncMock) as mock_get: - mock_get.return_value = Agency([], "manifesto") + mock_get.return_value = Agency([], "manifesto", async_mode="threading") agency = await agency_manager.get_agency("test_agency_id") assert agency is not None @@ -33,7 +33,7 @@ async def test_get_agency_repopulate_cache(agency_manager): agency_manager, "repopulate_cache_and_update_assistants", new_callable=AsyncMock ) as mock_repopulate: mock_get.return_value = None - mock_repopulate.return_value = Agency([], "manifesto") + mock_repopulate.return_value = Agency([], "manifesto", async_mode="threading") agency = await agency_manager.get_agency("test_agency_id") assert agency is not None @@ -96,7 +96,7 @@ async def test_repopulate_cache_success(agency_manager, mock_firestore_client): ) as mock_cache_agency: mock_firestore_client.setup_mock_data("agency_configs", agency_config.agency_id, agency_config) mock_load_agents.return_value = {"agent1": agent} - mock_construct_agency.return_value = Agency([], "manifesto") + mock_construct_agency.return_value = Agency([], "manifesto", async_mode="threading") result = await agency_manager.repopulate_cache_and_update_assistants("test_agency_id", None) assert result is not None From d925a1a502a0f12a917db5f19833ef487683bfa4 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 01:42:52 +0000 Subject: [PATCH 04/25] Revert async mode --- nalgonda/services/agency_manager.py | 2 +- tests/unit/services/test_agency_manager.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nalgonda/services/agency_manager.py b/nalgonda/services/agency_manager.py index 680d1242..91eb18ac 100644 --- a/nalgonda/services/agency_manager.py +++ b/nalgonda/services/agency_manager.py @@ -89,7 +89,7 @@ def construct_agency(agency_config: AgencyConfig, agents: dict[str, Agent]) -> A new_agency_chart = [[agents[name] for name in layer] for layer in agency_config.agency_chart] agency_chart.extend(new_agency_chart) - return Agency(agency_chart, shared_instructions=agency_config.shared_instructions, async_mode="threading") + return Agency(agency_chart, shared_instructions=agency_config.shared_instructions) async def cache_agency(self, agency: Agency, agency_id: str, session_id: str | None) -> None: """Cache the agency.""" diff --git a/tests/unit/services/test_agency_manager.py b/tests/unit/services/test_agency_manager.py index c206406f..4ec45d73 100644 --- a/tests/unit/services/test_agency_manager.py +++ b/tests/unit/services/test_agency_manager.py @@ -20,7 +20,7 @@ def agency_manager(): @pytest.mark.asyncio async def test_get_agency_from_cache(agency_manager): with patch.object(agency_manager.cache_manager, "get", new_callable=AsyncMock) as mock_get: - mock_get.return_value = Agency([], "manifesto", async_mode="threading") + mock_get.return_value = Agency([], "manifesto") agency = await agency_manager.get_agency("test_agency_id") assert agency is not None @@ -33,7 +33,7 @@ async def test_get_agency_repopulate_cache(agency_manager): agency_manager, "repopulate_cache_and_update_assistants", new_callable=AsyncMock ) as mock_repopulate: mock_get.return_value = None - mock_repopulate.return_value = Agency([], "manifesto", async_mode="threading") + mock_repopulate.return_value = Agency([], "manifesto") agency = await agency_manager.get_agency("test_agency_id") assert agency is not None @@ -96,7 +96,7 @@ async def test_repopulate_cache_success(agency_manager, mock_firestore_client): ) as mock_cache_agency: mock_firestore_client.setup_mock_data("agency_configs", agency_config.agency_id, agency_config) mock_load_agents.return_value = {"agent1": agent} - mock_construct_agency.return_value = Agency([], "manifesto", async_mode="threading") + mock_construct_agency.return_value = Agency([], "manifesto") result = await agency_manager.repopulate_cache_and_update_assistants("test_agency_id", None) assert result is not None From 8cd119998f79a5878c448df6bc6faa41895fdaec Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 01:51:49 +0000 Subject: [PATCH 05/25] Adjust GET /message endpoint --- nalgonda/routers/v1/api/message.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nalgonda/routers/v1/api/message.py b/nalgonda/routers/v1/api/message.py index 11e7bd12..331999a6 100644 --- a/nalgonda/routers/v1/api/message.py +++ b/nalgonda/routers/v1/api/message.py @@ -25,6 +25,7 @@ async def get_message_list( current_user: Annotated[UserInDB, Depends(get_current_active_user)], session_id: str, + before: str | None = None, session_storage: SessionConfigFirestoreStorage = Depends(SessionConfigFirestoreStorage), ): """Return a list of last 20 messages for the given session.""" @@ -37,7 +38,7 @@ async def get_message_list( # use OpenAI's Assistants API to get the messages by thread_id=session_id client = get_openai_client() - messages = client.beta.threads.messages.list(thread_id=session_id) + messages = client.beta.threads.messages.list(thread_id=session_id, limit=20, before=before) return messages From d8ea538c10b3fc7a8bcd68c41fb07f549452305a Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 02:21:00 +0000 Subject: [PATCH 06/25] Add tests --- tests/test_utils/mock_firestore_client.py | 5 +- .../test_session_firestore_storage.py | 50 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 tests/unit/repositories/test_session_firestore_storage.py diff --git a/tests/test_utils/mock_firestore_client.py b/tests/test_utils/mock_firestore_client.py index 756f0841..c556ecf3 100644 --- a/tests/test_utils/mock_firestore_client.py +++ b/tests/test_utils/mock_firestore_client.py @@ -63,10 +63,11 @@ def where(self, filter: FieldFilter): def stream(self): matching_docs = [] - for doc_id, doc in self._collections.get(self._current_collection, {}).items(): + collection = self._collections.get(self._current_collection, {}) + for doc_id, doc in collection.items(): if doc.get(self._where_field) == self._where_value: matching_docs.append(MockDocumentSnapshot(doc_id, doc)) - return matching_docs + return iter(matching_docs) def add(self, data) -> list: # This method should add a new document to the collection diff --git a/tests/unit/repositories/test_session_firestore_storage.py b/tests/unit/repositories/test_session_firestore_storage.py new file mode 100644 index 00000000..5f2a2b75 --- /dev/null +++ b/tests/unit/repositories/test_session_firestore_storage.py @@ -0,0 +1,50 @@ +import pytest + +from nalgonda.models.session_config import SessionConfig +from nalgonda.repositories.session_firestore_storage import SessionConfigFirestoreStorage +from tests.test_utils import TEST_USER_ID + + +@pytest.fixture +def session_data(): + # Using an integer for the created_at field to align with the model definition + return { + "session_id": "session1", + "owner_id": TEST_USER_ID, + "agency_id": "agency1", + "created_at": 161803398874, # Example integer timestamp + } + + +@pytest.fixture +def storage(): + return SessionConfigFirestoreStorage() + + +def test_load_session_config_by_session_id(mock_firestore_client, session_data, storage): + mock_firestore_client.setup_mock_data("session_configs", session_data["session_id"], session_data) + + loaded_session_config = storage.load_by_session_id(session_data["session_id"]) + + assert loaded_session_config is not None + assert loaded_session_config.session_id == session_data["session_id"] + assert loaded_session_config.model_dump() == session_data + + +def test_load_session_config_by_owner_id(mock_firestore_client, session_data, storage): + # Setup mock data + mock_firestore_client.setup_mock_data("session_configs", session_data["session_id"], session_data) + + # Simulate loading session configs by owner ID, reflecting the correct usage of `where()` before `stream()` + loaded_sessions = storage.load_by_owner_id(session_data["owner_id"]) + + assert loaded_sessions is not None + # Verify that all loaded sessions have the correct owner ID + assert all(session.owner_id == session_data["owner_id"] for session in loaded_sessions) + + +def test_save_session_config(mock_firestore_client, session_data, storage): + session_to_save = SessionConfig(**session_data) + storage.save(session_to_save) + + assert mock_firestore_client.to_dict()["session_id"] == session_data["session_id"] From 3648cf09299bc87b69ebe7b5db3b66558b64ae18 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 14:21:17 +0000 Subject: [PATCH 07/25] Add manual tool execution endpoint --- nalgonda/models/request_models.py | 5 +++ nalgonda/routers/v1/api/tool.py | 27 +++++++++++++- nalgonda/services/tool_service.py | 61 +++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 1 deletion(-) diff --git a/nalgonda/models/request_models.py b/nalgonda/models/request_models.py index 81e7e838..20bed3a4 100644 --- a/nalgonda/models/request_models.py +++ b/nalgonda/models/request_models.py @@ -9,3 +9,8 @@ class SessionMessagePostRequest(BaseModel): agency_id: str = Field(..., description="The unique identifier for the agency.") session_id: str = Field(..., description="The identifier for the conversational thread.") message: str = Field(..., description="The message to be sent to the agency.") + + +class ToolExecutePostRequest(BaseModel): + tool_id: str = Field(..., description="The unique identifier for the tool.") + user_prompt: str = Field(..., description="The user prompt to extract parameters from.") diff --git a/nalgonda/routers/v1/api/tool.py b/nalgonda/routers/v1/api/tool.py index 19ae9b7e..4902335b 100644 --- a/nalgonda/routers/v1/api/tool.py +++ b/nalgonda/routers/v1/api/tool.py @@ -5,9 +5,10 @@ from nalgonda.dependencies.auth import get_current_active_user, get_current_superuser from nalgonda.models.auth import UserInDB +from nalgonda.models.request_models import ToolExecutePostRequest from nalgonda.models.tool_config import ToolConfig from nalgonda.repositories.tool_config_firestore_storage import ToolConfigFirestoreStorage -from nalgonda.services.tool_service import generate_tool_description +from nalgonda.services.tool_service import ToolService, generate_tool_description tool_router = APIRouter(tags=["tool"]) @@ -88,3 +89,27 @@ async def approve_tool( storage.save(tool_config) return {"message": "Tool configuration approved"} + + +@tool_router.post("/tool/execute") +async def execute_tool( + current_user: Annotated[UserInDB, Depends(get_current_active_user)], + request: ToolExecutePostRequest, + storage: ToolConfigFirestoreStorage = Depends(ToolConfigFirestoreStorage), + tool_service: ToolService = Depends(ToolService), +): + tool_config = storage.load_by_tool_id(request.tool_id) + if not tool_config: + raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail="Tool not found") + + # check if the current_user has permissions to execute the tool + if tool_config.owner_id and tool_config.owner_id != current_user.id: + raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Forbidden") + + # check if the tool is approved + if not tool_config.approved: + raise HTTPException(status_code=HTTP_403_FORBIDDEN, detail="Tool not approved") + + tool_output = tool_service.execute_tool(tool_config.name, request.user_prompt) + + return {"tool_output": tool_output} diff --git a/nalgonda/services/tool_service.py b/nalgonda/services/tool_service.py index fb69144b..f0dbea4a 100644 --- a/nalgonda/services/tool_service.py +++ b/nalgonda/services/tool_service.py @@ -1,3 +1,9 @@ +import json +import logging + +from agency_swarm import BaseTool + +from nalgonda import custom_tools from nalgonda.settings import settings from nalgonda.utils import get_chat_completion @@ -7,6 +13,8 @@ """ USER_PROMPT = "In one succinct sentence, describe the functionality of the tool provided below:\n" +logger = logging.getLogger(__name__) + def generate_tool_description(code: str): summary = get_chat_completion( @@ -16,3 +24,56 @@ def generate_tool_description(code: str): model=settings.gpt_cheap_model, ) return summary + + +class ToolService: + SYSTEM_MESSAGE = """\ +You are an assistant that responds with JSON only. You are presented with a user prompt and a function specification, \ +and you MUST return the function call parameters in JSON format. +For example, if the function has parameters file_name and file_size, \ +and the user prompt is ```file name is test.txt, and the size is 1MB```, \ +then the function call parameters are {\"file_name\": \"test.txt\", \"file_size\": \"1MB\"} +The function call parameters must be returned in JSON format.\ +""" + USER_PROMPT_PREFIX = "Return the function call parameters in JSON format based on the following user prompt: " + + def execute_tool(self, tool_name: str, user_prompt: str): + """ + Import the tool from nalgonda.custom_tools package, initialize it (using GPT to fill in kwargs), and run it + """ + tool_class = self._get_tool_class(tool_name) + tool_args = self._get_tool_arguments(json.dumps(tool_class.openai_schema), user_prompt) + return self._execute_tool(tool_class, tool_args) + + def _get_tool_class(self, tool_name: str) -> BaseTool: + """Get a tool function by name from nalgonda.custom_tools""" + try: + return getattr(custom_tools, tool_name) + except AttributeError as e: + logger.exception(f"Tool {tool_name} not found") + raise Exception(f"Tool {tool_name} not found") from e + + def _get_tool_arguments(self, function_spec: str, user_prompt: str) -> str: + user_prompt = ( + f"{self.USER_PROMPT_PREFIX}\n```\n{user_prompt}\n```. \n" + f"The function specification:\n```{function_spec}```" + ) + args_str = get_chat_completion( + system_message=self.SYSTEM_MESSAGE, user_prompt=user_prompt, temperature=0.0, model=settings.gpt_model + ) + return args_str.strip("`json\n ").replace("\n", "") + + def _execute_tool(self, tool_class: BaseTool, args: str): + if not tool_class: + return f"Error: Tool {tool_class.__name__} not found" + + try: + # init tool + func = tool_class(**eval(args)) + # get outputs from the tool + return func.run() + except Exception as e: + error_message = f"Error: {e}" + if "For further information visit" in error_message: + error_message = error_message.split("For further information visit")[0] + return error_message From 0841eee85450b1ce1eac02fee04581265a1ce5c6 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 21:50:01 +0000 Subject: [PATCH 08/25] Update Python deps --- poetry.lock | 746 ++++++++++++++++++----------------------------- pyproject.toml | 6 +- requirements.txt | 191 ++++++------ 3 files changed, 384 insertions(+), 559 deletions(-) diff --git a/poetry.lock b/poetry.lock index 2888c017..ad45f062 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2,7 +2,7 @@ [[package]] name = "agency-swarm" -version = "0.1.0" +version = "0.1.1" description = "An open source agent orchestration framework built on top of the latest OpenAI Assistants API." optional = false python-versions = ">=3.7" @@ -22,102 +22,91 @@ termcolor = "2.3.0" type = "git" url = "https://github.com/VRSEN/agency-swarm.git" reference = "main" -resolved_reference = "b2f5d4ee73aa373c7cb2ecd6e1291e15753a7c10" - -[[package]] -name = "aiofiles" -version = "23.2.1" -description = "File support for asyncio." -optional = false -python-versions = ">=3.7" -files = [ - {file = "aiofiles-23.2.1-py3-none-any.whl", hash = "sha256:19297512c647d4b27a2cf7c34caa7e405c0d60b5560618a29a9fe027b18b0107"}, - {file = "aiofiles-23.2.1.tar.gz", hash = "sha256:84ec2218d8419404abcb9f0c02df3f34c6e0a68ed41072acfb1cef5cbc29051a"}, -] +resolved_reference = "2c598e2e7fa3e531244319627d4b3c270bfcb3f2" [[package]] name = "aiohttp" -version = "3.9.1" +version = "3.9.3" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1f80197f8b0b846a8d5cf7b7ec6084493950d0882cc5537fb7b96a69e3c8590"}, - {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72444d17777865734aa1a4d167794c34b63e5883abb90356a0364a28904e6c0"}, - {file = "aiohttp-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b05d5cbe9dafcdc733262c3a99ccf63d2f7ce02543620d2bd8db4d4f7a22f83"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c4fa235d534b3547184831c624c0b7c1e262cd1de847d95085ec94c16fddcd5"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:289ba9ae8e88d0ba16062ecf02dd730b34186ea3b1e7489046fc338bdc3361c4"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bff7e2811814fa2271be95ab6e84c9436d027a0e59665de60edf44e529a42c1f"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81b77f868814346662c96ab36b875d7814ebf82340d3284a31681085c051320f"}, - {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b9c7426923bb7bd66d409da46c41e3fb40f5caf679da624439b9eba92043fa6"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8d44e7bf06b0c0a70a20f9100af9fcfd7f6d9d3913e37754c12d424179b4e48f"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22698f01ff5653fe66d16ffb7658f582a0ac084d7da1323e39fd9eab326a1f26"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ca7ca5abfbfe8d39e653870fbe8d7710be7a857f8a8386fc9de1aae2e02ce7e4"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8d7f98fde213f74561be1d6d3fa353656197f75d4edfbb3d94c9eb9b0fc47f5d"}, - {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5216b6082c624b55cfe79af5d538e499cd5f5b976820eac31951fb4325974501"}, - {file = "aiohttp-3.9.1-cp310-cp310-win32.whl", hash = "sha256:0e7ba7ff228c0d9a2cd66194e90f2bca6e0abca810b786901a569c0de082f489"}, - {file = "aiohttp-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c7e939f1ae428a86e4abbb9a7c4732bf4706048818dfd979e5e2839ce0159f23"}, - {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df9cf74b9bc03d586fc53ba470828d7b77ce51b0582d1d0b5b2fb673c0baa32d"}, - {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecca113f19d5e74048c001934045a2b9368d77b0b17691d905af18bd1c21275e"}, - {file = "aiohttp-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8cef8710fb849d97c533f259103f09bac167a008d7131d7b2b0e3a33269185c0"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea94403a21eb94c93386d559bce297381609153e418a3ffc7d6bf772f59cc35"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91c742ca59045dce7ba76cab6e223e41d2c70d79e82c284a96411f8645e2afff"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c93b7c2e52061f0925c3382d5cb8980e40f91c989563d3d32ca280069fd6a87"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee2527134f95e106cc1653e9ac78846f3a2ec1004cf20ef4e02038035a74544d"}, - {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11ff168d752cb41e8492817e10fb4f85828f6a0142b9726a30c27c35a1835f01"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8c3a67eb87394386847d188996920f33b01b32155f0a94f36ca0e0c635bf3e3"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c7b5d5d64e2a14e35a9240b33b89389e0035e6de8dbb7ffa50d10d8b65c57449"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:69985d50a2b6f709412d944ffb2e97d0be154ea90600b7a921f95a87d6f108a2"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c9110c06eaaac7e1f5562caf481f18ccf8f6fdf4c3323feab28a93d34cc646bd"}, - {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737e69d193dac7296365a6dcb73bbbf53bb760ab25a3727716bbd42022e8d7a"}, - {file = "aiohttp-3.9.1-cp311-cp311-win32.whl", hash = "sha256:4ee8caa925aebc1e64e98432d78ea8de67b2272252b0a931d2ac3bd876ad5544"}, - {file = "aiohttp-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:a34086c5cc285be878622e0a6ab897a986a6e8bf5b67ecb377015f06ed316587"}, - {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f800164276eec54e0af5c99feb9494c295118fc10a11b997bbb1348ba1a52065"}, - {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:500f1c59906cd142d452074f3811614be04819a38ae2b3239a48b82649c08821"}, - {file = "aiohttp-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0b0a6a36ed7e164c6df1e18ee47afbd1990ce47cb428739d6c99aaabfaf1b3af"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69da0f3ed3496808e8cbc5123a866c41c12c15baaaead96d256477edf168eb57"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176df045597e674fa950bf5ae536be85699e04cea68fa3a616cf75e413737eb5"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b796b44111f0cab6bbf66214186e44734b5baab949cb5fb56154142a92989aeb"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f27fdaadce22f2ef950fc10dcdf8048407c3b42b73779e48a4e76b3c35bca26c"}, - {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb6532b9814ea7c5a6a3299747c49de30e84472fa72821b07f5a9818bce0f66"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:54631fb69a6e44b2ba522f7c22a6fb2667a02fd97d636048478db2fd8c4e98fe"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4b4c452d0190c5a820d3f5c0f3cd8a28ace48c54053e24da9d6041bf81113183"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:cae4c0c2ca800c793cae07ef3d40794625471040a87e1ba392039639ad61ab5b"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:565760d6812b8d78d416c3c7cfdf5362fbe0d0d25b82fed75d0d29e18d7fc30f"}, - {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54311eb54f3a0c45efb9ed0d0a8f43d1bc6060d773f6973efd90037a51cd0a3f"}, - {file = "aiohttp-3.9.1-cp312-cp312-win32.whl", hash = "sha256:85c3e3c9cb1d480e0b9a64c658cd66b3cfb8e721636ab8b0e746e2d79a7a9eed"}, - {file = "aiohttp-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:11cb254e397a82efb1805d12561e80124928e04e9c4483587ce7390b3866d213"}, - {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8a22a34bc594d9d24621091d1b91511001a7eea91d6652ea495ce06e27381f70"}, - {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:598db66eaf2e04aa0c8900a63b0101fdc5e6b8a7ddd805c56d86efb54eb66672"}, - {file = "aiohttp-3.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c9376e2b09895c8ca8b95362283365eb5c03bdc8428ade80a864160605715f1"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41473de252e1797c2d2293804e389a6d6986ef37cbb4a25208de537ae32141dd"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c5857612c9813796960c00767645cb5da815af16dafb32d70c72a8390bbf690"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffcd828e37dc219a72c9012ec44ad2e7e3066bec6ff3aaa19e7d435dbf4032ca"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:219a16763dc0294842188ac8a12262b5671817042b35d45e44fd0a697d8c8361"}, - {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f694dc8a6a3112059258a725a4ebe9acac5fe62f11c77ac4dcf896edfa78ca28"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bcc0ea8d5b74a41b621ad4a13d96c36079c81628ccc0b30cfb1603e3dfa3a014"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:90ec72d231169b4b8d6085be13023ece8fa9b1bb495e4398d847e25218e0f431"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cf2a0ac0615842b849f40c4d7f304986a242f1e68286dbf3bd7a835e4f83acfd"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0e49b08eafa4f5707ecfb321ab9592717a319e37938e301d462f79b4e860c32a"}, - {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c59e0076ea31c08553e868cec02d22191c086f00b44610f8ab7363a11a5d9d8"}, - {file = "aiohttp-3.9.1-cp38-cp38-win32.whl", hash = "sha256:4831df72b053b1eed31eb00a2e1aff6896fb4485301d4ccb208cac264b648db4"}, - {file = "aiohttp-3.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:3135713c5562731ee18f58d3ad1bf41e1d8883eb68b363f2ffde5b2ea4b84cc7"}, - {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cfeadf42840c1e870dc2042a232a8748e75a36b52d78968cda6736de55582766"}, - {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:70907533db712f7aa791effb38efa96f044ce3d4e850e2d7691abd759f4f0ae0"}, - {file = "aiohttp-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdefe289681507187e375a5064c7599f52c40343a8701761c802c1853a504558"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7481f581251bb5558ba9f635db70908819caa221fc79ee52a7f58392778c636"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49f0c1b3c2842556e5de35f122fc0f0b721334ceb6e78c3719693364d4af8499"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d406b01a9f5a7e232d1b0d161b40c05275ffbcbd772dc18c1d5a570961a1ca4"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d8e4450e7fe24d86e86b23cc209e0023177b6d59502e33807b732d2deb6975f"}, - {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c0266cd6f005e99f3f51e583012de2778e65af6b73860038b968a0a8888487a"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab221850108a4a063c5b8a70f00dd7a1975e5a1713f87f4ab26a46e5feac5a0e"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c88a15f272a0ad3d7773cf3a37cc7b7d077cbfc8e331675cf1346e849d97a4e5"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:237533179d9747080bcaad4d02083ce295c0d2eab3e9e8ce103411a4312991a0"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:02ab6006ec3c3463b528374c4cdce86434e7b89ad355e7bf29e2f16b46c7dd6f"}, - {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04fa38875e53eb7e354ece1607b1d2fdee2d175ea4e4d745f6ec9f751fe20c7c"}, - {file = "aiohttp-3.9.1-cp39-cp39-win32.whl", hash = "sha256:82eefaf1a996060602f3cc1112d93ba8b201dbf5d8fd9611227de2003dddb3b7"}, - {file = "aiohttp-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:9b05d33ff8e6b269e30a7957bd3244ffbce2a7a35a81b81c382629b80af1a8bf"}, - {file = "aiohttp-3.9.1.tar.gz", hash = "sha256:8fc49a87ac269d4529da45871e2ffb6874e87779c3d0e2ccd813c0899221239d"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:939677b61f9d72a4fa2a042a5eee2a99a24001a67c13da113b2e30396567db54"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f5cd333fcf7590a18334c90f8c9147c837a6ec8a178e88d90a9b96ea03194cc"}, + {file = "aiohttp-3.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82e6aa28dd46374f72093eda8bcd142f7771ee1eb9d1e223ff0fa7177a96b4a5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f56455b0c2c7cc3b0c584815264461d07b177f903a04481dfc33e08a89f0c26b"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bca77a198bb6e69795ef2f09a5f4c12758487f83f33d63acde5f0d4919815768"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e083c285857b78ee21a96ba1eb1b5339733c3563f72980728ca2b08b53826ca5"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab40e6251c3873d86ea9b30a1ac6d7478c09277b32e14745d0d3c6e76e3c7e29"}, + {file = "aiohttp-3.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:df822ee7feaaeffb99c1a9e5e608800bd8eda6e5f18f5cfb0dc7eeb2eaa6bbec"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:acef0899fea7492145d2bbaaaec7b345c87753168589cc7faf0afec9afe9b747"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cd73265a9e5ea618014802ab01babf1940cecb90c9762d8b9e7d2cc1e1969ec6"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:a78ed8a53a1221393d9637c01870248a6f4ea5b214a59a92a36f18151739452c"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:6b0e029353361f1746bac2e4cc19b32f972ec03f0f943b390c4ab3371840aabf"}, + {file = "aiohttp-3.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7cf5c9458e1e90e3c390c2639f1017a0379a99a94fdfad3a1fd966a2874bba52"}, + {file = "aiohttp-3.9.3-cp310-cp310-win32.whl", hash = "sha256:3e59c23c52765951b69ec45ddbbc9403a8761ee6f57253250c6e1536cacc758b"}, + {file = "aiohttp-3.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:055ce4f74b82551678291473f66dc9fb9048a50d8324278751926ff0ae7715e5"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6b88f9386ff1ad91ace19d2a1c0225896e28815ee09fc6a8932fded8cda97c3d"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c46956ed82961e31557b6857a5ca153c67e5476972e5f7190015018760938da2"}, + {file = "aiohttp-3.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:07b837ef0d2f252f96009e9b8435ec1fef68ef8b1461933253d318748ec1acdc"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad46e6f620574b3b4801c68255492e0159d1712271cc99d8bdf35f2043ec266"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5ed3e046ea7b14938112ccd53d91c1539af3e6679b222f9469981e3dac7ba1ce"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:039df344b45ae0b34ac885ab5b53940b174530d4dd8a14ed8b0e2155b9dddccb"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7943c414d3a8d9235f5f15c22ace69787c140c80b718dcd57caaade95f7cd93b"}, + {file = "aiohttp-3.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84871a243359bb42c12728f04d181a389718710129b36b6aad0fc4655a7647d4"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5eafe2c065df5401ba06821b9a054d9cb2848867f3c59801b5d07a0be3a380ae"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:9d3c9b50f19704552f23b4eaea1fc082fdd82c63429a6506446cbd8737823da3"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:f033d80bc6283092613882dfe40419c6a6a1527e04fc69350e87a9df02bbc283"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:2c895a656dd7e061b2fd6bb77d971cc38f2afc277229ce7dd3552de8313a483e"}, + {file = "aiohttp-3.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1f5a71d25cd8106eab05f8704cd9167b6e5187bcdf8f090a66c6d88b634802b4"}, + {file = "aiohttp-3.9.3-cp311-cp311-win32.whl", hash = "sha256:50fca156d718f8ced687a373f9e140c1bb765ca16e3d6f4fe116e3df7c05b2c5"}, + {file = "aiohttp-3.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:5fe9ce6c09668063b8447f85d43b8d1c4e5d3d7e92c63173e6180b2ac5d46dd8"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:38a19bc3b686ad55804ae931012f78f7a534cce165d089a2059f658f6c91fa60"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:770d015888c2a598b377bd2f663adfd947d78c0124cfe7b959e1ef39f5b13869"}, + {file = "aiohttp-3.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ee43080e75fc92bf36219926c8e6de497f9b247301bbf88c5c7593d931426679"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52df73f14ed99cee84865b95a3d9e044f226320a87af208f068ecc33e0c35b96"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc9b311743a78043b26ffaeeb9715dc360335e5517832f5a8e339f8a43581e4d"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b955ed993491f1a5da7f92e98d5dad3c1e14dc175f74517c4e610b1f2456fb11"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:504b6981675ace64c28bf4a05a508af5cde526e36492c98916127f5a02354d53"}, + {file = "aiohttp-3.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a6fe5571784af92b6bc2fda8d1925cccdf24642d49546d3144948a6a1ed58ca5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ba39e9c8627edc56544c8628cc180d88605df3892beeb2b94c9bc857774848ca"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e5e46b578c0e9db71d04c4b506a2121c0cb371dd89af17a0586ff6769d4c58c1"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:938a9653e1e0c592053f815f7028e41a3062e902095e5a7dc84617c87267ebd5"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:c3452ea726c76e92f3b9fae4b34a151981a9ec0a4847a627c43d71a15ac32aa6"}, + {file = "aiohttp-3.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ff30218887e62209942f91ac1be902cc80cddb86bf00fbc6783b7a43b2bea26f"}, + {file = "aiohttp-3.9.3-cp312-cp312-win32.whl", hash = "sha256:38f307b41e0bea3294a9a2a87833191e4bcf89bb0365e83a8be3a58b31fb7f38"}, + {file = "aiohttp-3.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:b791a3143681a520c0a17e26ae7465f1b6f99461a28019d1a2f425236e6eedb5"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ed621426d961df79aa3b963ac7af0d40392956ffa9be022024cd16297b30c8c"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7f46acd6a194287b7e41e87957bfe2ad1ad88318d447caf5b090012f2c5bb528"}, + {file = "aiohttp-3.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:feeb18a801aacb098220e2c3eea59a512362eb408d4afd0c242044c33ad6d542"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f734e38fd8666f53da904c52a23ce517f1b07722118d750405af7e4123933511"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b40670ec7e2156d8e57f70aec34a7216407848dfe6c693ef131ddf6e76feb672"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdd215b7b7fd4a53994f238d0f46b7ba4ac4c0adb12452beee724ddd0743ae5d"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:017a21b0df49039c8f46ca0971b3a7fdc1f56741ab1240cb90ca408049766168"}, + {file = "aiohttp-3.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e99abf0bba688259a496f966211c49a514e65afa9b3073a1fcee08856e04425b"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:648056db9a9fa565d3fa851880f99f45e3f9a771dd3ff3bb0c048ea83fb28194"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8aacb477dc26797ee089721536a292a664846489c49d3ef9725f992449eda5a8"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:522a11c934ea660ff8953eda090dcd2154d367dec1ae3c540aff9f8a5c109ab4"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:5bce0dc147ca85caa5d33debc4f4d65e8e8b5c97c7f9f660f215fa74fc49a321"}, + {file = "aiohttp-3.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:4b4af9f25b49a7be47c0972139e59ec0e8285c371049df1a63b6ca81fdd216a2"}, + {file = "aiohttp-3.9.3-cp38-cp38-win32.whl", hash = "sha256:298abd678033b8571995650ccee753d9458dfa0377be4dba91e4491da3f2be63"}, + {file = "aiohttp-3.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:69361bfdca5468c0488d7017b9b1e5ce769d40b46a9f4a2eed26b78619e9396c"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:0fa43c32d1643f518491d9d3a730f85f5bbaedcbd7fbcae27435bb8b7a061b29"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:835a55b7ca49468aaaac0b217092dfdff370e6c215c9224c52f30daaa735c1c1"}, + {file = "aiohttp-3.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:06a9b2c8837d9a94fae16c6223acc14b4dfdff216ab9b7202e07a9a09541168f"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abf151955990d23f84205286938796c55ff11bbfb4ccfada8c9c83ae6b3c89a3"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59c26c95975f26e662ca78fdf543d4eeaef70e533a672b4113dd888bd2423caa"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f95511dd5d0e05fd9728bac4096319f80615aaef4acbecb35a990afebe953b0e"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:595f105710293e76b9dc09f52e0dd896bd064a79346234b521f6b968ffdd8e58"}, + {file = "aiohttp-3.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c7c8b816c2b5af5c8a436df44ca08258fc1a13b449393a91484225fcb7545533"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f1088fa100bf46e7b398ffd9904f4808a0612e1d966b4aa43baa535d1b6341eb"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f59dfe57bb1ec82ac0698ebfcdb7bcd0e99c255bd637ff613760d5f33e7c81b3"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:361a1026c9dd4aba0109e4040e2aecf9884f5cfe1b1b1bd3d09419c205e2e53d"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:363afe77cfcbe3a36353d8ea133e904b108feea505aa4792dad6585a8192c55a"}, + {file = "aiohttp-3.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e2c45c208c62e955e8256949eb225bd8b66a4c9b6865729a786f2aa79b72e9d"}, + {file = "aiohttp-3.9.3-cp39-cp39-win32.whl", hash = "sha256:f7217af2e14da0856e082e96ff637f14ae45c10a5714b63c77f26d8884cf1051"}, + {file = "aiohttp-3.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:27468897f628c627230dba07ec65dc8d0db566923c48f29e084ce382119802bc"}, + {file = "aiohttp-3.9.3.tar.gz", hash = "sha256:90842933e5d1ff760fae6caca4b2b3edba53ba8f4b71e95dacf2818a2aca06f7"}, ] [package.dependencies] @@ -157,13 +146,13 @@ files = [ [[package]] name = "anyio" -version = "3.7.1" +version = "4.2.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, - {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, + {file = "anyio-4.2.0-py3-none-any.whl", hash = "sha256:745843b39e829e108e518c489b31dc757de7d2131d53fac32bd8df268227bfee"}, + {file = "anyio-4.2.0.tar.gz", hash = "sha256:e1875bb4b4e2de1669f4bc7869b6d3f54231cdced71605e6e64c9be77e3be50f"}, ] [package.dependencies] @@ -171,9 +160,9 @@ idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] -test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] -trio = ["trio (<0.22)"] +doc = ["Sphinx (>=7)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme"] +test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (>=0.23)"] [[package]] name = "async-timeout" @@ -245,137 +234,6 @@ files = [ tests = ["pytest (>=3.2.1,!=3.3.0)"] typecheck = ["mypy"] -[[package]] -name = "brotli" -version = "1.1.0" -description = "Python bindings for the Brotli compression library" -optional = false -python-versions = "*" -files = [ - {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1140c64812cb9b06c922e77f1c26a75ec5e3f0fb2bf92cc8c58720dec276752"}, - {file = "Brotli-1.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c8fd5270e906eef71d4a8d19b7c6a43760c6abcfcc10c9101d14eb2357418de9"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ae56aca0402a0f9a3431cddda62ad71666ca9d4dc3a10a142b9dce2e3c0cda3"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:43ce1b9935bfa1ede40028054d7f48b5469cd02733a365eec8a329ffd342915d"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c4855522edb2e6ae7fdb58e07c3ba9111e7621a8956f481c68d5d979c93032e"}, - {file = "Brotli-1.1.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:38025d9f30cf4634f8309c6874ef871b841eb3c347e90b0851f63d1ded5212da"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e6a904cb26bfefc2f0a6f240bdf5233be78cd2488900a2f846f3c3ac8489ab80"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a37b8f0391212d29b3a91a799c8e4a2855e0576911cdfb2515487e30e322253d"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e84799f09591700a4154154cab9787452925578841a94321d5ee8fb9a9a328f0"}, - {file = "Brotli-1.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f66b5337fa213f1da0d9000bc8dc0cb5b896b726eefd9c6046f699b169c41b9e"}, - {file = "Brotli-1.1.0-cp310-cp310-win32.whl", hash = "sha256:be36e3d172dc816333f33520154d708a2657ea63762ec16b62ece02ab5e4daf2"}, - {file = "Brotli-1.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:0c6244521dda65ea562d5a69b9a26120769b7a9fb3db2fe9545935ed6735b128"}, - {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a3daabb76a78f829cafc365531c972016e4aa8d5b4bf60660ad8ecee19df7ccc"}, - {file = "Brotli-1.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c8146669223164fc87a7e3de9f81e9423c67a79d6b3447994dfb9c95da16e2d6"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30924eb4c57903d5a7526b08ef4a584acc22ab1ffa085faceb521521d2de32dd"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ceb64bbc6eac5a140ca649003756940f8d6a7c444a68af170b3187623b43bebf"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a469274ad18dc0e4d316eefa616d1d0c2ff9da369af19fa6f3daa4f09671fd61"}, - {file = "Brotli-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:524f35912131cc2cabb00edfd8d573b07f2d9f21fa824bd3fb19725a9cf06327"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5b3cc074004d968722f51e550b41a27be656ec48f8afaeeb45ebf65b561481dd"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:19c116e796420b0cee3da1ccec3b764ed2952ccfcc298b55a10e5610ad7885f9"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:510b5b1bfbe20e1a7b3baf5fed9e9451873559a976c1a78eebaa3b86c57b4265"}, - {file = "Brotli-1.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:a1fd8a29719ccce974d523580987b7f8229aeace506952fa9ce1d53a033873c8"}, - {file = "Brotli-1.1.0-cp311-cp311-win32.whl", hash = "sha256:39da8adedf6942d76dc3e46653e52df937a3c4d6d18fdc94a7c29d263b1f5b50"}, - {file = "Brotli-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:aac0411d20e345dc0920bdec5548e438e999ff68d77564d5e9463a7ca9d3e7b1"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:316cc9b17edf613ac76b1f1f305d2a748f1b976b033b049a6ecdfd5612c70409"}, - {file = "Brotli-1.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:caf9ee9a5775f3111642d33b86237b05808dafcd6268faa492250e9b78046eb2"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70051525001750221daa10907c77830bc889cb6d865cc0b813d9db7fefc21451"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f4bf76817c14aa98cc6697ac02f3972cb8c3da93e9ef16b9c66573a68014f91"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0c5516f0aed654134a2fc936325cc2e642f8a0e096d075209672eb321cff408"}, - {file = "Brotli-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6c3020404e0b5eefd7c9485ccf8393cfb75ec38ce75586e046573c9dc29967a0"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4ed11165dd45ce798d99a136808a794a748d5dc38511303239d4e2363c0695dc"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4093c631e96fdd49e0377a9c167bfd75b6d0bad2ace734c6eb20b348bc3ea180"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e4c4629ddad63006efa0ef968c8e4751c5868ff0b1c5c40f76524e894c50248"}, - {file = "Brotli-1.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:861bf317735688269936f755fa136a99d1ed526883859f86e41a5d43c61d8966"}, - {file = "Brotli-1.1.0-cp312-cp312-win32.whl", hash = "sha256:5f4d5ea15c9382135076d2fb28dde923352fe02951e66935a9efaac8f10e81b0"}, - {file = "Brotli-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:906bc3a79de8c4ae5b86d3d75a8b77e44404b0f4261714306e3ad248d8ab0951"}, - {file = "Brotli-1.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a090ca607cbb6a34b0391776f0cb48062081f5f60ddcce5d11838e67a01928d1"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2de9d02f5bda03d27ede52e8cfe7b865b066fa49258cbab568720aa5be80a47d"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2333e30a5e00fe0fe55903c8832e08ee9c3b1382aacf4db26664a16528d51b4b"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4d4a848d1837973bf0f4b5e54e3bec977d99be36a7895c61abb659301b02c112"}, - {file = "Brotli-1.1.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fdc3ff3bfccdc6b9cc7c342c03aa2400683f0cb891d46e94b64a197910dc4064"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:5eeb539606f18a0b232d4ba45adccde4125592f3f636a6182b4a8a436548b914"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:fd5f17ff8f14003595ab414e45fce13d073e0762394f957182e69035c9f3d7c2"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:069a121ac97412d1fe506da790b3e69f52254b9df4eb665cd42460c837193354"}, - {file = "Brotli-1.1.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e93dfc1a1165e385cc8239fab7c036fb2cd8093728cbd85097b284d7b99249a2"}, - {file = "Brotli-1.1.0-cp36-cp36m-win32.whl", hash = "sha256:a599669fd7c47233438a56936988a2478685e74854088ef5293802123b5b2460"}, - {file = "Brotli-1.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:d143fd47fad1db3d7c27a1b1d66162e855b5d50a89666af46e1679c496e8e579"}, - {file = "Brotli-1.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:11d00ed0a83fa22d29bc6b64ef636c4552ebafcef57154b4ddd132f5638fbd1c"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f733d788519c7e3e71f0855c96618720f5d3d60c3cb829d8bbb722dddce37985"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:929811df5462e182b13920da56c6e0284af407d1de637d8e536c5cd00a7daf60"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b63b949ff929fbc2d6d3ce0e924c9b93c9785d877a21a1b678877ffbbc4423a"}, - {file = "Brotli-1.1.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d192f0f30804e55db0d0e0a35d83a9fead0e9a359a9ed0285dbacea60cc10a84"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:f296c40e23065d0d6650c4aefe7470d2a25fffda489bcc3eb66083f3ac9f6643"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:919e32f147ae93a09fe064d77d5ebf4e35502a8df75c29fb05788528e330fe74"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:23032ae55523cc7bccb4f6a0bf368cd25ad9bcdcc1990b64a647e7bbcce9cb5b"}, - {file = "Brotli-1.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:224e57f6eac61cc449f498cc5f0e1725ba2071a3d4f48d5d9dffba42db196438"}, - {file = "Brotli-1.1.0-cp37-cp37m-win32.whl", hash = "sha256:587ca6d3cef6e4e868102672d3bd9dc9698c309ba56d41c2b9c85bbb903cdb95"}, - {file = "Brotli-1.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:2954c1c23f81c2eaf0b0717d9380bd348578a94161a65b3a2afc62c86467dd68"}, - {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:efa8b278894b14d6da122a72fefcebc28445f2d3f880ac59d46c90f4c13be9a3"}, - {file = "Brotli-1.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:03d20af184290887bdea3f0f78c4f737d126c74dc2f3ccadf07e54ceca3bf208"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6172447e1b368dcbc458925e5ddaf9113477b0ed542df258d84fa28fc45ceea7"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a743e5a28af5f70f9c080380a5f908d4d21d40e8f0e0c8901604d15cfa9ba751"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0541e747cce78e24ea12d69176f6a7ddb690e62c425e01d31cc065e69ce55b48"}, - {file = "Brotli-1.1.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cdbc1fc1bc0bff1cef838eafe581b55bfbffaed4ed0318b724d0b71d4d377619"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:890b5a14ce214389b2cc36ce82f3093f96f4cc730c1cffdbefff77a7c71f2a97"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ab4fbee0b2d9098c74f3057b2bc055a8bd92ccf02f65944a241b4349229185a"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:141bd4d93984070e097521ed07e2575b46f817d08f9fa42b16b9b5f27b5ac088"}, - {file = "Brotli-1.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fce1473f3ccc4187f75b4690cfc922628aed4d3dd013d047f95a9b3919a86596"}, - {file = "Brotli-1.1.0-cp38-cp38-win32.whl", hash = "sha256:db85ecf4e609a48f4b29055f1e144231b90edc90af7481aa731ba2d059226b1b"}, - {file = "Brotli-1.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:3d7954194c36e304e1523f55d7042c59dc53ec20dd4e9ea9d151f1b62b4415c0"}, - {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5fb2ce4b8045c78ebbc7b8f3c15062e435d47e7393cc57c25115cfd49883747a"}, - {file = "Brotli-1.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7905193081db9bfa73b1219140b3d315831cbff0d8941f22da695832f0dd188f"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a77def80806c421b4b0af06f45d65a136e7ac0bdca3c09d9e2ea4e515367c7e9"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dadd1314583ec0bf2d1379f7008ad627cd6336625d6679cf2f8e67081b83acf"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:901032ff242d479a0efa956d853d16875d42157f98951c0230f69e69f9c09bac"}, - {file = "Brotli-1.1.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22fc2a8549ffe699bfba2256ab2ed0421a7b8fadff114a3d201794e45a9ff578"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ae15b066e5ad21366600ebec29a7ccbc86812ed267e4b28e860b8ca16a2bc474"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:949f3b7c29912693cee0afcf09acd6ebc04c57af949d9bf77d6101ebb61e388c"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:89f4988c7203739d48c6f806f1e87a1d96e0806d44f0fba61dba81392c9e474d"}, - {file = "Brotli-1.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:de6551e370ef19f8de1807d0a9aa2cdfdce2e85ce88b122fe9f6b2b076837e59"}, - {file = "Brotli-1.1.0-cp39-cp39-win32.whl", hash = "sha256:f0d8a7a6b5983c2496e364b969f0e526647a06b075d034f3297dc66f3b360c64"}, - {file = "Brotli-1.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:cdad5b9014d83ca68c25d2e9444e28e967ef16e80f6b436918c700c117a85467"}, - {file = "Brotli-1.1.0.tar.gz", hash = "sha256:81de08ac11bcb85841e440c13611c00b67d3bf82698314928d0b676362546724"}, -] - -[[package]] -name = "brotlicffi" -version = "1.1.0.0" -description = "Python CFFI bindings to the Brotli library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "brotlicffi-1.1.0.0-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:9b7ae6bd1a3f0df532b6d67ff674099a96d22bc0948955cb338488c31bfb8851"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:19ffc919fa4fc6ace69286e0a23b3789b4219058313cf9b45625016bf7ff996b"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9feb210d932ffe7798ee62e6145d3a757eb6233aa9a4e7db78dd3690d7755814"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84763dbdef5dd5c24b75597a77e1b30c66604725707565188ba54bab4f114820"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-win32.whl", hash = "sha256:1b12b50e07c3911e1efa3a8971543e7648100713d4e0971b13631cce22c587eb"}, - {file = "brotlicffi-1.1.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:994a4f0681bb6c6c3b0925530a1926b7a189d878e6e5e38fae8efa47c5d9c613"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2e4aeb0bd2540cb91b069dbdd54d458da8c4334ceaf2d25df2f4af576d6766ca"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b7b0033b0d37bb33009fb2fef73310e432e76f688af76c156b3594389d81391"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54a07bb2374a1eba8ebb52b6fafffa2afd3c4df85ddd38fcc0511f2bb387c2a8"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7901a7dc4b88f1c1475de59ae9be59799db1007b7d059817948d8e4f12e24e35"}, - {file = "brotlicffi-1.1.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ce01c7316aebc7fce59da734286148b1d1b9455f89cf2c8a4dfce7d41db55c2d"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:246f1d1a90279bb6069de3de8d75a8856e073b8ff0b09dcca18ccc14cec85979"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc4bc5d82bc56ebd8b514fb8350cfac4627d6b0743382e46d033976a5f80fab6"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37c26ecb14386a44b118ce36e546ce307f4810bc9598a6e6cb4f7fca725ae7e6"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca72968ae4eaf6470498d5c2887073f7efe3b1e7d7ec8be11a06a79cc810e990"}, - {file = "brotlicffi-1.1.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:add0de5b9ad9e9aa293c3aa4e9deb2b61e99ad6c1634e01d01d98c03e6a354cc"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:9b6068e0f3769992d6b622a1cd2e7835eae3cf8d9da123d7f51ca9c1e9c333e5"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8557a8559509b61e65083f8782329188a250102372576093c88930c875a69838"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a7ae37e5d79c5bdfb5b4b99f2715a6035e6c5bf538c3746abc8e26694f92f33"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391151ec86bb1c683835980f4816272a87eaddc46bb91cbf44f62228b84d8cca"}, - {file = "brotlicffi-1.1.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:2f3711be9290f0453de8eed5275d93d286abe26b08ab4a35d7452caa1fef532f"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a807d760763e398bbf2c6394ae9da5815901aa93ee0a37bca5efe78d4ee3171"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa8ca0623b26c94fccc3a1fdd895be1743b838f3917300506d04aa3346fd2a14"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3de0cf28a53a3238b252aca9fed1593e9d36c1d116748013339f0949bfc84112"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6be5ec0e88a4925c91f3dea2bb0013b3a2accda6f77238f76a34a1ea532a1cb0"}, - {file = "brotlicffi-1.1.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:d9eb71bb1085d996244439154387266fd23d6ad37161f6f52f1cd41dd95a3808"}, - {file = "brotlicffi-1.1.0.0.tar.gz", hash = "sha256:b77827a689905143f87915310b93b273ab17888fd43ef350d4832c4a71083c13"}, -] - -[package.dependencies] -cffi = ">=1.0.0" - [[package]] name = "build" version = "1.0.3" @@ -735,43 +593,43 @@ files = [ [[package]] name = "cryptography" -version = "42.0.1" +version = "42.0.2" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = ">=3.7" files = [ - {file = "cryptography-42.0.1-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:265bdc693570b895eb641410b8fc9e8ddbce723a669236162b9d9cfb70bd8d77"}, - {file = "cryptography-42.0.1-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:160fa08dfa6dca9cb8ad9bd84e080c0db6414ba5ad9a7470bc60fb154f60111e"}, - {file = "cryptography-42.0.1-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:727387886c9c8de927c360a396c5edcb9340d9e960cda145fca75bdafdabd24c"}, - {file = "cryptography-42.0.1-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d84673c012aa698555d4710dcfe5f8a0ad76ea9dde8ef803128cc669640a2e0"}, - {file = "cryptography-42.0.1-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e6edc3a568667daf7d349d7e820783426ee4f1c0feab86c29bd1d6fe2755e009"}, - {file = "cryptography-42.0.1-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:d50718dd574a49d3ef3f7ef7ece66ef281b527951eb2267ce570425459f6a404"}, - {file = "cryptography-42.0.1-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:9544492e8024f29919eac2117edd8c950165e74eb551a22c53f6fdf6ba5f4cb8"}, - {file = "cryptography-42.0.1-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ab6b302d51fbb1dd339abc6f139a480de14d49d50f65fdc7dff782aa8631d035"}, - {file = "cryptography-42.0.1-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2fe16624637d6e3e765530bc55caa786ff2cbca67371d306e5d0a72e7c3d0407"}, - {file = "cryptography-42.0.1-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ed1b2130f5456a09a134cc505a17fc2830a1a48ed53efd37dcc904a23d7b82fa"}, - {file = "cryptography-42.0.1-cp37-abi3-win32.whl", hash = "sha256:e5edf189431b4d51f5c6fb4a95084a75cef6b4646c934eb6e32304fc720e1453"}, - {file = "cryptography-42.0.1-cp37-abi3-win_amd64.whl", hash = "sha256:6bfd823b336fdcd8e06285ae8883d3d2624d3bdef312a0e2ef905f332f8e9302"}, - {file = "cryptography-42.0.1-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:351db02c1938c8e6b1fee8a78d6b15c5ccceca7a36b5ce48390479143da3b411"}, - {file = "cryptography-42.0.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430100abed6d3652208ae1dd410c8396213baee2e01a003a4449357db7dc9e14"}, - {file = "cryptography-42.0.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dff7a32880a51321f5de7869ac9dde6b1fca00fc1fef89d60e93f215468e824"}, - {file = "cryptography-42.0.1-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:b512f33c6ab195852595187af5440d01bb5f8dd57cb7a91e1e009a17f1b7ebca"}, - {file = "cryptography-42.0.1-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:95d900d19a370ae36087cc728e6e7be9c964ffd8cbcb517fd1efb9c9284a6abc"}, - {file = "cryptography-42.0.1-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:6ac8924085ed8287545cba89dc472fc224c10cc634cdf2c3e2866fe868108e77"}, - {file = "cryptography-42.0.1-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:cb2861a9364fa27d24832c718150fdbf9ce6781d7dc246a516435f57cfa31fe7"}, - {file = "cryptography-42.0.1-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:25ec6e9e81de5d39f111a4114193dbd39167cc4bbd31c30471cebedc2a92c323"}, - {file = "cryptography-42.0.1-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:9d61fcdf37647765086030d81872488e4cb3fafe1d2dda1d487875c3709c0a49"}, - {file = "cryptography-42.0.1-cp39-abi3-win32.whl", hash = "sha256:16b9260d04a0bfc8952b00335ff54f471309d3eb9d7e8dbfe9b0bd9e26e67881"}, - {file = "cryptography-42.0.1-cp39-abi3-win_amd64.whl", hash = "sha256:7911586fc69d06cd0ab3f874a169433db1bc2f0e40988661408ac06c4527a986"}, - {file = "cryptography-42.0.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d3594947d2507d4ef7a180a7f49a6db41f75fb874c2fd0e94f36b89bfd678bf2"}, - {file = "cryptography-42.0.1-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:8d7efb6bf427d2add2f40b6e1e8e476c17508fa8907234775214b153e69c2e11"}, - {file = "cryptography-42.0.1-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:126e0ba3cc754b200a2fb88f67d66de0d9b9e94070c5bc548318c8dab6383cb6"}, - {file = "cryptography-42.0.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:802d6f83233cf9696b59b09eb067e6b4d5ae40942feeb8e13b213c8fad47f1aa"}, - {file = "cryptography-42.0.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:0b7cacc142260ada944de070ce810c3e2a438963ee3deb45aa26fd2cee94c9a4"}, - {file = "cryptography-42.0.1-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:32ea63ceeae870f1a62e87f9727359174089f7b4b01e4999750827bf10e15d60"}, - {file = "cryptography-42.0.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d3902c779a92151f134f68e555dd0b17c658e13429f270d8a847399b99235a3f"}, - {file = "cryptography-42.0.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:50aecd93676bcca78379604ed664c45da82bc1241ffb6f97f6b7392ed5bc6f04"}, - {file = "cryptography-42.0.1.tar.gz", hash = "sha256:fd33f53809bb363cf126bebe7a99d97735988d9b0131a2be59fbf83e1259a5b7"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_universal2.whl", hash = "sha256:701171f825dcab90969596ce2af253143b93b08f1a716d4b2a9d2db5084ef7be"}, + {file = "cryptography-42.0.2-cp37-abi3-macosx_10_12_x86_64.whl", hash = "sha256:61321672b3ac7aade25c40449ccedbc6db72c7f5f0fdf34def5e2f8b51ca530d"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea2c3ffb662fec8bbbfce5602e2c159ff097a4631d96235fcf0fb00e59e3ece4"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b15c678f27d66d247132cbf13df2f75255627bcc9b6a570f7d2fd08e8c081d2"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8e88bb9eafbf6a4014d55fb222e7360eef53e613215085e65a13290577394529"}, + {file = "cryptography-42.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:a047682d324ba56e61b7ea7c7299d51e61fd3bca7dad2ccc39b72bd0118d60a1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:36d4b7c4be6411f58f60d9ce555a73df8406d484ba12a63549c88bd64f7967f1"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:a00aee5d1b6c20620161984f8ab2ab69134466c51f58c052c11b076715e72929"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b97fe7d7991c25e6a31e5d5e795986b18fbbb3107b873d5f3ae6dc9a103278e9"}, + {file = "cryptography-42.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5fa82a26f92871eca593b53359c12ad7949772462f887c35edaf36f87953c0e2"}, + {file = "cryptography-42.0.2-cp37-abi3-win32.whl", hash = "sha256:4b063d3413f853e056161eb0c7724822a9740ad3caa24b8424d776cebf98e7ee"}, + {file = "cryptography-42.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:841ec8af7a8491ac76ec5a9522226e287187a3107e12b7d686ad354bb78facee"}, + {file = "cryptography-42.0.2-cp39-abi3-macosx_10_12_universal2.whl", hash = "sha256:55d1580e2d7e17f45d19d3b12098e352f3a37fe86d380bf45846ef257054b242"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28cb2c41f131a5758d6ba6a0504150d644054fd9f3203a1e8e8d7ac3aea7f73a"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9097a208875fc7bbeb1286d0125d90bdfed961f61f214d3f5be62cd4ed8a446"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:44c95c0e96b3cb628e8452ec060413a49002a247b2b9938989e23a2c8291fc90"}, + {file = "cryptography-42.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f14185962e6a04ab32d1abe34eae8a9001569ee4edb64d2304bf0d65c53f3"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:09a77e5b2e8ca732a19a90c5bca2d124621a1edb5438c5daa2d2738bfeb02589"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ad28cff53f60d99a928dfcf1e861e0b2ceb2bc1f08a074fdd601b314e1cc9e0a"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:130c0f77022b2b9c99d8cebcdd834d81705f61c68e91ddd614ce74c657f8b3ea"}, + {file = "cryptography-42.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:fa3dec4ba8fb6e662770b74f62f1a0c7d4e37e25b58b2bf2c1be4c95372b4a33"}, + {file = "cryptography-42.0.2-cp39-abi3-win32.whl", hash = "sha256:3dbd37e14ce795b4af61b89b037d4bc157f2cb23e676fa16932185a04dfbf635"}, + {file = "cryptography-42.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:8a06641fb07d4e8f6c7dda4fc3f8871d327803ab6542e33831c7ccfdcb4d0ad6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:087887e55e0b9c8724cf05361357875adb5c20dec27e5816b653492980d20380"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:a7ef8dd0bf2e1d0a27042b231a3baac6883cdd5557036f5e8df7139255feaac6"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4383b47f45b14459cab66048d384614019965ba6c1a1a141f11b5a551cace1b2"}, + {file = "cryptography-42.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fbeb725c9dc799a574518109336acccaf1303c30d45c075c665c0793c2f79a7f"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:320948ab49883557a256eab46149df79435a22d2fefd6a66fe6946f1b9d9d008"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:5ef9bc3d046ce83c4bbf4c25e1e0547b9c441c01d30922d812e887dc5f125c12"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:52ed9ebf8ac602385126c9a2fe951db36f2cb0c2538d22971487f89d0de4065a"}, + {file = "cryptography-42.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:141e2aa5ba100d3788c0ad7919b288f89d1fe015878b9659b307c9ef867d3a65"}, + {file = "cryptography-42.0.2.tar.gz", hash = "sha256:e0ec52ba3c7f1b7d813cd52649a5b3ef1fc0d433219dc8c93827c57eab6cf888"}, ] [package.dependencies] @@ -787,6 +645,30 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["certifi", "pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "curl-cffi" +version = "0.6.0b7" +description = "libcurl ffi bindings for Python, with impersonation support" +optional = false +python-versions = ">=3.7" +files = [ + {file = "curl_cffi-0.6.0b7-cp37-abi3-macosx_10_9_x86_64.whl", hash = "sha256:0939e97e8f999bc51bde6a70e19b23aaee542ccc341955989d81e5b11d616cef"}, + {file = "curl_cffi-0.6.0b7-cp37-abi3-macosx_11_0_arm64.whl", hash = "sha256:3dcb245dacaec556ed48dc8e4c609bdf5dfe52b84073a124ee3f5e9342e5a7af"}, + {file = "curl_cffi-0.6.0b7-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef7e73e3148b482f69e30c7651dde5fd98900df597c0cdfdf1f433f3fb819037"}, + {file = "curl_cffi-0.6.0b7-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4df7a48a313688cb0dc2924229a3604b71700494a31ac7920701a2f930b49cdf"}, + {file = "curl_cffi-0.6.0b7-cp37-abi3-win_amd64.whl", hash = "sha256:271892078905981880a40fbdf2d53c0e5814dcc102836d69d30772b731898de4"}, + {file = "curl_cffi-0.6.0b7.tar.gz", hash = "sha256:f945aac9de95b53c3d0c77ce18391a7a4c7553b6eeb420631532dafdff298d43"}, +] + +[package.dependencies] +certifi = "*" +cffi = ">=1.12.0" + +[package.extras] +build = ["cibuildwheel", "wheel"] +dev = ["autoflake (==1.4)", "black (==22.8.0)", "coverage (==6.4.1)", "cryptography (==38.0.3)", "flake8 (==6.0.0)", "flake8-bugbear (==22.7.1)", "flake8-pie (==0.15.0)", "httpx (==0.23.1)", "isort (==5.10.1)", "mypy (==0.971)", "pytest (==7.1.2)", "pytest-asyncio (==0.19.0)", "pytest-trio (==0.7.0)", "trio (==0.21.0)", "trio-typing (==0.7.0)", "trustme (==0.9.0)", "types-certifi (==2021.10.8.2)", "uvicorn (==0.18.3)", "websockets (==11.0.3)"] +test = ["cryptography (==38.0.3)", "httpx (==0.23.1)", "proxy.py (==2.4.3)", "pytest (==7.1.2)", "pytest-asyncio (==0.19.0)", "pytest-trio (==0.7.0)", "trio (==0.21.0)", "trio-typing (==0.7.0)", "trustme (==0.9.0)", "types-certifi (==2021.10.8.2)", "uvicorn (==0.18.3)", "websockets (==11.0.3)"] + [[package]] name = "deepdiff" version = "6.7.1" @@ -827,6 +709,20 @@ files = [ {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, ] +[[package]] +name = "docstring-inheritance" +version = "2.1.2" +description = "Avoid writing and maintaining duplicated docstrings." +optional = false +python-versions = "<3.13,>=3.8" +files = [ + {file = "docstring-inheritance-2.1.2.tar.gz", hash = "sha256:ac9af95a7b06a305d43720274d0e62523d23f835bf94ce2bb814687e6fe3957b"}, + {file = "docstring_inheritance-2.1.2-py3-none-any.whl", hash = "sha256:05c8e3ef4c308406e66345b6d583df48ec1803ae90ae39cb7be03a78ac8d88d1"}, +] + +[package.extras] +test = ["covdefaults", "pytest", "pytest-cov"] + [[package]] name = "docstring-parser" version = "0.15" @@ -840,23 +736,24 @@ files = [ [[package]] name = "duckduckgo-search" -version = "3.9.11" +version = "4.4" description = "Search for words, documents, images, news, maps and text translation using the DuckDuckGo.com search engine." optional = false python-versions = ">=3.8" files = [ - {file = "duckduckgo_search-3.9.11-py3-none-any.whl", hash = "sha256:3636df4c5eec383c1c02f89c9693b6c5bbaeda38952e467a2fa930132f632ed4"}, - {file = "duckduckgo_search-3.9.11.tar.gz", hash = "sha256:4d07a02647da58f1e46e35f11719265f0ce06eed60e2c9c2b00b1105b9084d07"}, + {file = "duckduckgo_search-4.4-py3-none-any.whl", hash = "sha256:74ff68837332e7907e9ea134e1277afd7d77a4c368d0e7a508fc8dd9b45d2a8d"}, + {file = "duckduckgo_search-4.4.tar.gz", hash = "sha256:56a044b2c20547bb1e5e4133d3ff6edd70cab6c99321bf03e55e0e53c3393545"}, ] [package.dependencies] -aiofiles = ">=23.2.1" click = ">=8.1.7" -httpx = {version = ">=0.25.1", extras = ["brotli", "http2", "socks"]} +curl-cffi = ">=0.6.0b7" +docstring-inheritance = ">=2.1.2" lxml = ">=4.9.3" +nest-asyncio = ">=1.6.0" [package.extras] -dev = ["pytest (>=7.4.2)", "pytest-asyncio (>=0.21.1)", "ruff (>=0.1.6)"] +dev = ["pytest (>=7.4.2)", "ruff (>=0.1.6)"] [[package]] name = "dulwich" @@ -965,19 +862,18 @@ gmpy2 = ["gmpy2"] [[package]] name = "fastapi" -version = "0.104.1" +version = "0.109.0" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.104.1-py3-none-any.whl", hash = "sha256:752dc31160cdbd0436bb93bad51560b57e525cbb1d4bbf6f4904ceee75548241"}, - {file = "fastapi-0.104.1.tar.gz", hash = "sha256:e5e4540a7c5e1dcfbbcf5b903c234feddcdcd881f191977a1c5dfd917487e7ae"}, + {file = "fastapi-0.109.0-py3-none-any.whl", hash = "sha256:8c77515984cd8e8cfeb58364f8cc7a28f0692088475e2614f7bf03275eba9093"}, + {file = "fastapi-0.109.0.tar.gz", hash = "sha256:b978095b9ee01a5cf49b19f4bc1ac9b8ca83aa076e770ef8fd9af09a2b88d191"}, ] [package.dependencies] -anyio = ">=3.7.1,<4.0.0" pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.27.0,<0.28.0" +starlette = ">=0.35.0,<0.36.0" typing-extensions = ">=4.8.0" [package.extras] @@ -1130,13 +1026,13 @@ files = [ [[package]] name = "google-api-core" -version = "2.15.0" +version = "2.16.1" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.15.0.tar.gz", hash = "sha256:abc978a72658f14a2df1e5e12532effe40f94f868f6e23d95133bd6abcca35ca"}, - {file = "google_api_core-2.15.0-py3-none-any.whl", hash = "sha256:2aa56d2be495551e66bbff7f729b790546f87d5c90e74781aa77233bcb395a8a"}, + {file = "google-api-core-2.16.1.tar.gz", hash = "sha256:7f668ffa3d5b9f3c6930407e5f5d691c05a376050a5a5fd772b9dc32e70a0c30"}, + {file = "google_api_core-2.16.1-py3-none-any.whl", hash = "sha256:257e9e152cd18da0c6701113c122ade04dca04731e179fc5c7dca48e1396ec4c"}, ] [package.dependencies] @@ -1154,13 +1050,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] [[package]] name = "google-api-python-client" -version = "2.115.0" +version = "2.116.0" description = "Google API Client Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-python-client-2.115.0.tar.gz", hash = "sha256:96af11376535236ba600ebbe23588cfe003ec9b74e66dd6ddb53aa3ec87e1b52"}, - {file = "google_api_python_client-2.115.0-py2.py3-none-any.whl", hash = "sha256:26178e33684763099142e2cad201057bd27d4efefd859a495aac21ab3e6129c2"}, + {file = "google-api-python-client-2.116.0.tar.gz", hash = "sha256:f9f32361e16114d62929638fe07f77be30216b079ad316dc2ced859d9f72e5ad"}, + {file = "google_api_python_client-2.116.0-py2.py3-none-any.whl", hash = "sha256:846e44417c6b7385fa5f5a46cb6b9d23327754c560830245ee53a577c5e44cec"}, ] [package.dependencies] @@ -1493,32 +1389,6 @@ files = [ {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, ] -[[package]] -name = "h2" -version = "4.1.0" -description = "HTTP/2 State-Machine based protocol implementation" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, - {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, -] - -[package.dependencies] -hpack = ">=4.0,<5" -hyperframe = ">=6.0,<7" - -[[package]] -name = "hpack" -version = "4.0.0" -description = "Pure-Python HPACK header compression" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, - {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, -] - [[package]] name = "httpcore" version = "1.0.2" @@ -1615,14 +1485,10 @@ files = [ [package.dependencies] anyio = "*" -brotli = {version = "*", optional = true, markers = "platform_python_implementation == \"CPython\" and extra == \"brotli\""} -brotlicffi = {version = "*", optional = true, markers = "platform_python_implementation != \"CPython\" and extra == \"brotli\""} certifi = "*" -h2 = {version = ">=3,<5", optional = true, markers = "extra == \"http2\""} httpcore = "==1.*" idna = "*" sniffio = "*" -socksio = {version = "==1.*", optional = true, markers = "extra == \"socks\""} [package.extras] brotli = ["brotli", "brotlicffi"] @@ -1630,17 +1496,6 @@ cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] -[[package]] -name = "hyperframe" -version = "6.0.1" -description = "HTTP/2 framing layer for Python" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, - {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, -] - [[package]] name = "identify" version = "2.5.33" @@ -2091,6 +1946,17 @@ files = [ {file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"}, ] +[[package]] +name = "nest-asyncio" +version = "1.6.0" +description = "Patch asyncio to allow nested event loops" +optional = false +python-versions = ">=3.5" +files = [ + {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, + {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, +] + [[package]] name = "nodeenv" version = "1.8.0" @@ -2360,13 +2226,13 @@ files = [ [[package]] name = "pyairtable" -version = "2.2.1" +version = "2.2.2" description = "Python Client for the Airtable API" optional = false python-versions = "*" files = [ - {file = "pyairtable-2.2.1-py2.py3-none-any.whl", hash = "sha256:682f69611c80dcf769ec75c033a75dc5390ad530a4423b145333d8a3bdfca6f1"}, - {file = "pyairtable-2.2.1.tar.gz", hash = "sha256:1913cfa5a1fc98c4723fafcafde1b4cbc97546b7ea84f37e51f24d43f641f051"}, + {file = "pyairtable-2.2.2-py2.py3-none-any.whl", hash = "sha256:8b1ceca8d5ce1aaaea1170addffb013c243207fa02087d514eff7f3a2058b804"}, + {file = "pyairtable-2.2.2.tar.gz", hash = "sha256:0b4ac555e9b0672ee26df9fa1caa9560318cbef70e692e69a97dad6ed8c0a500"}, ] [package.dependencies] @@ -2414,18 +2280,18 @@ files = [ [[package]] name = "pydantic" -version = "2.5.3" +version = "2.6.0" description = "Data validation using Python type hints" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic-2.5.3-py3-none-any.whl", hash = "sha256:d0caf5954bee831b6bfe7e338c32b9e30c85dfe080c843680783ac2b631673b4"}, - {file = "pydantic-2.5.3.tar.gz", hash = "sha256:b3ef57c62535b0941697cce638c08900d87fcb67e29cfa99e8a68f747f393f7a"}, + {file = "pydantic-2.6.0-py3-none-any.whl", hash = "sha256:1440966574e1b5b99cf75a13bec7b20e3512e8a61b894ae252f56275e2c465ae"}, + {file = "pydantic-2.6.0.tar.gz", hash = "sha256:ae887bd94eb404b09d86e4d12f93893bdca79d766e738528c6fa1c849f3c6bcf"}, ] [package.dependencies] annotated-types = ">=0.4.0" -pydantic-core = "2.14.6" +pydantic-core = "2.16.1" typing-extensions = ">=4.6.1" [package.extras] @@ -2433,116 +2299,90 @@ email = ["email-validator (>=2.0.0)"] [[package]] name = "pydantic-core" -version = "2.14.6" +version = "2.16.1" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:72f9a942d739f09cd42fffe5dc759928217649f070056f03c70df14f5770acf9"}, - {file = "pydantic_core-2.14.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6a31d98c0d69776c2576dda4b77b8e0c69ad08e8b539c25c7d0ca0dc19a50d6c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5aa90562bc079c6c290f0512b21768967f9968e4cfea84ea4ff5af5d917016e4"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:370ffecb5316ed23b667d99ce4debe53ea664b99cc37bfa2af47bc769056d534"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f85f3843bdb1fe80e8c206fe6eed7a1caeae897e496542cee499c374a85c6e08"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9862bf828112e19685b76ca499b379338fd4c5c269d897e218b2ae8fcb80139d"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:036137b5ad0cb0004c75b579445a1efccd072387a36c7f217bb8efd1afbe5245"}, - {file = "pydantic_core-2.14.6-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:92879bce89f91f4b2416eba4429c7b5ca22c45ef4a499c39f0c5c69257522c7c"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0c08de15d50fa190d577e8591f0329a643eeaed696d7771760295998aca6bc66"}, - {file = "pydantic_core-2.14.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:36099c69f6b14fc2c49d7996cbf4f87ec4f0e66d1c74aa05228583225a07b590"}, - {file = "pydantic_core-2.14.6-cp310-none-win32.whl", hash = "sha256:7be719e4d2ae6c314f72844ba9d69e38dff342bc360379f7c8537c48e23034b7"}, - {file = "pydantic_core-2.14.6-cp310-none-win_amd64.whl", hash = "sha256:36fa402dcdc8ea7f1b0ddcf0df4254cc6b2e08f8cd80e7010d4c4ae6e86b2a87"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:dea7fcd62915fb150cdc373212141a30037e11b761fbced340e9db3379b892d4"}, - {file = "pydantic_core-2.14.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ffff855100bc066ff2cd3aa4a60bc9534661816b110f0243e59503ec2df38421"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b027c86c66b8627eb90e57aee1f526df77dc6d8b354ec498be9a757d513b92b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:00b1087dabcee0b0ffd104f9f53d7d3eaddfaa314cdd6726143af6bc713aa27e"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:75ec284328b60a4e91010c1acade0c30584f28a1f345bc8f72fe8b9e46ec6a96"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e1f4744eea1501404b20b0ac059ff7e3f96a97d3e3f48ce27a139e053bb370b"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2602177668f89b38b9f84b7b3435d0a72511ddef45dc14446811759b82235a1"}, - {file = "pydantic_core-2.14.6-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6c8edaea3089bf908dd27da8f5d9e395c5b4dc092dbcce9b65e7156099b4b937"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:478e9e7b360dfec451daafe286998d4a1eeaecf6d69c427b834ae771cad4b622"}, - {file = "pydantic_core-2.14.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b6ca36c12a5120bad343eef193cc0122928c5c7466121da7c20f41160ba00ba2"}, - {file = "pydantic_core-2.14.6-cp311-none-win32.whl", hash = "sha256:2b8719037e570639e6b665a4050add43134d80b687288ba3ade18b22bbb29dd2"}, - {file = "pydantic_core-2.14.6-cp311-none-win_amd64.whl", hash = "sha256:78ee52ecc088c61cce32b2d30a826f929e1708f7b9247dc3b921aec367dc1b23"}, - {file = "pydantic_core-2.14.6-cp311-none-win_arm64.whl", hash = "sha256:a19b794f8fe6569472ff77602437ec4430f9b2b9ec7a1105cfd2232f9ba355e6"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:667aa2eac9cd0700af1ddb38b7b1ef246d8cf94c85637cbb03d7757ca4c3fdec"}, - {file = "pydantic_core-2.14.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cdee837710ef6b56ebd20245b83799fce40b265b3b406e51e8ccc5b85b9099b7"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c5bcf3414367e29f83fd66f7de64509a8fd2368b1edf4351e862910727d3e51"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a92ae76f75d1915806b77cf459811e772d8f71fd1e4339c99750f0e7f6324f"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a983cca5ed1dd9a35e9e42ebf9f278d344603bfcb174ff99a5815f953925140a"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cb92f9061657287eded380d7dc455bbf115430b3aa4741bdc662d02977e7d0af"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4ace1e220b078c8e48e82c081e35002038657e4b37d403ce940fa679e57113b"}, - {file = "pydantic_core-2.14.6-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ef633add81832f4b56d3b4c9408b43d530dfca29e68fb1b797dcb861a2c734cd"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7e90d6cc4aad2cc1f5e16ed56e46cebf4877c62403a311af20459c15da76fd91"}, - {file = "pydantic_core-2.14.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e8a5ac97ea521d7bde7621d86c30e86b798cdecd985723c4ed737a2aa9e77d0c"}, - {file = "pydantic_core-2.14.6-cp312-none-win32.whl", hash = "sha256:f27207e8ca3e5e021e2402ba942e5b4c629718e665c81b8b306f3c8b1ddbb786"}, - {file = "pydantic_core-2.14.6-cp312-none-win_amd64.whl", hash = "sha256:b3e5fe4538001bb82e2295b8d2a39356a84694c97cb73a566dc36328b9f83b40"}, - {file = "pydantic_core-2.14.6-cp312-none-win_arm64.whl", hash = "sha256:64634ccf9d671c6be242a664a33c4acf12882670b09b3f163cd00a24cffbd74e"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:24368e31be2c88bd69340fbfe741b405302993242ccb476c5c3ff48aeee1afe0"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e33b0834f1cf779aa839975f9d8755a7c2420510c0fa1e9fa0497de77cd35d2c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6af4b3f52cc65f8a0bc8b1cd9676f8c21ef3e9132f21fed250f6958bd7223bed"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d15687d7d7f40333bd8266f3814c591c2e2cd263fa2116e314f60d82086e353a"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:095b707bb287bfd534044166ab767bec70a9bba3175dcdc3371782175c14e43c"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94fc0e6621e07d1e91c44e016cc0b189b48db053061cc22d6298a611de8071bb"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ce830e480f6774608dedfd4a90c42aac4a7af0a711f1b52f807130c2e434c06"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a306cdd2ad3a7d795d8e617a58c3a2ed0f76c8496fb7621b6cd514eb1532cae8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:2f5fa187bde8524b1e37ba894db13aadd64faa884657473b03a019f625cee9a8"}, - {file = "pydantic_core-2.14.6-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:438027a975cc213a47c5d70672e0d29776082155cfae540c4e225716586be75e"}, - {file = "pydantic_core-2.14.6-cp37-none-win32.whl", hash = "sha256:f96ae96a060a8072ceff4cfde89d261837b4294a4f28b84a28765470d502ccc6"}, - {file = "pydantic_core-2.14.6-cp37-none-win_amd64.whl", hash = "sha256:e646c0e282e960345314f42f2cea5e0b5f56938c093541ea6dbf11aec2862391"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:db453f2da3f59a348f514cfbfeb042393b68720787bbef2b4c6068ea362c8149"}, - {file = "pydantic_core-2.14.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3860c62057acd95cc84044e758e47b18dcd8871a328ebc8ccdefd18b0d26a21b"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36026d8f99c58d7044413e1b819a67ca0e0b8ebe0f25e775e6c3d1fabb3c38fb"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ed1af8692bd8d2a29d702f1a2e6065416d76897d726e45a1775b1444f5928a7"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:314ccc4264ce7d854941231cf71b592e30d8d368a71e50197c905874feacc8a8"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:982487f8931067a32e72d40ab6b47b1628a9c5d344be7f1a4e668fb462d2da42"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2dbe357bc4ddda078f79d2a36fc1dd0494a7f2fad83a0a684465b6f24b46fe80"}, - {file = "pydantic_core-2.14.6-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2f6ffc6701a0eb28648c845f4945a194dc7ab3c651f535b81793251e1185ac3d"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7f5025db12fc6de7bc1104d826d5aee1d172f9ba6ca936bf6474c2148ac336c1"}, - {file = "pydantic_core-2.14.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dab03ed811ed1c71d700ed08bde8431cf429bbe59e423394f0f4055f1ca0ea60"}, - {file = "pydantic_core-2.14.6-cp38-none-win32.whl", hash = "sha256:dfcbebdb3c4b6f739a91769aea5ed615023f3c88cb70df812849aef634c25fbe"}, - {file = "pydantic_core-2.14.6-cp38-none-win_amd64.whl", hash = "sha256:99b14dbea2fdb563d8b5a57c9badfcd72083f6006caf8e126b491519c7d64ca8"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:4ce8299b481bcb68e5c82002b96e411796b844d72b3e92a3fbedfe8e19813eab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b9a9d92f10772d2a181b5ca339dee066ab7d1c9a34ae2421b2a52556e719756f"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd9e98b408384989ea4ab60206b8e100d8687da18b5c813c11e92fd8212a98e0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4f86f1f318e56f5cbb282fe61eb84767aee743ebe32c7c0834690ebea50c0a6b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86ce5fcfc3accf3a07a729779d0b86c5d0309a4764c897d86c11089be61da160"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dcf1978be02153c6a31692d4fbcc2a3f1db9da36039ead23173bc256ee3b91b"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eedf97be7bc3dbc8addcef4142f4b4164066df0c6f36397ae4aaed3eb187d8ab"}, - {file = "pydantic_core-2.14.6-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d5f916acf8afbcab6bacbb376ba7dc61f845367901ecd5e328fc4d4aef2fcab0"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:8a14c192c1d724c3acbfb3f10a958c55a2638391319ce8078cb36c02283959b9"}, - {file = "pydantic_core-2.14.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0348b1dc6b76041516e8a854ff95b21c55f5a411c3297d2ca52f5528e49d8411"}, - {file = "pydantic_core-2.14.6-cp39-none-win32.whl", hash = "sha256:de2a0645a923ba57c5527497daf8ec5df69c6eadf869e9cd46e86349146e5975"}, - {file = "pydantic_core-2.14.6-cp39-none-win_amd64.whl", hash = "sha256:aca48506a9c20f68ee61c87f2008f81f8ee99f8d7f0104bff3c47e2d148f89d9"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:d5c28525c19f5bb1e09511669bb57353d22b94cf8b65f3a8d141c389a55dec95"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:78d0768ee59baa3de0f4adac9e3748b4b1fffc52143caebddfd5ea2961595277"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b93785eadaef932e4fe9c6e12ba67beb1b3f1e5495631419c784ab87e975670"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a874f21f87c485310944b2b2734cd6d318765bcbb7515eead33af9641816506e"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b89f4477d915ea43b4ceea6756f63f0288941b6443a2b28c69004fe07fde0d0d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:172de779e2a153d36ee690dbc49c6db568d7b33b18dc56b69a7514aecbcf380d"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:dfcebb950aa7e667ec226a442722134539e77c575f6cfaa423f24371bb8d2e94"}, - {file = "pydantic_core-2.14.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:55a23dcd98c858c0db44fc5c04fc7ed81c4b4d33c653a7c45ddaebf6563a2f66"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:4241204e4b36ab5ae466ecec5c4c16527a054c69f99bba20f6f75232a6a534e2"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e574de99d735b3fc8364cba9912c2bec2da78775eba95cbb225ef7dda6acea24"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1302a54f87b5cd8528e4d6d1bf2133b6aa7c6122ff8e9dc5220fbc1e07bffebd"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f8e81e4b55930e5ffab4a68db1af431629cf2e4066dbdbfef65348b8ab804ea8"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c99462ffc538717b3e60151dfaf91125f637e801f5ab008f81c402f1dff0cd0f"}, - {file = "pydantic_core-2.14.6-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:e4cf2d5829f6963a5483ec01578ee76d329eb5caf330ecd05b3edd697e7d768a"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:cf10b7d58ae4a1f07fccbf4a0a956d705356fea05fb4c70608bb6fa81d103cda"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:399ac0891c284fa8eb998bcfa323f2234858f5d2efca3950ae58c8f88830f145"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c6a5c79b28003543db3ba67d1df336f253a87d3112dac3a51b94f7d48e4c0e1"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599c87d79cab2a6a2a9df4aefe0455e61e7d2aeede2f8577c1b7c0aec643ee8e"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:43e166ad47ba900f2542a80d83f9fc65fe99eb63ceec4debec160ae729824052"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3a0b5db001b98e1c649dd55afa928e75aa4087e587b9524a4992316fa23c9fba"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:747265448cb57a9f37572a488a57d873fd96bf51e5bb7edb52cfb37124516da4"}, - {file = "pydantic_core-2.14.6-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7ebe3416785f65c28f4f9441e916bfc8a54179c8dea73c23023f7086fa601c5d"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:86c963186ca5e50d5c8287b1d1c9d3f8f024cbe343d048c5bd282aec2d8641f2"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e0641b506486f0b4cd1500a2a65740243e8670a2549bb02bc4556a83af84ae03"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71d72ca5eaaa8d38c8df16b7deb1a2da4f650c41b58bb142f3fb75d5ad4a611f"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27e524624eace5c59af499cd97dc18bb201dc6a7a2da24bfc66ef151c69a5f2a"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3dde6cac75e0b0902778978d3b1646ca9f438654395a362cb21d9ad34b24acf"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:00646784f6cd993b1e1c0e7b0fdcbccc375d539db95555477771c27555e3c556"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:23598acb8ccaa3d1d875ef3b35cb6376535095e9405d91a3d57a8c7db5d29341"}, - {file = "pydantic_core-2.14.6-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7f41533d7e3cf9520065f610b41ac1c76bc2161415955fbcead4981b22c7611e"}, - {file = "pydantic_core-2.14.6.tar.gz", hash = "sha256:1fd0c1d395372843fba13a51c28e3bb9d59bd7aebfeb17358ffaaa1e4dbbe948"}, + {file = "pydantic_core-2.16.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:300616102fb71241ff477a2cbbc847321dbec49428434a2f17f37528721c4948"}, + {file = "pydantic_core-2.16.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5511f962dd1b9b553e9534c3b9c6a4b0c9ded3d8c2be96e61d56f933feef9e1f"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:98f0edee7ee9cc7f9221af2e1b95bd02810e1c7a6d115cfd82698803d385b28f"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9795f56aa6b2296f05ac79d8a424e94056730c0b860a62b0fdcfe6340b658cc8"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c45f62e4107ebd05166717ac58f6feb44471ed450d07fecd90e5f69d9bf03c48"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462d599299c5971f03c676e2b63aa80fec5ebc572d89ce766cd11ca8bcb56f3f"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21ebaa4bf6386a3b22eec518da7d679c8363fb7fb70cf6972161e5542f470798"}, + {file = "pydantic_core-2.16.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:99f9a50b56713a598d33bc23a9912224fc5d7f9f292444e6664236ae471ddf17"}, + {file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8ec364e280db4235389b5e1e6ee924723c693cbc98e9d28dc1767041ff9bc388"}, + {file = "pydantic_core-2.16.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:653a5dfd00f601a0ed6654a8b877b18d65ac32c9d9997456e0ab240807be6cf7"}, + {file = "pydantic_core-2.16.1-cp310-none-win32.whl", hash = "sha256:1661c668c1bb67b7cec96914329d9ab66755911d093bb9063c4c8914188af6d4"}, + {file = "pydantic_core-2.16.1-cp310-none-win_amd64.whl", hash = "sha256:561be4e3e952c2f9056fba5267b99be4ec2afadc27261505d4992c50b33c513c"}, + {file = "pydantic_core-2.16.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:102569d371fadc40d8f8598a59379c37ec60164315884467052830b28cc4e9da"}, + {file = "pydantic_core-2.16.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:735dceec50fa907a3c314b84ed609dec54b76a814aa14eb90da31d1d36873a5e"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e83ebbf020be727d6e0991c1b192a5c2e7113eb66e3def0cd0c62f9f266247e4"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:30a8259569fbeec49cfac7fda3ec8123486ef1b729225222f0d41d5f840b476f"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:920c4897e55e2881db6a6da151198e5001552c3777cd42b8a4c2f72eedc2ee91"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f5247a3d74355f8b1d780d0f3b32a23dd9f6d3ff43ef2037c6dcd249f35ecf4c"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d5bea8012df5bb6dda1e67d0563ac50b7f64a5d5858348b5c8cb5043811c19d"}, + {file = "pydantic_core-2.16.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ed3025a8a7e5a59817b7494686d449ebfbe301f3e757b852c8d0d1961d6be864"}, + {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:06f0d5a1d9e1b7932477c172cc720b3b23c18762ed7a8efa8398298a59d177c7"}, + {file = "pydantic_core-2.16.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:150ba5c86f502c040b822777e2e519b5625b47813bd05f9273a8ed169c97d9ae"}, + {file = "pydantic_core-2.16.1-cp311-none-win32.whl", hash = "sha256:d6cbdf12ef967a6aa401cf5cdf47850559e59eedad10e781471c960583f25aa1"}, + {file = "pydantic_core-2.16.1-cp311-none-win_amd64.whl", hash = "sha256:afa01d25769af33a8dac0d905d5c7bb2d73c7c3d5161b2dd6f8b5b5eea6a3c4c"}, + {file = "pydantic_core-2.16.1-cp311-none-win_arm64.whl", hash = "sha256:1a2fe7b00a49b51047334d84aafd7e39f80b7675cad0083678c58983662da89b"}, + {file = "pydantic_core-2.16.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:0f478ec204772a5c8218e30eb813ca43e34005dff2eafa03931b3d8caef87d51"}, + {file = "pydantic_core-2.16.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f1936ef138bed2165dd8573aa65e3095ef7c2b6247faccd0e15186aabdda7f66"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99d3a433ef5dc3021c9534a58a3686c88363c591974c16c54a01af7efd741f13"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd88f40f2294440d3f3c6308e50d96a0d3d0973d6f1a5732875d10f569acef49"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fac641bbfa43d5a1bed99d28aa1fded1984d31c670a95aac1bf1d36ac6ce137"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:72bf9308a82b75039b8c8edd2be2924c352eda5da14a920551a8b65d5ee89253"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb4363e6c9fc87365c2bc777a1f585a22f2f56642501885ffc7942138499bf54"}, + {file = "pydantic_core-2.16.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:20f724a023042588d0f4396bbbcf4cffd0ddd0ad3ed4f0d8e6d4ac4264bae81e"}, + {file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fb4370b15111905bf8b5ba2129b926af9470f014cb0493a67d23e9d7a48348e8"}, + {file = "pydantic_core-2.16.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23632132f1fd608034f1a56cc3e484be00854db845b3a4a508834be5a6435a6f"}, + {file = "pydantic_core-2.16.1-cp312-none-win32.whl", hash = "sha256:b9f3e0bffad6e238f7acc20c393c1ed8fab4371e3b3bc311020dfa6020d99212"}, + {file = "pydantic_core-2.16.1-cp312-none-win_amd64.whl", hash = "sha256:a0b4cfe408cd84c53bab7d83e4209458de676a6ec5e9c623ae914ce1cb79b96f"}, + {file = "pydantic_core-2.16.1-cp312-none-win_arm64.whl", hash = "sha256:d195add190abccefc70ad0f9a0141ad7da53e16183048380e688b466702195dd"}, + {file = "pydantic_core-2.16.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:502c062a18d84452858f8aea1e520e12a4d5228fc3621ea5061409d666ea1706"}, + {file = "pydantic_core-2.16.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8c032ccee90b37b44e05948b449a2d6baed7e614df3d3f47fe432c952c21b60"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:920f4633bee43d7a2818e1a1a788906df5a17b7ab6fe411220ed92b42940f818"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9f5d37ff01edcbace53a402e80793640c25798fb7208f105d87a25e6fcc9ea06"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:399166f24c33a0c5759ecc4801f040dbc87d412c1a6d6292b2349b4c505effc9"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ac89ccc39cd1d556cc72d6752f252dc869dde41c7c936e86beac5eb555041b66"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73802194f10c394c2bedce7a135ba1d8ba6cff23adf4217612bfc5cf060de34c"}, + {file = "pydantic_core-2.16.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8fa00fa24ffd8c31fac081bf7be7eb495be6d248db127f8776575a746fa55c95"}, + {file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:601d3e42452cd4f2891c13fa8c70366d71851c1593ed42f57bf37f40f7dca3c8"}, + {file = "pydantic_core-2.16.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:07982b82d121ed3fc1c51faf6e8f57ff09b1325d2efccaa257dd8c0dd937acca"}, + {file = "pydantic_core-2.16.1-cp38-none-win32.whl", hash = "sha256:d0bf6f93a55d3fa7a079d811b29100b019784e2ee6bc06b0bb839538272a5610"}, + {file = "pydantic_core-2.16.1-cp38-none-win_amd64.whl", hash = "sha256:fbec2af0ebafa57eb82c18c304b37c86a8abddf7022955d1742b3d5471a6339e"}, + {file = "pydantic_core-2.16.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a497be217818c318d93f07e14502ef93d44e6a20c72b04c530611e45e54c2196"}, + {file = "pydantic_core-2.16.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:694a5e9f1f2c124a17ff2d0be613fd53ba0c26de588eb4bdab8bca855e550d95"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4dfc66abea3ec6d9f83e837a8f8a7d9d3a76d25c9911735c76d6745950e62c"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8655f55fe68c4685673265a650ef71beb2d31871c049c8b80262026f23605ee3"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:21e3298486c4ea4e4d5cc6fb69e06fb02a4e22089304308817035ac006a7f506"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71b4a48a7427f14679f0015b13c712863d28bb1ab700bd11776a5368135c7d60"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dca874e35bb60ce4f9f6665bfbfad050dd7573596608aeb9e098621ac331dc"}, + {file = "pydantic_core-2.16.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fa496cd45cda0165d597e9d6f01e36c33c9508f75cf03c0a650018c5048f578e"}, + {file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5317c04349472e683803da262c781c42c5628a9be73f4750ac7d13040efb5d2d"}, + {file = "pydantic_core-2.16.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:42c29d54ed4501a30cd71015bf982fa95e4a60117b44e1a200290ce687d3e640"}, + {file = "pydantic_core-2.16.1-cp39-none-win32.whl", hash = "sha256:ba07646f35e4e49376c9831130039d1b478fbfa1215ae62ad62d2ee63cf9c18f"}, + {file = "pydantic_core-2.16.1-cp39-none-win_amd64.whl", hash = "sha256:2133b0e412a47868a358713287ff9f9a328879da547dc88be67481cdac529118"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d25ef0c33f22649b7a088035fd65ac1ce6464fa2876578df1adad9472f918a76"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:99c095457eea8550c9fa9a7a992e842aeae1429dab6b6b378710f62bfb70b394"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b49c604ace7a7aa8af31196abbf8f2193be605db6739ed905ecaf62af31ccae0"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c56da23034fe66221f2208c813d8aa509eea34d97328ce2add56e219c3a9f41c"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cebf8d56fee3b08ad40d332a807ecccd4153d3f1ba8231e111d9759f02edfd05"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1ae8048cba95f382dba56766525abca438328455e35c283bb202964f41a780b0"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:780daad9e35b18d10d7219d24bfb30148ca2afc309928e1d4d53de86822593dc"}, + {file = "pydantic_core-2.16.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c94b5537bf6ce66e4d7830c6993152940a188600f6ae044435287753044a8fe2"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:adf28099d061a25fbcc6531febb7a091e027605385de9fe14dd6a97319d614cf"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:644904600c15816a1f9a1bafa6aab0d21db2788abcdf4e2a77951280473f33e1"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87bce04f09f0552b66fca0c4e10da78d17cb0e71c205864bab4e9595122cb9d9"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:877045a7969ace04d59516d5d6a7dee13106822f99a5d8df5e6822941f7bedc8"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9c46e556ee266ed3fb7b7a882b53df3c76b45e872fdab8d9cf49ae5e91147fd7"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4eebbd049008eb800f519578e944b8dc8e0f7d59a5abb5924cc2d4ed3a1834ff"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c0be58529d43d38ae849a91932391eb93275a06b93b79a8ab828b012e916a206"}, + {file = "pydantic_core-2.16.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b1fc07896fc1851558f532dffc8987e526b682ec73140886c831d773cef44b76"}, + {file = "pydantic_core-2.16.1.tar.gz", hash = "sha256:daff04257b49ab7f4b3f73f98283d3dbb1a65bf3500d55c7beac3c66c310fe34"}, ] [package.dependencies] @@ -2645,17 +2485,17 @@ testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "no [[package]] name = "pytest-asyncio" -version = "0.23.3" +version = "0.23.4" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-asyncio-0.23.3.tar.gz", hash = "sha256:af313ce900a62fbe2b1aed18e37ad757f1ef9940c6b6a88e2954de38d6b1fb9f"}, - {file = "pytest_asyncio-0.23.3-py3-none-any.whl", hash = "sha256:37a9d912e8338ee7b4a3e917381d1c95bfc8682048cb0fbc35baba316ec1faba"}, + {file = "pytest-asyncio-0.23.4.tar.gz", hash = "sha256:2143d9d9375bf372a73260e4114541485e84fca350b0b6b92674ca56ff5f7ea2"}, + {file = "pytest_asyncio-0.23.4-py3-none-any.whl", hash = "sha256:b0079dfac14b60cd1ce4691fbfb1748fe939db7d0234b5aba97197d10fbe0fef"}, ] [package.dependencies] -pytest = ">=7.0.0" +pytest = ">=7.0.0,<8" [package.extras] docs = ["sphinx (>=5.3)", "sphinx-rtd-theme (>=1.0)"] @@ -3040,26 +2880,15 @@ files = [ {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, ] -[[package]] -name = "socksio" -version = "1.0.0" -description = "Sans-I/O implementation of SOCKS4, SOCKS4A, and SOCKS5." -optional = false -python-versions = ">=3.6" -files = [ - {file = "socksio-1.0.0-py3-none-any.whl", hash = "sha256:95dc1f15f9b34e8d7b16f06d74b8ccf48f609af32ab33c608d08761c5dcbb1f3"}, - {file = "socksio-1.0.0.tar.gz", hash = "sha256:f88beb3da5b5c38b9890469de67d0cb0f9d494b78b106ca1845f96c10b91c4ac"}, -] - [[package]] name = "starlette" -version = "0.27.0" +version = "0.35.1" description = "The little ASGI library that shines." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91"}, - {file = "starlette-0.27.0.tar.gz", hash = "sha256:6a6b0d042acb8d469a01eba54e9cda6cbd24ac602c4cd016723117d6a7e73b75"}, + {file = "starlette-0.35.1-py3-none-any.whl", hash = "sha256:50bbbda9baa098e361f398fda0928062abbaf1f54f4fadcbe17c092a01eb9a25"}, + {file = "starlette-0.35.1.tar.gz", hash = "sha256:3e2639dac3520e4f58734ed22553f950d3f3cb1001cd2eaac4d57e8cdc5f66bc"}, ] [package.dependencies] @@ -3180,17 +3009,18 @@ files = [ [[package]] name = "urllib3" -version = "2.1.0" +version = "2.2.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.8" files = [ - {file = "urllib3-2.1.0-py3-none-any.whl", hash = "sha256:55901e917a5896a349ff771be919f8bd99aff50b79fe58fec595eb37bbc56bb3"}, - {file = "urllib3-2.1.0.tar.gz", hash = "sha256:df7aa8afb0148fa78488e7899b2c59b5f4ffcfa82e6c54ccb9dd37c1d7b52d54"}, + {file = "urllib3-2.2.0-py3-none-any.whl", hash = "sha256:ce3711610ddce217e6d113a2732fafad960a03fd0318c91faa79481e35c11224"}, + {file = "urllib3-2.2.0.tar.gz", hash = "sha256:051d961ad0c62a94e50ecf1af379c3aba230c66c710493493560c0c223c49f20"}, ] [package.extras] brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] +h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -3655,5 +3485,5 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" -python-versions = ">=3.11.0a1,<4.0.0" -content-hash = "358734c295d95d10e9fe259e6fd7ee749305a716c3df0611d5b06d2e66f7cda8" +python-versions = ">=3.11.0a1,<3.13" +content-hash = "f6fc59682c7b0dff88c047fb2e75711a3f1ec116753cf608b947c36f8304f1f2" diff --git a/pyproject.toml b/pyproject.toml index 8ee2806d..42a8cfbb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,9 +9,9 @@ authors = [ readme = "README.md" [tool.poetry.dependencies] -python = ">=3.11.0a1,<4.0.0" -duckduckgo-search = "^3.9.11" -fastapi = "^0.104.1" +python = ">=3.11.0a1,<3.13" +duckduckgo-search = "^4.4" +fastapi = "^0.109.0" firebase-admin = "^6.3.0" functions = ">=0.7.0" gunicorn = "^21.2.0" diff --git a/requirements.txt b/requirements.txt index 9843230e..baf3d0a2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,99 +1,94 @@ -agency-swarm @ git+https://github.com/VRSEN/agency-swarm.git@b2f5d4ee73aa373c7cb2ecd6e1291e15753a7c10 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -aiofiles==23.2.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -aiohttp==3.9.1 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -aiosignal==1.3.1 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -annotated-types==0.6.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -anyio==3.7.1 ; python_full_version >= "3.11.0a1" and python_version < "4.0" +agency-swarm @ git+https://github.com/VRSEN/agency-swarm.git@2c598e2e7fa3e531244319627d4b3c270bfcb3f2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +aiohttp==3.9.3 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +aiosignal==1.3.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +annotated-types==0.6.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +anyio==4.2.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" async-timeout==4.0.3 ; python_full_version >= "3.11.0a1" and python_full_version <= "3.11.2" -attrs==23.2.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -bcrypt==4.1.2 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -brotli==1.1.0 ; platform_python_implementation == "CPython" and python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -brotlicffi==1.1.0.0 ; platform_python_implementation != "CPython" and python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -cachecontrol==0.13.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -cachetools==5.3.2 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -certifi==2023.11.17 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -cffi==1.16.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -charset-normalizer==3.3.2 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -click==8.1.7 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -colorama==0.4.6 ; python_full_version >= "3.11.0a1" and python_version < "4.0" and (sys_platform == "win32" or platform_system == "Windows") -cryptography==42.0.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -deepdiff==6.7.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -distro==1.9.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -docstring-parser==0.15 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -duckduckgo-search==3.9.11 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -ecdsa==0.18.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -fastapi==0.104.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -firebase-admin==6.4.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -frozenlist==1.4.1 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -functions==0.7.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -google-api-core==2.15.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -google-api-core[grpc]==2.15.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" and platform_python_implementation != "PyPy" -google-api-python-client==2.115.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -google-auth-httplib2==0.2.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -google-auth==2.27.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -google-cloud-core==2.4.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -google-cloud-firestore==2.14.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" and platform_python_implementation != "PyPy" -google-cloud-storage==2.14.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -google-crc32c==1.5.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -google-resumable-media==2.7.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -googleapis-common-protos==1.62.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -grpcio-status==1.60.0 ; python_version >= "3.11" and python_full_version < "4.0.0" and platform_python_implementation != "PyPy" -grpcio==1.60.0 ; python_version >= "3.11" and python_full_version < "4.0.0" and platform_python_implementation != "PyPy" -gunicorn==21.2.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -h11==0.14.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -h2==4.1.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -hpack==4.0.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -httpcore==1.0.2 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -httplib2==0.22.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -httptools==0.6.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -httpx==0.26.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -httpx[brotli,http2,socks]==0.26.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -hyperframe==6.0.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -idna==3.6 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -inflection==0.5.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -instructor==0.4.5 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -jsonref==1.1.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -lxml==5.1.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -markdown-it-py==3.0.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -mdurl==0.1.2 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -msgpack==1.0.7 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -multidict==6.0.4 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -openai==1.5.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -ordered-set==4.1.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -packaging==23.2 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -passlib[bcrypt]==1.7.4 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -proto-plus==1.23.0 ; python_version >= "3.11" and python_full_version < "4.0.0" and platform_python_implementation != "PyPy" -protobuf==4.25.2 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -pyairtable==2.2.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -pyasn1-modules==0.3.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -pyasn1==0.5.1 ; python_full_version >= "3.11.0a1" and python_version < "4" -pycparser==2.21 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -pydantic-core==2.14.6 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -pydantic-settings==2.1.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -pydantic==2.5.3 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -pygments==2.17.2 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -pyjwt[crypto]==2.8.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -pyparsing==3.1.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -python-dotenv==1.0.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -python-jose[cryptography]==3.3.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -python-multipart==0.0.6 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -pyyaml==6.0.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -redis==5.0.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -requests==2.31.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -rich==13.7.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -rsa==4.9 ; python_full_version >= "3.11.0a1" and python_version < "4" -six==1.16.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -sniffio==1.3.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -socksio==1.0.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -starlette==0.27.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -termcolor==2.3.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -tqdm==4.66.1 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -typer==0.9.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -typing-extensions==4.9.0 ; python_full_version >= "3.11.0a1" and python_version < "4.0" -uritemplate==4.1.1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -urllib3==2.1.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -uvicorn[standard]==0.24.0.post1 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -watchfiles==0.21.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -websockets==12.0 ; python_full_version >= "3.11.0a1" and python_full_version < "4.0.0" -yarl==1.9.4 ; python_full_version >= "3.11.0a1" and python_version < "4.0" +attrs==23.2.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +bcrypt==4.1.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +cachecontrol==0.13.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +cachetools==5.3.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +certifi==2023.11.17 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +cffi==1.16.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +charset-normalizer==3.3.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +click==8.1.7 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +colorama==0.4.6 ; python_full_version >= "3.11.0a1" and python_version < "3.13" and (sys_platform == "win32" or platform_system == "Windows") +cryptography==42.0.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +curl-cffi==0.6.0b7 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +deepdiff==6.7.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +distro==1.9.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +docstring-inheritance==2.1.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +docstring-parser==0.15 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +duckduckgo-search==4.4 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +ecdsa==0.18.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +fastapi==0.109.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +firebase-admin==6.4.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +frozenlist==1.4.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +functions==0.7.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +google-api-core==2.16.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +google-api-core[grpc]==2.16.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" and platform_python_implementation != "PyPy" +google-api-python-client==2.116.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +google-auth-httplib2==0.2.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +google-auth==2.27.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +google-cloud-core==2.4.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +google-cloud-firestore==2.14.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" and platform_python_implementation != "PyPy" +google-cloud-storage==2.14.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +google-crc32c==1.5.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +google-resumable-media==2.7.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +googleapis-common-protos==1.62.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +grpcio-status==1.60.0 ; python_version >= "3.11" and python_version < "3.13" and platform_python_implementation != "PyPy" +grpcio==1.60.0 ; python_version >= "3.11" and python_version < "3.13" and platform_python_implementation != "PyPy" +gunicorn==21.2.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +h11==0.14.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +httpcore==1.0.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +httplib2==0.22.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +httptools==0.6.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +httpx==0.26.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +idna==3.6 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +inflection==0.5.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +instructor==0.4.5 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +jsonref==1.1.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +lxml==5.1.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +markdown-it-py==3.0.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +mdurl==0.1.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +msgpack==1.0.7 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +multidict==6.0.4 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +nest-asyncio==1.6.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +openai==1.5.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +ordered-set==4.1.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +packaging==23.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +passlib[bcrypt]==1.7.4 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +proto-plus==1.23.0 ; python_version >= "3.11" and python_version < "3.13" and platform_python_implementation != "PyPy" +protobuf==4.25.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pyairtable==2.2.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pyasn1-modules==0.3.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pyasn1==0.5.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pycparser==2.21 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pydantic-core==2.16.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pydantic-settings==2.1.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pydantic==2.6.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pygments==2.17.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pyjwt[crypto]==2.8.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pyparsing==3.1.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +python-dotenv==1.0.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +python-jose[cryptography]==3.3.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +python-multipart==0.0.6 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +pyyaml==6.0.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +redis==5.0.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +requests==2.31.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +rich==13.7.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +rsa==4.9 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +six==1.16.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +sniffio==1.3.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +starlette==0.35.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +termcolor==2.3.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +tqdm==4.66.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +typer==0.9.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +typing-extensions==4.9.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +uritemplate==4.1.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +urllib3==2.2.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +uvicorn[standard]==0.24.0.post1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +uvloop==0.19.0 ; (sys_platform != "win32" and sys_platform != "cygwin") and platform_python_implementation != "PyPy" and python_full_version >= "3.11.0a1" and python_version < "3.13" +watchfiles==0.21.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +websockets==12.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +yarl==1.9.4 ; python_full_version >= "3.11.0a1" and python_version < "3.13" From 16b302fd96cf074650c405a611ff12ef21f72fcd Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 21:54:16 +0000 Subject: [PATCH 09/25] Add print_all_files_in_path.py --- nalgonda/custom_tools/__init__.py | 2 + .../custom_tools/print_all_files_in_path.py | 69 +++++++++++++++++ .../test_print_all_files_in_path.py | 75 +++++++++++++++++++ 3 files changed, 146 insertions(+) create mode 100644 nalgonda/custom_tools/print_all_files_in_path.py create mode 100644 tests/unit/custom_tools/test_print_all_files_in_path.py diff --git a/nalgonda/custom_tools/__init__.py b/nalgonda/custom_tools/__init__.py index 0cb46a37..922e7a01 100644 --- a/nalgonda/custom_tools/__init__.py +++ b/nalgonda/custom_tools/__init__.py @@ -3,6 +3,7 @@ from nalgonda.custom_tools.build_directory_tree import BuildDirectoryTree from nalgonda.custom_tools.generate_proposal import GenerateProposal +from nalgonda.custom_tools.print_all_files_in_path import PrintAllFilesInPath from nalgonda.custom_tools.print_file_contents import PrintFileContents from nalgonda.custom_tools.save_lead_to_airtable import SaveLeadToAirtable from nalgonda.custom_tools.search_web import SearchWeb @@ -14,6 +15,7 @@ "Retrieval": Retrieval, "BuildDirectoryTree": BuildDirectoryTree, "GenerateProposal": GenerateProposal, + "PrintAllFilesInPath": PrintAllFilesInPath, "PrintFileContents": PrintFileContents, "SaveLeadToAirtable": SaveLeadToAirtable, "SearchWeb": SearchWeb, diff --git a/nalgonda/custom_tools/print_all_files_in_path.py b/nalgonda/custom_tools/print_all_files_in_path.py new file mode 100644 index 00000000..8ae5576f --- /dev/null +++ b/nalgonda/custom_tools/print_all_files_in_path.py @@ -0,0 +1,69 @@ +from pathlib import Path + +from agency_swarm import BaseTool +from pydantic import Field, field_validator + +from nalgonda.custom_tools.utils import check_directory_traversal + + +class PrintAllFilesInPath(BaseTool): + """Print the contents of all files in a start_path recursively. + The parameters are: start_path, file_extensions. + Directory traversal is not allowed (you cannot read /* or ../*). + """ + + start_path: Path = Field( + default_factory=Path.cwd, + description="The starting path to search for files, defaults to the current working directory. " + "Can be a filename or a directory.", + ) + file_extensions: set[str] = Field( + default_factory=set, + description="Set of file extensions to include in the tree. If empty, all files will be included. " + "Examples are {'.py', '.txt', '.md'}.", + ) + + _validate_start_path = field_validator("start_path", mode="after")(check_directory_traversal) + + def run(self) -> str: + """ + Recursively searches for files within `start_path` and compiles their contents into a single string. + """ + output = [] + start_path = self.start_path.resolve() + + # if start_path is a file, just read it + if start_path.is_file(): + return f"{str(start_path)}:\n```\n{self.read_file(start_path)}\n```\n" + + for path in start_path.rglob("*"): + # ignore files in hidden directories + if any(part.startswith(".") for part in path.parts): + continue + if path.is_file() and (not self.file_extensions or path.suffix in self.file_extensions): + output.append(f"{str(path)}:\n```\n{self.read_file(path)}\n```\n") + + output_str = "\n".join(output) + + if len(output_str) > 20000: + output_str = ( + output_str[:20000] + "\n\n... (truncated output, please use a smaller directory or apply a filter)" + ) + return output_str + + @staticmethod + def read_file(file_path: Path): + """Read and return the contents of a file.""" + try: + return file_path.read_text() + except OSError as e: + return f"Error reading file {file_path}: {e}" + + +if __name__ == "__main__": + print( + PrintAllFilesInPath( + start_path=".", + file_extensions={".py", ".json", ".yaml", ".yml", ".md", ".txt", ".tsx", ".ts", ".js", ".jsx", ".html"}, + ).run() + ) diff --git a/tests/unit/custom_tools/test_print_all_files_in_path.py b/tests/unit/custom_tools/test_print_all_files_in_path.py new file mode 100644 index 00000000..3ecb4f7a --- /dev/null +++ b/tests/unit/custom_tools/test_print_all_files_in_path.py @@ -0,0 +1,75 @@ +import pytest + +from nalgonda.custom_tools import PrintAllFilesInPath + + +def test_print_all_files_no_extension_filter(temp_dir): + """ + Test if PrintAllFilesInPath correctly prints contents of all files when no file extension filter is applied. + """ + pafid = PrintAllFilesInPath(start_path=temp_dir) + expected_output = { + f"{temp_dir}/sub/test.py:\n```\nprint('hello')\n```", + f"{temp_dir}/sub/test.txt:\n```\nhello world\n```", + } + actual_output = set(pafid.run().strip().split("\n\n")) + assert actual_output == expected_output + + +def test_print_all_files_with_py_extension(temp_dir): + """ + Test if PrintAllFilesInPath correctly prints contents of .py files only. + """ + pafid = PrintAllFilesInPath(start_path=temp_dir, file_extensions={".py"}) + expected_output = f"{temp_dir.joinpath('sub', 'test.py')}:\n```\nprint('hello')\n```\n" + assert pafid.run() == expected_output + + +def test_print_all_files_with_txt_extension(temp_dir): + """ + Test if PrintAllFilesInPath correctly prints contents of .txt files only. + """ + pafid = PrintAllFilesInPath(start_path=temp_dir, file_extensions={".txt"}) + expected_output = f"{temp_dir.joinpath('sub', 'test.txt')}:\n```\nhello world\n```\n" + assert pafid.run() == expected_output + + +def test_print_all_files_error_reading_file(temp_dir): + """ + Test if PrintAllFilesInPath handles errors while reading a file. + """ + # Create an unreadable file + unreadable_file = temp_dir.joinpath("unreadable_file.txt") + unreadable_file.write_text("content") + unreadable_file.chmod(0o000) # make the file unreadable + + pafid = PrintAllFilesInPath(start_path=temp_dir, file_extensions={".txt"}) + assert "Error reading file" in pafid.run() + + unreadable_file.chmod(0o644) # reset file permissions for cleanup + + +@pytest.mark.parametrize("extension, expected_file", [(".py", "test.py"), (".txt", "test.txt")]) +def test_print_all_files_with_extension_filter(temp_dir, extension, expected_file): + pafip = PrintAllFilesInPath(start_path=temp_dir, file_extensions={extension}) + expected_output = ( + f"{temp_dir.joinpath('sub', expected_file)}:\n```\n" + + temp_dir.joinpath("sub", expected_file).read_text() + + "\n```" + ) + assert pafip.run().strip() == expected_output.strip() + + +@pytest.fixture +def create_file_in_path(tmp_path): + # Create a file and write contents to it + file_path = tmp_path / "example.txt" + file_path.write_text("File content") + return file_path + + +def test_print_file_contents(create_file_in_path): + tool = PrintAllFilesInPath(start_path=create_file_in_path, file_extensions=[".txt"]) + result = tool.run() + expected_result = f"{str(create_file_in_path)}:\n```\nFile content\n```\n" + assert result == expected_result From b31880e84fcafb2b133368c54cff39c8340234e6 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:02:58 +0000 Subject: [PATCH 10/25] Add summarize_all_code_in_path.py --- nalgonda/custom_tools/__init__.py | 2 + .../custom_tools/print_all_files_in_path.py | 9 ++- .../summarize_all_code_in_path.py | 77 +++++++++++++++++++ .../test_summarize_all_code_in_path.py | 40 ++++++++++ 4 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 nalgonda/custom_tools/summarize_all_code_in_path.py create mode 100644 tests/unit/custom_tools/test_summarize_all_code_in_path.py diff --git a/nalgonda/custom_tools/__init__.py b/nalgonda/custom_tools/__init__.py index 922e7a01..dea25c0f 100644 --- a/nalgonda/custom_tools/__init__.py +++ b/nalgonda/custom_tools/__init__.py @@ -7,6 +7,7 @@ from nalgonda.custom_tools.print_file_contents import PrintFileContents from nalgonda.custom_tools.save_lead_to_airtable import SaveLeadToAirtable from nalgonda.custom_tools.search_web import SearchWeb +from nalgonda.custom_tools.summarize_all_code_in_path import SummarizeAllCodeInPath from nalgonda.custom_tools.summarize_code import SummarizeCode from nalgonda.custom_tools.write_and_save_program import WriteAndSaveProgram @@ -19,6 +20,7 @@ "PrintFileContents": PrintFileContents, "SaveLeadToAirtable": SaveLeadToAirtable, "SearchWeb": SearchWeb, + "SummarizeAllCodeInPath": SummarizeAllCodeInPath, "SummarizeCode": SummarizeCode, "WriteAndSaveProgram": WriteAndSaveProgram, } diff --git a/nalgonda/custom_tools/print_all_files_in_path.py b/nalgonda/custom_tools/print_all_files_in_path.py index 8ae5576f..434a362f 100644 --- a/nalgonda/custom_tools/print_all_files_in_path.py +++ b/nalgonda/custom_tools/print_all_files_in_path.py @@ -22,6 +22,10 @@ class PrintAllFilesInPath(BaseTool): description="Set of file extensions to include in the tree. If empty, all files will be included. " "Examples are {'.py', '.txt', '.md'}.", ) + truncate_to: int = Field( + default=None, + description="Truncate the output to this many characters. If None or skipped, the output is not truncated.", + ) _validate_start_path = field_validator("start_path", mode="after")(check_directory_traversal) @@ -45,9 +49,10 @@ def run(self) -> str: output_str = "\n".join(output) - if len(output_str) > 20000: + if self.truncate_to and len(output_str) > self.truncate_to: output_str = ( - output_str[:20000] + "\n\n... (truncated output, please use a smaller directory or apply a filter)" + output_str[: self.truncate_to] + + "\n\n... (truncated output, please use a smaller directory or apply a filter)" ) return output_str diff --git a/nalgonda/custom_tools/summarize_all_code_in_path.py b/nalgonda/custom_tools/summarize_all_code_in_path.py new file mode 100644 index 00000000..10959f52 --- /dev/null +++ b/nalgonda/custom_tools/summarize_all_code_in_path.py @@ -0,0 +1,77 @@ +from pathlib import Path + +from agency_swarm import BaseTool +from pydantic import Field + +from nalgonda.custom_tools import PrintAllFilesInPath +from nalgonda.settings import settings +from nalgonda.utils import get_chat_completion + +USER_PROMPT_PREFIX = "Summarize the code of each file below.\n\n" +SYSTEM_MESSAGE = """\ +Your main job is to handle programming code from SEVERAL FILES. \ +Each file's content is shown within triple backticks and has a FILE PATH as a title. \ +It's vital to KEEP the FILE PATHS. +Here's what to do: +1. ALWAYS KEEP the FILE PATHS for each file. +2. Start each file with a short SUMMARY of its content. Mention important points but don't repeat details found later. +3. KEEP important elements like non-trivial imports, function details, type hints, and key constants. \ +Don't change these. +4. In functions or class methods, replace long code with a short SUMMARY in the docstrings, keeping the main logic. +5. Shorten and combine docstrings and comments into the function or method descriptions. +6. For classes, provide a brief SUMMARY in the docstrings, explaining the class's purpose and main logic. +7. Cut down long strings to keep things brief. +8. If there's a comment about "truncated output" at the end, KEEP it. + +Your task is to create a concise version of the code, strictly keeping the FILE PATHS and structure, \ +without extra comments or explanations. Focus on clarity and avoiding repeated information within each file.\ +""" + + +class SummarizeAllCodeInPath(BaseTool): + """Summarize code using GPT-3. The tool uses the `PrintAllFilesInPath` tool to get the code to summarize. + The parameters are: start_path, file_extensions. + Directory traversal is not allowed (you cannot read /* or ../*). + """ + + start_path: Path = Field( + default_factory=Path.cwd, + description="The starting path to search for files, defaults to the current working directory. " + "Can be a filename or a directory.", + ) + file_extensions: set[str] = Field( + default_factory=set, + description="Set of file extensions to include in the tree. If empty, all files will be included. " + "Examples are {'.py', '.txt', '.md'}.", + ) + truncate_to: int = Field( + default=None, + description="Truncate the output to this many characters. If None or skipped, the output is not truncated.", + ) + + def run(self) -> str: + full_code = PrintAllFilesInPath( + start_path=self.start_path, + file_extensions=self.file_extensions, + ).run() + user_prompt = f"{USER_PROMPT_PREFIX}{full_code}" + + output = get_chat_completion( + user_prompt=user_prompt, system_message=SYSTEM_MESSAGE, temperature=0.0, model=settings.gpt_cheap_model + ) + + if self.truncate_to and len(output) > self.truncate_to: + output = ( + output[: self.truncate_to] + + "\n\n... (truncated output, please use a smaller directory or apply a filter)" + ) + return output + + +if __name__ == "__main__": + print( + SummarizeAllCodeInPath( + start_path=".", + file_extensions={".py"}, + ).run() + ) diff --git a/tests/unit/custom_tools/test_summarize_all_code_in_path.py b/tests/unit/custom_tools/test_summarize_all_code_in_path.py new file mode 100644 index 00000000..43294b92 --- /dev/null +++ b/tests/unit/custom_tools/test_summarize_all_code_in_path.py @@ -0,0 +1,40 @@ +from pathlib import Path +from unittest.mock import Mock, patch + +import pytest + +from nalgonda.custom_tools import SummarizeAllCodeInPath + + +@pytest.fixture +def mock_openai_response(): + class MockCompletion: + message = Mock(content="Summary of the code") + + class MockOpenAIResponse: + choices = [MockCompletion()] + + return MockOpenAIResponse() + + +@patch("nalgonda.utils.get_openai_client") +def test_summarize_all_code_in_path_with_valid_codebase(mock_openai_client, mock_openai_response, tmp_path): + # Create a simple Python file + (tmp_path / "test.py").write_text('print("Hello, World!")') + mock_openai_client.return_value.chat.completions.create.return_value = mock_openai_response + + summarize_tool = SummarizeAllCodeInPath(start_path=Path(tmp_path)) + results = summarize_tool.run() + assert "Summary of the code" in results + mock_openai_client.assert_called_once_with() + + +@patch("nalgonda.utils.get_openai_client", side_effect=Exception("API failed")) +def test_summarize_all_code_in_path_with_api_failure(mock_openai_client, tmp_path): + # Create a simple Python file + (tmp_path / "test.py").write_text('print("Hello, World!")') + summarize_tool = SummarizeAllCodeInPath(start_path=Path(tmp_path)) + with pytest.raises(Exception) as exc_info: + summarize_tool.run() + assert "API failed" in str(exc_info.value) + mock_openai_client.assert_called_once_with() From 66a0cf330e52a69531a22d067b4dca1b2c31c304 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:10:32 +0000 Subject: [PATCH 11/25] Bug fix --- nalgonda/custom_tools/print_all_files_in_path.py | 14 +++----------- nalgonda/custom_tools/print_file_contents.py | 12 ++---------- nalgonda/custom_tools/utils.py | 10 ++++++++++ 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/nalgonda/custom_tools/print_all_files_in_path.py b/nalgonda/custom_tools/print_all_files_in_path.py index 434a362f..fb658aa0 100644 --- a/nalgonda/custom_tools/print_all_files_in_path.py +++ b/nalgonda/custom_tools/print_all_files_in_path.py @@ -3,7 +3,7 @@ from agency_swarm import BaseTool from pydantic import Field, field_validator -from nalgonda.custom_tools.utils import check_directory_traversal +from nalgonda.custom_tools.utils import check_directory_traversal, read_file class PrintAllFilesInPath(BaseTool): @@ -38,14 +38,14 @@ def run(self) -> str: # if start_path is a file, just read it if start_path.is_file(): - return f"{str(start_path)}:\n```\n{self.read_file(start_path)}\n```\n" + return f"{str(start_path)}:\n```\n{read_file(start_path)}\n```\n" for path in start_path.rglob("*"): # ignore files in hidden directories if any(part.startswith(".") for part in path.parts): continue if path.is_file() and (not self.file_extensions or path.suffix in self.file_extensions): - output.append(f"{str(path)}:\n```\n{self.read_file(path)}\n```\n") + output.append(f"{str(path)}:\n```\n{read_file(path)}\n```\n") output_str = "\n".join(output) @@ -56,14 +56,6 @@ def run(self) -> str: ) return output_str - @staticmethod - def read_file(file_path: Path): - """Read and return the contents of a file.""" - try: - return file_path.read_text() - except OSError as e: - return f"Error reading file {file_path}: {e}" - if __name__ == "__main__": print( diff --git a/nalgonda/custom_tools/print_file_contents.py b/nalgonda/custom_tools/print_file_contents.py index 0db03486..b82c3908 100644 --- a/nalgonda/custom_tools/print_file_contents.py +++ b/nalgonda/custom_tools/print_file_contents.py @@ -3,7 +3,7 @@ from agency_swarm import BaseTool from pydantic import Field, field_validator -from nalgonda.custom_tools.utils import check_directory_traversal +from nalgonda.custom_tools.utils import check_directory_traversal, read_file class PrintFileContents(BaseTool): @@ -18,18 +18,10 @@ def run(self) -> str: file_path = Path(self.file_name).resolve() if file_path.is_file(): - return f"{str(file_path)}:\n```\n{self.read_file(file_path)}\n```\n" + return f"{str(file_path)}:\n```\n{read_file(file_path)}\n```\n" else: return f"File {self.file_name} not found or is not a file." - @staticmethod - def read_file(file_path: Path): - """Read and return the contents of a file.""" - try: - return file_path.read_text() - except OSError as e: - return f"Error reading file {file_path}: {e}" - if __name__ == "__main__": print( diff --git a/nalgonda/custom_tools/utils.py b/nalgonda/custom_tools/utils.py index 2abb413b..18a62158 100644 --- a/nalgonda/custom_tools/utils.py +++ b/nalgonda/custom_tools/utils.py @@ -16,3 +16,13 @@ def check_directory_traversal(path: Path) -> Path: raise ValueError("Directory traversal is not allowed.") return resolved_path + + +def read_file(file_path: Path): + """Read and return the contents of a file.""" + try: + return file_path.read_text() + except OSError as e: + return f"Error reading file {file_path}: {e}" + except UnicodeDecodeError as e: + return f"Error decoding file {file_path}: {e}" From f852b1d6897c0b97d730972e9e83556192622af2 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:41:56 +0000 Subject: [PATCH 12/25] Add output truncation based on tokens --- .../summarize_all_code_in_path.py | 28 +++- nalgonda/utils.py | 51 ++++++ poetry.lock | 156 +++++++++++++++++- pyproject.toml | 1 + requirements.txt | 2 + 5 files changed, 229 insertions(+), 9 deletions(-) diff --git a/nalgonda/custom_tools/summarize_all_code_in_path.py b/nalgonda/custom_tools/summarize_all_code_in_path.py index 10959f52..fc8fb77f 100644 --- a/nalgonda/custom_tools/summarize_all_code_in_path.py +++ b/nalgonda/custom_tools/summarize_all_code_in_path.py @@ -5,7 +5,7 @@ from nalgonda.custom_tools import PrintAllFilesInPath from nalgonda.settings import settings -from nalgonda.utils import get_chat_completion +from nalgonda.utils import chunk_input_with_token_limit, get_chat_completion USER_PROMPT_PREFIX = "Summarize the code of each file below.\n\n" SYSTEM_MESSAGE = """\ @@ -50,22 +50,34 @@ class SummarizeAllCodeInPath(BaseTool): ) def run(self) -> str: + """Run the tool and return the output.""" + delimiter = "\n```\n" + full_code = PrintAllFilesInPath( start_path=self.start_path, file_extensions=self.file_extensions, ).run() user_prompt = f"{USER_PROMPT_PREFIX}{full_code}" - output = get_chat_completion( - user_prompt=user_prompt, system_message=SYSTEM_MESSAGE, temperature=0.0, model=settings.gpt_cheap_model - ) + # Chunk the input based on token limit + chunks = chunk_input_with_token_limit(user_prompt, max_tokens=16385, delimiter=delimiter) + + outputs = [] + for chunk in chunks: + output = get_chat_completion( + user_prompt=chunk, system_message=SYSTEM_MESSAGE, temperature=0.0, model=settings.gpt_cheap_model + ) + outputs.append(output) - if self.truncate_to and len(output) > self.truncate_to: - output = ( - output[: self.truncate_to] + # Concatenate and possibly truncate outputs + concatenated_output = delimiter.join(outputs) + if self.truncate_to and len(concatenated_output) > self.truncate_to: + concatenated_output = ( + concatenated_output[: self.truncate_to] + "\n\n... (truncated output, please use a smaller directory or apply a filter)" ) - return output + + return concatenated_output if __name__ == "__main__": diff --git a/nalgonda/utils.py b/nalgonda/utils.py index 4683c0fe..33b60bae 100644 --- a/nalgonda/utils.py +++ b/nalgonda/utils.py @@ -1,7 +1,11 @@ +import logging from pathlib import Path +import tiktoken from agency_swarm import get_openai_client +logger = logging.getLogger(__name__) + def init_webserver_folders(root_file_path: Path) -> dict[str, Path]: """ @@ -32,3 +36,50 @@ def get_chat_completion(system_message: str, user_prompt: str, model: str, **kwa **kwargs, ) return completion.choices[0].message.content + + +def tokenize(text: str) -> list[int]: + """Tokenize a string using tiktoken tokenizer.""" + return tiktoken.get_encoding("gpt-4").encode(text) + + +def get_token_count(text: str) -> int: + """Get the number of tokens using tiktoken tokenizer.""" + return len(tokenize(text)) + + +def chunk_input_with_token_limit(input_str: str, max_tokens: int = 16385, delimiter: str = "\n```\n") -> list: + chunks = [] + parts = input_str.split(delimiter) + current_chunk: list[str] = [] + + current_tokens = 0 + for part in parts: + part_token_count = get_token_count(part + delimiter) + if current_tokens + part_token_count > max_tokens: + new_chunk = delimiter.join(current_chunk) + if get_token_count(new_chunk) > max_tokens: + logger.warning(f"Part of the input is longer than {max_tokens} tokens.") + new_chunk = truncate_oversized_chunk(new_chunk, max_tokens, delimiter) + chunks.append(new_chunk) + current_chunk = [part] + current_tokens = part_token_count + else: + current_chunk.append(part) + current_tokens += part_token_count + + # Add the last chunk if it's not empty + if current_chunk: + chunks.append(delimiter.join(current_chunk)) + + return chunks + + +def truncate_oversized_chunk(chunk: str, max_tokens: int = 16385, delimiter: str = "\n```\n", margin: int = 10) -> str: + """Truncate the chunk if it is longer than max_tokens.""" + tokens = tokenize(chunk) + if len(tokens) > max_tokens: + tokens = tokens[: max_tokens - margin] + chunk = tiktoken.get_encoding("gpt-4").decode(tokens) + chunk += delimiter + return chunk diff --git a/poetry.lock b/poetry.lock index ad45f062..0ff83650 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2749,6 +2749,108 @@ async-timeout = {version = ">=4.0.2", markers = "python_full_version <= \"3.11.2 hiredis = ["hiredis (>=1.0.0)"] ocsp = ["cryptography (>=36.0.1)", "pyopenssl (==20.0.1)", "requests (>=2.26.0)"] +[[package]] +name = "regex" +version = "2023.12.25" +description = "Alternative regular expression module, to replace re." +optional = false +python-versions = ">=3.7" +files = [ + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, + {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, + {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, + {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, + {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, + {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, + {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, + {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, + {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, + {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, + {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, + {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, + {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, + {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, + {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, + {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, + {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, + {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, + {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, + {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, + {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, + {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, + {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, + {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, + {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, + {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, + {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, + {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, + {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, + {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, + {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, + {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, +] + [[package]] name = "requests" version = "2.31.0" @@ -2911,6 +3013,58 @@ files = [ [package.extras] tests = ["pytest", "pytest-cov"] +[[package]] +name = "tiktoken" +version = "0.5.2" +description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" +optional = false +python-versions = ">=3.8" +files = [ + {file = "tiktoken-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8c4e654282ef05ec1bd06ead22141a9a1687991cef2c6a81bdd1284301abc71d"}, + {file = "tiktoken-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7b3134aa24319f42c27718c6967f3c1916a38a715a0fa73d33717ba121231307"}, + {file = "tiktoken-0.5.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6092e6e77730929c8c6a51bb0d7cfdf1b72b63c4d033d6258d1f2ee81052e9e5"}, + {file = "tiktoken-0.5.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72ad8ae2a747622efae75837abba59be6c15a8f31b4ac3c6156bc56ec7a8e631"}, + {file = "tiktoken-0.5.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51cba7c8711afa0b885445f0637f0fcc366740798c40b981f08c5f984e02c9d1"}, + {file = "tiktoken-0.5.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3d8c7d2c9313f8e92e987d585ee2ba0f7c40a0de84f4805b093b634f792124f5"}, + {file = "tiktoken-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:692eca18c5fd8d1e0dde767f895c17686faaa102f37640e884eecb6854e7cca7"}, + {file = "tiktoken-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:138d173abbf1ec75863ad68ca289d4da30caa3245f3c8d4bfb274c4d629a2f77"}, + {file = "tiktoken-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7388fdd684690973fdc450b47dfd24d7f0cbe658f58a576169baef5ae4658607"}, + {file = "tiktoken-0.5.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a114391790113bcff670c70c24e166a841f7ea8f47ee2fe0e71e08b49d0bf2d4"}, + {file = "tiktoken-0.5.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca96f001e69f6859dd52926d950cfcc610480e920e576183497ab954e645e6ac"}, + {file = "tiktoken-0.5.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:15fed1dd88e30dfadcdd8e53a8927f04e1f6f81ad08a5ca824858a593ab476c7"}, + {file = "tiktoken-0.5.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:93f8e692db5756f7ea8cb0cfca34638316dcf0841fb8469de8ed7f6a015ba0b0"}, + {file = "tiktoken-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:bcae1c4c92df2ffc4fe9f475bf8148dbb0ee2404743168bbeb9dcc4b79dc1fdd"}, + {file = "tiktoken-0.5.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b76a1e17d4eb4357d00f0622d9a48ffbb23401dcf36f9716d9bd9c8e79d421aa"}, + {file = "tiktoken-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:01d8b171bb5df4035580bc26d4f5339a6fd58d06f069091899d4a798ea279d3e"}, + {file = "tiktoken-0.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42adf7d4fb1ed8de6e0ff2e794a6a15005f056a0d83d22d1d6755a39bffd9e7f"}, + {file = "tiktoken-0.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3f894dbe0adb44609f3d532b8ea10820d61fdcb288b325a458dfc60fefb7db"}, + {file = "tiktoken-0.5.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58ccfddb4e62f0df974e8f7e34a667981d9bb553a811256e617731bf1d007d19"}, + {file = "tiktoken-0.5.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58902a8bad2de4268c2a701f1c844d22bfa3cbcc485b10e8e3e28a050179330b"}, + {file = "tiktoken-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:5e39257826d0647fcac403d8fa0a474b30d02ec8ffc012cfaf13083e9b5e82c5"}, + {file = "tiktoken-0.5.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8bde3b0fbf09a23072d39c1ede0e0821f759b4fa254a5f00078909158e90ae1f"}, + {file = "tiktoken-0.5.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2ddee082dcf1231ccf3a591d234935e6acf3e82ee28521fe99af9630bc8d2a60"}, + {file = "tiktoken-0.5.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35c057a6a4e777b5966a7540481a75a31429fc1cb4c9da87b71c8b75b5143037"}, + {file = "tiktoken-0.5.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c4a049b87e28f1dc60509f8eb7790bc8d11f9a70d99b9dd18dfdd81a084ffe6"}, + {file = "tiktoken-0.5.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5bf5ce759089f4f6521ea6ed89d8f988f7b396e9f4afb503b945f5c949c6bec2"}, + {file = "tiktoken-0.5.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0c964f554af1a96884e01188f480dad3fc224c4bbcf7af75d4b74c4b74ae0125"}, + {file = "tiktoken-0.5.2-cp38-cp38-win_amd64.whl", hash = "sha256:368dd5726d2e8788e47ea04f32e20f72a2012a8a67af5b0b003d1e059f1d30a3"}, + {file = "tiktoken-0.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a2deef9115b8cd55536c0a02c0203512f8deb2447f41585e6d929a0b878a0dd2"}, + {file = "tiktoken-0.5.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2ed7d380195affbf886e2f8b92b14edfe13f4768ff5fc8de315adba5b773815e"}, + {file = "tiktoken-0.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c76fce01309c8140ffe15eb34ded2bb94789614b7d1d09e206838fc173776a18"}, + {file = "tiktoken-0.5.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60a5654d6a2e2d152637dd9a880b4482267dfc8a86ccf3ab1cec31a8c76bfae8"}, + {file = "tiktoken-0.5.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:41d4d3228e051b779245a8ddd21d4336f8975563e92375662f42d05a19bdff41"}, + {file = "tiktoken-0.5.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a5c1cdec2c92fcde8c17a50814b525ae6a88e8e5b02030dc120b76e11db93f13"}, + {file = "tiktoken-0.5.2-cp39-cp39-win_amd64.whl", hash = "sha256:84ddb36faedb448a50b246e13d1b6ee3437f60b7169b723a4b2abad75e914f3e"}, + {file = "tiktoken-0.5.2.tar.gz", hash = "sha256:f54c581f134a8ea96ce2023ab221d4d4d81ab614efa0b2fbce926387deb56c80"}, +] + +[package.dependencies] +regex = ">=2022.1.18" +requests = ">=2.26.0" + +[package.extras] +blobfile = ["blobfile (>=2)"] + [[package]] name = "tomli" version = "2.0.1" @@ -3486,4 +3640,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = ">=3.11.0a1,<3.13" -content-hash = "f6fc59682c7b0dff88c047fb2e75711a3f1ec116753cf608b947c36f8304f1f2" +content-hash = "93af780e36394e0379964328c733fed71151e9296d6a3404e37f326a79fe060e" diff --git a/pyproject.toml b/pyproject.toml index 42a8cfbb..aa035dc2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,6 +24,7 @@ pydantic-settings = "^2.1.0" python-jose = {extras = ["cryptography"], version = "^3.3.0"} python-multipart = "^0.0.6" redis = "^5.0.1" +tiktoken = "^0.5.2" uvicorn = {extras = ["standard"], version = "^0.24.0.post1"} # a dependency from a GitHub repository agency-swarm = { git = "https://github.com/VRSEN/agency-swarm.git", branch = "main" } diff --git a/requirements.txt b/requirements.txt index baf3d0a2..8559cf7b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -75,6 +75,7 @@ python-jose[cryptography]==3.3.0 ; python_full_version >= "3.11.0a1" and python_ python-multipart==0.0.6 ; python_full_version >= "3.11.0a1" and python_version < "3.13" pyyaml==6.0.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" redis==5.0.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +regex==2023.12.25 ; python_full_version >= "3.11.0a1" and python_version < "3.13" requests==2.31.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" rich==13.7.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" rsa==4.9 ; python_full_version >= "3.11.0a1" and python_version < "3.13" @@ -82,6 +83,7 @@ six==1.16.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" sniffio==1.3.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" starlette==0.35.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" termcolor==2.3.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" +tiktoken==0.5.2 ; python_full_version >= "3.11.0a1" and python_version < "3.13" tqdm==4.66.1 ; python_full_version >= "3.11.0a1" and python_version < "3.13" typer==0.9.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" typing-extensions==4.9.0 ; python_full_version >= "3.11.0a1" and python_version < "3.13" From e6bb4819a1b985f85bf72bcc1e77f2fe84d74767 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:47:37 +0000 Subject: [PATCH 13/25] Add output truncation based on tokens --- nalgonda/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nalgonda/utils.py b/nalgonda/utils.py index 33b60bae..8f85162c 100644 --- a/nalgonda/utils.py +++ b/nalgonda/utils.py @@ -60,7 +60,7 @@ def chunk_input_with_token_limit(input_str: str, max_tokens: int = 16385, delimi new_chunk = delimiter.join(current_chunk) if get_token_count(new_chunk) > max_tokens: logger.warning(f"Part of the input is longer than {max_tokens} tokens.") - new_chunk = truncate_oversized_chunk(new_chunk, max_tokens, delimiter) + new_chunk = truncate_oversized_chunk(new_chunk, max_tokens=max_tokens, delimiter=delimiter) chunks.append(new_chunk) current_chunk = [part] current_tokens = part_token_count From 3ba7e77c6247e1d00e07b8f6cd181f53973c436f Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:53:41 +0000 Subject: [PATCH 14/25] Fix --- nalgonda/custom_tools/summarize_all_code_in_path.py | 6 ++---- nalgonda/custom_tools/summarize_code.py | 4 +--- nalgonda/utils.py | 8 ++++---- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/nalgonda/custom_tools/summarize_all_code_in_path.py b/nalgonda/custom_tools/summarize_all_code_in_path.py index fc8fb77f..6953480f 100644 --- a/nalgonda/custom_tools/summarize_all_code_in_path.py +++ b/nalgonda/custom_tools/summarize_all_code_in_path.py @@ -7,7 +7,6 @@ from nalgonda.settings import settings from nalgonda.utils import chunk_input_with_token_limit, get_chat_completion -USER_PROMPT_PREFIX = "Summarize the code of each file below.\n\n" SYSTEM_MESSAGE = """\ Your main job is to handle programming code from SEVERAL FILES. \ Each file's content is shown within triple backticks and has a FILE PATH as a title. \ @@ -51,16 +50,15 @@ class SummarizeAllCodeInPath(BaseTool): def run(self) -> str: """Run the tool and return the output.""" - delimiter = "\n```\n" + delimiter = "\n\n```\n" full_code = PrintAllFilesInPath( start_path=self.start_path, file_extensions=self.file_extensions, ).run() - user_prompt = f"{USER_PROMPT_PREFIX}{full_code}" # Chunk the input based on token limit - chunks = chunk_input_with_token_limit(user_prompt, max_tokens=16385, delimiter=delimiter) + chunks = chunk_input_with_token_limit(full_code, max_tokens=16385, delimiter=delimiter) outputs = [] for chunk in chunks: diff --git a/nalgonda/custom_tools/summarize_code.py b/nalgonda/custom_tools/summarize_code.py index aa828e05..f0b774c9 100644 --- a/nalgonda/custom_tools/summarize_code.py +++ b/nalgonda/custom_tools/summarize_code.py @@ -7,7 +7,6 @@ from nalgonda.settings import settings from nalgonda.utils import get_chat_completion -USER_PROMPT_PREFIX = "Summarize the code of the file below.\n\n" SYSTEM_MESSAGE = """\ Your main job is to handle programming code from A FILE. \ The file's content is shown within triple backticks and has a FILE PATH as a title. \ @@ -35,9 +34,8 @@ class SummarizeCode(BaseTool): def run(self) -> str: code = PrintFileContents(file_name=self.file_name).run() - user_prompt = f"{USER_PROMPT_PREFIX}{code}" output = get_chat_completion( - system_message=SYSTEM_MESSAGE, user_prompt=user_prompt, temperature=0.0, model=settings.gpt_cheap_model + system_message=SYSTEM_MESSAGE, user_prompt=code, temperature=0.0, model=settings.gpt_cheap_model ) return output diff --git a/nalgonda/utils.py b/nalgonda/utils.py index 8f85162c..c1d0c7f6 100644 --- a/nalgonda/utils.py +++ b/nalgonda/utils.py @@ -48,7 +48,7 @@ def get_token_count(text: str) -> int: return len(tokenize(text)) -def chunk_input_with_token_limit(input_str: str, max_tokens: int = 16385, delimiter: str = "\n```\n") -> list: +def chunk_input_with_token_limit(input_str: str, max_tokens: int, delimiter: str) -> list: chunks = [] parts = input_str.split(delimiter) current_chunk: list[str] = [] @@ -75,11 +75,11 @@ def chunk_input_with_token_limit(input_str: str, max_tokens: int = 16385, delimi return chunks -def truncate_oversized_chunk(chunk: str, max_tokens: int = 16385, delimiter: str = "\n```\n", margin: int = 10) -> str: +def truncate_oversized_chunk(chunk: str, max_tokens: int, delimiter: str) -> str: """Truncate the chunk if it is longer than max_tokens.""" - tokens = tokenize(chunk) + tokens = tokenize(chunk + delimiter) if len(tokens) > max_tokens: - tokens = tokens[: max_tokens - margin] + tokens = tokens[: max_tokens - get_token_count(delimiter)] chunk = tiktoken.get_encoding("gpt-4").decode(tokens) chunk += delimiter return chunk From 3d3e63131276e5e21b584848b69af118348d0bd6 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 23:01:06 +0000 Subject: [PATCH 15/25] Fix --- nalgonda/custom_tools/summarize_all_code_in_path.py | 4 ++-- nalgonda/utils.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/nalgonda/custom_tools/summarize_all_code_in_path.py b/nalgonda/custom_tools/summarize_all_code_in_path.py index 6953480f..8033cb37 100644 --- a/nalgonda/custom_tools/summarize_all_code_in_path.py +++ b/nalgonda/custom_tools/summarize_all_code_in_path.py @@ -58,12 +58,12 @@ def run(self) -> str: ).run() # Chunk the input based on token limit - chunks = chunk_input_with_token_limit(full_code, max_tokens=16385, delimiter=delimiter) + chunks = chunk_input_with_token_limit(full_code, max_tokens=16385 // 2, delimiter=delimiter) outputs = [] for chunk in chunks: output = get_chat_completion( - user_prompt=chunk, system_message=SYSTEM_MESSAGE, temperature=0.0, model=settings.gpt_cheap_model + system_message=SYSTEM_MESSAGE, user_prompt=chunk, temperature=0.0, model=settings.gpt_cheap_model ) outputs.append(output) diff --git a/nalgonda/utils.py b/nalgonda/utils.py index c1d0c7f6..70174139 100644 --- a/nalgonda/utils.py +++ b/nalgonda/utils.py @@ -40,7 +40,7 @@ def get_chat_completion(system_message: str, user_prompt: str, model: str, **kwa def tokenize(text: str) -> list[int]: """Tokenize a string using tiktoken tokenizer.""" - return tiktoken.get_encoding("gpt-4").encode(text) + return tiktoken.get_encoding("cl100k_base").encode(text) def get_token_count(text: str) -> int: @@ -61,7 +61,7 @@ def chunk_input_with_token_limit(input_str: str, max_tokens: int, delimiter: str if get_token_count(new_chunk) > max_tokens: logger.warning(f"Part of the input is longer than {max_tokens} tokens.") new_chunk = truncate_oversized_chunk(new_chunk, max_tokens=max_tokens, delimiter=delimiter) - chunks.append(new_chunk) + chunks.append(new_chunk + delimiter) current_chunk = [part] current_tokens = part_token_count else: @@ -70,7 +70,7 @@ def chunk_input_with_token_limit(input_str: str, max_tokens: int, delimiter: str # Add the last chunk if it's not empty if current_chunk: - chunks.append(delimiter.join(current_chunk)) + chunks.append(delimiter.join(current_chunk) + delimiter) return chunks From 92df98d928aeb08af3aef49cba3e34f5f14d9da0 Mon Sep 17 00:00:00 2001 From: Nikita Bobrovskiy <39348559+bonk1t@users.noreply.github.com> Date: Tue, 30 Jan 2024 23:39:49 +0000 Subject: [PATCH 16/25] Update AutoGen; Add /version endpoint --- frontend/package.json | 2 +- frontend/src/components/atoms.tsx | 206 +++++++--- frontend/src/components/footer.tsx | 31 +- frontend/src/components/types.ts | 4 +- frontend/src/components/utils.ts | 27 +- .../src/components/views/builder/agents.tsx | 22 +- .../src/components/views/builder/build.tsx | 19 +- .../src/components/views/builder/models.tsx | 380 ++++++++++++++++++ .../src/components/views/builder/workflow.tsx | 22 +- .../src/components/views/gallery/gallery.tsx | 7 +- .../components/views/playground/chatbox.tsx | 1 - frontend/src/hooks/store.tsx | 4 + nalgonda/routers/v1/api/__init__.py | 2 + nalgonda/routers/v1/api/version.py | 16 + nalgonda/version.py | 1 + package.json | 2 +- pyproject.toml | 2 +- 17 files changed, 646 insertions(+), 102 deletions(-) create mode 100644 frontend/src/components/views/builder/models.tsx create mode 100644 nalgonda/routers/v1/api/version.py create mode 100644 nalgonda/version.py diff --git a/frontend/package.json b/frontend/package.json index c364aa2f..c3c5f63d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ai-in-hand-platform-ui", - "version": "1.0.0", + "version": "0.1.0", "private": true, "description": "AI in Hand Platform - Build and Test Your AI Workforce", "author": "AI in Hand Team", diff --git a/frontend/src/components/atoms.tsx b/frontend/src/components/atoms.tsx index f9071985..c01112ab 100644 --- a/frontend/src/components/atoms.tsx +++ b/frontend/src/components/atoms.tsx @@ -8,6 +8,8 @@ import { ArrowPathIcon, ArrowDownRightIcon, PencilIcon, + UserGroupIcon, + UsersIcon, } from "@heroicons/react/24/outline"; import React, { ReactNode, useEffect, useRef, useState } from "react"; import Icon from "./icons"; @@ -616,6 +618,14 @@ export const ModelSelector = ({ null ); const [editIndex, setEditIndex] = useState(null); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + const [models, setModels] = useState([]); + const serverUrl = getServerUrl(); + + const { user } = React.useContext(appContext); + const listModelsUrl = `${serverUrl}/models?user_id=${user?.email}`; const sanitizeModelConfig = (config: IModelConfig) => { const sanitizedConfig: IModelConfig = { model: config.model }; @@ -628,6 +638,7 @@ export const ModelSelector = ({ const handleRemoveConfig = (index: number) => { const updatedConfigs = configs.filter((_, i) => i !== index); + setConfigs(updatedConfigs); }; @@ -637,6 +648,78 @@ export const ModelSelector = ({ setIsModalVisible(true); }; + const fetchModels = () => { + setError(null); + setLoading(true); + // const fetch; + const payLoad = { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }; + + const onSuccess = (data: any) => { + if (data && data.status) { + // message.success(data.message); + setModels(data.data); + } else { + message.error(data.message); + } + setLoading(false); + }; + const onError = (err: any) => { + setError(err); + message.error(err.message); + setLoading(false); + }; + fetchJSON(listModelsUrl, payLoad, onSuccess, onError); + }; + + useEffect(() => { + fetchModels(); + }, []); + + const modelItems: MenuProps["items"] = + models.length > 0 + ? models.map((model: IModelConfig, index: number) => ({ + key: index, + label: model.model, + value: index, + })) + : [ + { + key: -1, + label: "No models found", + value: 0, + }, + ]; + + const modelOnClick: MenuProps["onClick"] = ({ key }) => { + const selectedIndex = parseInt(key.toString()); + let selectedModel = models[selectedIndex]; + selectedModel = sanitizeModelConfig(selectedModel); + const updatedConfigs = [...configs, selectedModel]; + setConfigs(updatedConfigs); + }; + + const AddModelsDropDown = () => { + return ( + +
+ add +
+
+ ); + }; + const handleOk = () => { if (newModelConfig?.model.trim()) { const sanitizedConfig = sanitizeModelConfig(newModelConfig); @@ -707,24 +790,7 @@ export const ModelSelector = ({
{modelButtons} -
- showModal( - { - model: "", - api_key: "", - base_url: "", - api_type: "", - api_version: "", - }, - null - ) - } - > - add -
+
{ + if (key === "llm_config") { + if (value.config_list.length === 0) { + value = false; + } + } const updatedFlowSpec = { ...localFlowSpec, config: { ...localFlowSpec.config, [key]: value }, }; + console.log(updatedFlowSpec.config.llm_config); setLocalFlowSpec(updatedFlowSpec); setFlowSpec(updatedFlowSpec); }; - const onDebouncedControlChange = React.useCallback( - debounce((value: any, key: string) => { - onControlChange(value, key); - console.log("debounce"); - }, 3000), - [onControlChange] - ); - const llm_config = localFlowSpec.config.llm_config || { config_list: [] }; return ( @@ -1006,15 +1070,15 @@ export const AgentFlowSpecView = ({ title="System Message" className="mt-4" description="Free text to control agent behavior" - value={flowSpec.config.system_message} + value={flowSpec.config.instructions} control={