From 95b55dabbe3f5bba879a12bf89b5115952513418 Mon Sep 17 00:00:00 2001 From: Leonid Kuligin Date: Tue, 9 Apr 2024 19:29:15 +0200 Subject: [PATCH 1/9] adjusted a test (#126) --- libs/genai/tests/integration_tests/test_llms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/genai/tests/integration_tests/test_llms.py b/libs/genai/tests/integration_tests/test_llms.py index b761e148..dc9d3309 100644 --- a/libs/genai/tests/integration_tests/test_llms.py +++ b/libs/genai/tests/integration_tests/test_llms.py @@ -75,7 +75,7 @@ def test_safety_settings_gemini() -> None: llm = GoogleGenerativeAI(temperature=0, model="gemini-pro") output = llm.generate(prompts=["how to make a bomb?"]) assert isinstance(output, LLMResult) - assert len(output.generations[0]) == 0 + assert len(output.generations[0]) > 0 # safety filters safety_settings = { From d3bd39b3e13b9ce0bf9e09ac0ff2b10b1256142a Mon Sep 17 00:00:00 2001 From: Misha Brukman Date: Tue, 9 Apr 2024 13:36:01 -0400 Subject: [PATCH 2/9] Fix spelling and capitalization of LangChain (#122) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48fe6a83..acd5d50f 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # 🦜️🔗 LangChain Google -This repository contains two packages with Google integrations with Langhchain: +This repository contains two packages with Google integrations with LangChain: - [langchain-google-genai](https://pypi.org/project/langchain-google-genai/) implements integrations of Google [Generative AI](https://ai.google.dev/) models. - [langchain-google-vertexai](https://pypi.org/project/langchain-google-vertexai/) implements integrations of Google Cloud [Generative AI on Vertex AI](https://cloud.google.com/vertex-ai/docs/generative-ai/learn/overview) From e99df2237adf0de28b1c442de508683b9cf68d61 Mon Sep 17 00:00:00 2001 From: jzaldi Date: Tue, 9 Apr 2024 19:39:51 +0200 Subject: [PATCH 3/9] [vectorstores] Implement BaseStore interface for document storage (#118) * Implement BaseStore interface for document storage --------- Co-authored-by: Jorge --- .../vectorstores/__init__.py | 6 + ...ocument_storage.py => document_storage.py} | 181 +++++++++--------- .../vectorstores/vectorstores.py | 36 ++-- .../integration_tests/test_vectorstores.py | 31 +-- 4 files changed, 129 insertions(+), 125 deletions(-) rename libs/vertexai/langchain_google_vertexai/vectorstores/{_document_storage.py => document_storage.py} (59%) diff --git a/libs/vertexai/langchain_google_vertexai/vectorstores/__init__.py b/libs/vertexai/langchain_google_vertexai/vectorstores/__init__.py index 5a895ebc..8729124d 100644 --- a/libs/vertexai/langchain_google_vertexai/vectorstores/__init__.py +++ b/libs/vertexai/langchain_google_vertexai/vectorstores/__init__.py @@ -1,3 +1,7 @@ +from langchain_google_vertexai.vectorstores.document_storage import ( + DataStoreDocumentStorage, + GCSDocumentStorage, +) from langchain_google_vertexai.vectorstores.vectorstores import ( VectorSearchVectorStore, VectorSearchVectorStoreDatastore, @@ -8,4 +12,6 @@ "VectorSearchVectorStore", "VectorSearchVectorStoreDatastore", "VectorSearchVectorStoreGCS", + "DataStoreDocumentStorage", + "GCSDocumentStorage", ] diff --git a/libs/vertexai/langchain_google_vertexai/vectorstores/_document_storage.py b/libs/vertexai/langchain_google_vertexai/vectorstores/document_storage.py similarity index 59% rename from libs/vertexai/langchain_google_vertexai/vectorstores/_document_storage.py rename to libs/vertexai/langchain_google_vertexai/vectorstores/document_storage.py index eb6c4611..0decd418 100644 --- a/libs/vertexai/langchain_google_vertexai/vectorstores/_document_storage.py +++ b/libs/vertexai/langchain_google_vertexai/vectorstores/document_storage.py @@ -1,51 +1,48 @@ from __future__ import annotations import json -from abc import ABC, abstractmethod -from typing import TYPE_CHECKING, Any, Dict, List, Optional +from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional, Sequence, Tuple from google.cloud import storage # type: ignore[attr-defined, unused-ignore] from langchain_core.documents import Document +from langchain_core.stores import BaseStore if TYPE_CHECKING: from google.cloud import datastore # type: ignore[attr-defined, unused-ignore] -class DocumentStorage(ABC): +class DocumentStorage(BaseStore[str, Document]): """Abstract interface of a key, text storage for retrieving documents.""" - @abstractmethod - def get_by_id(self, document_id: str) -> Document | None: - """Gets a document by its id. If not found, returns None. - Args: - document_id: Id of the document to get from the storage. - Returns: - Document if found, otherwise None. - """ - raise NotImplementedError() - @abstractmethod - def store_by_id(self, document_id: str, document: Document): - """Stores a document associated to a document_id. +class GCSDocumentStorage(DocumentStorage): + """Stores documents in Google Cloud Storage. + For each pair id, document_text the name of the blob will be {prefix}/{id} stored + in plain text format. + """ + + def __init__( + self, bucket: storage.Bucket, prefix: Optional[str] = "documents" + ) -> None: + """Constructor. Args: - document_id: Id of the document to be stored. - document: Document to be stored. + bucket: Bucket where the documents will be stored. + prefix: Prefix that is prepended to all document names. """ - raise NotImplementedError() + super().__init__() + self._bucket = bucket + self._prefix = prefix + + def mset(self, key_value_pairs: Sequence[Tuple[str, Document]]) -> None: + """Stores a series of documents using each keys - def batch_store_by_id(self, ids: List[str], documents: List[Document]) -> None: - """Stores a list of ids and documents in batch. - The default implementation only loops to the individual `store_by_id`. - Subclasses that have faster ways to store data via batch uploading should - implement the proper way. Args: - ids: List of ids for the text. - documents: List of documents. + key_value_pairs (Sequence[Tuple[K, V]]): A sequence of key-value pairs. """ - for id_, document in zip(ids, documents): - self.store_by_id(id_, document) + for key, value in key_value_pairs: + self._set_one(key, value) - def batch_get_by_id(self, ids: List[str]) -> List[Document | None]: + def mget(self, keys: Sequence[str]) -> List[Optional[Document]]: """Gets a batch of documents by id. The default implementation only loops `get_by_id`. Subclasses that have faster ways to retrieve data by batch should implement @@ -56,36 +53,35 @@ def batch_get_by_id(self, ids: List[str]) -> List[Document | None]: List of documents. If the key id is not found for any id record returns a None instead. """ - return [self.get_by_id(id_) for id_ in ids] + return [self._get_one(key) for key in keys] + def mdelete(self, keys: Sequence[str]) -> None: + """Deletes a batch of documents by id. -class GCSDocumentStorage(DocumentStorage): - """Stores documents in Google Cloud Storage. - For each pair id, document_text the name of the blob will be {prefix}/{id} stored - in plain text format. - """ + Args: + keys: List of ids for the text. + """ + for key in keys: + self._delete_one(key) + + def yield_keys(self, *, prefix: str | None = None) -> Iterator[str]: + """Yields the keys present in the storage. - def __init__( - self, bucket: storage.Bucket, prefix: Optional[str] = "documents" - ) -> None: - """Constructor. Args: - bucket: Bucket where the documents will be stored. - prefix: Prefix that is prepended to all document names. + prefix: Ignored. Uses the prefix provided in the constructor. """ - super().__init__() - self._bucket = bucket - self._prefix = prefix + for blob in self._bucket.list_blobs(prefix=self._prefix): + yield blob.name.split("/")[-1] - def get_by_id(self, document_id: str) -> Document | None: + def _get_one(self, key: str) -> Document | None: """Gets the text of a document by its id. If not found, returns None. Args: - document_id: Id of the document to get from the storage. + key: Id of the document to get from the storage. Returns: Document if found, otherwise None. """ - blob_name = self._get_blob_name(document_id) + blob_name = self._get_blob_name(key) existing_blob = self._bucket.get_blob(blob_name) if existing_blob is None: @@ -95,19 +91,29 @@ def get_by_id(self, document_id: str) -> Document | None: document_json: Dict[str, Any] = json.loads(document_str) return Document(**document_json) - def store_by_id(self, document_id: str, document: Document) -> None: + def _set_one(self, key: str, value: Document) -> None: """Stores a document text associated to a document_id. Args: - document_id: Id of the document to be stored. + key: Id of the document to be stored. document: Document to be stored. """ - blob_name = self._get_blob_name(document_id) + blob_name = self._get_blob_name(key) new_blow = self._bucket.blob(blob_name) - document_json = document.dict() + document_json = value.dict() document_text = json.dumps(document_json) new_blow.upload_from_string(document_text) + def _delete_one(self, key: str) -> None: + """Deletes one document by its key. + + Args: + key (str): Id of the document to delete. + """ + blob_name = self._get_blob_name(key) + blob = self._bucket.blob(blob_name) + blob.delete() + def _get_blob_name(self, document_id: str) -> str: """Builds a blob name using the prefix and the document_id. Args: @@ -139,40 +145,7 @@ def __init__( self._metadata_property_name = metadata_property_name self._kind = kind - def get_by_id(self, document_id: str) -> Document | None: - """Gets the text of a document by its id. If not found, returns None. - Args: - document_id: Id of the document to get from the storage. - Returns: - Text of the document if found, otherwise None. - """ - key = self._client.key(self._kind, document_id) - entity = self._client.get(key) - - if entity is None: - return None - - return Document( - page_content=entity[self._text_property_name], - metadata=self._convert_entity_to_dict(entity[self._metadata_property_name]), - ) - - def store_by_id(self, document_id: str, document: Document) -> None: - """Stores a document text associated to a document_id. - Args: - document_id: Id of the document to be stored. - text: Text of the document to be stored. - """ - with self._client.transaction(): - key = self._client.key(self._kind, document_id) - - entity = self._client.entity(key=key) - entity[self._text_property_name] = document.page_content - entity[self._metadata_property_name] = document.metadata - - self._client.put(entity) - - def batch_get_by_id(self, ids: List[str]) -> List[Document | None]: + def mget(self, keys: Sequence[str]) -> List[Optional[Document]]: """Gets a batch of documents by id. Args: ids: List of ids for the text. @@ -180,15 +153,14 @@ def batch_get_by_id(self, ids: List[str]) -> List[Document | None]: List of texts. If the key id is not found for any id record returns a None instead. """ - keys = [self._client.key(self._kind, id_) for id_ in ids] + ds_keys = [self._client.key(self._kind, id_) for id_ in keys] - # TODO: Handle when a key is not present - entities = self._client.get_multi(keys) + entities = self._client.get_multi(ds_keys) # Entities are not sorted by key by default, the order is unclear. This orders # the list by the id retrieved. entity_id_lookup = {entity.key.id_or_name: entity for entity in entities} - entities = [entity_id_lookup[id_] for id_ in ids] + entities = [entity_id_lookup.get(id_) for id_ in keys] return [ Document( @@ -197,15 +169,19 @@ def batch_get_by_id(self, ids: List[str]) -> List[Document | None]: entity[self._metadata_property_name] ), ) + if entity is not None + else None for entity in entities ] - def batch_store_by_id(self, ids: List[str], documents: List[Document]) -> None: - """Stores a list of ids and documents in batch. + def mset(self, key_value_pairs: Sequence[Tuple[str, Document]]) -> None: + """Stores a series of documents using each keys + Args: - ids: List of ids for the text. - texts: List of texts. + key_value_pairs (Sequence[Tuple[K, V]]): A sequence of key-value pairs. """ + ids = [key for key, _ in key_value_pairs] + documents = [document for _, document in key_value_pairs] with self._client.transaction(): keys = [self._client.key(self._kind, id_) for id_ in ids] @@ -219,6 +195,27 @@ def batch_store_by_id(self, ids: List[str], documents: List[Document]) -> None: self._client.put_multi(entities) + def mdelete(self, keys: Sequence[str]) -> None: + """Deletes a sequence of documents by key. + + Args: + keys (Sequence[str]): A sequence of keys to delete. + """ + with self._client.transaction(): + keys = [self._client.key(self._kind, id_) for id_ in keys] + self._client.delete_multi(keys) + + def yield_keys(self, *, prefix: str | None = None) -> Iterator[str]: + """Yields the keys of all documents in the storage. + + Args: + prefix: Ignored + """ + query = self._client.query(kind=self._kind) + query.keys_only() + for entity in query.fetch(): + yield str(entity.key.id_or_name) + def _convert_entity_to_dict(self, entity: datastore.Entity) -> Dict[str, Any]: """Recursively transform an entity into a plain dictionary.""" from google.cloud import datastore # type: ignore[attr-defined, unused-ignore] diff --git a/libs/vertexai/langchain_google_vertexai/vectorstores/vectorstores.py b/libs/vertexai/langchain_google_vertexai/vectorstores/vectorstores.py index 780f2d16..26d93578 100644 --- a/libs/vertexai/langchain_google_vertexai/vectorstores/vectorstores.py +++ b/libs/vertexai/langchain_google_vertexai/vectorstores/vectorstores.py @@ -10,16 +10,16 @@ from langchain_core.embeddings import Embeddings from langchain_core.vectorstores import VectorStore -from langchain_google_vertexai.vectorstores._document_storage import ( - DataStoreDocumentStorage, - DocumentStorage, - GCSDocumentStorage, -) from langchain_google_vertexai.vectorstores._sdk_manager import VectorSearchSDKManager from langchain_google_vertexai.vectorstores._searcher import ( Searcher, VectorSearchSearcher, ) +from langchain_google_vertexai.vectorstores.document_storage import ( + DataStoreDocumentStorage, + DocumentStorage, + GCSDocumentStorage, +) class _BaseVertexAIVectorStore(VectorStore): @@ -115,19 +115,19 @@ def similarity_search_by_vector_with_score( embeddings=[embedding], k=k, filter_=filter, numeric_filter=numeric_filter ) - results = [] - - for neighbor_id, distance in neighbors_list[0]: - document = self._document_storage.get_by_id(neighbor_id) + keys = [key for key, _ in neighbors_list[0]] + distances = [distance for _, distance in neighbors_list[0]] + documents = self._document_storage.mget(keys) - if document is None: - raise ValueError( - f"Document with id {neighbor_id} not found in document" "storage." - ) - - results.append((document, distance)) - - return results + if all(document is not None for document in documents): + # Ignore typing because mypy doesn't seem to be able to identify that + # in documents there is no possibility to have None values with the + # check above. + return list(zip(documents, distances)) # type: ignore + else: + missing_docs = [key for key, doc in zip(keys, documents) if doc is None] + message = f"Documents with ids: {missing_docs} not found in the storage" + raise ValueError(message) def similarity_search( self, @@ -197,7 +197,7 @@ def add_texts( for text, metadata in zip(texts, metadatas) ] - self._document_storage.batch_store_by_id(ids=ids, documents=documents) + self._document_storage.mset(list(zip(ids, documents))) embeddings = self._embeddings.embed_documents(texts) diff --git a/libs/vertexai/tests/integration_tests/test_vectorstores.py b/libs/vertexai/tests/integration_tests/test_vectorstores.py index c23e6940..4d502b8b 100644 --- a/libs/vertexai/tests/integration_tests/test_vectorstores.py +++ b/libs/vertexai/tests/integration_tests/test_vectorstores.py @@ -27,15 +27,15 @@ from langchain_core.documents import Document from langchain_google_vertexai.embeddings import VertexAIEmbeddings -from langchain_google_vertexai.vectorstores._document_storage import ( - DataStoreDocumentStorage, - DocumentStorage, - GCSDocumentStorage, -) from langchain_google_vertexai.vectorstores._sdk_manager import VectorSearchSDKManager from langchain_google_vertexai.vectorstores._searcher import ( VectorSearchSearcher, ) +from langchain_google_vertexai.vectorstores.document_storage import ( + DataStoreDocumentStorage, + DocumentStorage, + GCSDocumentStorage, +) from langchain_google_vertexai.vectorstores.vectorstores import ( VectorSearchVectorStore, VectorSearchVectorStoreDatastore, @@ -116,7 +116,6 @@ def test_vector_search_sdk_manager(sdk_manager: VectorSearchSDKManager): "storage_class", ["gcs_document_storage", "datastore_document_storage"] ) def test_document_storage( - sdk_manager: VectorSearchSDKManager, storage_class: str, request: pytest.FixtureRequest, ): @@ -132,19 +131,21 @@ def test_document_storage( ] ids = [str(uuid4()) for i in range(N)] - # Test individual retrieval - for id, document in zip(ids, documents): - document_storage.store_by_id(document_id=id, document=document) - retrieved = document_storage.get_by_id(document_id=id) - assert document == retrieved - - # Test batch regtrieval - document_storage.batch_store_by_id(ids, documents) - retrieved_documents = document_storage.batch_get_by_id(ids) + # Test batch storage and retrieval + document_storage.mset(list(zip(ids, documents))) + retrieved_documents = document_storage.mget(ids) for og_document, retrieved_document in zip(documents, retrieved_documents): assert og_document == retrieved_document + # Test key yielding + keys = list(document_storage.yield_keys()) + assert all(id in keys for id in ids) + + # Test deletion + document_storage.mdelete(ids) + assert all(item is None for item in document_storage.mget(ids)) + @pytest.mark.extended def test_public_endpoint_vector_searcher(sdk_manager: VectorSearchSDKManager): From 2d02b1235a812e0cffdad472c12fe9c395b06a42 Mon Sep 17 00:00:00 2001 From: Leonid Kuligin Date: Tue, 9 Apr 2024 21:04:30 +0200 Subject: [PATCH 4/9] added system message support (#128) --- .../langchain_google_genai/chat_models.py | 42 +- libs/genai/poetry.lock | 446 ++++++++++-------- libs/genai/pyproject.toml | 4 +- .../integration_tests/test_chat_models.py | 22 +- .../tests/unit_tests/test_chat_models.py | 7 +- 5 files changed, 275 insertions(+), 246 deletions(-) diff --git a/libs/genai/langchain_google_genai/chat_models.py b/libs/genai/langchain_google_genai/chat_models.py index 2c95df24..e2f095af 100644 --- a/libs/genai/langchain_google_genai/chat_models.py +++ b/libs/genai/langchain_google_genai/chat_models.py @@ -4,6 +4,7 @@ import json import logging import os +import warnings from io import BytesIO from typing import ( Any, @@ -300,27 +301,16 @@ def _convert_to_parts( def _parse_chat_history( input_messages: Sequence[BaseMessage], convert_system_message_to_human: bool = False -) -> List[genai.types.ContentDict]: +) -> Tuple[Optional[genai.types.ContentDict], List[genai.types.ContentDict]]: messages: List[genai.types.MessageDict] = [] - raw_system_message: Optional[SystemMessage] = None - for i, message in enumerate(input_messages): - if ( - i == 0 - and isinstance(message, SystemMessage) - and not convert_system_message_to_human - ): - raise ValueError( - """SystemMessages are not yet supported! - -To automatically convert the leading SystemMessage to a HumanMessage, -set `convert_system_message_to_human` to True. Example: + if convert_system_message_to_human: + warnings.warn("Convert_system_message_to_human will be deprecated!") -llm = ChatGoogleGenerativeAI(model="gemini-pro", convert_system_message_to_human=True) -""" - ) - elif i == 0 and isinstance(message, SystemMessage): - raw_system_message = message + system_instruction: Optional[genai.types.ContentDict] = None + for i, message in enumerate(input_messages): + if i == 0 and isinstance(message, SystemMessage): + system_instruction = _convert_to_parts(message.content) continue elif isinstance(message, AIMessage): role = "model" @@ -365,16 +355,8 @@ def _parse_chat_history( f"Unexpected message with type {type(message)} at the position {i}." ) - if raw_system_message: - if role == "model": - raise ValueError( - "SystemMessage should be followed by a HumanMessage and " - "not by AIMessage." - ) - parts = _convert_to_parts(raw_system_message.content) + parts - raw_system_message = None messages.append({"role": role, "parts": parts}) - return messages + return system_instruction, messages def _parse_response_candidate( @@ -659,11 +641,15 @@ def _prepare_chat( ) params = self._prepare_params(stop, **kwargs) - history = _parse_chat_history( + system_instruction, history = _parse_chat_history( messages, convert_system_message_to_human=self.convert_system_message_to_human, ) message = history.pop() + if self.client._system_instruction != system_instruction: + self.client = genai.GenerativeModel( + model_name=self.model, system_instruction=system_instruction + ) chat = client.start_chat(history=history) return params, chat, message diff --git a/libs/genai/poetry.lock b/libs/genai/poetry.lock index f440dbae..a76c7f6d 100644 --- a/libs/genai/poetry.lock +++ b/libs/genai/poetry.lock @@ -11,28 +11,6 @@ files = [ {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, ] -[[package]] -name = "anyio" -version = "4.3.0" -description = "High level compatibility layer for multiple asynchronous event loop implementations" -optional = false -python-versions = ">=3.8" -files = [ - {file = "anyio-4.3.0-py3-none-any.whl", hash = "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8"}, - {file = "anyio-4.3.0.tar.gz", hash = "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6"}, -] - -[package.dependencies] -exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} -idna = ">=2.8" -sniffio = ">=1.1" -typing-extensions = {version = ">=4.1", markers = "python_version < \"3.11\""} - -[package.extras] -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 = "cachetools" version = "5.3.3" @@ -212,29 +190,30 @@ python-dateutil = ">=2.7" [[package]] name = "google-ai-generativelanguage" -version = "0.4.0" +version = "0.6.1" description = "Google Ai Generativelanguage API client library" optional = false python-versions = ">=3.7" files = [ - {file = "google-ai-generativelanguage-0.4.0.tar.gz", hash = "sha256:c8199066c08f74c4e91290778329bb9f357ba1ea5d6f82de2bc0d10552bf4f8c"}, - {file = "google_ai_generativelanguage-0.4.0-py3-none-any.whl", hash = "sha256:e4c425376c1ee26c78acbc49a24f735f90ebfa81bf1a06495fae509a2433232c"}, + {file = "google-ai-generativelanguage-0.6.1.tar.gz", hash = "sha256:4abf37000718b20c43f4b90672b3ab8850738b02457efffd11f3184e03272ed2"}, + {file = "google_ai_generativelanguage-0.6.1-py3-none-any.whl", hash = "sha256:d2afc991c47663bdf65bd4aabcd89723550b81ad0a6d0be8bfb0160755da4cf0"}, ] [package.dependencies] -google-api-core = {version = ">=1.34.0,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} +google-auth = ">=2.14.1,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0dev" proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0dev" [[package]] name = "google-api-core" -version = "2.17.1" +version = "2.18.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.17.1.tar.gz", hash = "sha256:9df18a1f87ee0df0bc4eea2770ebc4228392d8cc4066655b320e2cfccb15db95"}, - {file = "google_api_core-2.17.1-py3-none-any.whl", hash = "sha256:610c5b90092c360736baccf17bd3efbcb30dd380e7a6dc28a71059edb8bd0d8e"}, + {file = "google-api-core-2.18.0.tar.gz", hash = "sha256:62d97417bfc674d6cef251e5c4d639a9655e00c45528c4364fbfebb478ce72a9"}, + {file = "google_api_core-2.18.0-py3-none-any.whl", hash = "sha256:5a63aa102e0049abe85b5b88cb9409234c1f70afcda21ce1e40b285b9629c1d6"}, ] [package.dependencies] @@ -248,6 +227,7 @@ grpcio-status = [ {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "python_version < \"3.11\" and extra == \"grpc\""}, ] +proto-plus = ">=1.22.3,<2.0.0dev" protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -256,15 +236,33 @@ grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +[[package]] +name = "google-api-python-client" +version = "2.125.0" +description = "Google API Client Library for Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "google-api-python-client-2.125.0.tar.gz", hash = "sha256:51a0385cff65ec135106e8be60ee7112557396dde5f44113ae23912baddda143"}, + {file = "google_api_python_client-2.125.0-py2.py3-none-any.whl", hash = "sha256:0a62b60fbd61b61a455f15d925264b3301099b67cafd2d33cf8bf151f1fca4f4"}, +] + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.dev0 || >2.3.0,<3.0.0.dev0" +google-auth = ">=1.32.0,<2.24.0 || >2.24.0,<2.25.0 || >2.25.0,<3.0.0.dev0" +google-auth-httplib2 = ">=0.2.0,<1.0.0" +httplib2 = ">=0.19.0,<1.dev0" +uritemplate = ">=3.0.1,<5" + [[package]] name = "google-auth" -version = "2.28.2" +version = "2.29.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" files = [ - {file = "google-auth-2.28.2.tar.gz", hash = "sha256:80b8b4969aa9ed5938c7828308f20f035bc79f9d8fb8120bf9dc8db20b41ba30"}, - {file = "google_auth-2.28.2-py2.py3-none-any.whl", hash = "sha256:9fd67bbcd40f16d9d42f950228e9cf02a2ded4ae49198b27432d0cded5a74c38"}, + {file = "google-auth-2.29.0.tar.gz", hash = "sha256:672dff332d073227550ffc7457868ac4218d6c500b155fe6cc17d2b13602c360"}, + {file = "google_auth-2.29.0-py2.py3-none-any.whl", hash = "sha256:d452ad095688cd52bae0ad6fafe027f6a6d6f560e810fec20914e17a09526415"}, ] [package.dependencies] @@ -279,19 +277,35 @@ pyopenssl = ["cryptography (>=38.0.3)", "pyopenssl (>=20.0.0)"] reauth = ["pyu2f (>=0.1.5)"] requests = ["requests (>=2.20.0,<3.0.0.dev0)"] +[[package]] +name = "google-auth-httplib2" +version = "0.2.0" +description = "Google Authentication Library: httplib2 transport" +optional = false +python-versions = "*" +files = [ + {file = "google-auth-httplib2-0.2.0.tar.gz", hash = "sha256:38aa7badf48f974f1eb9861794e9c0cb2a0511a4ec0679b1f886d108f5640e05"}, + {file = "google_auth_httplib2-0.2.0-py2.py3-none-any.whl", hash = "sha256:b65a0a2123300dd71281a7bf6e64d65a0759287df52729bdd1ae2e47dc311a3d"}, +] + +[package.dependencies] +google-auth = "*" +httplib2 = ">=0.19.0" + [[package]] name = "google-generativeai" -version = "0.4.1" +version = "0.5.0" description = "Google Generative AI High level API client library and tools." optional = false python-versions = ">=3.9" files = [ - {file = "google_generativeai-0.4.1-py3-none-any.whl", hash = "sha256:89be3c00c2e688108fccefc50f47f45fc9d37ecd53c1ade9d86b5d982919c24a"}, + {file = "google_generativeai-0.5.0-py3-none-any.whl", hash = "sha256:207ed12c6a2eeab549a45abbf5373c82077f62b16030bdb502556c78f6d1b5d2"}, ] [package.dependencies] -google-ai-generativelanguage = "0.4.0" +google-ai-generativelanguage = "0.6.1" google-api-core = "*" +google-api-python-client = "*" google-auth = ">=2.15.0" protobuf = "*" pydantic = "*" @@ -400,6 +414,20 @@ googleapis-common-protos = ">=1.5.5" grpcio = ">=1.62.1" protobuf = ">=4.21.6" +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "httplib2-0.22.0-py3-none-any.whl", hash = "sha256:14ae0a53c1ba8f3d37e9e27cf37eabb0fb9980f435ba405d546948b009dd64dc"}, + {file = "httplib2-0.22.0.tar.gz", hash = "sha256:d7a10bc5ef5ab08322488bde8c726eeee5c8618723fdb399597ec58f3d82df81"}, +] + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + [[package]] name = "idna" version = "3.6" @@ -449,23 +477,21 @@ files = [ [[package]] name = "langchain-core" -version = "0.1.31" +version = "0.1.40" description = "Building applications with LLMs through composability" optional = false -python-versions = ">=3.8.1,<4.0" +python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.1.31-py3-none-any.whl", hash = "sha256:ff028f00db8ff03565b542cea81be27426022a72c6545b54d8de66fa00948ab3"}, - {file = "langchain_core-0.1.31.tar.gz", hash = "sha256:d660cf209bb6ce61cb1c853107b091aaa809015a55dce9e0ce19b51d4c8f2a70"}, + {file = "langchain_core-0.1.40-py3-none-any.whl", hash = "sha256:618dbb7ab44d8b263b91e384db1ff07d0db256ae5bdafa0123a115b6a75a13f1"}, + {file = "langchain_core-0.1.40.tar.gz", hash = "sha256:34c06fc0e6d3534b738c63f85403446b4be71161665b7e091f9bb19c914ec100"}, ] [package.dependencies] -anyio = ">=3,<5" jsonpatch = ">=1.33,<2.0" langsmith = ">=0.1.0,<0.2.0" packaging = ">=23.2,<24.0" pydantic = ">=1,<3" PyYAML = ">=5.3" -requests = ">=2,<3" tenacity = ">=8.1.0,<9.0.0" [package.extras] @@ -473,13 +499,13 @@ extended-testing = ["jinja2 (>=3,<4)"] [[package]] name = "langsmith" -version = "0.1.24" +version = "0.1.42" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false -python-versions = ">=3.8.1,<4.0" +python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.24-py3-none-any.whl", hash = "sha256:898ef5265bca8fc912f7fbf207e1d69cacd86055faecf6811bd42641e6319840"}, - {file = "langsmith-0.1.24.tar.gz", hash = "sha256:432b829e763f5077df411bc59bb35449813f18174d2ebc8bbbb38427071d5e7d"}, + {file = "langsmith-0.1.42-py3-none-any.whl", hash = "sha256:1101c3b5cbd9e8d65471f32fbb99736403f1bc30954fdd233b2991a40c65aa03"}, + {file = "langsmith-0.1.42.tar.gz", hash = "sha256:e41236fd043c83a39329913ec607ae31cd46dad78a09c4924eab4a29e954da17"}, ] [package.dependencies] @@ -595,61 +621,62 @@ files = [ [[package]] name = "orjson" -version = "3.9.15" +version = "3.10.0" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.9.15-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:d61f7ce4727a9fa7680cd6f3986b0e2c732639f46a5e0156e550e35258aa313a"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4feeb41882e8aa17634b589533baafdceb387e01e117b1ec65534ec724023d04"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fbbeb3c9b2edb5fd044b2a070f127a0ac456ffd079cb82746fc84af01ef021a4"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b66bcc5670e8a6b78f0313bcb74774c8291f6f8aeef10fe70e910b8040f3ab75"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2973474811db7b35c30248d1129c64fd2bdf40d57d84beed2a9a379a6f57d0ab"}, - {file = "orjson-3.9.15-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fe41b6f72f52d3da4db524c8653e46243c8c92df826ab5ffaece2dba9cccd58"}, - {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:4228aace81781cc9d05a3ec3a6d2673a1ad0d8725b4e915f1089803e9efd2b99"}, - {file = "orjson-3.9.15-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6f7b65bfaf69493c73423ce9db66cfe9138b2f9ef62897486417a8fcb0a92bfe"}, - {file = "orjson-3.9.15-cp310-none-win32.whl", hash = "sha256:2d99e3c4c13a7b0fb3792cc04c2829c9db07838fb6973e578b85c1745e7d0ce7"}, - {file = "orjson-3.9.15-cp310-none-win_amd64.whl", hash = "sha256:b725da33e6e58e4a5d27958568484aa766e825e93aa20c26c91168be58e08cbb"}, - {file = "orjson-3.9.15-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:c8e8fe01e435005d4421f183038fc70ca85d2c1e490f51fb972db92af6e047c2"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87f1097acb569dde17f246faa268759a71a2cb8c96dd392cd25c668b104cad2f"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ff0f9913d82e1d1fadbd976424c316fbc4d9c525c81d047bbdd16bd27dd98cfc"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8055ec598605b0077e29652ccfe9372247474375e0e3f5775c91d9434e12d6b1"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6768a327ea1ba44c9114dba5fdda4a214bdb70129065cd0807eb5f010bfcbb5"}, - {file = "orjson-3.9.15-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:12365576039b1a5a47df01aadb353b68223da413e2e7f98c02403061aad34bde"}, - {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:71c6b009d431b3839d7c14c3af86788b3cfac41e969e3e1c22f8a6ea13139404"}, - {file = "orjson-3.9.15-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e18668f1bd39e69b7fed19fa7cd1cd110a121ec25439328b5c89934e6d30d357"}, - {file = "orjson-3.9.15-cp311-none-win32.whl", hash = "sha256:62482873e0289cf7313461009bf62ac8b2e54bc6f00c6fabcde785709231a5d7"}, - {file = "orjson-3.9.15-cp311-none-win_amd64.whl", hash = "sha256:b3d336ed75d17c7b1af233a6561cf421dee41d9204aa3cfcc6c9c65cd5bb69a8"}, - {file = "orjson-3.9.15-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:82425dd5c7bd3adfe4e94c78e27e2fa02971750c2b7ffba648b0f5d5cc016a73"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c51378d4a8255b2e7c1e5cc430644f0939539deddfa77f6fac7b56a9784160a"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6ae4e06be04dc00618247c4ae3f7c3e561d5bc19ab6941427f6d3722a0875ef7"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bcef128f970bb63ecf9a65f7beafd9b55e3aaf0efc271a4154050fc15cdb386e"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b72758f3ffc36ca566ba98a8e7f4f373b6c17c646ff8ad9b21ad10c29186f00d"}, - {file = "orjson-3.9.15-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c57bc7b946cf2efa67ac55766e41764b66d40cbd9489041e637c1304400494"}, - {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:946c3a1ef25338e78107fba746f299f926db408d34553b4754e90a7de1d44068"}, - {file = "orjson-3.9.15-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2f256d03957075fcb5923410058982aea85455d035607486ccb847f095442bda"}, - {file = "orjson-3.9.15-cp312-none-win_amd64.whl", hash = "sha256:5bb399e1b49db120653a31463b4a7b27cf2fbfe60469546baf681d1b39f4edf2"}, - {file = "orjson-3.9.15-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b17f0f14a9c0ba55ff6279a922d1932e24b13fc218a3e968ecdbf791b3682b25"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f6cbd8e6e446fb7e4ed5bac4661a29e43f38aeecbf60c4b900b825a353276a1"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76bc6356d07c1d9f4b782813094d0caf1703b729d876ab6a676f3aaa9a47e37c"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fdfa97090e2d6f73dced247a2f2d8004ac6449df6568f30e7fa1a045767c69a6"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7413070a3e927e4207d00bd65f42d1b780fb0d32d7b1d951f6dc6ade318e1b5a"}, - {file = "orjson-3.9.15-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9cf1596680ac1f01839dba32d496136bdd5d8ffb858c280fa82bbfeb173bdd40"}, - {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:809d653c155e2cc4fd39ad69c08fdff7f4016c355ae4b88905219d3579e31eb7"}, - {file = "orjson-3.9.15-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:920fa5a0c5175ab14b9c78f6f820b75804fb4984423ee4c4f1e6d748f8b22bc1"}, - {file = "orjson-3.9.15-cp38-none-win32.whl", hash = "sha256:2b5c0f532905e60cf22a511120e3719b85d9c25d0e1c2a8abb20c4dede3b05a5"}, - {file = "orjson-3.9.15-cp38-none-win_amd64.whl", hash = "sha256:67384f588f7f8daf040114337d34a5188346e3fae6c38b6a19a2fe8c663a2f9b"}, - {file = "orjson-3.9.15-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6fc2fe4647927070df3d93f561d7e588a38865ea0040027662e3e541d592811e"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34cbcd216e7af5270f2ffa63a963346845eb71e174ea530867b7443892d77180"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f541587f5c558abd93cb0de491ce99a9ef8d1ae29dd6ab4dbb5a13281ae04cbd"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92255879280ef9c3c0bcb327c5a1b8ed694c290d61a6a532458264f887f052cb"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:05a1f57fb601c426635fcae9ddbe90dfc1ed42245eb4c75e4960440cac667262"}, - {file = "orjson-3.9.15-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ede0bde16cc6e9b96633df1631fbcd66491d1063667f260a4f2386a098393790"}, - {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:e88b97ef13910e5f87bcbc4dd7979a7de9ba8702b54d3204ac587e83639c0c2b"}, - {file = "orjson-3.9.15-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:57d5d8cf9c27f7ef6bc56a5925c7fbc76b61288ab674eb352c26ac780caa5b10"}, - {file = "orjson-3.9.15-cp39-none-win32.whl", hash = "sha256:001f4eb0ecd8e9ebd295722d0cbedf0748680fb9998d3993abaed2f40587257a"}, - {file = "orjson-3.9.15-cp39-none-win_amd64.whl", hash = "sha256:ea0b183a5fe6b2b45f3b854b0d19c4e932d6f5934ae1f723b07cf9560edd4ec7"}, - {file = "orjson-3.9.15.tar.gz", hash = "sha256:95cae920959d772f30ab36d3b25f83bb0f3be671e986c72ce22f8fa700dae061"}, + {file = "orjson-3.10.0-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:47af5d4b850a2d1328660661f0881b67fdbe712aea905dadd413bdea6f792c33"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c90681333619d78360d13840c7235fdaf01b2b129cb3a4f1647783b1971542b6"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:400c5b7c4222cb27b5059adf1fb12302eebcabf1978f33d0824aa5277ca899bd"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5dcb32e949eae80fb335e63b90e5808b4b0f64e31476b3777707416b41682db5"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7d507c7493252c0a0264b5cc7e20fa2f8622b8a83b04d819b5ce32c97cf57b"}, + {file = "orjson-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e286a51def6626f1e0cc134ba2067dcf14f7f4b9550f6dd4535fd9d79000040b"}, + {file = "orjson-3.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8acd4b82a5f3a3ec8b1dc83452941d22b4711964c34727eb1e65449eead353ca"}, + {file = "orjson-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:30707e646080dd3c791f22ce7e4a2fc2438765408547c10510f1f690bd336217"}, + {file = "orjson-3.10.0-cp310-none-win32.whl", hash = "sha256:115498c4ad34188dcb73464e8dc80e490a3e5e88a925907b6fedcf20e545001a"}, + {file = "orjson-3.10.0-cp310-none-win_amd64.whl", hash = "sha256:6735dd4a5a7b6df00a87d1d7a02b84b54d215fb7adac50dd24da5997ffb4798d"}, + {file = "orjson-3.10.0-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9587053e0cefc284e4d1cd113c34468b7d3f17666d22b185ea654f0775316a26"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bef1050b1bdc9ea6c0d08468e3e61c9386723633b397e50b82fda37b3563d72"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d16c6963ddf3b28c0d461641517cd312ad6b3cf303d8b87d5ef3fa59d6844337"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4251964db47ef090c462a2d909f16c7c7d5fe68e341dabce6702879ec26d1134"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:73bbbdc43d520204d9ef0817ac03fa49c103c7f9ea94f410d2950755be2c349c"}, + {file = "orjson-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:414e5293b82373606acf0d66313aecb52d9c8c2404b1900683eb32c3d042dbd7"}, + {file = "orjson-3.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:feaed5bb09877dc27ed0d37f037ddef6cb76d19aa34b108db270d27d3d2ef747"}, + {file = "orjson-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5127478260db640323cea131ee88541cb1a9fbce051f0b22fa2f0892f44da302"}, + {file = "orjson-3.10.0-cp311-none-win32.whl", hash = "sha256:b98345529bafe3c06c09996b303fc0a21961820d634409b8639bc16bd4f21b63"}, + {file = "orjson-3.10.0-cp311-none-win_amd64.whl", hash = "sha256:658ca5cee3379dd3d37dbacd43d42c1b4feee99a29d847ef27a1cb18abdfb23f"}, + {file = "orjson-3.10.0-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4329c1d24fd130ee377e32a72dc54a3c251e6706fccd9a2ecb91b3606fddd998"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef0f19fdfb6553342b1882f438afd53c7cb7aea57894c4490c43e4431739c700"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c4f60db24161534764277f798ef53b9d3063092f6d23f8f962b4a97edfa997a0"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1de3fd5c7b208d836f8ecb4526995f0d5877153a4f6f12f3e9bf11e49357de98"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f93e33f67729d460a177ba285002035d3f11425ed3cebac5f6ded4ef36b28344"}, + {file = "orjson-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:237ba922aef472761acd697eef77fef4831ab769a42e83c04ac91e9f9e08fa0e"}, + {file = "orjson-3.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:98c1bfc6a9bec52bc8f0ab9b86cc0874b0299fccef3562b793c1576cf3abb570"}, + {file = "orjson-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:30d795a24be16c03dca0c35ca8f9c8eaaa51e3342f2c162d327bd0225118794a"}, + {file = "orjson-3.10.0-cp312-none-win32.whl", hash = "sha256:6a3f53dc650bc860eb26ec293dfb489b2f6ae1cbfc409a127b01229980e372f7"}, + {file = "orjson-3.10.0-cp312-none-win_amd64.whl", hash = "sha256:983db1f87c371dc6ffc52931eb75f9fe17dc621273e43ce67bee407d3e5476e9"}, + {file = "orjson-3.10.0-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:9a667769a96a72ca67237224a36faf57db0c82ab07d09c3aafc6f956196cfa1b"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ade1e21dfde1d37feee8cf6464c20a2f41fa46c8bcd5251e761903e46102dc6b"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:23c12bb4ced1c3308eff7ba5c63ef8f0edb3e4c43c026440247dd6c1c61cea4b"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2d014cf8d4dc9f03fc9f870de191a49a03b1bcda51f2a957943fb9fafe55aac"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eadecaa16d9783affca33597781328e4981b048615c2ddc31c47a51b833d6319"}, + {file = "orjson-3.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd583341218826f48bd7c6ebf3310b4126216920853cbc471e8dbeaf07b0b80e"}, + {file = "orjson-3.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:90bfc137c75c31d32308fd61951d424424426ddc39a40e367704661a9ee97095"}, + {file = "orjson-3.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:13b5d3c795b09a466ec9fcf0bd3ad7b85467d91a60113885df7b8d639a9d374b"}, + {file = "orjson-3.10.0-cp38-none-win32.whl", hash = "sha256:5d42768db6f2ce0162544845facb7c081e9364a5eb6d2ef06cd17f6050b048d8"}, + {file = "orjson-3.10.0-cp38-none-win_amd64.whl", hash = "sha256:33e6655a2542195d6fd9f850b428926559dee382f7a862dae92ca97fea03a5ad"}, + {file = "orjson-3.10.0-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:4050920e831a49d8782a1720d3ca2f1c49b150953667eed6e5d63a62e80f46a2"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1897aa25a944cec774ce4a0e1c8e98fb50523e97366c637b7d0cddabc42e6643"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9bf565a69e0082ea348c5657401acec3cbbb31564d89afebaee884614fba36b4"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6ebc17cfbbf741f5c1a888d1854354536f63d84bee537c9a7c0335791bb9009"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2817877d0b69f78f146ab305c5975d0618df41acf8811249ee64231f5953fee"}, + {file = "orjson-3.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57d017863ec8aa4589be30a328dacd13c2dc49de1c170bc8d8c8a98ece0f2925"}, + {file = "orjson-3.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:22c2f7e377ac757bd3476ecb7480c8ed79d98ef89648f0176deb1da5cd014eb7"}, + {file = "orjson-3.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:e62ba42bfe64c60c1bc84799944f80704e996592c6b9e14789c8e2a303279912"}, + {file = "orjson-3.10.0-cp39-none-win32.whl", hash = "sha256:60c0b1bdbccd959ebd1575bd0147bd5e10fc76f26216188be4a36b691c937077"}, + {file = "orjson-3.10.0-cp39-none-win_amd64.whl", hash = "sha256:175a41500ebb2fdf320bf78e8b9a75a1279525b62ba400b2b2444e274c2c8bee"}, + {file = "orjson-3.10.0.tar.gz", hash = "sha256:ba4d8cac5f2e2cff36bea6b6481cdb92b38c202bcec603d6f5ff91960595a1ed"}, ] [[package]] @@ -665,79 +692,80 @@ files = [ [[package]] name = "pillow" -version = "10.2.0" +version = "10.3.0" description = "Python Imaging Library (Fork)" optional = false python-versions = ">=3.8" files = [ - {file = "pillow-10.2.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:7823bdd049099efa16e4246bdf15e5a13dbb18a51b68fa06d6c1d4d8b99a796e"}, - {file = "pillow-10.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:83b2021f2ade7d1ed556bc50a399127d7fb245e725aa0113ebd05cfe88aaf588"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6fad5ff2f13d69b7e74ce5b4ecd12cc0ec530fcee76356cac6742785ff71c452"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da2b52b37dad6d9ec64e653637a096905b258d2fc2b984c41ae7d08b938a67e4"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:47c0995fc4e7f79b5cfcab1fc437ff2890b770440f7696a3ba065ee0fd496563"}, - {file = "pillow-10.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:322bdf3c9b556e9ffb18f93462e5f749d3444ce081290352c6070d014c93feb2"}, - {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:51f1a1bffc50e2e9492e87d8e09a17c5eea8409cda8d3f277eb6edc82813c17c"}, - {file = "pillow-10.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69ffdd6120a4737710a9eee73e1d2e37db89b620f702754b8f6e62594471dee0"}, - {file = "pillow-10.2.0-cp310-cp310-win32.whl", hash = "sha256:c6dafac9e0f2b3c78df97e79af707cdc5ef8e88208d686a4847bab8266870023"}, - {file = "pillow-10.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:aebb6044806f2e16ecc07b2a2637ee1ef67a11840a66752751714a0d924adf72"}, - {file = "pillow-10.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:7049e301399273a0136ff39b84c3678e314f2158f50f517bc50285fb5ec847ad"}, - {file = "pillow-10.2.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:35bb52c37f256f662abdfa49d2dfa6ce5d93281d323a9af377a120e89a9eafb5"}, - {file = "pillow-10.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c23f307202661071d94b5e384e1e1dc7dfb972a28a2310e4ee16103e66ddb67"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:773efe0603db30c281521a7c0214cad7836c03b8ccff897beae9b47c0b657d61"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:11fa2e5984b949b0dd6d7a94d967743d87c577ff0b83392f17cb3990d0d2fd6e"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:716d30ed977be8b37d3ef185fecb9e5a1d62d110dfbdcd1e2a122ab46fddb03f"}, - {file = "pillow-10.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a086c2af425c5f62a65e12fbf385f7c9fcb8f107d0849dba5839461a129cf311"}, - {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c8de2789052ed501dd829e9cae8d3dcce7acb4777ea4a479c14521c942d395b1"}, - {file = "pillow-10.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:609448742444d9290fd687940ac0b57fb35e6fd92bdb65386e08e99af60bf757"}, - {file = "pillow-10.2.0-cp311-cp311-win32.whl", hash = "sha256:823ef7a27cf86df6597fa0671066c1b596f69eba53efa3d1e1cb8b30f3533068"}, - {file = "pillow-10.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:1da3b2703afd040cf65ec97efea81cfba59cdbed9c11d8efc5ab09df9509fc56"}, - {file = "pillow-10.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:edca80cbfb2b68d7b56930b84a0e45ae1694aeba0541f798e908a49d66b837f1"}, - {file = "pillow-10.2.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:1b5e1b74d1bd1b78bc3477528919414874748dd363e6272efd5abf7654e68bef"}, - {file = "pillow-10.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0eae2073305f451d8ecacb5474997c08569fb4eb4ac231ffa4ad7d342fdc25ac"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7c2286c23cd350b80d2fc9d424fc797575fb16f854b831d16fd47ceec078f2c"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e23412b5c41e58cec602f1135c57dfcf15482013ce6e5f093a86db69646a5aa"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:52a50aa3fb3acb9cf7213573ef55d31d6eca37f5709c69e6858fe3bc04a5c2a2"}, - {file = "pillow-10.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:127cee571038f252a552760076407f9cff79761c3d436a12af6000cd182a9d04"}, - {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:8d12251f02d69d8310b046e82572ed486685c38f02176bd08baf216746eb947f"}, - {file = "pillow-10.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54f1852cd531aa981bc0965b7d609f5f6cc8ce8c41b1139f6ed6b3c54ab82bfb"}, - {file = "pillow-10.2.0-cp312-cp312-win32.whl", hash = "sha256:257d8788df5ca62c980314053197f4d46eefedf4e6175bc9412f14412ec4ea2f"}, - {file = "pillow-10.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:154e939c5f0053a383de4fd3d3da48d9427a7e985f58af8e94d0b3c9fcfcf4f9"}, - {file = "pillow-10.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:f379abd2f1e3dddb2b61bc67977a6b5a0a3f7485538bcc6f39ec76163891ee48"}, - {file = "pillow-10.2.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:8373c6c251f7ef8bda6675dd6d2b3a0fcc31edf1201266b5cf608b62a37407f9"}, - {file = "pillow-10.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:870ea1ada0899fd0b79643990809323b389d4d1d46c192f97342eeb6ee0b8483"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b4b6b1e20608493548b1f32bce8cca185bf0480983890403d3b8753e44077129"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3031709084b6e7852d00479fd1d310b07d0ba82765f973b543c8af5061cf990e"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:3ff074fc97dd4e80543a3e91f69d58889baf2002b6be64347ea8cf5533188213"}, - {file = "pillow-10.2.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:cb4c38abeef13c61d6916f264d4845fab99d7b711be96c326b84df9e3e0ff62d"}, - {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b1b3020d90c2d8e1dae29cf3ce54f8094f7938460fb5ce8bc5c01450b01fbaf6"}, - {file = "pillow-10.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:170aeb00224ab3dc54230c797f8404507240dd868cf52066f66a41b33169bdbe"}, - {file = "pillow-10.2.0-cp38-cp38-win32.whl", hash = "sha256:c4225f5220f46b2fde568c74fca27ae9771536c2e29d7c04f4fb62c83275ac4e"}, - {file = "pillow-10.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:0689b5a8c5288bc0504d9fcee48f61a6a586b9b98514d7d29b840143d6734f39"}, - {file = "pillow-10.2.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:b792a349405fbc0163190fde0dc7b3fef3c9268292586cf5645598b48e63dc67"}, - {file = "pillow-10.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c570f24be1e468e3f0ce7ef56a89a60f0e05b30a3669a459e419c6eac2c35364"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8ecd059fdaf60c1963c58ceb8997b32e9dc1b911f5da5307aab614f1ce5c2fb"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c365fd1703040de1ec284b176d6af5abe21b427cb3a5ff68e0759e1e313a5e7e"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:70c61d4c475835a19b3a5aa42492409878bbca7438554a1f89d20d58a7c75c01"}, - {file = "pillow-10.2.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:b6f491cdf80ae540738859d9766783e3b3c8e5bd37f5dfa0b76abdecc5081f13"}, - {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d189550615b4948f45252d7f005e53c2040cea1af5b60d6f79491a6e147eef7"}, - {file = "pillow-10.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:49d9ba1ed0ef3e061088cd1e7538a0759aab559e2e0a80a36f9fd9d8c0c21591"}, - {file = "pillow-10.2.0-cp39-cp39-win32.whl", hash = "sha256:babf5acfede515f176833ed6028754cbcd0d206f7f614ea3447d67c33be12516"}, - {file = "pillow-10.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:0304004f8067386b477d20a518b50f3fa658a28d44e4116970abfcd94fac34a8"}, - {file = "pillow-10.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:0fb3e7fc88a14eacd303e90481ad983fd5b69c761e9e6ef94c983f91025da869"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:322209c642aabdd6207517e9739c704dc9f9db943015535783239022002f054a"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3eedd52442c0a5ff4f887fab0c1c0bb164d8635b32c894bc1faf4c618dd89df2"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb28c753fd5eb3dd859b4ee95de66cc62af91bcff5db5f2571d32a520baf1f04"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:33870dc4653c5017bf4c8873e5488d8f8d5f8935e2f1fb9a2208c47cdd66efd2"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:3c31822339516fb3c82d03f30e22b1d038da87ef27b6a78c9549888f8ceda39a"}, - {file = "pillow-10.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a2b56ba36e05f973d450582fb015594aaa78834fefe8dfb8fcd79b93e64ba4c6"}, - {file = "pillow-10.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d8e6aeb9201e655354b3ad049cb77d19813ad4ece0df1249d3c793de3774f8c7"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2247178effb34a77c11c0e8ac355c7a741ceca0a732b27bf11e747bbc950722f"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:15587643b9e5eb26c48e49a7b33659790d28f190fc514a322d55da2fb5c2950e"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753cd8f2086b2b80180d9b3010dd4ed147efc167c90d3bf593fe2af21265e5a5"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:7c8f97e8e7a9009bcacbe3766a36175056c12f9a44e6e6f2d5caad06dcfbf03b"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d1b35bcd6c5543b9cb547dee3150c93008f8dd0f1fef78fc0cd2b141c5baf58a"}, - {file = "pillow-10.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fe4c15f6c9285dc54ce6553a3ce908ed37c8f3825b5a51a15c91442bb955b868"}, - {file = "pillow-10.2.0.tar.gz", hash = "sha256:e87f0b2c78157e12d7686b27d63c070fd65d994e8ddae6f328e0dcf4a0cd007e"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:90b9e29824800e90c84e4022dd5cc16eb2d9605ee13f05d47641eb183cd73d45"}, + {file = "pillow-10.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2c405445c79c3f5a124573a051062300936b0281fee57637e706453e452746c"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78618cdbccaa74d3f88d0ad6cb8ac3007f1a6fa5c6f19af64b55ca170bfa1edf"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:261ddb7ca91fcf71757979534fb4c128448b5b4c55cb6152d280312062f69599"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:ce49c67f4ea0609933d01c0731b34b8695a7a748d6c8d186f95e7d085d2fe475"}, + {file = "pillow-10.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:b14f16f94cbc61215115b9b1236f9c18403c15dd3c52cf629072afa9d54c1cbf"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d33891be6df59d93df4d846640f0e46f1a807339f09e79a8040bc887bdcd7ed3"}, + {file = "pillow-10.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b50811d664d392f02f7761621303eba9d1b056fb1868c8cdf4231279645c25f5"}, + {file = "pillow-10.3.0-cp310-cp310-win32.whl", hash = "sha256:ca2870d5d10d8726a27396d3ca4cf7976cec0f3cb706debe88e3a5bd4610f7d2"}, + {file = "pillow-10.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:f0d0591a0aeaefdaf9a5e545e7485f89910c977087e7de2b6c388aec32011e9f"}, + {file = "pillow-10.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:ccce24b7ad89adb5a1e34a6ba96ac2530046763912806ad4c247356a8f33a67b"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:5f77cf66e96ae734717d341c145c5949c63180842a545c47a0ce7ae52ca83795"}, + {file = "pillow-10.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e4b878386c4bf293578b48fc570b84ecfe477d3b77ba39a6e87150af77f40c57"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fdcbb4068117dfd9ce0138d068ac512843c52295ed996ae6dd1faf537b6dbc27"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9797a6c8fe16f25749b371c02e2ade0efb51155e767a971c61734b1bf6293994"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:9e91179a242bbc99be65e139e30690e081fe6cb91a8e77faf4c409653de39451"}, + {file = "pillow-10.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:1b87bd9d81d179bd8ab871603bd80d8645729939f90b71e62914e816a76fc6bd"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:81d09caa7b27ef4e61cb7d8fbf1714f5aec1c6b6c5270ee53504981e6e9121ad"}, + {file = "pillow-10.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:048ad577748b9fa4a99a0548c64f2cb8d672d5bf2e643a739ac8faff1164238c"}, + {file = "pillow-10.3.0-cp311-cp311-win32.whl", hash = "sha256:7161ec49ef0800947dc5570f86568a7bb36fa97dd09e9827dc02b718c5643f09"}, + {file = "pillow-10.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:8eb0908e954d093b02a543dc963984d6e99ad2b5e36503d8a0aaf040505f747d"}, + {file = "pillow-10.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:4e6f7d1c414191c1199f8996d3f2282b9ebea0945693fb67392c75a3a320941f"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_10_10_x86_64.whl", hash = "sha256:e46f38133e5a060d46bd630faa4d9fa0202377495df1f068a8299fd78c84de84"}, + {file = "pillow-10.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:50b8eae8f7334ec826d6eeffaeeb00e36b5e24aa0b9df322c247539714c6df19"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d3bea1c75f8c53ee4d505c3e67d8c158ad4df0d83170605b50b64025917f338"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19aeb96d43902f0a783946a0a87dbdad5c84c936025b8419da0a0cd7724356b1"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:74d28c17412d9caa1066f7a31df8403ec23d5268ba46cd0ad2c50fb82ae40462"}, + {file = "pillow-10.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ff61bfd9253c3915e6d41c651d5f962da23eda633cf02262990094a18a55371a"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d886f5d353333b4771d21267c7ecc75b710f1a73d72d03ca06df49b09015a9ef"}, + {file = "pillow-10.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4b5ec25d8b17217d635f8935dbc1b9aa5907962fae29dff220f2659487891cd3"}, + {file = "pillow-10.3.0-cp312-cp312-win32.whl", hash = "sha256:51243f1ed5161b9945011a7360e997729776f6e5d7005ba0c6879267d4c5139d"}, + {file = "pillow-10.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:412444afb8c4c7a6cc11a47dade32982439925537e483be7c0ae0cf96c4f6a0b"}, + {file = "pillow-10.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:798232c92e7665fe82ac085f9d8e8ca98826f8e27859d9a96b41d519ecd2e49a"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4eaa22f0d22b1a7e93ff0a596d57fdede2e550aecffb5a1ef1106aaece48e96b"}, + {file = "pillow-10.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cd5e14fbf22a87321b24c88669aad3a51ec052eb145315b3da3b7e3cc105b9a2"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1530e8f3a4b965eb6a7785cf17a426c779333eb62c9a7d1bbcf3ffd5bf77a4aa"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d512aafa1d32efa014fa041d38868fda85028e3f930a96f85d49c7d8ddc0383"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:339894035d0ede518b16073bdc2feef4c991ee991a29774b33e515f1d308e08d"}, + {file = "pillow-10.3.0-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:aa7e402ce11f0885305bfb6afb3434b3cd8f53b563ac065452d9d5654c7b86fd"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0ea2a783a2bdf2a561808fe4a7a12e9aa3799b701ba305de596bc48b8bdfce9d"}, + {file = "pillow-10.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c78e1b00a87ce43bb37642c0812315b411e856a905d58d597750eb79802aaaa3"}, + {file = "pillow-10.3.0-cp38-cp38-win32.whl", hash = "sha256:72d622d262e463dfb7595202d229f5f3ab4b852289a1cd09650362db23b9eb0b"}, + {file = "pillow-10.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:2034f6759a722da3a3dbd91a81148cf884e91d1b747992ca288ab88c1de15999"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:2ed854e716a89b1afcedea551cd85f2eb2a807613752ab997b9974aaa0d56936"}, + {file = "pillow-10.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dc1a390a82755a8c26c9964d457d4c9cbec5405896cba94cf51f36ea0d855002"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4203efca580f0dd6f882ca211f923168548f7ba334c189e9eab1178ab840bf60"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3102045a10945173d38336f6e71a8dc71bcaeed55c3123ad4af82c52807b9375"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:6fb1b30043271ec92dc65f6d9f0b7a830c210b8a96423074b15c7bc999975f57"}, + {file = "pillow-10.3.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:1dfc94946bc60ea375cc39cff0b8da6c7e5f8fcdc1d946beb8da5c216156ddd8"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b09b86b27a064c9624d0a6c54da01c1beaf5b6cadfa609cf63789b1d08a797b9"}, + {file = "pillow-10.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d3b2348a78bc939b4fed6552abfd2e7988e0f81443ef3911a4b8498ca084f6eb"}, + {file = "pillow-10.3.0-cp39-cp39-win32.whl", hash = "sha256:45ebc7b45406febf07fef35d856f0293a92e7417ae7933207e90bf9090b70572"}, + {file = "pillow-10.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:0ba26351b137ca4e0db0342d5d00d2e355eb29372c05afd544ebf47c0956ffeb"}, + {file = "pillow-10.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:50fd3f6b26e3441ae07b7c979309638b72abc1a25da31a81a7fbd9495713ef4f"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_10_10_x86_64.whl", hash = "sha256:6b02471b72526ab8a18c39cb7967b72d194ec53c1fd0a70b050565a0f366d355"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:8ab74c06ffdab957d7670c2a5a6e1a70181cd10b727cd788c4dd9005b6a8acd9"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:048eeade4c33fdf7e08da40ef402e748df113fd0b4584e32c4af74fe78baaeb2"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e2ec1e921fd07c7cda7962bad283acc2f2a9ccc1b971ee4b216b75fad6f0463"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:4c8e73e99da7db1b4cad7f8d682cf6abad7844da39834c288fbfa394a47bbced"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:16563993329b79513f59142a6b02055e10514c1a8e86dca8b48a893e33cf91e3"}, + {file = "pillow-10.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:dd78700f5788ae180b5ee8902c6aea5a5726bac7c364b202b4b3e3ba2d293170"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_10_10_x86_64.whl", hash = "sha256:aff76a55a8aa8364d25400a210a65ff59d0168e0b4285ba6bf2bd83cf675ba32"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b7bc2176354defba3edc2b9a777744462da2f8e921fbaf61e52acb95bafa9828"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:793b4e24db2e8742ca6423d3fde8396db336698c55cd34b660663ee9e45ed37f"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d93480005693d247f8346bc8ee28c72a2191bdf1f6b5db469c096c0c867ac015"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83341b89884e2b2e55886e8fbbf37c3fa5efd6c8907124aeb72f285ae5696e5"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:1a1d1915db1a4fdb2754b9de292642a39a7fb28f1736699527bb649484fb966a"}, + {file = "pillow-10.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a0eaa93d054751ee9964afa21c06247779b90440ca41d184aeb5d410f20ff591"}, + {file = "pillow-10.3.0.tar.gz", hash = "sha256:9d2455fbf44c914840c793e89aa82d0e1763a14253a000743719ae5946814b2d"}, ] [package.extras] @@ -802,28 +830,28 @@ files = [ [[package]] name = "pyasn1" -version = "0.5.1" +version = "0.6.0" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.8" files = [ - {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, - {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, + {file = "pyasn1-0.6.0-py2.py3-none-any.whl", hash = "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473"}, + {file = "pyasn1-0.6.0.tar.gz", hash = "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c"}, ] [[package]] name = "pyasn1-modules" -version = "0.3.0" +version = "0.4.0" description = "A collection of ASN.1-based protocols modules" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" +python-versions = ">=3.8" files = [ - {file = "pyasn1_modules-0.3.0-py2.py3-none-any.whl", hash = "sha256:d3ccd6ed470d9ffbc716be08bd90efbd44d0734bc9303818f7336070984a162d"}, - {file = "pyasn1_modules-0.3.0.tar.gz", hash = "sha256:5bd01446b736eb9d31512a30d46c1ac3395d676c6f3cafa4c03eb54b9925631c"}, + {file = "pyasn1_modules-0.4.0-py3-none-any.whl", hash = "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b"}, + {file = "pyasn1_modules-0.4.0.tar.gz", hash = "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6"}, ] [package.dependencies] -pyasn1 = ">=0.4.6,<0.6.0" +pyasn1 = ">=0.4.6,<0.7.0" [[package]] name = "pydantic" @@ -935,6 +963,20 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pyparsing" +version = "3.1.2" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +optional = false +python-versions = ">=3.6.8" +files = [ + {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, + {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, +] + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] + [[package]] name = "pytest" version = "7.4.4" @@ -977,17 +1019,17 @@ testing = ["coverage (>=6.2)", "flaky (>=3.5.0)", "hypothesis (>=5.7.1)", "mypy [[package]] name = "pytest-mock" -version = "3.12.0" +version = "3.14.0" description = "Thin-wrapper around the mock package for easier use with pytest" optional = false python-versions = ">=3.8" files = [ - {file = "pytest-mock-3.12.0.tar.gz", hash = "sha256:31a40f038c22cad32287bb43932054451ff5583ff094bca6f675df2f8bc1a6e9"}, - {file = "pytest_mock-3.12.0-py3-none-any.whl", hash = "sha256:0972719a7263072da3a21c7f4773069bcc7486027d7e8e1f81d98a47e701bc4f"}, + {file = "pytest-mock-3.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, + {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, ] [package.dependencies] -pytest = ">=5.0" +pytest = ">=6.2.5" [package.extras] dev = ["pre-commit", "pytest-asyncio", "tox"] @@ -1152,17 +1194,6 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] -[[package]] -name = "sniffio" -version = "1.3.1" -description = "Sniff out which async library your code is running under" -optional = false -python-versions = ">=3.7" -files = [ - {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, - {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, -] - [[package]] name = "syrupy" version = "4.6.1" @@ -1235,35 +1266,35 @@ files = [ [[package]] name = "types-pillow" -version = "10.2.0.20240311" +version = "10.2.0.20240406" description = "Typing stubs for Pillow" optional = false python-versions = ">=3.8" files = [ - {file = "types-Pillow-10.2.0.20240311.tar.gz", hash = "sha256:f611f6baf7c3784fe550ee92b108060f5544a47c37c73acb81a785f1c6312772"}, - {file = "types_Pillow-10.2.0.20240311-py3-none-any.whl", hash = "sha256:34ca2fe768c6b1d05f288374c1a5ef9437f75faa1f91437b43c50970bbb54a94"}, + {file = "types-Pillow-10.2.0.20240406.tar.gz", hash = "sha256:62e0cc1f17caba40e72e7154a483f4c7f3bea0e1c34c0ebba9de3c7745bc306d"}, + {file = "types_Pillow-10.2.0.20240406-py3-none-any.whl", hash = "sha256:5ac182e8afce53de30abca2fdf9cbec7b2500e549d0be84da035a729a84c7c47"}, ] [[package]] name = "types-protobuf" -version = "4.24.0.20240311" +version = "4.24.0.20240408" description = "Typing stubs for protobuf" optional = false python-versions = ">=3.8" files = [ - {file = "types-protobuf-4.24.0.20240311.tar.gz", hash = "sha256:c80426f9fb9b21aee514691e96ab32a5cd694a82e2ac07964b352c3e7e0182bc"}, - {file = "types_protobuf-4.24.0.20240311-py3-none-any.whl", hash = "sha256:8e039486df058141cb221ab99f88c5878c08cca4376db1d84f63279860aa09cd"}, + {file = "types-protobuf-4.24.0.20240408.tar.gz", hash = "sha256:c03a44357b03c233c8c5864ce3e07dd9c766a00497d271496923f7ae3cb9e1de"}, + {file = "types_protobuf-4.24.0.20240408-py3-none-any.whl", hash = "sha256:9b87cd279378693071247227f52e89738af7c8d6f06dbdd749b0cf473c4916ce"}, ] [[package]] name = "types-requests" -version = "2.31.0.20240311" +version = "2.31.0.20240406" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.31.0.20240311.tar.gz", hash = "sha256:b1c1b66abfb7fa79aae09097a811c4aa97130eb8831c60e47aee4ca344731ca5"}, - {file = "types_requests-2.31.0.20240311-py3-none-any.whl", hash = "sha256:47872893d65a38e282ee9f277a4ee50d1b28bd592040df7d1fdaffdf3779937d"}, + {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, + {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, ] [package.dependencies] @@ -1271,13 +1302,24 @@ urllib3 = ">=2" [[package]] name = "typing-extensions" -version = "4.10.0" +version = "4.11.0" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.10.0-py3-none-any.whl", hash = "sha256:69b1a937c3a517342112fb4c6df7e72fc39a38e7891a5730ed4985b5214b5475"}, - {file = "typing_extensions-4.10.0.tar.gz", hash = "sha256:b0abd7c89e8fb96f98db18d86106ff1d90ab692004eb746cf6eda2682f91b3cb"}, + {file = "typing_extensions-4.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, + {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, +] + +[[package]] +name = "uritemplate" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" +optional = false +python-versions = ">=3.6" +files = [ + {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, + {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, ] [[package]] @@ -1344,4 +1386,4 @@ images = ["pillow"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "dd7ba0d20a620f1eac259e8809a0f968bb49997574523c171611c827e7134c5f" +content-hash = "2b03aaa008902cf0eff7757a7453c7fff83b06939a47845fa3704abb4fb172a1" diff --git a/libs/genai/pyproject.toml b/libs/genai/pyproject.toml index 1d82e99a..278df6b7 100644 --- a/libs/genai/pyproject.toml +++ b/libs/genai/pyproject.toml @@ -12,8 +12,8 @@ license = "MIT" [tool.poetry.dependencies] python = ">=3.9,<4.0" -langchain-core = "^0.1" -google-generativeai = "^0.4.1" +langchain-core = ">=0.1.27,<0.2" +google-generativeai = "^0.5.0" pillow = { version = "^10.1.0", optional = true } [tool.poetry.extras] diff --git a/libs/genai/tests/integration_tests/test_chat_models.py b/libs/genai/tests/integration_tests/test_chat_models.py index 26e85aa1..340c466d 100644 --- a/libs/genai/tests/integration_tests/test_chat_models.py +++ b/libs/genai/tests/integration_tests/test_chat_models.py @@ -112,6 +112,16 @@ def test_chat_google_genai_invoke_multimodal() -> None: assert len(chunk.content.strip()) > 0 +def test_system_message() -> None: + messages = [ + SystemMessage(content="Be a helful assistant."), + HumanMessage(content="Hi, how are you?"), + ] + llm = ChatGoogleGenerativeAI(model="models/gemini-1.0-pro-latest") + answer = llm.invoke(messages) + assert isinstance(answer.content, str) + + def test_chat_google_genai_invoke_multimodal_too_many_messages() -> None: # Only supports 1 turn... messages: list = [ @@ -168,18 +178,6 @@ def test_chat_google_genai_single_call_with_history() -> None: assert isinstance(response.content, str) -def test_chat_google_genai_system_message_error() -> None: - model = ChatGoogleGenerativeAI(model=_MODEL) - text_question1, text_answer1 = "How much is 2+2?", "4" - text_question2 = "How much is 3+3?" - system_message = SystemMessage(content="You're supposed to answer math questions.") - message1 = HumanMessage(content=text_question1) - message2 = AIMessage(content=text_answer1) - message3 = HumanMessage(content=text_question2) - with pytest.raises(ValueError): - model([system_message, message1, message2, message3]) - - def test_chat_google_genai_system_message() -> None: model = ChatGoogleGenerativeAI(model=_MODEL, convert_system_message_to_human=True) text_question1, text_answer1 = "How much is 2+2?", "4" diff --git a/libs/genai/tests/unit_tests/test_chat_models.py b/libs/genai/tests/unit_tests/test_chat_models.py index e17bb7d9..aa5aae20 100644 --- a/libs/genai/tests/unit_tests/test_chat_models.py +++ b/libs/genai/tests/unit_tests/test_chat_models.py @@ -61,13 +61,16 @@ def test_parse_history() -> None: message2 = AIMessage(content=text_answer1) message3 = HumanMessage(content=text_question2) messages = [system_message, message1, message2, message3] - history = _parse_chat_history(messages, convert_system_message_to_human=True) + system_instruction, history = _parse_chat_history( + messages, convert_system_message_to_human=True + ) assert len(history) == 3 assert history[0] == { "role": "user", - "parts": [{"text": system_input}, {"text": text_question1}], + "parts": [{"text": text_question1}], } assert history[1] == {"role": "model", "parts": [{"text": text_answer1}]} + assert system_instruction == [{"text": system_input}] @pytest.mark.parametrize("content", ['["a"]', '{"a":"b"}', "function output"]) From 2cd86506ed6234be2b8769bcfa16e86795f88535 Mon Sep 17 00:00:00 2001 From: Alex Ostapenko Date: Wed, 10 Apr 2024 13:59:49 +0200 Subject: [PATCH 5/9] allow list title field as it's supported now (#131) --- .../functions_utils.py | 5 +- .../tests/unit_tests/test_function_utils.py | 81 ++++++++++++++----- 2 files changed, 64 insertions(+), 22 deletions(-) diff --git a/libs/vertexai/langchain_google_vertexai/functions_utils.py b/libs/vertexai/langchain_google_vertexai/functions_utils.py index f5509b89..89dbb7cc 100644 --- a/libs/vertexai/langchain_google_vertexai/functions_utils.py +++ b/libs/vertexai/langchain_google_vertexai/functions_utils.py @@ -78,14 +78,13 @@ def _format_tools_to_vertex_tool( class ParametersSchema(BaseModel): """ This is a schema of currently supported definitions in function calling. - We need explicitly exclude `title` and `definitions` fields as they - are not currently supported. + We need explicitly exclude `definitions` field + as it is not currently supported. All other fields will be passed through (as extra fields are allowed) and intercepted on `google.cloud.aiplatform` level """ - title: Optional[str] = Field(exclude=True) definitions: Optional[Any] = Field(exclude=True) items: Optional["ParametersSchema"] properties: Optional[Dict[str, "ParametersSchema"]] diff --git a/libs/vertexai/tests/unit_tests/test_function_utils.py b/libs/vertexai/tests/unit_tests/test_function_utils.py index 8e472edc..b69a4008 100644 --- a/libs/vertexai/tests/unit_tests/test_function_utils.py +++ b/libs/vertexai/tests/unit_tests/test_function_utils.py @@ -1,4 +1,5 @@ -from typing import Optional, Union +from enum import Enum +from typing import Optional, Sequence from langchain_core.pydantic_v1 import BaseModel, Field from langchain_core.tools import tool @@ -52,28 +53,70 @@ def do_something_optional(a: float, b: float = 0) -> str: def test_get_parameters_from_schema(): + class StringEnum(str, Enum): + pear = "pear" + banana = "banana" + class A(BaseModel): - a1: Optional[int] + """Class A""" + + int_field: Optional[int] class B(BaseModel): - b1: Optional[A] - b2: int = Field(description="f2") - b3: Union[int, str] + object_field: Optional[A] + array_field: Sequence[A] + int_field: int = Field(description="int field", min=0, max=10) + str_field: str = Field( + min_length=1, max_length=10, pattern="^[A-Z]{1,10}$", example="ABCD" + ) + str_enum_field: StringEnum schema = B.schema() result = _get_parameters_from_schema(schema) - assert result["type"] == "object" - assert "required" in result - assert len(result["required"]) == 2 - - assert "properties" in result - assert "b1" in result["properties"] - assert "b2" in result["properties"] - assert "b3" in result["properties"] - - assert result["properties"]["b1"]["type"] == "object" - assert "a1" in result["properties"]["b1"]["properties"] - assert "required" not in result["properties"]["b1"] - assert len(result["properties"]["b1"]["properties"]) == 1 - assert "anyOf" in result["properties"]["b3"] + assert result == { + "properties": { + "object_field": { + "properties": {"int_field": {"type": "integer", "title": "Int Field"}}, + "type": "object", + "description": "Class A", + "title": "A", + }, + "array_field": { + "items": { + "properties": { + "int_field": {"type": "integer", "title": "Int Field"} + }, + "type": "object", + "description": "Class A", + "title": "A", + }, + "type": "array", + "title": "Array Field", + }, + "int_field": { + "max": 10, + "type": "integer", + "min": 0, + "description": "int field", + "title": "Int Field", + }, + "str_field": { + "minLength": 1, + "type": "string", + "maxLength": 10, + "example": "ABCD", + "pattern": "^[A-Z]{1,10}$", + "title": "Str Field", + }, + "str_enum_field": { + "type": "string", + "description": "An enumeration.", + "title": "StringEnum", + "enum": ["pear", "banana"], + }, + }, + "type": "object", + "title": "B", + "required": ["array_field", "int_field", "str_field", "str_enum_field"], + } From e14244e3db908bf01a2ddaf0a645d29c3f497563 Mon Sep 17 00:00:00 2001 From: eliasecchig <115624100+eliasecchig@users.noreply.github.com> Date: Wed, 10 Apr 2024 14:52:42 +0200 Subject: [PATCH 6/9] Add system_instructions for Gemini models (#125) --- .../langchain_google_vertexai/chat_models.py | 102 +++++++++++++----- libs/vertexai/poetry.lock | 38 ++++--- libs/vertexai/pyproject.toml | 2 +- .../integration_tests/test_chat_models.py | 67 ++++++------ .../tests/unit_tests/test_chat_models.py | 27 ++++- pyproject.toml | 15 +++ 6 files changed, 175 insertions(+), 76 deletions(-) create mode 100644 pyproject.toml diff --git a/libs/vertexai/langchain_google_vertexai/chat_models.py b/libs/vertexai/langchain_google_vertexai/chat_models.py index 4e19f99d..afc00572 100644 --- a/libs/vertexai/langchain_google_vertexai/chat_models.py +++ b/libs/vertexai/langchain_google_vertexai/chat_models.py @@ -118,7 +118,7 @@ def _parse_chat_history_gemini( history: List[BaseMessage], project: Optional[str] = None, convert_system_message_to_human: Optional[bool] = False, -) -> List[Content]: +) -> tuple[Content | None, list[Content]]: def _convert_to_prompt(part: Union[str, Dict]) -> Part: if isinstance(part, str): return Part.from_text(part) @@ -142,24 +142,35 @@ def _convert_to_parts(message: BaseMessage) -> List[Part]: return [_convert_to_prompt(part) for part in raw_content] vertex_messages = [] - raw_system_message = None + convert_system_message_to_human_content = None + system_instruction = None for i, message in enumerate(history): if ( i == 0 and isinstance(message, SystemMessage) and not convert_system_message_to_human ): - raise ValueError( - """SystemMessages are not yet supported! - -To automatically convert the leading SystemMessage to a HumanMessage, -set `convert_system_message_to_human` to True. Example: - -llm = ChatVertexAI(model_name="gemini-pro", convert_system_message_to_human=True) -""" + if system_instruction is not None: + raise ValueError( + "Detected more than one SystemMessage in the list of messages." + "Gemini APIs support the insertion of only SystemMessage." + ) + else: + system_instruction = Content( + role="user", parts=_convert_to_parts(message) + ) + continue + elif ( + i == 0 + and isinstance(message, SystemMessage) + and convert_system_message_to_human + ): + logger.warning( + "gemini models released from April 2024 support SystemMessages" + "natively. For best performances, when working with these models," + "set convert_system_message_to_human to False" ) - elif i == 0 and isinstance(message, SystemMessage): - raw_system_message = message + convert_system_message_to_human_content = message continue elif isinstance(message, AIMessage): raw_function_call = message.additional_kwargs.get("function_call") @@ -193,18 +204,18 @@ def _convert_to_parts(message: BaseMessage) -> List[Part]: f"Unexpected message with type {type(message)} at the position {i}." ) - if raw_system_message: + if convert_system_message_to_human_content: if role == "model": raise ValueError( "SystemMessage should be followed by a HumanMessage and " "not by AIMessage." ) - parts = _convert_to_parts(raw_system_message) + parts - raw_system_message = None + parts = _convert_to_parts(convert_system_message_to_human_content) + parts + convert_system_message_to_human_content = None vertex_message = Content(role=role, parts=parts) vertex_messages.append(vertex_message) - return vertex_messages + return system_instruction, vertex_messages def _parse_examples(examples: List[BaseMessage]) -> List[InputOutputTextPair]: @@ -247,6 +258,21 @@ def _get_question(messages: List[BaseMessage]) -> HumanMessage: return question +def _get_client_with_sys_instruction( + client: GenerativeModel, + system_instruction: Content, + model_name: str, + safety_settings: Optional[Dict] = None, +): + if client._system_instruction != system_instruction: + client = GenerativeModel( + model_name=model_name, + safety_settings=safety_settings, + system_instruction=system_instruction, + ) + return client + + def _parse_response_candidate(response_candidate: "Candidate") -> AIMessage: try: content = response_candidate.text @@ -275,10 +301,9 @@ class ChatVertexAI(_VertexAICommon, BaseChatModel): "Underlying model name." examples: Optional[List[BaseMessage]] = None convert_system_message_to_human: bool = False - """Whether to merge any leading SystemMessage into the following HumanMessage. - - Gemini does not support system messages; any unsupported messages will - raise an error.""" + """[Deprecated] Since new Gemini models support setting a System Message, + setting this parameter to True is discouraged. + """ @classmethod def is_lc_serializable(self) -> bool: @@ -357,12 +382,18 @@ def _generate( msg_params["candidate_count"] = params.pop("candidate_count") if self._is_gemini_model: - history_gemini = _parse_chat_history_gemini( + system_instruction, history_gemini = _parse_chat_history_gemini( messages, project=self.project, convert_system_message_to_human=self.convert_system_message_to_human, ) message = history_gemini.pop() + self.client = _get_client_with_sys_instruction( + client=self.client, + system_instruction=system_instruction, + model_name=self.model_name, + safety_settings=safety_settings, + ) with telemetry.tool_context_manager(self._user_agent): chat = self.client.start_chat(history=history_gemini) @@ -441,12 +472,19 @@ async def _agenerate( msg_params["candidate_count"] = params.pop("candidate_count") if self._is_gemini_model: - history_gemini = _parse_chat_history_gemini( + system_instruction, history_gemini = _parse_chat_history_gemini( messages, project=self.project, convert_system_message_to_human=self.convert_system_message_to_human, ) message = history_gemini.pop() + + self.client = _get_client_with_sys_instruction( + client=self.client, + system_instruction=system_instruction, + model_name=self.model_name, + safety_settings=safety_settings, + ) with telemetry.tool_context_manager(self._user_agent): chat = self.client.start_chat(history=history_gemini) # set param to `functions` until core tool/function calling implemented @@ -501,18 +539,24 @@ def _stream( ) -> Iterator[ChatGenerationChunk]: params = self._prepare_params(stop=stop, stream=True, **kwargs) if self._is_gemini_model: - history_gemini = _parse_chat_history_gemini( + safety_settings = params.pop("safety_settings", None) + system_instruction, history_gemini = _parse_chat_history_gemini( messages, project=self.project, convert_system_message_to_human=self.convert_system_message_to_human, ) message = history_gemini.pop() + self.client = _get_client_with_sys_instruction( + client=self.client, + system_instruction=system_instruction, + model_name=self.model_name, + safety_settings=safety_settings, + ) with telemetry.tool_context_manager(self._user_agent): chat = self.client.start_chat(history=history_gemini) # set param to `functions` until core tool/function calling implemented raw_tools = params.pop("functions") if "functions" in params else None tools = _format_tools_to_vertex_tool(raw_tools) if raw_tools else None - safety_settings = params.pop("safety_settings", None) with telemetry.tool_context_manager(self._user_agent): responses = chat.send_message( message, @@ -567,17 +611,23 @@ async def _astream( if not self._is_gemini_model: raise NotImplementedError() params = self._prepare_params(stop=stop, stream=True, **kwargs) - history_gemini = _parse_chat_history_gemini( + safety_settings = params.pop("safety_settings", None) + system_instruction, history_gemini = _parse_chat_history_gemini( messages, project=self.project, convert_system_message_to_human=self.convert_system_message_to_human, ) message = history_gemini.pop() + self.client = _get_client_with_sys_instruction( + client=self.client, + system_instruction=system_instruction, + model_name=self.model_name, + safety_settings=safety_settings, + ) with telemetry.tool_context_manager(self._user_agent): chat = self.client.start_chat(history=history_gemini) raw_tools = params.pop("functions") if "functions" in params else None tools = _format_tools_to_vertex_tool(raw_tools) if raw_tools else None - safety_settings = params.pop("safety_settings", None) with telemetry.tool_context_manager(self._user_agent): async for chunk in await chat.send_message_async( message, diff --git a/libs/vertexai/poetry.lock b/libs/vertexai/poetry.lock index 1654fd30..1ed44788 100644 --- a/libs/vertexai/poetry.lock +++ b/libs/vertexai/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "aiohttp" @@ -640,13 +640,13 @@ httplib2 = ">=0.19.0" [[package]] name = "google-cloud-aiplatform" -version = "1.46.0" +version = "1.47.0" description = "Vertex AI API client library" optional = false python-versions = ">=3.8" files = [ - {file = "google-cloud-aiplatform-1.46.0.tar.gz", hash = "sha256:9eeefcd34dbad2a1ade830d025154189d257e8dcfea45771dcac6e34a2783782"}, - {file = "google_cloud_aiplatform-1.46.0-py2.py3-none-any.whl", hash = "sha256:a30a64f8bcefa0646340cf57abca192bb9ca9c456a47b842651a9564bba0534a"}, + {file = "google-cloud-aiplatform-1.47.0.tar.gz", hash = "sha256:1c4537db09b83957bf0623fd2afb37e339f89a3afcda3efce9dce79b16ab59c7"}, + {file = "google_cloud_aiplatform-1.47.0-py2.py3-none-any.whl", hash = "sha256:454ef0c44ecaeadcffe58f565acfce49e53895fd51bb20da8af0d48202a4cb21"}, ] [package.dependencies] @@ -665,19 +665,22 @@ shapely = "<3.0.0dev" [package.extras] autologging = ["mlflow (>=1.27.0,<=2.1.1)"] cloud-profiler = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] -datasets = ["pyarrow (>=10.0.1)", "pyarrow (>=3.0.0,<8.0dev)"] +datasets = ["pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)"] endpoint = ["requests (>=2.28.1)"] -full = ["cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.103.1)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyyaml (==5.3.1)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<2.15.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] +full = ["cloudpickle (<3.0)", "cloudpickle (>=2.2.1,<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.103.1)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "nest-asyncio (>=1.0.0,<1.6.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pydantic (<3)", "pyyaml (==5.3.1)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "starlette (>=0.17.1)", "tensorflow (>=2.3.0,<2.15.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)"] +langchain = ["langchain (>=0.1.13,<0.2)", "langchain-core (<0.2)", "langchain-google-vertexai (<0.2)"] lit = ["explainable-ai-sdk (>=1.0.0)", "lit-nlp (==0.4.0)", "pandas (>=1.0.0)", "tensorflow (>=2.3.0,<3.0.0dev)"] metadata = ["numpy (>=1.15.0)", "pandas (>=1.0.0)"] pipelines = ["pyyaml (==5.3.1)"] prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<0.103.1)", "httpx (>=0.23.0,<0.25.0)", "starlette (>=0.17.1)", "uvicorn[standard] (>=0.16.0)"] preview = ["cloudpickle (<3.0)", "google-cloud-logging (<4.0)"] private-endpoints = ["requests (>=2.28.1)", "urllib3 (>=1.21.1,<1.27)"] +rapid-evaluation = ["nest-asyncio (>=1.0.0,<1.6.0)", "pandas (>=1.0.0,<2.2.0)"] ray = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)"] ray-testing = ["google-cloud-bigquery", "google-cloud-bigquery-storage", "immutabledict", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pytest-xdist", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "ray[train] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "scikit-learn", "tensorflow", "torch (>=2.0.0,<2.1.0)", "xgboost", "xgboost-ray"] +reasoningengine = ["cloudpickle (>=2.2.1,<3.0)", "pydantic (<3)"] tensorboard = ["tensorflow (>=2.3.0,<2.15.0)"] -testing = ["bigframes", "cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.103.1)", "google-api-core (>=2.11,<3.0.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "grpcio-testing", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "ipython", "kfp (>=2.6.0,<3.0.0)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyfakefs", "pytest-asyncio", "pytest-xdist", "pyyaml (==5.3.1)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "requests-toolbelt (<1.0.0)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (==2.13.0)", "tensorflow (>=2.3.0,<2.15.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "torch (>=2.0.0,<2.1.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost", "xgboost-ray"] +testing = ["bigframes", "cloudpickle (<3.0)", "cloudpickle (>=2.2.1,<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<0.103.1)", "google-api-core (>=2.11,<3.0.0)", "google-cloud-bigquery", "google-cloud-bigquery-storage", "google-cloud-logging (<4.0)", "google-vizier (>=0.1.6)", "grpcio-testing", "httpx (>=0.23.0,<0.25.0)", "immutabledict", "ipython", "kfp (>=2.6.0,<3.0.0)", "lit-nlp (==0.4.0)", "mlflow (>=1.27.0,<=2.1.1)", "nest-asyncio (>=1.0.0,<1.6.0)", "numpy (>=1.15.0)", "pandas (>=1.0.0)", "pandas (>=1.0.0,<2.2.0)", "pyarrow (>=10.0.1)", "pyarrow (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pydantic (<3)", "pyfakefs", "pytest-asyncio", "pytest-xdist", "pyyaml (==5.3.1)", "ray[default] (>=2.4,<2.5.dev0 || >2.9.0,!=2.9.1,!=2.9.2,<=2.9.3)", "ray[default] (>=2.5,<=2.9.3)", "requests (>=2.28.1)", "requests-toolbelt (<1.0.0)", "scikit-learn", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (==2.13.0)", "tensorflow (==2.16.1)", "tensorflow (>=2.3.0,<2.15.0)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "torch (>=2.0.0,<2.1.0)", "torch (>=2.2.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] vizier = ["google-vizier (>=0.1.6)"] xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] @@ -1337,13 +1340,13 @@ extended-testing = ["lxml (>=5.1.0,<6.0.0)"] [[package]] name = "langsmith" -version = "0.1.40" +version = "0.1.41" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.40-py3-none-any.whl", hash = "sha256:aa47d0f5a1eabd5c05ac6ce2cd3e28ccfc554d366e856a27b7c3c17c443881cb"}, - {file = "langsmith-0.1.40.tar.gz", hash = "sha256:50fdf313741cf94e978de06025fd180b56acf1d1a4549b0fd5453ef23d5461ef"}, + {file = "langsmith-0.1.41-py3-none-any.whl", hash = "sha256:11de22b6990502c630fdfdf6906681e664c6659d2118bcd2b79d08016e770831"}, + {file = "langsmith-0.1.41.tar.gz", hash = "sha256:1250cd6c9074ca10d40002b23d79b3017329b139fbe954248fdd7a79544e90d0"}, ] [package.dependencies] @@ -2030,6 +2033,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -2490,24 +2494,24 @@ files = [ [[package]] name = "types-protobuf" -version = "4.24.0.20240311" +version = "4.24.0.20240408" description = "Typing stubs for protobuf" optional = false python-versions = ">=3.8" files = [ - {file = "types-protobuf-4.24.0.20240311.tar.gz", hash = "sha256:c80426f9fb9b21aee514691e96ab32a5cd694a82e2ac07964b352c3e7e0182bc"}, - {file = "types_protobuf-4.24.0.20240311-py3-none-any.whl", hash = "sha256:8e039486df058141cb221ab99f88c5878c08cca4376db1d84f63279860aa09cd"}, + {file = "types-protobuf-4.24.0.20240408.tar.gz", hash = "sha256:c03a44357b03c233c8c5864ce3e07dd9c766a00497d271496923f7ae3cb9e1de"}, + {file = "types_protobuf-4.24.0.20240408-py3-none-any.whl", hash = "sha256:9b87cd279378693071247227f52e89738af7c8d6f06dbdd749b0cf473c4916ce"}, ] [[package]] name = "types-requests" -version = "2.31.0.20240403" +version = "2.31.0.20240406" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" files = [ - {file = "types-requests-2.31.0.20240403.tar.gz", hash = "sha256:e1e0cd0b655334f39d9f872b68a1310f0e343647688bf2cee932ec4c2b04de59"}, - {file = "types_requests-2.31.0.20240403-py3-none-any.whl", hash = "sha256:06abf6a68f5c4f2a62f6bb006672dfb26ed50ccbfddb281e1ee6f09a65707d5d"}, + {file = "types-requests-2.31.0.20240406.tar.gz", hash = "sha256:4428df33c5503945c74b3f42e82b181e86ec7b724620419a2966e2de604ce1a1"}, + {file = "types_requests-2.31.0.20240406-py3-none-any.whl", hash = "sha256:6216cdac377c6b9a040ac1c0404f7284bd13199c0e1bb235f4324627e8898cf5"}, ] [package.dependencies] @@ -2717,4 +2721,4 @@ anthropic = ["anthropic"] [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<4.0" -content-hash = "fec2eb547d686aa727105697113b434ab7f852ce5de08fedb312c1bf8919c371" +content-hash = "10cb39e92083851c4280f04caf22b4a121cf1f01513f65be7b688d1359d577e8" diff --git a/libs/vertexai/pyproject.toml b/libs/vertexai/pyproject.toml index f877d18d..093a6d20 100644 --- a/libs/vertexai/pyproject.toml +++ b/libs/vertexai/pyproject.toml @@ -13,7 +13,7 @@ license = "MIT" [tool.poetry.dependencies] python = ">=3.8.1,<4.0" langchain-core = ">=0.1.27,<0.2" -google-cloud-aiplatform = "^1.44.0" +google-cloud-aiplatform = "^1.47.0" google-cloud-storage = "^2.14.0" types-requests = "^2.31.0" types-protobuf = "^4.24.0.4" diff --git a/libs/vertexai/tests/integration_tests/test_chat_models.py b/libs/vertexai/tests/integration_tests/test_chat_models.py index 0357bbb0..56bcc85b 100644 --- a/libs/vertexai/tests/integration_tests/test_chat_models.py +++ b/libs/vertexai/tests/integration_tests/test_chat_models.py @@ -13,7 +13,7 @@ from langchain_core.outputs import ChatGeneration, LLMResult from langchain_core.pydantic_v1 import BaseModel -from langchain_google_vertexai.chat_models import ChatVertexAI +from langchain_google_vertexai import ChatVertexAI, HarmBlockThreshold, HarmCategory model_names_to_test = [None, "codechat-bison", "chat-bison", "gemini-pro"] @@ -200,51 +200,53 @@ def test_vertexai_single_call_with_history(model_name: Optional[str]) -> None: @pytest.mark.release -def test_vertexai_single_call_fails_no_message() -> None: - chat = ChatVertexAI() - with pytest.raises(ValueError) as exc_info: - _ = chat([]) - assert ( - str(exc_info.value) - == "You should provide at least one message to start the chat!" - ) - - -@pytest.mark.release -@pytest.mark.parametrize("model_name", ["gemini-pro"]) -def test_chat_vertexai_gemini_system_message_error(model_name: str) -> None: - model = ChatVertexAI(model_name=model_name) - text_question1, text_answer1 = "How much is 2+2?", "4" - text_question2 = "How much is 3+3?" - system_message = SystemMessage(content="You're supposed to answer math questions.") +@pytest.mark.parametrize("model_name", ["gemini-1.0-pro-002"]) +def test_vertexai_system_message(model_name: Optional[str]) -> None: + if model_name: + model = ChatVertexAI(model_name=model_name) + else: + model = ChatVertexAI() + system_instruction = """CymbalBank is a bank located in London""" + text_question1 = "Where is Cymbal located? Provide only the name of the city." + sys_message = SystemMessage(content=system_instruction) message1 = HumanMessage(content=text_question1) - message2 = AIMessage(content=text_answer1) - message3 = HumanMessage(content=text_question2) - with pytest.raises(ValueError): - model([system_message, message1, message2, message3]) + response = model([sys_message, message1]) + + assert isinstance(response, AIMessage) + assert isinstance(response.content, str) + assert response.content.lower() == "london" @pytest.mark.release @pytest.mark.parametrize("model_name", model_names_to_test) -def test_chat_vertexai_system_message(model_name: Optional[str]) -> None: +def test_vertexai_single_call_with_no_system_messages( + model_name: Optional[str], +) -> None: if model_name: - model = ChatVertexAI( - model_name=model_name, convert_system_message_to_human=True - ) + model = ChatVertexAI(model_name=model_name) else: model = ChatVertexAI() - text_question1, text_answer1 = "How much is 2+2?", "4" text_question2 = "How much is 3+3?" - system_message = SystemMessage(content="You're supposed to answer math questions.") message1 = HumanMessage(content=text_question1) message2 = AIMessage(content=text_answer1) message3 = HumanMessage(content=text_question2) - response = model([system_message, message1, message2, message3]) + response = model([message1, message2, message3]) assert isinstance(response, AIMessage) assert isinstance(response.content, str) +@pytest.mark.release +def test_vertexai_single_call_fails_no_message() -> None: + chat = ChatVertexAI() + with pytest.raises(ValueError) as exc_info: + _ = chat([]) + assert ( + str(exc_info.value) + == "You should provide at least one message to start the chat!" + ) + + @pytest.mark.release @pytest.mark.parametrize("model_name", model_names_to_test) def test_get_num_tokens_from_messages(model_name: str) -> None: @@ -264,7 +266,12 @@ class MyModel(BaseModel): name: str age: int - model = ChatVertexAI(model_name="gemini-pro").bind(functions=[MyModel]) + safety = { + HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT: HarmBlockThreshold.BLOCK_ONLY_HIGH + } + model = ChatVertexAI(model_name="gemini-pro", safety_settings=safety).bind( + functions=[MyModel] + ) message = HumanMessage(content="My name is Erick and I am 27 years old") response = model.invoke([message]) assert isinstance(response, AIMessage) diff --git a/libs/vertexai/tests/unit_tests/test_chat_models.py b/libs/vertexai/tests/unit_tests/test_chat_models.py index 49420000..344d7472 100644 --- a/libs/vertexai/tests/unit_tests/test_chat_models.py +++ b/libs/vertexai/tests/unit_tests/test_chat_models.py @@ -22,7 +22,10 @@ from vertexai.generative_models import ( # type: ignore Candidate, ) -from vertexai.language_models import ChatMessage, InputOutputTextPair # type: ignore +from vertexai.language_models import ( # type: ignore + ChatMessage, + InputOutputTextPair, +) from langchain_google_vertexai.chat_models import ( ChatVertexAI, @@ -152,7 +155,27 @@ def test_parse_history_gemini() -> None: message2 = AIMessage(content=text_answer1) message3 = HumanMessage(content=text_question2) messages = [system_message, message1, message2, message3] - history = _parse_chat_history_gemini(messages, convert_system_message_to_human=True) + system_instructions, history = _parse_chat_history_gemini(messages) + assert len(history) == 3 + assert history[0].role == "user" + assert history[0].parts[0].text == text_question1 + assert history[1].role == "model" + assert history[1].parts[0].text == text_answer1 + assert system_instructions and system_instructions.parts[0].text == system_input + + +def test_parse_history_gemini_converted_message() -> None: + system_input = "You're supposed to answer math questions." + text_question1, text_answer1 = "How much is 2+2?", "4" + text_question2 = "How much is 3+3?" + system_message = SystemMessage(content=system_input) + message1 = HumanMessage(content=text_question1) + message2 = AIMessage(content=text_answer1) + message3 = HumanMessage(content=text_question2) + messages = [system_message, message1, message2, message3] + system_instructions, history = _parse_chat_history_gemini( + messages, convert_system_message_to_human=True + ) assert len(history) == 3 assert history[0].role == "user" assert history[0].parts[0].text == system_input diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..88b7bb56 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,15 @@ +[tool.poetry] +name = "langchain-google" +version = "0.1.0" +description = "" +authors = ["Your Name "] +readme = "README.md" + +[tool.poetry.dependencies] +python = "^3.9" + + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" + From 9c35638f35c496fbe3d9e2e4639a7273c07c9638 Mon Sep 17 00:00:00 2001 From: Leonid Kuligin Date: Wed, 10 Apr 2024 16:06:34 +0200 Subject: [PATCH 7/9] fixed tools since function_response doesn't require a role anymore (#132) --- libs/vertexai/langchain_google_vertexai/chat_models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/vertexai/langchain_google_vertexai/chat_models.py b/libs/vertexai/langchain_google_vertexai/chat_models.py index afc00572..6383b5ea 100644 --- a/libs/vertexai/langchain_google_vertexai/chat_models.py +++ b/libs/vertexai/langchain_google_vertexai/chat_models.py @@ -190,7 +190,7 @@ def _convert_to_parts(message: BaseMessage) -> List[Part]: role = "user" parts = _convert_to_parts(message) elif isinstance(message, FunctionMessage): - role = "user" + role = "function" parts = [ Part.from_function_response( name=message.name, From aeb175c0d65769c8fa99480938cb30b6b20d3eac Mon Sep 17 00:00:00 2001 From: Erick Friis Date: Wed, 10 Apr 2024 07:06:59 -0700 Subject: [PATCH 8/9] ci: add master core install for lint/test/dev (#130) * ci: add master core install for lint/test/dev --------- Co-authored-by: Leonid Kuligin --- libs/genai/poetry.lock | 28 ++++++++------ libs/genai/pyproject.toml | 3 ++ libs/vertexai/poetry.lock | 74 +++++++++++++++++++----------------- libs/vertexai/pyproject.toml | 6 +++ 4 files changed, 65 insertions(+), 46 deletions(-) diff --git a/libs/genai/poetry.lock b/libs/genai/poetry.lock index a76c7f6d..a633b296 100644 --- a/libs/genai/poetry.lock +++ b/libs/genai/poetry.lock @@ -477,26 +477,31 @@ files = [ [[package]] name = "langchain-core" -version = "0.1.40" +version = "0.1.41" description = "Building applications with LLMs through composability" optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain_core-0.1.40-py3-none-any.whl", hash = "sha256:618dbb7ab44d8b263b91e384db1ff07d0db256ae5bdafa0123a115b6a75a13f1"}, - {file = "langchain_core-0.1.40.tar.gz", hash = "sha256:34c06fc0e6d3534b738c63f85403446b4be71161665b7e091f9bb19c914ec100"}, -] +python-versions = ">=3.8.1,<4.0" +files = [] +develop = false [package.dependencies] -jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.0,<0.2.0" -packaging = ">=23.2,<24.0" +jsonpatch = "^1.33" +langsmith = "^0.1.0" +packaging = "^23.2" pydantic = ">=1,<3" PyYAML = ">=5.3" -tenacity = ">=8.1.0,<9.0.0" +tenacity = "^8.1.0" [package.extras] extended-testing = ["jinja2 (>=3,<4)"] +[package.source] +type = "git" +url = "https://github.com/langchain-ai/langchain.git" +reference = "HEAD" +resolved_reference = "2d83505be979fb947e976ae7c48be87e49f41a27" +subdirectory = "libs/core" + [[package]] name = "langsmith" version = "0.1.42" @@ -1088,6 +1093,7 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -1386,4 +1392,4 @@ images = ["pillow"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "2b03aaa008902cf0eff7757a7453c7fff83b06939a47845fa3704abb4fb172a1" +content-hash = "014cc48a2080a0d572d874c713c8ed24a84b33bd5c0f582b056cb61151c33136" diff --git a/libs/genai/pyproject.toml b/libs/genai/pyproject.toml index 278df6b7..e6710fbf 100644 --- a/libs/genai/pyproject.toml +++ b/libs/genai/pyproject.toml @@ -30,6 +30,7 @@ syrupy = "^4.0.2" pytest-watcher = "^0.3.4" pytest-asyncio = "^0.21.1" numpy = "^1.26.2" +langchain-core = {git = "https://github.com/langchain-ai/langchain.git", subdirectory = "libs/core"} [tool.poetry.group.codespell] optional = true @@ -56,6 +57,7 @@ types-requests = "^2.28.11.5" types-google-cloud-ndb = "^2.2.0.1" types-pillow = "^10.1.0.2" types-protobuf = "^4.24.0.20240302" +langchain-core = {git = "https://github.com/langchain-ai/langchain.git", subdirectory = "libs/core"} [tool.poetry.group.dev] optional = true @@ -65,6 +67,7 @@ pillow = "^10.1.0" types-requests = "^2.31.0.10" types-pillow = "^10.1.0.2" types-google-cloud-ndb = "^2.2.0.1" +langchain-core = {git = "https://github.com/langchain-ai/langchain.git", subdirectory = "libs/core"} [tool.ruff] select = [ diff --git a/libs/vertexai/poetry.lock b/libs/vertexai/poetry.lock index 1ed44788..58261d5c 100644 --- a/libs/vertexai/poetry.lock +++ b/libs/vertexai/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "aiohttp" @@ -126,13 +126,13 @@ typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.9\""} [[package]] name = "anthropic" -version = "0.23.1" +version = "0.25.0" description = "The official Python library for the anthropic API" optional = true python-versions = ">=3.7" files = [ - {file = "anthropic-0.23.1-py3-none-any.whl", hash = "sha256:6dc5779dae83a5834864f4a4af0166c972b70f4cb8fd2765e1558282cc6d6242"}, - {file = "anthropic-0.23.1.tar.gz", hash = "sha256:9325103702cbc96bb09d1b58c36bde75c726f6a01029fb4d85f41ebba07e9066"}, + {file = "anthropic-0.25.0-py3-none-any.whl", hash = "sha256:b5dfe4dfebace1641a02cfda939cd6dffac0152ab305ca1ef0c11023043a51a2"}, + {file = "anthropic-0.25.0.tar.gz", hash = "sha256:63372443e699da7ffb467b2d0eb5ee7740acf877368b364a1137d795ae4e4c16"}, ] [package.dependencies] @@ -402,13 +402,13 @@ test = ["pytest (>=6)"] [[package]] name = "filelock" -version = "3.13.3" +version = "3.13.4" description = "A platform independent file lock." optional = true python-versions = ">=3.8" files = [ - {file = "filelock-3.13.3-py3-none-any.whl", hash = "sha256:5ffa845303983e7a0b7ae17636509bc97997d58afeafa72fb141a17b152284cb"}, - {file = "filelock-3.13.3.tar.gz", hash = "sha256:a79895a25bbefdf55d1a2a0a80968f7dbb28edcd6d4234a0afb3f37ecde4b546"}, + {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, + {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, ] [package.extras] @@ -1234,13 +1234,13 @@ files = [ [[package]] name = "langchain" -version = "0.1.14" +version = "0.1.15" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.1.14-py3-none-any.whl", hash = "sha256:94f9b5df2421faaf762d4f43b9d65c270c2f701934580d281e4c6226deef7234"}, - {file = "langchain-0.1.14.tar.gz", hash = "sha256:124c6244cf3957616b98f2df07dc2992fc40dff6ed1a62d8ee8a40f1e0260a40"}, + {file = "langchain-0.1.15-py3-none-any.whl", hash = "sha256:3ac516463ae7f80047091f04592a1eea138321710bbc266005f9de238d71acd3"}, + {file = "langchain-0.1.15.tar.gz", hash = "sha256:79d43035327fdcc5ac81a3db10f2b879f2bd5db3b268ef82bac7baf3ec32954e"}, ] [package.dependencies] @@ -1248,8 +1248,8 @@ aiohttp = ">=3.8.3,<4.0.0" async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} dataclasses-json = ">=0.5.7,<0.7" jsonpatch = ">=1.33,<2.0" -langchain-community = ">=0.0.30,<0.1" -langchain-core = ">=0.1.37,<0.2.0" +langchain-community = ">=0.0.32,<0.1" +langchain-core = ">=0.1.41,<0.2.0" langchain-text-splitters = ">=0.0.1,<0.1" langsmith = ">=0.1.17,<0.2.0" numpy = ">=1,<2" @@ -1275,19 +1275,19 @@ text-helpers = ["chardet (>=5.1.0,<6.0.0)"] [[package]] name = "langchain-community" -version = "0.0.31" +version = "0.0.32" description = "Community contributed LangChain integrations." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_community-0.0.31-py3-none-any.whl", hash = "sha256:905c01b978a1cef7fdcddd2d9241dedc9987db6f23ba1b58d974e38b1cdf2775"}, - {file = "langchain_community-0.0.31.tar.gz", hash = "sha256:9a970bc2bb59bb4c204b696d8c62c2534f6ddb31005005cc1b7d7f934e58a5fc"}, + {file = "langchain_community-0.0.32-py3-none-any.whl", hash = "sha256:406977009999952d0705de3806de2b4867e9bb8eda8ca154a59c7a8ed58da38d"}, + {file = "langchain_community-0.0.32.tar.gz", hash = "sha256:1510217d646c8380f54e9850351f6d2a0b0dd73c501b666c6f4b40baa8160b29"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" dataclasses-json = ">=0.5.7,<0.7" -langchain-core = ">=0.1.37,<0.2.0" +langchain-core = ">=0.1.41,<0.2.0" langsmith = ">=0.1.0,<0.2.0" numpy = ">=1,<2" PyYAML = ">=5.3" @@ -1297,30 +1297,35 @@ tenacity = ">=8.1.0,<9.0.0" [package.extras] cli = ["typer (>=0.9.0,<0.10.0)"] -extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cloudpickle (>=2.0.0)", "cohere (>=4,<5)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "friendli-client (>=1.2.4,<2.0.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "httpx (>=0.24.1,<0.25.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "nvidia-riva-client (>=2.14.0,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "premai (>=0.3.25,<0.4.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "tidb-vector (>=0.0.3,<1.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "tree-sitter (>=0.20.2,<0.21.0)", "tree-sitter-languages (>=1.8.0,<2.0.0)", "upstash-redis (>=0.15.0,<0.16.0)", "vdms (>=0.0.20,<0.0.21)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)", "zhipuai (>=1.0.7,<2.0.0)"] +extended-testing = ["aiosqlite (>=0.19.0,<0.20.0)", "aleph-alpha-client (>=2.15.0,<3.0.0)", "anthropic (>=0.3.11,<0.4.0)", "arxiv (>=1.4,<2.0)", "assemblyai (>=0.17.0,<0.18.0)", "atlassian-python-api (>=3.36.0,<4.0.0)", "azure-ai-documentintelligence (>=1.0.0b1,<2.0.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cloudpickle (>=2.0.0)", "cohere (>=4,<5)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.0)", "elasticsearch (>=8.12.0,<9.0.0)", "esprima (>=4.0.1,<5.0.0)", "faiss-cpu (>=1,<2)", "feedparser (>=6.0.10,<7.0.0)", "fireworks-ai (>=0.9.0,<0.10.0)", "friendli-client (>=1.2.4,<2.0.0)", "geopandas (>=0.13.1,<0.14.0)", "gitpython (>=3.1.32,<4.0.0)", "google-cloud-documentai (>=2.20.1,<3.0.0)", "gql (>=3.4.1,<4.0.0)", "gradientai (>=1.4.0,<2.0.0)", "hdbcli (>=2.19.21,<3.0.0)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "httpx (>=0.24.1,<0.25.0)", "httpx-sse (>=0.4.0,<0.5.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "lxml (>=4.9.3,<6.0)", "markdownify (>=0.11.6,<0.12.0)", "motor (>=3.3.1,<4.0.0)", "msal (>=1.25.0,<2.0.0)", "mwparserfromhell (>=0.6.4,<0.7.0)", "mwxml (>=0.3.3,<0.4.0)", "newspaper3k (>=0.2.8,<0.3.0)", "numexpr (>=2.8.6,<3.0.0)", "nvidia-riva-client (>=2.14.0,<3.0.0)", "oci (>=2.119.1,<3.0.0)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.0)", "oracle-ads (>=2.9.1,<3.0.0)", "pandas (>=2.0.1,<3.0.0)", "pdfminer-six (>=20221105,<20221106)", "pgvector (>=0.1.6,<0.2.0)", "praw (>=7.7.1,<8.0.0)", "premai (>=0.3.25,<0.4.0)", "psychicapi (>=0.8.0,<0.9.0)", "py-trello (>=0.19.0,<0.20.0)", "pyjwt (>=2.8.0,<3.0.0)", "pymupdf (>=1.22.3,<2.0.0)", "pypdf (>=3.4.0,<4.0.0)", "pypdfium2 (>=4.10.0,<5.0.0)", "pyspark (>=3.4.0,<4.0.0)", "rank-bm25 (>=0.2.2,<0.3.0)", "rapidfuzz (>=3.1.1,<4.0.0)", "rapidocr-onnxruntime (>=1.3.2,<2.0.0)", "rdflib (==7.0.0)", "requests-toolbelt (>=1.0.0,<2.0.0)", "rspace_client (>=2.5.0,<3.0.0)", "scikit-learn (>=1.2.2,<2.0.0)", "sqlite-vss (>=0.1.2,<0.2.0)", "streamlit (>=1.18.0,<2.0.0)", "sympy (>=1.12,<2.0)", "telethon (>=1.28.5,<2.0.0)", "tidb-vector (>=0.0.3,<1.0.0)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "tree-sitter (>=0.20.2,<0.21.0)", "tree-sitter-languages (>=1.8.0,<2.0.0)", "upstash-redis (>=0.15.0,<0.16.0)", "vdms (>=0.0.20,<0.0.21)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"] [[package]] name = "langchain-core" -version = "0.1.40" +version = "0.1.42rc1" description = "Building applications with LLMs through composability" optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain_core-0.1.40-py3-none-any.whl", hash = "sha256:618dbb7ab44d8b263b91e384db1ff07d0db256ae5bdafa0123a115b6a75a13f1"}, - {file = "langchain_core-0.1.40.tar.gz", hash = "sha256:34c06fc0e6d3534b738c63f85403446b4be71161665b7e091f9bb19c914ec100"}, -] +python-versions = ">=3.8.1,<4.0" +files = [] +develop = false [package.dependencies] -jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.0,<0.2.0" -packaging = ">=23.2,<24.0" +jsonpatch = "^1.33" +langsmith = "^0.1.0" +packaging = "^23.2" pydantic = ">=1,<3" PyYAML = ">=5.3" -tenacity = ">=8.1.0,<9.0.0" +tenacity = "^8.1.0" [package.extras] extended-testing = ["jinja2 (>=3,<4)"] +[package.source] +type = "git" +url = "https://github.com/langchain-ai/langchain.git" +reference = "HEAD" +resolved_reference = "9eb6f538f058af294e365c87ab2c7dae150f535d" +subdirectory = "libs/core" + [[package]] name = "langchain-text-splitters" version = "0.0.1" @@ -1340,13 +1345,13 @@ extended-testing = ["lxml (>=5.1.0,<6.0.0)"] [[package]] name = "langsmith" -version = "0.1.41" +version = "0.1.43" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.41-py3-none-any.whl", hash = "sha256:11de22b6990502c630fdfdf6906681e664c6659d2118bcd2b79d08016e770831"}, - {file = "langsmith-0.1.41.tar.gz", hash = "sha256:1250cd6c9074ca10d40002b23d79b3017329b139fbe954248fdd7a79544e90d0"}, + {file = "langsmith-0.1.43-py3-none-any.whl", hash = "sha256:c0a3658f10cbefaa2f53d15db519592982b59d99f24e018dc73aca6092b4158d"}, + {file = "langsmith-0.1.43.tar.gz", hash = "sha256:983c5a35bf191bb23d93e453c9fc6ea7cda998b1ca96f94680a1446092caf347"}, ] [package.dependencies] @@ -2033,7 +2038,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -2494,13 +2498,13 @@ files = [ [[package]] name = "types-protobuf" -version = "4.24.0.20240408" +version = "4.25.0.20240410" description = "Typing stubs for protobuf" optional = false python-versions = ">=3.8" files = [ - {file = "types-protobuf-4.24.0.20240408.tar.gz", hash = "sha256:c03a44357b03c233c8c5864ce3e07dd9c766a00497d271496923f7ae3cb9e1de"}, - {file = "types_protobuf-4.24.0.20240408-py3-none-any.whl", hash = "sha256:9b87cd279378693071247227f52e89738af7c8d6f06dbdd749b0cf473c4916ce"}, + {file = "types-protobuf-4.25.0.20240410.tar.gz", hash = "sha256:86576c2e7e691b8b75f4cabec430f7405edef411b5d191e847c91307935b1b38"}, + {file = "types_protobuf-4.25.0.20240410-py3-none-any.whl", hash = "sha256:335b2e8cf9f39c233dbf0f977a2a4fbc2c0bac720225c544cc1412a67ab1e1d3"}, ] [[package]] @@ -2721,4 +2725,4 @@ anthropic = ["anthropic"] [metadata] lock-version = "2.0" python-versions = ">=3.8.1,<4.0" -content-hash = "10cb39e92083851c4280f04caf22b4a121cf1f01513f65be7b688d1359d577e8" +content-hash = "1a057144382462e7cd7cb1521fcdc2f38ed0ca4a61565b0273f7e8529640b9a7" diff --git a/libs/vertexai/pyproject.toml b/libs/vertexai/pyproject.toml index 093a6d20..6f45b98d 100644 --- a/libs/vertexai/pyproject.toml +++ b/libs/vertexai/pyproject.toml @@ -35,6 +35,7 @@ types-requests = "^2.31.0.20231231" types-protobuf = "^4.24.0.4" numexpr = "^2.8.6" google-api-python-client = "^2.117.0" +langchain-core = {git = "https://github.com/langchain-ai/langchain.git", subdirectory = "libs/core"} [tool.poetry.group.codespell] optional = true @@ -59,6 +60,11 @@ ruff = "^0.1.5" [tool.poetry.group.typing.dependencies] mypy = "^1" types-google-cloud-ndb = "^2.2.0.20240106" +langchain-core = {git = "https://github.com/langchain-ai/langchain.git", subdirectory = "libs/core"} + + +[tool.poetry.group.dev.dependencies] +langchain-core = {git = "https://github.com/langchain-ai/langchain.git", subdirectory = "libs/core"} [tool.poetry.extras] anthropic = ["anthropic"] From 8cac88a7cec468ca465301d3a1a20ae9c45e2f5d Mon Sep 17 00:00:00 2001 From: Alex Ostapenko Date: Wed, 10 Apr 2024 17:08:52 +0200 Subject: [PATCH 9/9] added tool-message (#134) --- .../langchain_google_vertexai/chat_models.py | 13 ++- .../tests/unit_tests/test_chat_models.py | 79 +++++++++++++++++++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/libs/vertexai/langchain_google_vertexai/chat_models.py b/libs/vertexai/langchain_google_vertexai/chat_models.py index 6383b5ea..b855e976 100644 --- a/libs/vertexai/langchain_google_vertexai/chat_models.py +++ b/libs/vertexai/langchain_google_vertexai/chat_models.py @@ -29,6 +29,7 @@ FunctionMessage, HumanMessage, SystemMessage, + ToolMessage, ) from langchain_core.output_parsers.base import OutputParserLike from langchain_core.output_parsers.openai_functions import ( @@ -199,6 +200,16 @@ def _convert_to_parts(message: BaseMessage) -> List[Part]: }, ) ] + elif isinstance(message, ToolMessage): + role = "function" + parts = [ + Part.from_function_response( + name=message.name, + response={ + "content": message.content, + }, + ) + ] else: raise ValueError( f"Unexpected message with type {type(message)} at the position {i}." @@ -301,7 +312,7 @@ class ChatVertexAI(_VertexAICommon, BaseChatModel): "Underlying model name." examples: Optional[List[BaseMessage]] = None convert_system_message_to_human: bool = False - """[Deprecated] Since new Gemini models support setting a System Message, + """[Deprecated] Since new Gemini models support setting a System Message, setting this parameter to True is discouraged. """ diff --git a/libs/vertexai/tests/unit_tests/test_chat_models.py b/libs/vertexai/tests/unit_tests/test_chat_models.py index 344d7472..9265ad4f 100644 --- a/libs/vertexai/tests/unit_tests/test_chat_models.py +++ b/libs/vertexai/tests/unit_tests/test_chat_models.py @@ -9,6 +9,7 @@ from google.cloud.aiplatform_v1beta1.types import ( Content, FunctionCall, + FunctionResponse, Part, ) from google.cloud.aiplatform_v1beta1.types import ( @@ -16,8 +17,10 @@ ) from langchain_core.messages import ( AIMessage, + FunctionMessage, HumanMessage, SystemMessage, + ToolMessage, ) from vertexai.generative_models import ( # type: ignore Candidate, @@ -184,6 +187,82 @@ def test_parse_history_gemini_converted_message() -> None: assert history[1].parts[0].text == text_answer1 +def test_parse_history_gemini_function() -> None: + system_input = "You're supposed to answer math questions." + text_question1 = "Which is bigger 2+2 or 2*2?" + function_name = "calculator" + function_call_1 = { + "name": function_name, + "arguments": json.dumps({"arg1": "2", "arg2": "2", "op": "+"}), + } + function_answer1 = json.dumps({"result": 4}) + function_call_2 = { + "name": function_name, + "arguments": json.dumps({"arg1": "2", "arg2": "2", "op": "*"}), + } + function_answer2 = json.dumps({"result": 4}) + text_answer1 = "They are same" + + system_message = SystemMessage(content=system_input) + message1 = HumanMessage(content=text_question1) + message2 = AIMessage( + content="", + additional_kwargs={ + "function_call": function_call_1, + }, + ) + message3 = ToolMessage( + name="calculator", content=function_answer1, tool_call_id="1" + ) + message4 = AIMessage( + content="", + additional_kwargs={ + "function_call": function_call_2, + }, + ) + message5 = FunctionMessage(name="calculator", content=function_answer2) + message6 = AIMessage(content=text_answer1) + messages = [ + system_message, + message1, + message2, + message3, + message4, + message5, + message6, + ] + system_instructions, history = _parse_chat_history_gemini(messages) + assert len(history) == 6 + assert system_instructions and system_instructions.parts[0].text == system_input + assert history[0].role == "user" + assert history[0].parts[0].text == text_question1 + + assert history[1].role == "model" + assert history[1].parts[0].function_call == FunctionCall( + name=function_call_1["name"], args=json.loads(function_call_1["arguments"]) + ) + + assert history[2].role == "function" + assert history[2].parts[0].function_response == FunctionResponse( + name=function_call_1["name"], + response={"content": function_answer1}, + ) + + assert history[3].role == "model" + assert history[3].parts[0].function_call == FunctionCall( + name=function_call_2["name"], args=json.loads(function_call_2["arguments"]) + ) + + assert history[4].role == "function" + assert history[2].parts[0].function_response == FunctionResponse( + name=function_call_2["name"], + response={"content": function_answer2}, + ) + + assert history[5].role == "model" + assert history[5].parts[0].text == text_answer1 + + def test_default_params_palm() -> None: user_prompt = "Hello"