From 10d3600d307b73ad9c96fb6a861ca78b8a64a666 Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Sun, 4 Aug 2024 11:18:13 -0400 Subject: [PATCH 001/110] feat(agents-api): Implement doc* models (#442) * refactor(agents-api): Minor refactors Signed-off-by: Diwank Tomer * feat(typespec): Add create-doc endpoint Signed-off-by: Diwank Tomer * feat(agents-api): Add migrations for unifying the owner-docs tables Signed-off-by: Diwank Tomer * feat(agents-api): Implement doc* models Signed-off-by: Diwank Tomer --------- Signed-off-by: Diwank Tomer Co-authored-by: Diwank Tomer --- agents-api/agents_api/autogen/Docs.py | 55 +++- agents-api/agents_api/models/__init__.py | 11 + .../agents_api/models/agent/__init__.py | 10 + agents-api/agents_api/models/docs/__init__.py | 9 + .../agents_api/models/docs/create_doc.py | 117 ++++++++ .../agents_api/models/docs/create_docs.py | 97 ------- .../docs/{delete_docs.py => delete_doc.py} | 80 ++++-- .../agents_api/models/docs/embed_docs.py | 51 ---- .../agents_api/models/docs/embed_snippets.py | 96 +++++++ agents-api/agents_api/models/docs/get_doc.py | 93 ++++++ agents-api/agents_api/models/docs/get_docs.py | 60 ---- .../agents_api/models/docs/list_docs.py | 134 +++++---- .../agents_api/models/docs/search_docs.py | 209 +++++++++++--- .../agents_api/models/entry/__init__.py | 7 + .../agents_api/models/execution/__init__.py | 10 + .../agents_api/models/session/__init__.py | 14 + agents-api/agents_api/models/task/__init__.py | 9 + .../agents_api/models/tools/__init__.py | 9 + .../tools/{delete_tools.py => delete_tool.py} | 0 agents-api/agents_api/models/user/__init__.py | 9 + agents-api/agents_api/models/utils.py | 2 + .../routers/agents/delete_agent_tools.py | 15 +- agents-api/agents_api/web.py | 3 - ...te_1722710530_unify_owner_doc_relations.py | 204 ++++++++++++++ agents-api/poetry.lock | 50 +--- fern/fern.config.json | 2 +- sdks/python/julep/api/__init__.py | 14 +- sdks/python/julep/api/client.py | 265 ++++++++++++++++++ sdks/python/julep/api/reference.md | 194 +++++++++++++ sdks/python/julep/api/types/__init__.py | 14 +- .../api/types/docs_create_doc_request.py | 57 ++++ ....py => docs_create_doc_request_content.py} | 2 +- .../julep/api/types/docs_doc_reference.py | 9 +- .../types/docs_hybrid_doc_search_request.py | 10 +- .../docs_hybrid_doc_search_request_vector.py | 7 - sdks/python/julep/api/types/docs_snippet.py | 43 +++ .../docs_text_only_doc_search_request.py | 5 +- .../docs_text_only_doc_search_request_text.py | 5 - .../types/docs_vector_doc_search_request.py | 5 +- .../docs_vector_doc_search_request_vector.py | 7 - sdks/python/poetry.lock | 50 +--- sdks/ts/src/api/index.ts | 4 + .../src/api/models/Docs_CreateDocRequest.ts | 19 ++ sdks/ts/src/api/models/Docs_DocReference.ts | 8 +- .../api/models/Docs_HybridDocSearchRequest.ts | 8 +- sdks/ts/src/api/models/Docs_Snippet.ts | 8 + .../models/Docs_TextOnlyDocSearchRequest.ts | 4 +- .../api/models/Docs_VectorDocSearchRequest.ts | 4 +- .../src/api/schemas/$Docs_CreateDocRequest.ts | 41 +++ sdks/ts/src/api/schemas/$Docs_DocReference.ts | 17 +- .../schemas/$Docs_HybridDocSearchRequest.ts | 38 +-- sdks/ts/src/api/schemas/$Docs_Snippet.ts | 17 ++ .../schemas/$Docs_TextOnlyDocSearchRequest.ts | 15 +- .../schemas/$Docs_VectorDocSearchRequest.ts | 23 +- sdks/ts/src/api/services/DefaultService.ts | 51 ++++ typespec/docs/endpoints.tsp | 10 +- typespec/docs/models.tsp | 40 ++- 57 files changed, 1780 insertions(+), 570 deletions(-) create mode 100644 agents-api/agents_api/models/docs/create_doc.py delete mode 100644 agents-api/agents_api/models/docs/create_docs.py rename agents-api/agents_api/models/docs/{delete_docs.py => delete_doc.py} (52%) delete mode 100644 agents-api/agents_api/models/docs/embed_docs.py create mode 100644 agents-api/agents_api/models/docs/embed_snippets.py create mode 100644 agents-api/agents_api/models/docs/get_doc.py delete mode 100644 agents-api/agents_api/models/docs/get_docs.py rename agents-api/agents_api/models/tools/{delete_tools.py => delete_tool.py} (100%) create mode 100644 agents-api/migrations/migrate_1722710530_unify_owner_doc_relations.py create mode 100644 sdks/python/julep/api/types/docs_create_doc_request.py rename sdks/python/julep/api/types/{docs_hybrid_doc_search_request_text.py => docs_create_doc_request_content.py} (53%) delete mode 100644 sdks/python/julep/api/types/docs_hybrid_doc_search_request_vector.py create mode 100644 sdks/python/julep/api/types/docs_snippet.py delete mode 100644 sdks/python/julep/api/types/docs_text_only_doc_search_request_text.py delete mode 100644 sdks/python/julep/api/types/docs_vector_doc_search_request_vector.py create mode 100644 sdks/ts/src/api/models/Docs_CreateDocRequest.ts create mode 100644 sdks/ts/src/api/models/Docs_Snippet.ts create mode 100644 sdks/ts/src/api/schemas/$Docs_CreateDocRequest.ts create mode 100644 sdks/ts/src/api/schemas/$Docs_Snippet.ts diff --git a/agents-api/agents_api/autogen/Docs.py b/agents-api/agents_api/autogen/Docs.py index 7c4c3b446..b9da3646d 100644 --- a/agents-api/agents_api/autogen/Docs.py +++ b/agents-api/agents_api/autogen/Docs.py @@ -31,6 +31,30 @@ class BaseDocSearchRequest(BaseModel): """ +class CreateDocRequest(BaseModel): + """ + Payload for creating a doc + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + metadata: dict[str, Any] | None = None + title: Annotated[ + str, + Field( + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$" + ), + ] + """ + Title describing what this document contains + """ + content: str | list[str] + """ + Contents of the document + """ + + class Doc(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -76,12 +100,9 @@ class DocReference(BaseModel): """ ID of the document """ - snippet_index: list[int] - """ - Snippets referred to of the document - """ title: str | None = None - snippet: str | None = None + snippets: Annotated[list[Snippet], Field(min_length=1)] + distance: float | None = None class EmbedQueryRequest(BaseModel): @@ -108,23 +129,31 @@ class HybridDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( populate_by_name=True, ) - text: str | list[str] + text: str """ - Text or texts to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. + Text to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. """ - vector: list[float] | list[list[float]] + vector: list[float] """ - Vector or vectors to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. + Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. """ +class Snippet(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + index: int + content: str + + class TextOnlyDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( populate_by_name=True, ) - text: str | list[str] + text: str """ - Text or texts to use in the search. + Text to use in the search. """ @@ -132,7 +161,7 @@ class VectorDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( populate_by_name=True, ) - vector: list[float] | list[list[float]] + vector: list[float] """ - Vector or vectors to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. + Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. """ diff --git a/agents-api/agents_api/models/__init__.py b/agents-api/agents_api/models/__init__.py index d90013a48..e84bb5ecc 100644 --- a/agents-api/agents_api/models/__init__.py +++ b/agents-api/agents_api/models/__init__.py @@ -5,3 +5,14 @@ This module also integrates with the `common` module for exception handling and utility functions, ensuring robust error management and providing reusable components for data processing and query construction. """ + +# ruff: noqa: F401, F403, F405 + +import agents_api.models.agent as agent +import agents_api.models.docs as docs +import agents_api.models.entry as entry +import agents_api.models.execution as execution +import agents_api.models.session as session +import agents_api.models.task as task +import agents_api.models.tools as tools +import agents_api.models.user as user diff --git a/agents-api/agents_api/models/agent/__init__.py b/agents-api/agents_api/models/agent/__init__.py index 41d808c70..2beaf8166 100644 --- a/agents-api/agents_api/models/agent/__init__.py +++ b/agents-api/agents_api/models/agent/__init__.py @@ -10,3 +10,13 @@ This module serves as the backbone for agent management within the CozoDB ecosystem, facilitating a wide range of operations necessary for the effective handling of agent data. """ + +# ruff: noqa: F401, F403, F405 + +from .create_agent import create_agent +from .create_or_update_agent import create_or_update_agent +from .delete_agent import delete_agent +from .get_agent import get_agent +from .list_agents import list_agents +from .patch_agent import patch_agent +from .update_agent import update_agent diff --git a/agents-api/agents_api/models/docs/__init__.py b/agents-api/agents_api/models/docs/__init__.py index 4cda7a210..f668e048d 100644 --- a/agents-api/agents_api/models/docs/__init__.py +++ b/agents-api/agents_api/models/docs/__init__.py @@ -13,3 +13,12 @@ This documentation aims to provide clear, concise, and sufficient context for new developers or contributors to understand the module's role without needing to dive deep into the code immediately. """ + +# ruff: noqa: F401, F403, F405 + +from .create_doc import create_doc +from .delete_doc import delete_doc +from .embed_snippets import embed_snippets +from .get_doc import get_doc +from .list_docs import list_docs +from .search_docs import search_docs_by_embedding diff --git a/agents-api/agents_api/models/docs/create_doc.py b/agents-api/agents_api/models/docs/create_doc.py new file mode 100644 index 000000000..23c849515 --- /dev/null +++ b/agents-api/agents_api/models/docs/create_doc.py @@ -0,0 +1,117 @@ +from typing import Literal +from uuid import UUID, uuid4 + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...autogen.openapi_model import CreateDocRequest, Doc +from ...common.utils.cozo import cozo_process_mutate_data +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + Doc, + one=True, + transform=lambda d: { + "id": UUID(d["doc_id"]), + "content": [], # <-- Note: we do not return content on creation + **d, + }, +) +@cozo_query +@beartype +def create_doc( + *, + developer_id: UUID, + owner_type: Literal["user", "agent"], + owner_id: UUID, + doc_id: UUID | None = None, + data: CreateDocRequest, +) -> tuple[list[str], dict]: + """ + Constructs and executes a datalog query to create a new document and its associated snippets in the 'cozodb' database. + + Parameters: + - owner_type (Literal["user", "agent"]): The type of the owner of the document. + - owner_id (UUID): The UUID of the document owner. + - id (UUID): The UUID of the document to be created. + - data (CreateDocRequest): The content of the document. + """ + + doc_id = str(doc_id or uuid4()) + owner_id = str(owner_id) + + if isinstance(data.content, str): + data.content = [data.content] + + data.metadata = data.metadata or {} + + doc_data = data.model_dump() + content = doc_data.pop("content") + + doc_data["owner_type"] = owner_type + doc_data["owner_id"] = owner_id + doc_data["doc_id"] = doc_id + + doc_cols, doc_rows = cozo_process_mutate_data(doc_data) + + snippet_cols, snippet_rows = "", [] + + # Process each content snippet and prepare data for the datalog query. + for snippet_idx, snippet in enumerate(content): + snippet_cols, new_snippet_rows = cozo_process_mutate_data( + dict( + doc_id=doc_id, + index=snippet_idx, + content=snippet, + ) + ) + + snippet_rows += new_snippet_rows + + create_snippets_query = f""" + ?[{snippet_cols}] <- $snippet_rows + :insert snippets {{ {snippet_cols} }} + :returning + """ + + # Construct the datalog query for creating the document and its snippets. + create_doc_query = f""" + ?[{doc_cols}] <- $doc_rows + :insert docs {{ {doc_cols} }} + :returning + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query( + developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} + ), + create_snippets_query, + create_doc_query, + ] + + # Execute the constructed datalog query and return the results as a DataFrame. + return ( + queries, + { + "doc_rows": doc_rows, + "snippet_rows": snippet_rows, + }, + ) diff --git a/agents-api/agents_api/models/docs/create_docs.py b/agents-api/agents_api/models/docs/create_docs.py deleted file mode 100644 index fd2edc645..000000000 --- a/agents-api/agents_api/models/docs/create_docs.py +++ /dev/null @@ -1,97 +0,0 @@ -from typing import Literal -from uuid import UUID - -from beartype import beartype - -from ...common.utils.cozo import cozo_process_mutate_data -from ...common.utils.datetime import utcnow -from ..utils import cozo_query - - -@cozo_query -@beartype -def create_docs_query( - owner_type: Literal["user", "agent"], - owner_id: UUID, - id: UUID, - title: str, - content: list[str] | str, - metadata: dict = {}, -): - """ - Constructs and executes a datalog query to create a new document and its associated snippets in the 'cozodb' database. - - Parameters: - - owner_type (Literal["user", "agent"]): The type of the owner of the document. - - owner_id (UUID): The UUID of the document owner. - - id (UUID): The UUID of the document to be created. - - title (str): The title of the document. - - content (str): The content of the document, which will be split into snippets. - - metadata (dict): Metadata associated with the document. Defaults to an empty dictionary. - - Returns: - pd.DataFrame: A DataFrame containing the results of the query execution. - """ - - if isinstance(content, str): - content = [content] - - created_at: float = utcnow().timestamp() - snippet_cols, snippet_rows = "", [] - - # Process each content snippet and prepare data for the datalog query. - for snippet_idx, snippet in enumerate(content): - snippet_cols, new_snippet_rows = cozo_process_mutate_data( - dict( - doc_id=str(id), - snippet_idx=snippet_idx, - title=title, - snippet=snippet, - ) - ) - - snippet_rows += new_snippet_rows - - # Construct the datalog query for creating the document and its snippets. - query = f""" - {{ - # This query creates a new document and its associated snippets in the database. - # Section to create the document in the database - ?[{owner_type}_id, doc_id, created_at, metadata] <- [[ - to_uuid($owner_id), - to_uuid($id), - $created_at, - $metadata, - ]] - - :insert {owner_type}_docs {{ - {owner_type}_id, doc_id, created_at, metadata, - }} - }} {{ - # Section to create and associate snippets with the document - ?[{snippet_cols}] <- $snippet_rows - - :insert information_snippets {{ - {snippet_cols} - }} - }} {{ - # Section to return the created document and its snippets - ?[{owner_type}_id, doc_id, created_at, metadata] <- [[ - to_uuid($owner_id), - to_uuid($id), - $created_at, - $metadata, - ]] - }}""" - - # Execute the constructed datalog query and return the results as a DataFrame. - return ( - query, - { - "owner_id": str(owner_id), - "id": str(id), - "created_at": created_at, - "metadata": metadata, - "snippet_rows": snippet_rows, - }, - ) diff --git a/agents-api/agents_api/models/docs/delete_docs.py b/agents-api/agents_api/models/docs/delete_doc.py similarity index 52% rename from agents-api/agents_api/models/docs/delete_docs.py rename to agents-api/agents_api/models/docs/delete_doc.py index 8be5d243b..842fc9d6f 100644 --- a/agents-api/agents_api/models/docs/delete_docs.py +++ b/agents-api/agents_api/models/docs/delete_doc.py @@ -2,17 +2,47 @@ from uuid import UUID from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError -from ..utils import cozo_query +from ...autogen.openapi_model import ResourceDeletedResponse +from ...common.utils.datetime import utcnow +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + ResourceDeletedResponse, + one=True, + transform=lambda d: { + "id": UUID(d.pop("doc_id")), + "deleted_at": utcnow(), + "jobs": [], + }, +) @cozo_query @beartype -def delete_docs_by_id_query( +def delete_doc( + *, + developer_id: UUID, owner_type: Literal["user", "agent"], owner_id: UUID, doc_id: UUID, -) -> tuple[str, dict]: +) -> tuple[list[str], dict]: """Constructs and returns a datalog query for deleting documents and associated information snippets. This function targets the 'cozodb' database, allowing for the removal of documents and their related information snippets based on the provided document ID and owner (user or agent). @@ -33,35 +63,47 @@ def delete_docs_by_id_query( # The following query is divided into two main parts: # 1. Deleting information snippets associated with the document # 2. Deleting the document itself from the owner's collection - query = f""" - {{ + delete_snippets_query = """ # This section constructs the subquery for identifying and deleting all information snippets associated with the given document ID. # Delete snippets input[doc_id] <- [[to_uuid($doc_id)]] - ?[doc_id, snippet_idx] := + ?[doc_id, index] := input[doc_id], - *information_snippets {{ + *snippets { doc_id, - snippet_idx, - }} + index, + } - :delete information_snippets {{ + :delete snippets { doc_id, - snippet_idx - }} - }} {{ + index + } + """ + + delete_doc_query = """ # This section constructs the subquery for deleting the document from the specified owner's (user or agent) document collection. # Delete the docs - ?[doc_id, {owner_type}_id] <- [[ + ?[doc_id, owner_id, owner_type] <- [[ to_uuid($doc_id), to_uuid($owner_id), + $owner_type, ]] - :delete {owner_type}_docs {{ + :delete docs { doc_id, - {owner_type}_id, - }} + owner_type, + owner_id, + } :returning - }}""" + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query( + developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} + ), + delete_snippets_query, + delete_doc_query, + ] - return (query, {"doc_id": doc_id, "owner_id": owner_id}) + return (queries, {"doc_id": doc_id, "owner_id": owner_id, "owner_type": owner_type}) diff --git a/agents-api/agents_api/models/docs/embed_docs.py b/agents-api/agents_api/models/docs/embed_docs.py deleted file mode 100644 index 3b5c9cb41..000000000 --- a/agents-api/agents_api/models/docs/embed_docs.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Module for embedding documents in the cozodb database. Contains functions to update document embeddings.""" - -from uuid import UUID - -from beartype import beartype - -from ..utils import cozo_query - - -@cozo_query -@beartype -def embed_docs_snippets_query( - doc_id: UUID, - snippet_indices: list[int] | tuple[int], - embeddings: list[list[float]], -) -> tuple[str, dict]: - """Embeds document snippets in the cozodb database. - - Parameters: - doc_id (UUID): The unique identifier for the document. - snippet_indices (list[int]): Indices of the snippets in the document. - embeddings (list[list[float]]): Embedding vectors for the snippets. - - Returns: - tuple[str, dict]: A DataFrame containing the results of the embedding operation. - """ - - doc_id = str(doc_id) - # Ensure the number of snippet indices matches the number of embeddings. - assert len(snippet_indices) == len(embeddings) - - # Prepare records for the database query by combining doc_id, snippet indices, and embeddings. - records = [ - [doc_id, snippet_idx, embedding] - for snippet_idx, embedding in zip(snippet_indices, embeddings) - ] - - # Define the datalog query for updating document snippet embeddings in the database. - query = """ - { - ?[doc_id, snippet_idx, embedding] <- $records - - :update information_snippets { - doc_id, - snippet_idx, - embedding, - } - :returning - }""" - - return (query, {"records": records}) diff --git a/agents-api/agents_api/models/docs/embed_snippets.py b/agents-api/agents_api/models/docs/embed_snippets.py new file mode 100644 index 000000000..5dd4b4457 --- /dev/null +++ b/agents-api/agents_api/models/docs/embed_snippets.py @@ -0,0 +1,96 @@ +"""Module for embedding documents in the cozodb database. Contains functions to update document embeddings.""" + +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...autogen.openapi_model import ResourceUpdatedResponse +from ...common.utils.cozo import cozo_process_mutate_data +from ...common.utils.datetime import utcnow +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + wrap_in_class, +) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + ResourceUpdatedResponse, + one=True, + transform=lambda d: {"id": d["doc_id"], "updated_at": utcnow(), "jobs": []}, +) +@cozo_query +@beartype +def embed_snippets( + *, + developer_id: UUID, + doc_id: UUID, + snippet_indices: list[int] | tuple[int], + embeddings: list[list[float]], + embedding_size: int = 1024, +) -> tuple[list[str], dict]: + """Embeds document snippets in the cozodb database. + + Parameters: + doc_id (UUID): The unique identifier for the document. + snippet_indices (list[int]): Indices of the snippets in the document. + embeddings (list[list[float]]): Embedding vectors for the snippets. + """ + + doc_id = str(doc_id) + + # Ensure the number of snippet indices matches the number of embeddings. + assert len(snippet_indices) == len(embeddings) + assert all(len(embedding) == embedding_size for embedding in embeddings) + assert min(snippet_indices) >= 0 + + # Ensure all embeddings are non-zero. + assert all(sum(embedding) for embedding in embeddings) + + # Create a list of records to update the document snippet embeddings in the database. + records = [ + {"doc_id": doc_id, "index": snippet_idx, "embedding": embedding} + for snippet_idx, embedding in zip(snippet_indices, embeddings) + ] + + cols, vals = cozo_process_mutate_data(records) + + # Ensure that index is present in the records. + check_indices_query = f""" + ?[index] := + *snippets {{ + doc_id: $doc_id, + index, + }}, + index > {max(snippet_indices)} + + :assert none + """ + + # Define the datalog query for updating document snippet embeddings in the database. + embed_query = f""" + ?[{cols}] <- $vals + + :update snippets {{ {cols} }} + :returning + """ + + queries = [ + verify_developer_id_query(developer_id), + check_indices_query, + embed_query, + ] + + return (queries, {"vals": vals, "doc_id": doc_id}) diff --git a/agents-api/agents_api/models/docs/get_doc.py b/agents-api/agents_api/models/docs/get_doc.py new file mode 100644 index 000000000..95b9414f4 --- /dev/null +++ b/agents-api/agents_api/models/docs/get_doc.py @@ -0,0 +1,93 @@ +"""Module for retrieving document snippets from the CozoDB based on document IDs.""" + +from typing import Literal +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...autogen.openapi_model import Doc +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + wrap_in_class, +) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + Doc, + one=True, + transform=lambda d: { + "content": [s[1] for s in sorted(d["snippet_data"], key=lambda x: x[0])], + **d, + }, +) +@cozo_query +@beartype +def get_doc( + *, + developer_id: UUID, + owner_type: Literal["user", "agent"], + doc_id: UUID, +) -> tuple[list[str], dict]: + """ + Retrieves snippets of documents by their ID from the CozoDB. + + Parameters: + owner_type (Literal["user", "agent"]): The type of the owner of the document. + doc_id (UUID): The unique identifier of the document. + client (CozoClient, optional): The CozoDB client instance. Defaults to a pre-configured client. + + Returns: + pd.DataFrame: A DataFrame containing the document snippets and related metadata. + """ + + doc_id = str(doc_id) + + get_query = """ + input[doc_id] <- [[to_uuid($doc_id)]] + snippets[collect(snippet_data)] := + input[doc_id], + *snippets { + doc_id, + index, + content, + }, + snippet_data = [index, content] + + ?[ + owner_type, + id, + title, + snippet_data, + created_at, + metadata, + ] := input[id], + owner_type = $owner_type, + *docs { + owner_type, + doc_id: id, + title, + created_at, + metadata, + }, + snippets[snippet_data] + """ + + queries = [ + verify_developer_id_query(developer_id), + get_query, + ] + + return (queries, {"doc_id": doc_id, "owner_type": owner_type}) diff --git a/agents-api/agents_api/models/docs/get_docs.py b/agents-api/agents_api/models/docs/get_docs.py deleted file mode 100644 index 862b17a0c..000000000 --- a/agents-api/agents_api/models/docs/get_docs.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Module for retrieving document snippets from the CozoDB based on document IDs.""" - -from typing import Literal -from uuid import UUID - -from beartype import beartype - -from ..utils import cozo_query - - -@cozo_query -@beartype -def get_docs_snippets_by_id_query( - owner_type: Literal["user", "agent"], - doc_id: UUID, -) -> tuple[str, dict]: - """ - Retrieves snippets of documents by their ID from the CozoDB. - - Parameters: - owner_type (Literal["user", "agent"]): The type of the owner of the document. - doc_id (UUID): The unique identifier of the document. - client (CozoClient, optional): The CozoDB client instance. Defaults to a pre-configured client. - - Returns: - pd.DataFrame: A DataFrame containing the document snippets and related metadata. - """ - - doc_id = str(doc_id) - - query = f""" - {{ - input[doc_id] <- [[to_uuid($doc_id)]] - - ?[ - {owner_type}_id, - doc_id, - title, - snippet, - snippet_idx, - created_at, - embed_instruction, - metadata, - ] := input[doc_id], - *{owner_type}_docs {{ - {owner_type}_id, - doc_id, - created_at, - metadata, - }}, - *information_snippets {{ - doc_id, - snippet_idx, - title, - snippet, - embed_instruction, - }} - }}""" - - return (query, {"doc_id": doc_id}) diff --git a/agents-api/agents_api/models/docs/list_docs.py b/agents-api/agents_api/models/docs/list_docs.py index 576c4a7f6..d785cc331 100644 --- a/agents-api/agents_api/models/docs/list_docs.py +++ b/agents-api/agents_api/models/docs/list_docs.py @@ -5,44 +5,49 @@ from uuid import UUID from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError -from ..utils import cozo_query +from ...autogen.openapi_model import Doc +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + Doc, + transform=lambda d: { + "content": [s[1] for s in sorted(d["snippet_data"], key=lambda x: x[0])], + **d, + }, +) @cozo_query @beartype -def ensure_owner_exists_query( - owner_type: Literal["user", "agent"], - owner_id: UUID, -) -> tuple[str, dict]: - owner_id = str(owner_id) - - # Query to check if an owner (user or agent) exists in the database - query = f"""{{ - # Convert owner_id to UUID and set as input - input[{owner_type}_id] <- [[to_uuid($owner_id)]] - - # Retrieve owner_id if it exists in the database - ?[ - {owner_type}_id, - ] := input[{owner_type}_id], - *{owner_type}s {{ - {owner_type}_id, - }} - }}""" - - return (query, {"owner_id": owner_id}) - - -@cozo_query -@beartype -def list_docs_snippets_by_owner_query( +def list_docs( + *, + developer_id: UUID, owner_type: Literal["user", "agent"], owner_id: UUID, + limit: int = 100, + offset: int = 0, + sort_by: Literal["created_at"] = "created_at", + direction: Literal["asc", "desc"] = "desc", metadata_filter: dict[str, Any] = {}, -) -> tuple[str, dict]: - owner_id = str(owner_id) - +) -> tuple[list[str], dict]: + # Transforms the metadata_filter dictionary into a string representation for the datalog query. metadata_filter_str = ", ".join( [ f"metadata->{json.dumps(k)} == {json.dumps(v)}" @@ -50,35 +55,58 @@ def list_docs_snippets_by_owner_query( ] ) - # Query to retrieve document snippets by owner (user or agent) - query = f""" - {{ - # Convert owner_id to UUID and set as input - input[{owner_type}_id] <- [[to_uuid($owner_id)]] + owner_id = str(owner_id) + sort = f"{'-' if direction == 'desc' else ''}{sort_by}" + + get_query = f""" + snippets[id, collect(snippet_data)] := + *snippets {{ + doc_id: id, + index, + content, + }}, + snippet_data = [index, content] - # Retrieve documents and snippets associated with the owner ?[ - {owner_type}_id, - doc_id, + owner_type, + id, title, - snippet, - snippet_idx, + snippet_data, created_at, metadata, - ] := input[{owner_type}_id], - *{owner_type}_docs {{ - {owner_type}_id, - doc_id, + ] := + owner_type = $owner_type, + owner_id = to_uuid($owner_id), + *docs {{ + owner_type, + owner_id, + doc_id: id, + title, created_at, metadata, }}, - *information_snippets {{ - doc_id, - snippet_idx, - title, - snippet, - }}, - {metadata_filter_str} - }}""" + snippets[id, snippet_data] + + :limit $limit + :offset $offset + :sort {sort} + """ - return (query, {"owner_id": owner_id}) + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query( + developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} + ), + get_query, + ] + + return ( + queries, + { + "owner_id": owner_id, + "owner_type": owner_type, + "limit": limit, + "offset": offset, + "metadata_filter": metadata_filter_str, + }, + ) diff --git a/agents-api/agents_api/models/docs/search_docs.py b/agents-api/agents_api/models/docs/search_docs.py index 7c2ad90fa..d5903bfe7 100644 --- a/agents-api/agents_api/models/docs/search_docs.py +++ b/agents-api/agents_api/models/docs/search_docs.py @@ -4,19 +4,51 @@ from uuid import UUID from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError -from ..utils import cozo_query +from ...autogen.openapi_model import DocReference +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + DocReference, + transform=lambda d: { + "owner": { + "id": d["owner_id"], + "role": d["owner_type"], + }, + **d, + }, +) @cozo_query @beartype -def search_docs_snippets_by_embedding_query( +def search_docs_by_embedding( + *, + developer_id: UUID, owner_type: Literal["user", "agent"], owner_id: UUID, query_embedding: list[float], k: int = 3, - confidence: float = 0.8, -) -> tuple[str, dict]: + confidence: float = 0.7, + ef: int = 128, + mmr_lambda: float = 0.25, +) -> tuple[list[str], dict]: """ Searches for document snippets in CozoDB by embedding query. @@ -26,67 +58,172 @@ def search_docs_snippets_by_embedding_query( - query_embedding (list[float]): The embedding vector of the query. - k (int, optional): The number of nearest neighbors to retrieve. Defaults to 3. - confidence (float, optional): The confidence threshold for filtering results. Defaults to 0.8. - - Returns: - - pd.DataFrame: A DataFrame containing the search results. + - mmr_lambda (float, optional): The lambda parameter for MMR. Defaults to 0.25. """ owner_id = str(owner_id) + # Calculate the search radius based on confidence level radius: float = 1.0 - confidence # Construct the datalog query for searching document snippets - query = f""" - {{ + interim_query = f""" input[ - {owner_type}_id, + owner_id, query_embedding, ] <- [[ to_uuid($owner_id), vec($query_embedding), ]] - candidate[ - doc_id - ] := input[{owner_type}_id, _], - *{owner_type}_docs {{ - {owner_type}_id, + candidate[doc_id] := + input[owner_id, _], + *docs {{ + owner_type: $owner_type, + owner_id, doc_id }} - ?[ - {owner_type}_id, + intersnippet_distance[ doc_id, - title, - snippet, - snippet_idx, + index1, + min(dist) + ] := + *snippets {{ + doc_id, + index: index1, + embedding: embedding1 + }}, + *snippets {{ + doc_id, + index: index2, + embedding: embedding2 + }}, + index1 < index2, + dist = cos_dist(embedding1, embedding2) + + doclength[doc_id, max(index)] := + *snippets {{ + doc_id, + index, + }} + + get_intersnippet[doc_id, index, distance] := + intersnippet_distance[doc_id, _, distance] + + get_intersnippet[doc_id, index, distance] := + not intersnippet_distance[doc_id, _, distance], + distance = 0.0 + + search_result[ + doc_id, + content, + index, distance, - vector, - ] := input[{owner_type}_id, query_embedding], + ] := + input[owner_id, query], candidate[doc_id], - ~information_snippets:embedding_space {{ + ~snippets:embedding_space {{ doc_id, - snippet_idx, - title, - snippet | - query: query_embedding, - k: $k, - ef: 128, - radius: $radius, + index, + content + | + query: query, + k: {k*2}, + ef: {ef}, + radius: {radius}, bind_distance: distance, - bind_vector: vector, }} + apply_mmr[ + doc_id, + snippet_data, + distance, + mmr_score, + ] := + search_result[doc_id, content, index, distance], + get_intersnippet[doc_id, index, intersnippet_distance], + mmr_score = {mmr_lambda} * (distance - (1.0 - {mmr_lambda}) * intersnippet_distance), + snippet_data = [index, content] + + ?[ + doc_id, + snippet_data, + distance, + mmr_score, + title, + ] := + *docs {{ + owner_type, + owner_id, + doc_id, + title, + }}, + apply_mmr[ + doc_id, + snippet_data, + distance, + mmr_score, + ] + # Sort the results by distance to find the closest matches - :sort distance - }}""" + :sort -mmr_score + :limit {k} + + :create _interim {{ + doc_id, + snippet_data, + distance, + mmr_score, + title, + }} + """ + + collect_query = """ + m[ + doc_id, + collect(snippet), + distance, + title, + ] := *_interim { + doc_id, + snippet_data, + distance, + title, + }, snippet = { + "index": snippet_data->0, + "content": snippet_data->1, + } + + ?[ + id, + owner_type, + owner_id, + snippets, + distance, + title, + ] := m[ + id, + snippets, + distance, + title, + ], owner_type = $owner_type, owner_id = $owner_id + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query( + developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} + ), + interim_query, + collect_query, + ] return ( - query, + queries, { + "owner_type": owner_type, "owner_id": owner_id, "query_embedding": query_embedding, - "k": k, - "radius": radius, }, ) diff --git a/agents-api/agents_api/models/entry/__init__.py b/agents-api/agents_api/models/entry/__init__.py index 3ba31b722..32231c364 100644 --- a/agents-api/agents_api/models/entry/__init__.py +++ b/agents-api/agents_api/models/entry/__init__.py @@ -10,3 +10,10 @@ The module utilizes pandas DataFrames for handling query results and integrates with the CozoClient for database operations, ensuring efficient and effective management of entries. """ + +# ruff: noqa: F401, F403, F405 + +from .create_entries import create_entries +from .delete_entries import delete_entries +from .get_history import get_history +from .list_entries import list_entries diff --git a/agents-api/agents_api/models/execution/__init__.py b/agents-api/agents_api/models/execution/__init__.py index e69de29bb..1e4bd85be 100644 --- a/agents-api/agents_api/models/execution/__init__.py +++ b/agents-api/agents_api/models/execution/__init__.py @@ -0,0 +1,10 @@ +# ruff: noqa: F401, F403, F405 + +from .create_execution import create_execution +from .create_execution_transition import create_execution_transition +from .get_execution import get_execution +from .get_execution_transition import get_execution_transition +from .list_execution_transitions import list_execution_transitions +from .list_executions import list_executions +from .prepare_execution_input import prepare_execution_input +from .update_execution import update_execution diff --git a/agents-api/agents_api/models/session/__init__.py b/agents-api/agents_api/models/session/__init__.py index c73d7ee82..b4092611f 100644 --- a/agents-api/agents_api/models/session/__init__.py +++ b/agents-api/agents_api/models/session/__init__.py @@ -8,3 +8,17 @@ - Deleting sessions and their associated data from the database. This module plays a crucial role in the application by facilitating the management of session data, which is essential for tracking and analyzing user interactions and behaviors within the system.""" + +# ruff: noqa: F401, F403, F405 + +from .create_or_update_session import create_or_update_session +from .create_session import create_session +from .delete_session import delete_session +from .get_cached_response import get_cached_response +from .get_session import get_session +from .list_sessions import list_sessions +from .patch_session import patch_session +from .prepare_chat_context import prepare_chat_context +from .prepare_session_data import prepare_session_data +from .set_cached_response import set_cached_response +from .update_session import update_session diff --git a/agents-api/agents_api/models/task/__init__.py b/agents-api/agents_api/models/task/__init__.py index e69de29bb..2eaff3ab3 100644 --- a/agents-api/agents_api/models/task/__init__.py +++ b/agents-api/agents_api/models/task/__init__.py @@ -0,0 +1,9 @@ +# ruff: noqa: F401, F403, F405 + +from .create_or_update_task import create_or_update_task +from .create_task import create_task +from .delete_task import delete_task +from .get_task import get_task +from .list_tasks import list_tasks +from .patch_task import patch_task +from .update_task import update_task diff --git a/agents-api/agents_api/models/tools/__init__.py b/agents-api/agents_api/models/tools/__init__.py index 043da3916..98f3a5e3a 100644 --- a/agents-api/agents_api/models/tools/__init__.py +++ b/agents-api/agents_api/models/tools/__init__.py @@ -8,3 +8,12 @@ This module is crucial for the effective management and utilization of tools in the application, ensuring that tools can be created, managed, and utilized efficiently. """ + +# ruff: noqa: F401, F403, F405 + +from .create_tools import create_tools +from .delete_tool import delete_tool +from .get_tool import get_tool +from .list_tools import list_tools +from .patch_tool import patch_tool +from .update_tool import update_tool diff --git a/agents-api/agents_api/models/tools/delete_tools.py b/agents-api/agents_api/models/tools/delete_tool.py similarity index 100% rename from agents-api/agents_api/models/tools/delete_tools.py rename to agents-api/agents_api/models/tools/delete_tool.py diff --git a/agents-api/agents_api/models/user/__init__.py b/agents-api/agents_api/models/user/__init__.py index 1b1b2c0d9..5ae76865f 100644 --- a/agents-api/agents_api/models/user/__init__.py +++ b/agents-api/agents_api/models/user/__init__.py @@ -7,3 +7,12 @@ - list_users_query: Lists users associated with a specific developer, with support for pagination and metadata-based filtering. - patch_user_query: Updates a user's information in the CozoDB database, allowing for changes to fields such as name, about, and metadata. """ + +# ruff: noqa: F401, F403, F405 + +from .create_or_update_user import create_or_update_user +from .create_user import create_user +from .get_user import get_user +from .list_users import list_users +from .patch_user import patch_user +from .update_user import update_user diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index 120c8949c..1aea0e07e 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -183,7 +183,9 @@ def wrapper(*args, **kwargs): nonlocal transform transform = transform or (lambda x: x) + if one: + assert len(data) >= 1 return cls(**transform(data[0])) return [cls(**item) for item in map(transform, data)] diff --git a/agents-api/agents_api/routers/agents/delete_agent_tools.py b/agents-api/agents_api/routers/agents/delete_agent_tools.py index b1177b094..a2c5c147b 100644 --- a/agents-api/agents_api/routers/agents/delete_agent_tools.py +++ b/agents-api/agents_api/routers/agents/delete_agent_tools.py @@ -5,9 +5,8 @@ from pydantic import UUID4 from ...autogen.openapi_model import ResourceDeletedResponse -from ...common.utils.datetime import utcnow from ...dependencies.developer_id import get_developer_id -from ...models.tools.delete_tools import delete_tool as delete_tool_query +from ...models.tools.delete_tool import delete_tool from .router import router @@ -17,12 +16,8 @@ async def delete_agent_tools( tool_id: UUID, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> ResourceDeletedResponse: - _, resp = next( - delete_tool_query( - developer_id=x_developer_id, - agent_id=agent_id, - tool_id=tool_id, - ).iterrows() + return delete_tool( + developer_id=x_developer_id, + agent_id=agent_id, + tool_id=tool_id, ) - - return ResourceDeletedResponse(id=resp["tool_id"], deleted_at=utcnow()) diff --git a/agents-api/agents_api/web.py b/agents-api/agents_api/web.py index c408c152b..ab805a67c 100644 --- a/agents-api/agents_api/web.py +++ b/agents-api/agents_api/web.py @@ -21,10 +21,7 @@ from agents_api.exceptions import PromptTooBigError from agents_api.routers import ( agents, - jobs, - sessions, tasks, - users, ) if not sentry_dsn: diff --git a/agents-api/migrations/migrate_1722710530_unify_owner_doc_relations.py b/agents-api/migrations/migrate_1722710530_unify_owner_doc_relations.py new file mode 100644 index 000000000..a56bce674 --- /dev/null +++ b/agents-api/migrations/migrate_1722710530_unify_owner_doc_relations.py @@ -0,0 +1,204 @@ +# /usr/bin/env python3 + +MIGRATION_ID = "unify_owner_doc_relations" +CREATED_AT = 1722710530.126563 + +create_docs_relations_query = dict( + up=""" + :create docs { + owner_type: String, + owner_id: Uuid, + doc_id: Uuid, + => + title: String, + created_at: Float default now(), + metadata: Json default {}, + } + """, + down="::remove docs", +) + +remove_user_docs_table = dict( + up=""" + doc_title[doc_id, unique(title)] := + *snippets { + doc_id, + title, + } + + ?[owner_type, owner_id, doc_id, title, created_at, metadata] := + owner_type = "user", + *user_docs { + user_id: owner_id, + doc_id, + created_at, + metadata, + }, + doc_title[doc_id, title] + + :insert docs { + owner_type, + owner_id, + doc_id, + title, + created_at, + metadata, + } + + } { # <-- this is just a separator between the two queries + ::remove user_docs + """, + down=""" + :create user_docs { + user_id: Uuid, + doc_id: Uuid + => + created_at: Float default now(), + metadata: Json default {}, + } + """, +) + +remove_agent_docs_table = dict( + up=remove_user_docs_table["up"].replace("user", "agent"), + down=remove_user_docs_table["down"].replace("user", "agent"), +) + +# See: https://github.com/nmslib/hnswlib/blob/master/ALGO_PARAMS.md +snippets_hnsw_index = dict( + up=""" + ::hnsw create snippets:embedding_space { + fields: [embedding], + filter: !is_null(embedding), + dim: 1024, + distance: Cosine, + m: 64, + ef_construction: 256, + extend_candidates: true, + keep_pruned_connections: false, + } + """, + down=""" + ::hnsw drop snippets:embedding_space + """, +) + +# See: https://docs.cozodb.org/en/latest/vector.html#full-text-search-fts +snippets_fts_index = dict( + up=""" + ::fts create snippets:fts { + extractor: content, + tokenizer: Simple, + filters: [Lowercase, Stemmer('english'), Stopwords('en')], + } + """, + down=""" + ::fts drop snippets:fts + """, +) + +temp_rename_snippets_table = dict( + up=""" + ::rename snippets -> information_snippets + """, + down=""" + ::rename information_snippets -> snippets + """, +) + +temp_rename_snippets_table_back = dict( + up=temp_rename_snippets_table["down"], + down=temp_rename_snippets_table["up"], +) + +drop_snippets_hnsw_index = { + "up": snippets_hnsw_index["down"].replace("snippets:", "information_snippets:"), + "down": snippets_hnsw_index["up"].replace("snippets:", "information_snippets:"), +} + +drop_snippets_fts_index = dict( + up=""" + ::fts drop information_snippets:fts + """, + down=""" + ::fts create information_snippets:fts { + extractor: concat(title, ' ', snippet), + tokenizer: Simple, + filters: [Lowercase, Stemmer('english'), Stopwords('en')], + } + """, +) + + +remove_title_from_snippets_table = dict( + up=""" + ?[doc_id, index, content, embedding] := + *snippets { + doc_id, + snippet_idx: index, + snippet: content, + embedding, + } + + :replace snippets { + doc_id: Uuid, + index: Int, + => + content: String, + embedding: ? default null, + } + """, + down=""" + ?[doc_id, snippet_idx, title, snippet, embedding] := + *snippets { + doc_id, + index: snippet_idx, + content: snippet, + embedding, + }, + *docs { + doc_id, + title, + } + + :replace snippets { + doc_id: Uuid, + snippet_idx: Int, + => + title: String, + snippet: String, + embed_instruction: String default 'Encode this passage for retrieval: ', + embedding: ? default null, + } + """, +) + +queries = [ + create_docs_relations_query, + remove_user_docs_table, + remove_agent_docs_table, + temp_rename_snippets_table, # Because of a bug in Cozo + drop_snippets_hnsw_index, + drop_snippets_fts_index, + temp_rename_snippets_table_back, # Because of a bug in Cozo + remove_title_from_snippets_table, + snippets_fts_index, + snippets_hnsw_index, +] + + +def run(client, queries): + joiner = "}\n\n{" + + query = joiner.join(queries) + query = f"{{\n{query}\n}}" + + client.run(query) + + +def up(client): + run(client, [q["up"] for q in queries]) + + +def down(client): + run(client, [q["down"] for q in reversed(queries)]) diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 64c74e3b6..cd7983feb 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -319,22 +319,22 @@ files = [ [[package]] name = "attrs" -version = "23.2.0" +version = "24.1.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, + {file = "attrs-24.1.0-py3-none-any.whl", hash = "sha256:377b47448cb61fea38533f671fba0d0f8a96fd58facd4dc518e3dac9dbea0905"}, + {file = "attrs-24.1.0.tar.gz", hash = "sha256:adbdec84af72d38be7628e353a09b6a6790d15cd71819f6e9d7b0faa8a125745"}, ] [package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "babel" @@ -848,33 +848,13 @@ validation = ["openapi-spec-validator (>=0.2.8,<0.7.0)", "prance (>=0.18.2)"] [[package]] name = "debugpy" -version = "1.8.2" +version = "1.8.3" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"}, - {file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"}, - {file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"}, - {file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"}, - {file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"}, - {file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"}, - {file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"}, - {file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"}, - {file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"}, - {file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"}, - {file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"}, - {file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"}, - {file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"}, - {file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"}, - {file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"}, - {file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"}, - {file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"}, - {file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"}, - {file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"}, - {file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"}, - {file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"}, - {file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"}, + {file = "debugpy-1.8.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0df2c400853150af14996b8d1a4f54d45ffa98e76c0f3de30665e89e273ea293"}, + {file = "debugpy-1.8.3.zip", hash = "sha256:0f5a6326d9fc375b864ed368d06cddf2dabe5135511e71cde3758be699847d36"}, ] [[package]] @@ -5350,13 +5330,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.4" +version = "4.66.5" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, - {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, ] [package.dependencies] diff --git a/fern/fern.config.json b/fern/fern.config.json index 0a2289fc0..1ccdf6af2 100644 --- a/fern/fern.config.json +++ b/fern/fern.config.json @@ -1,4 +1,4 @@ { "organization": "julep", - "version": "0.33.2" + "version": "0.37.6" } \ No newline at end of file diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index 6ea9e327d..fbcacb597 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -63,6 +63,8 @@ CommonUuid, CommonValidPythonIdentifier, DocsBaseDocSearchRequest, + DocsCreateDocRequest, + DocsCreateDocRequestContent, DocsDoc, DocsDocContent, DocsDocOwner, @@ -72,12 +74,9 @@ DocsEmbedQueryRequestText, DocsEmbedQueryResponse, DocsHybridDocSearchRequest, - DocsHybridDocSearchRequestText, - DocsHybridDocSearchRequestVector, + DocsSnippet, DocsTextOnlyDocSearchRequest, - DocsTextOnlyDocSearchRequestText, DocsVectorDocSearchRequest, - DocsVectorDocSearchRequestVector, EntriesBaseEntry, EntriesBaseEntryContent, EntriesBaseEntryContentItem, @@ -285,6 +284,8 @@ "CommonUuid", "CommonValidPythonIdentifier", "DocsBaseDocSearchRequest", + "DocsCreateDocRequest", + "DocsCreateDocRequestContent", "DocsDoc", "DocsDocContent", "DocsDocOwner", @@ -294,12 +295,9 @@ "DocsEmbedQueryRequestText", "DocsEmbedQueryResponse", "DocsHybridDocSearchRequest", - "DocsHybridDocSearchRequestText", - "DocsHybridDocSearchRequestVector", + "DocsSnippet", "DocsTextOnlyDocSearchRequest", - "DocsTextOnlyDocSearchRequestText", "DocsVectorDocSearchRequest", - "DocsVectorDocSearchRequestVector", "EntriesBaseEntry", "EntriesBaseEntryContent", "EntriesBaseEntryContentItem", diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index edd79bc0c..b0a21c027 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -67,6 +67,7 @@ from .types.common_resource_updated_response import CommonResourceUpdatedResponse from .types.common_uuid import CommonUuid from .types.common_valid_python_identifier import CommonValidPythonIdentifier +from .types.docs_create_doc_request_content import DocsCreateDocRequestContent from .types.docs_doc import DocsDoc from .types.docs_embed_query_request import DocsEmbedQueryRequest from .types.docs_embed_query_response import DocsEmbedQueryResponse @@ -775,6 +776,68 @@ def agent_docs_route_list( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def agent_docs_route_create( + self, + id: CommonUuid, + *, + title: CommonIdentifierSafeUnicode, + content: DocsCreateDocRequestContent, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceCreatedResponse: + """ + Create a Doc for this Agent + + Parameters + ---------- + id : CommonUuid + ID of parent resource + + title : CommonIdentifierSafeUnicode + Title describing what this document contains + + content : DocsCreateDocRequestContent + Contents of the document + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceCreatedResponse + The request has succeeded and a new resource has been created as a result. + + Examples + -------- + from julep.client import JulepApi + + client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + client.agent_docs_route_create( + id="id", + title="title", + content="content", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"agents/{jsonable_encoder(id)}/docs", + method="POST", + json={"metadata": metadata, "title": title, "content": content}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceCreatedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def agents_docs_search_route_search( self, id: CommonUuid, @@ -3441,6 +3504,68 @@ def user_docs_route_list( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def user_docs_route_create( + self, + id: CommonUuid, + *, + title: CommonIdentifierSafeUnicode, + content: DocsCreateDocRequestContent, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceCreatedResponse: + """ + Create a Doc for this User + + Parameters + ---------- + id : CommonUuid + ID of parent resource + + title : CommonIdentifierSafeUnicode + Title describing what this document contains + + content : DocsCreateDocRequestContent + Contents of the document + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceCreatedResponse + The request has succeeded and a new resource has been created as a result. + + Examples + -------- + from julep.client import JulepApi + + client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + client.user_docs_route_create( + id="id", + title="title", + content="content", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"users/{jsonable_encoder(id)}/docs", + method="POST", + json={"metadata": metadata, "title": title, "content": content}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceCreatedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def user_docs_search_route_search( self, id: CommonUuid, @@ -4239,6 +4364,76 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def agent_docs_route_create( + self, + id: CommonUuid, + *, + title: CommonIdentifierSafeUnicode, + content: DocsCreateDocRequestContent, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceCreatedResponse: + """ + Create a Doc for this Agent + + Parameters + ---------- + id : CommonUuid + ID of parent resource + + title : CommonIdentifierSafeUnicode + Title describing what this document contains + + content : DocsCreateDocRequestContent + Contents of the document + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceCreatedResponse + The request has succeeded and a new resource has been created as a result. + + Examples + -------- + import asyncio + + from julep.client import AsyncJulepApi + + client = AsyncJulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.agent_docs_route_create( + id="id", + title="title", + content="content", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"agents/{jsonable_encoder(id)}/docs", + method="POST", + json={"metadata": metadata, "title": title, "content": content}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceCreatedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def agents_docs_search_route_search( self, id: CommonUuid, @@ -7225,6 +7420,76 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def user_docs_route_create( + self, + id: CommonUuid, + *, + title: CommonIdentifierSafeUnicode, + content: DocsCreateDocRequestContent, + metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceCreatedResponse: + """ + Create a Doc for this User + + Parameters + ---------- + id : CommonUuid + ID of parent resource + + title : CommonIdentifierSafeUnicode + Title describing what this document contains + + content : DocsCreateDocRequestContent + Contents of the document + + metadata : typing.Optional[typing.Dict[str, typing.Any]] + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceCreatedResponse + The request has succeeded and a new resource has been created as a result. + + Examples + -------- + import asyncio + + from julep.client import AsyncJulepApi + + client = AsyncJulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.user_docs_route_create( + id="id", + title="title", + content="content", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"users/{jsonable_encoder(id)}/docs", + method="POST", + json={"metadata": metadata, "title": title, "content": content}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceCreatedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def user_docs_search_route_search( self, id: CommonUuid, diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index be1c91b28..795aeea75 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -839,6 +839,103 @@ client.agent_docs_route_list( + + + + +
client.agent_docs_route_create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a Doc for this Agent +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from julep.client import JulepApi + +client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", +) +client.agent_docs_route_create( + id="id", + title="title", + content="content", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `CommonUuid` — ID of parent resource + +
+
+ +
+
+ +**title:** `CommonIdentifierSafeUnicode` — Title describing what this document contains + +
+
+ +
+
+ +**content:** `DocsCreateDocRequestContent` — Contents of the document + +
+
+ +
+
+ +**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -4891,6 +4988,103 @@ client.user_docs_route_list( + + + + +
client.user_docs_route_create(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Create a Doc for this User +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from julep.client import JulepApi + +client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", +) +client.user_docs_route_create( + id="id", + title="title", + content="content", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `CommonUuid` — ID of parent resource + +
+
+ +
+
+ +**title:** `CommonIdentifierSafeUnicode` — Title describing what this document contains + +
+
+ +
+
+ +**content:** `DocsCreateDocRequestContent` — Contents of the document + +
+
+ +
+
+ +**metadata:** `typing.Optional[typing.Dict[str, typing.Any]]` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index d4d576485..6da79d6ab 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -90,6 +90,8 @@ from .common_uuid import CommonUuid from .common_valid_python_identifier import CommonValidPythonIdentifier from .docs_base_doc_search_request import DocsBaseDocSearchRequest +from .docs_create_doc_request import DocsCreateDocRequest +from .docs_create_doc_request_content import DocsCreateDocRequestContent from .docs_doc import DocsDoc from .docs_doc_content import DocsDocContent from .docs_doc_owner import DocsDocOwner @@ -99,12 +101,9 @@ from .docs_embed_query_request_text import DocsEmbedQueryRequestText from .docs_embed_query_response import DocsEmbedQueryResponse from .docs_hybrid_doc_search_request import DocsHybridDocSearchRequest -from .docs_hybrid_doc_search_request_text import DocsHybridDocSearchRequestText -from .docs_hybrid_doc_search_request_vector import DocsHybridDocSearchRequestVector +from .docs_snippet import DocsSnippet from .docs_text_only_doc_search_request import DocsTextOnlyDocSearchRequest -from .docs_text_only_doc_search_request_text import DocsTextOnlyDocSearchRequestText from .docs_vector_doc_search_request import DocsVectorDocSearchRequest -from .docs_vector_doc_search_request_vector import DocsVectorDocSearchRequestVector from .entries_base_entry import EntriesBaseEntry from .entries_base_entry_content import EntriesBaseEntryContent from .entries_base_entry_content_item import EntriesBaseEntryContentItem @@ -351,6 +350,8 @@ "CommonUuid", "CommonValidPythonIdentifier", "DocsBaseDocSearchRequest", + "DocsCreateDocRequest", + "DocsCreateDocRequestContent", "DocsDoc", "DocsDocContent", "DocsDocOwner", @@ -360,12 +361,9 @@ "DocsEmbedQueryRequestText", "DocsEmbedQueryResponse", "DocsHybridDocSearchRequest", - "DocsHybridDocSearchRequestText", - "DocsHybridDocSearchRequestVector", + "DocsSnippet", "DocsTextOnlyDocSearchRequest", - "DocsTextOnlyDocSearchRequestText", "DocsVectorDocSearchRequest", - "DocsVectorDocSearchRequestVector", "EntriesBaseEntry", "EntriesBaseEntryContent", "EntriesBaseEntryContentItem", diff --git a/sdks/python/julep/api/types/docs_create_doc_request.py b/sdks/python/julep/api/types/docs_create_doc_request.py new file mode 100644 index 000000000..caf1497b0 --- /dev/null +++ b/sdks/python/julep/api/types/docs_create_doc_request.py @@ -0,0 +1,57 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode +from .docs_create_doc_request_content import DocsCreateDocRequestContent + + +class DocsCreateDocRequest(pydantic_v1.BaseModel): + """ + Payload for creating a doc + """ + + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + title: CommonIdentifierSafeUnicode = pydantic_v1.Field() + """ + Title describing what this document contains + """ + + content: DocsCreateDocRequestContent = pydantic_v1.Field() + """ + Contents of the document + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/docs_hybrid_doc_search_request_text.py b/sdks/python/julep/api/types/docs_create_doc_request_content.py similarity index 53% rename from sdks/python/julep/api/types/docs_hybrid_doc_search_request_text.py rename to sdks/python/julep/api/types/docs_create_doc_request_content.py index b3cfcf16a..5382bf647 100644 --- a/sdks/python/julep/api/types/docs_hybrid_doc_search_request_text.py +++ b/sdks/python/julep/api/types/docs_create_doc_request_content.py @@ -2,4 +2,4 @@ import typing -DocsHybridDocSearchRequestText = typing.Union[str, typing.List[str]] +DocsCreateDocRequestContent = typing.Union[str, typing.List[str]] diff --git a/sdks/python/julep/api/types/docs_doc_reference.py b/sdks/python/julep/api/types/docs_doc_reference.py index 6d7ddb9c7..94c1eaefc 100644 --- a/sdks/python/julep/api/types/docs_doc_reference.py +++ b/sdks/python/julep/api/types/docs_doc_reference.py @@ -7,6 +7,7 @@ from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_uuid import CommonUuid from .docs_doc_owner import DocsDocOwner +from .docs_snippet import DocsSnippet class DocsDocReference(pydantic_v1.BaseModel): @@ -20,13 +21,9 @@ class DocsDocReference(pydantic_v1.BaseModel): ID of the document """ - snippet_index: typing.List[int] = pydantic_v1.Field() - """ - Snippets referred to of the document - """ - title: typing.Optional[str] = None - snippet: typing.Optional[str] = None + snippets: typing.List[DocsSnippet] + distance: typing.Optional[float] = None def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { diff --git a/sdks/python/julep/api/types/docs_hybrid_doc_search_request.py b/sdks/python/julep/api/types/docs_hybrid_doc_search_request.py index faaccc53b..51de991e1 100644 --- a/sdks/python/julep/api/types/docs_hybrid_doc_search_request.py +++ b/sdks/python/julep/api/types/docs_hybrid_doc_search_request.py @@ -6,19 +6,17 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .docs_base_doc_search_request import DocsBaseDocSearchRequest -from .docs_hybrid_doc_search_request_text import DocsHybridDocSearchRequestText -from .docs_hybrid_doc_search_request_vector import DocsHybridDocSearchRequestVector class DocsHybridDocSearchRequest(DocsBaseDocSearchRequest): - text: DocsHybridDocSearchRequestText = pydantic_v1.Field() + text: str = pydantic_v1.Field() """ - Text or texts to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. + Text to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. """ - vector: DocsHybridDocSearchRequestVector = pydantic_v1.Field() + vector: typing.List[float] = pydantic_v1.Field() """ - Vector or vectors to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. + Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. """ def json(self, **kwargs: typing.Any) -> str: diff --git a/sdks/python/julep/api/types/docs_hybrid_doc_search_request_vector.py b/sdks/python/julep/api/types/docs_hybrid_doc_search_request_vector.py deleted file mode 100644 index 64624e57e..000000000 --- a/sdks/python/julep/api/types/docs_hybrid_doc_search_request_vector.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -DocsHybridDocSearchRequestVector = typing.Union[ - typing.List[float], typing.List[typing.List[float]] -] diff --git a/sdks/python/julep/api/types/docs_snippet.py b/sdks/python/julep/api/types/docs_snippet.py new file mode 100644 index 000000000..9d080028a --- /dev/null +++ b/sdks/python/julep/api/types/docs_snippet.py @@ -0,0 +1,43 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class DocsSnippet(pydantic_v1.BaseModel): + index: int + content: str + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/docs_text_only_doc_search_request.py b/sdks/python/julep/api/types/docs_text_only_doc_search_request.py index 5f43e1b04..517b6a1ce 100644 --- a/sdks/python/julep/api/types/docs_text_only_doc_search_request.py +++ b/sdks/python/julep/api/types/docs_text_only_doc_search_request.py @@ -6,13 +6,12 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .docs_base_doc_search_request import DocsBaseDocSearchRequest -from .docs_text_only_doc_search_request_text import DocsTextOnlyDocSearchRequestText class DocsTextOnlyDocSearchRequest(DocsBaseDocSearchRequest): - text: DocsTextOnlyDocSearchRequestText = pydantic_v1.Field() + text: str = pydantic_v1.Field() """ - Text or texts to use in the search. + Text to use in the search. """ def json(self, **kwargs: typing.Any) -> str: diff --git a/sdks/python/julep/api/types/docs_text_only_doc_search_request_text.py b/sdks/python/julep/api/types/docs_text_only_doc_search_request_text.py deleted file mode 100644 index 5f957995e..000000000 --- a/sdks/python/julep/api/types/docs_text_only_doc_search_request_text.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -DocsTextOnlyDocSearchRequestText = typing.Union[str, typing.List[str]] diff --git a/sdks/python/julep/api/types/docs_vector_doc_search_request.py b/sdks/python/julep/api/types/docs_vector_doc_search_request.py index 1401f7535..4ea4e9632 100644 --- a/sdks/python/julep/api/types/docs_vector_doc_search_request.py +++ b/sdks/python/julep/api/types/docs_vector_doc_search_request.py @@ -6,13 +6,12 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .docs_base_doc_search_request import DocsBaseDocSearchRequest -from .docs_vector_doc_search_request_vector import DocsVectorDocSearchRequestVector class DocsVectorDocSearchRequest(DocsBaseDocSearchRequest): - vector: DocsVectorDocSearchRequestVector = pydantic_v1.Field() + vector: typing.List[float] = pydantic_v1.Field() """ - Vector or vectors to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. + Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. """ def json(self, **kwargs: typing.Any) -> str: diff --git a/sdks/python/julep/api/types/docs_vector_doc_search_request_vector.py b/sdks/python/julep/api/types/docs_vector_doc_search_request_vector.py deleted file mode 100644 index e64b2da82..000000000 --- a/sdks/python/julep/api/types/docs_vector_doc_search_request_vector.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -DocsVectorDocSearchRequestVector = typing.Union[ - typing.List[float], typing.List[typing.List[float]] -] diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 59d71e24a..02908773a 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -154,22 +154,22 @@ typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} [[package]] name = "attrs" -version = "23.2.0" +version = "24.1.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, + {file = "attrs-24.1.0-py3-none-any.whl", hash = "sha256:377b47448cb61fea38533f671fba0d0f8a96fd58facd4dc518e3dac9dbea0905"}, + {file = "attrs-24.1.0.tar.gz", hash = "sha256:adbdec84af72d38be7628e353a09b6a6790d15cd71819f6e9d7b0faa8a125745"}, ] [package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] +benchmark = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-codspeed", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +cov = ["cloudpickle", "coverage[toml] (>=5.3)", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +dev = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pre-commit", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +docs = ["cogapp", "furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +tests = ["cloudpickle", "hypothesis", "mypy (>=1.11.1)", "pympler", "pytest (>=4.3.0)", "pytest-mypy-plugins", "pytest-xdist[psutil]"] +tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "babel" @@ -577,33 +577,13 @@ develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest- [[package]] name = "debugpy" -version = "1.8.2" +version = "1.8.3" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"}, - {file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"}, - {file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"}, - {file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"}, - {file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"}, - {file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"}, - {file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"}, - {file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"}, - {file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"}, - {file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"}, - {file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"}, - {file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"}, - {file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"}, - {file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"}, - {file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"}, - {file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"}, - {file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"}, - {file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"}, - {file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"}, - {file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"}, - {file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"}, - {file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"}, + {file = "debugpy-1.8.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0df2c400853150af14996b8d1a4f54d45ffa98e76c0f3de30665e89e273ea293"}, + {file = "debugpy-1.8.3.zip", hash = "sha256:0f5a6326d9fc375b864ed368d06cddf2dabe5135511e71cde3758be699847d36"}, ] [[package]] @@ -2929,13 +2909,13 @@ files = [ [[package]] name = "tqdm" -version = "4.66.4" +version = "4.66.5" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" files = [ - {file = "tqdm-4.66.4-py3-none-any.whl", hash = "sha256:b75ca56b413b030bc3f00af51fd2c1a1a5eac6a0c1cca83cbb37a5c52abce644"}, - {file = "tqdm-4.66.4.tar.gz", hash = "sha256:e4d936c9de8727928f3be6079590e97d9abfe8d39a590be678eb5919ffc186bb"}, + {file = "tqdm-4.66.5-py3-none-any.whl", hash = "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd"}, + {file = "tqdm-4.66.5.tar.gz", hash = "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad"}, ] [package.dependencies] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index 61b146424..f279f2bb9 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -49,12 +49,14 @@ export type { Common_toolRef } from "./models/Common_toolRef"; export type { Common_uuid } from "./models/Common_uuid"; export type { Common_validPythonIdentifier } from "./models/Common_validPythonIdentifier"; export type { Docs_BaseDocSearchRequest } from "./models/Docs_BaseDocSearchRequest"; +export type { Docs_CreateDocRequest } from "./models/Docs_CreateDocRequest"; export type { Docs_Doc } from "./models/Docs_Doc"; export type { Docs_DocOwner } from "./models/Docs_DocOwner"; export type { Docs_DocReference } from "./models/Docs_DocReference"; export type { Docs_EmbedQueryRequest } from "./models/Docs_EmbedQueryRequest"; export type { Docs_EmbedQueryResponse } from "./models/Docs_EmbedQueryResponse"; export type { Docs_HybridDocSearchRequest } from "./models/Docs_HybridDocSearchRequest"; +export type { Docs_Snippet } from "./models/Docs_Snippet"; export type { Docs_TextOnlyDocSearchRequest } from "./models/Docs_TextOnlyDocSearchRequest"; export type { Docs_VectorDocSearchRequest } from "./models/Docs_VectorDocSearchRequest"; export type { Entries_BaseEntry } from "./models/Entries_BaseEntry"; @@ -161,12 +163,14 @@ export { $Common_toolRef } from "./schemas/$Common_toolRef"; export { $Common_uuid } from "./schemas/$Common_uuid"; export { $Common_validPythonIdentifier } from "./schemas/$Common_validPythonIdentifier"; export { $Docs_BaseDocSearchRequest } from "./schemas/$Docs_BaseDocSearchRequest"; +export { $Docs_CreateDocRequest } from "./schemas/$Docs_CreateDocRequest"; export { $Docs_Doc } from "./schemas/$Docs_Doc"; export { $Docs_DocOwner } from "./schemas/$Docs_DocOwner"; export { $Docs_DocReference } from "./schemas/$Docs_DocReference"; export { $Docs_EmbedQueryRequest } from "./schemas/$Docs_EmbedQueryRequest"; export { $Docs_EmbedQueryResponse } from "./schemas/$Docs_EmbedQueryResponse"; export { $Docs_HybridDocSearchRequest } from "./schemas/$Docs_HybridDocSearchRequest"; +export { $Docs_Snippet } from "./schemas/$Docs_Snippet"; export { $Docs_TextOnlyDocSearchRequest } from "./schemas/$Docs_TextOnlyDocSearchRequest"; export { $Docs_VectorDocSearchRequest } from "./schemas/$Docs_VectorDocSearchRequest"; export { $Entries_BaseEntry } from "./schemas/$Entries_BaseEntry"; diff --git a/sdks/ts/src/api/models/Docs_CreateDocRequest.ts b/sdks/ts/src/api/models/Docs_CreateDocRequest.ts new file mode 100644 index 000000000..1294bf701 --- /dev/null +++ b/sdks/ts/src/api/models/Docs_CreateDocRequest.ts @@ -0,0 +1,19 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; +/** + * Payload for creating a doc + */ +export type Docs_CreateDocRequest = { + metadata?: Record; + /** + * Title describing what this document contains + */ + title: Common_identifierSafeUnicode; + /** + * Contents of the document + */ + content: string | Array; +}; diff --git a/sdks/ts/src/api/models/Docs_DocReference.ts b/sdks/ts/src/api/models/Docs_DocReference.ts index 9848f4079..20fba1b7e 100644 --- a/sdks/ts/src/api/models/Docs_DocReference.ts +++ b/sdks/ts/src/api/models/Docs_DocReference.ts @@ -4,6 +4,7 @@ /* eslint-disable */ import type { Common_uuid } from "./Common_uuid"; import type { Docs_DocOwner } from "./Docs_DocOwner"; +import type { Docs_Snippet } from "./Docs_Snippet"; export type Docs_DocReference = { /** * The owner of this document. @@ -13,10 +14,7 @@ export type Docs_DocReference = { * ID of the document */ readonly id: Common_uuid; - /** - * Snippets referred to of the document - */ - snippet_index: Array; title?: string; - snippet?: string; + snippets: Array; + distance: number | null; }; diff --git a/sdks/ts/src/api/models/Docs_HybridDocSearchRequest.ts b/sdks/ts/src/api/models/Docs_HybridDocSearchRequest.ts index 1049e733d..93b099294 100644 --- a/sdks/ts/src/api/models/Docs_HybridDocSearchRequest.ts +++ b/sdks/ts/src/api/models/Docs_HybridDocSearchRequest.ts @@ -5,11 +5,11 @@ import type { Docs_BaseDocSearchRequest } from "./Docs_BaseDocSearchRequest"; export type Docs_HybridDocSearchRequest = Docs_BaseDocSearchRequest & { /** - * Text or texts to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. + * Text to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. */ - text: string | Array; + text: string; /** - * Vector or vectors to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. + * Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. */ - vector: Array | Array>; + vector: Array; }; diff --git a/sdks/ts/src/api/models/Docs_Snippet.ts b/sdks/ts/src/api/models/Docs_Snippet.ts new file mode 100644 index 000000000..f39583199 --- /dev/null +++ b/sdks/ts/src/api/models/Docs_Snippet.ts @@ -0,0 +1,8 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type Docs_Snippet = { + index: number; + content: string; +}; diff --git a/sdks/ts/src/api/models/Docs_TextOnlyDocSearchRequest.ts b/sdks/ts/src/api/models/Docs_TextOnlyDocSearchRequest.ts index 9a15040c4..2d05e9f9b 100644 --- a/sdks/ts/src/api/models/Docs_TextOnlyDocSearchRequest.ts +++ b/sdks/ts/src/api/models/Docs_TextOnlyDocSearchRequest.ts @@ -5,7 +5,7 @@ import type { Docs_BaseDocSearchRequest } from "./Docs_BaseDocSearchRequest"; export type Docs_TextOnlyDocSearchRequest = Docs_BaseDocSearchRequest & { /** - * Text or texts to use in the search. + * Text to use in the search. */ - text: string | Array; + text: string; }; diff --git a/sdks/ts/src/api/models/Docs_VectorDocSearchRequest.ts b/sdks/ts/src/api/models/Docs_VectorDocSearchRequest.ts index 949e87cbd..8839067b0 100644 --- a/sdks/ts/src/api/models/Docs_VectorDocSearchRequest.ts +++ b/sdks/ts/src/api/models/Docs_VectorDocSearchRequest.ts @@ -5,7 +5,7 @@ import type { Docs_BaseDocSearchRequest } from "./Docs_BaseDocSearchRequest"; export type Docs_VectorDocSearchRequest = Docs_BaseDocSearchRequest & { /** - * Vector or vectors to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. + * Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. */ - vector: Array | Array>; + vector: Array; }; diff --git a/sdks/ts/src/api/schemas/$Docs_CreateDocRequest.ts b/sdks/ts/src/api/schemas/$Docs_CreateDocRequest.ts new file mode 100644 index 000000000..a3e2a7075 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Docs_CreateDocRequest.ts @@ -0,0 +1,41 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Docs_CreateDocRequest = { + description: `Payload for creating a doc`, + properties: { + metadata: { + type: "dictionary", + contains: { + properties: {}, + }, + }, + title: { + type: "all-of", + description: `Title describing what this document contains`, + contains: [ + { + type: "Common_identifierSafeUnicode", + }, + ], + isRequired: true, + }, + content: { + type: "any-of", + description: `Contents of the document`, + contains: [ + { + type: "string", + }, + { + type: "array", + contains: { + type: "string", + }, + }, + ], + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Docs_DocReference.ts b/sdks/ts/src/api/schemas/$Docs_DocReference.ts index 5c02863c3..e8788d4ba 100644 --- a/sdks/ts/src/api/schemas/$Docs_DocReference.ts +++ b/sdks/ts/src/api/schemas/$Docs_DocReference.ts @@ -25,19 +25,20 @@ export const $Docs_DocReference = { isReadOnly: true, isRequired: true, }, - snippet_index: { + title: { + type: "string", + }, + snippets: { type: "array", contains: { - type: "number", - format: "uint16", + type: "Docs_Snippet", }, isRequired: true, }, - title: { - type: "string", - }, - snippet: { - type: "string", + distance: { + type: "number", + isRequired: true, + isNullable: true, }, }, } as const; diff --git a/sdks/ts/src/api/schemas/$Docs_HybridDocSearchRequest.ts b/sdks/ts/src/api/schemas/$Docs_HybridDocSearchRequest.ts index a6e9dfcad..14948c59a 100644 --- a/sdks/ts/src/api/schemas/$Docs_HybridDocSearchRequest.ts +++ b/sdks/ts/src/api/schemas/$Docs_HybridDocSearchRequest.ts @@ -11,41 +11,15 @@ export const $Docs_HybridDocSearchRequest = { { properties: { text: { - type: "any-of", - description: `Text or texts to use in the search. In \`hybrid\` search mode, either \`text\` or both \`text\` and \`vector\` fields are required.`, - contains: [ - { - type: "string", - }, - { - type: "array", - contains: { - type: "string", - }, - }, - ], + type: "string", + description: `Text to use in the search. In \`hybrid\` search mode, either \`text\` or both \`text\` and \`vector\` fields are required.`, isRequired: true, }, vector: { - type: "any-of", - description: `Vector or vectors to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown.`, - contains: [ - { - type: "array", - contains: { - type: "number", - }, - }, - { - type: "array", - contains: { - type: "array", - contains: { - type: "number", - }, - }, - }, - ], + type: "array", + contains: { + type: "number", + }, isRequired: true, }, }, diff --git a/sdks/ts/src/api/schemas/$Docs_Snippet.ts b/sdks/ts/src/api/schemas/$Docs_Snippet.ts new file mode 100644 index 000000000..36b6deef2 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Docs_Snippet.ts @@ -0,0 +1,17 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Docs_Snippet = { + properties: { + index: { + type: "number", + isRequired: true, + format: "uint16", + }, + content: { + type: "string", + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Docs_TextOnlyDocSearchRequest.ts b/sdks/ts/src/api/schemas/$Docs_TextOnlyDocSearchRequest.ts index 94a19e134..b9711dbc4 100644 --- a/sdks/ts/src/api/schemas/$Docs_TextOnlyDocSearchRequest.ts +++ b/sdks/ts/src/api/schemas/$Docs_TextOnlyDocSearchRequest.ts @@ -11,19 +11,8 @@ export const $Docs_TextOnlyDocSearchRequest = { { properties: { text: { - type: "any-of", - description: `Text or texts to use in the search.`, - contains: [ - { - type: "string", - }, - { - type: "array", - contains: { - type: "string", - }, - }, - ], + type: "string", + description: `Text to use in the search.`, isRequired: true, }, }, diff --git a/sdks/ts/src/api/schemas/$Docs_VectorDocSearchRequest.ts b/sdks/ts/src/api/schemas/$Docs_VectorDocSearchRequest.ts index cceba2263..54ba49bf5 100644 --- a/sdks/ts/src/api/schemas/$Docs_VectorDocSearchRequest.ts +++ b/sdks/ts/src/api/schemas/$Docs_VectorDocSearchRequest.ts @@ -11,25 +11,10 @@ export const $Docs_VectorDocSearchRequest = { { properties: { vector: { - type: "any-of", - description: `Vector or vectors to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown.`, - contains: [ - { - type: "array", - contains: { - type: "number", - }, - }, - { - type: "array", - contains: { - type: "array", - contains: { - type: "number", - }, - }, - }, - ], + type: "array", + contains: { + type: "number", + }, isRequired: true, }, }, diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index 2c8b9be9a..c642a99bd 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -18,6 +18,7 @@ import type { Common_ResourceCreatedResponse } from "../models/Common_ResourceCr import type { Common_ResourceDeletedResponse } from "../models/Common_ResourceDeletedResponse"; import type { Common_ResourceUpdatedResponse } from "../models/Common_ResourceUpdatedResponse"; import type { Common_uuid } from "../models/Common_uuid"; +import type { Docs_CreateDocRequest } from "../models/Docs_CreateDocRequest"; import type { Docs_Doc } from "../models/Docs_Doc"; import type { Docs_DocReference } from "../models/Docs_DocReference"; import type { Docs_EmbedQueryRequest } from "../models/Docs_EmbedQueryRequest"; @@ -287,6 +288,31 @@ export class DefaultService { }, }); } + /** + * Create a Doc for this Agent + * @returns Common_ResourceCreatedResponse The request has succeeded and a new resource has been created as a result. + * @throws ApiError + */ + public agentDocsRouteCreate({ + id, + requestBody, + }: { + /** + * ID of parent resource + */ + id: Common_uuid; + requestBody: Docs_CreateDocRequest; + }): CancelablePromise { + return this.httpRequest.request({ + method: "POST", + url: "/agents/{id}/docs", + path: { + id: id, + }, + body: requestBody, + mediaType: "application/json", + }); + } /** * Search Docs owned by an Agent * @returns any The request has succeeded. @@ -1719,6 +1745,31 @@ export class DefaultService { }, }); } + /** + * Create a Doc for this User + * @returns Common_ResourceCreatedResponse The request has succeeded and a new resource has been created as a result. + * @throws ApiError + */ + public userDocsRouteCreate({ + id, + requestBody, + }: { + /** + * ID of parent resource + */ + id: Common_uuid; + requestBody: Docs_CreateDocRequest; + }): CancelablePromise { + return this.httpRequest.request({ + method: "POST", + url: "/users/{id}/docs", + path: { + id: id, + }, + body: requestBody, + mediaType: "application/json", + }); + } /** * Search Docs owned by a User * @returns any The request has succeeded. diff --git a/typespec/docs/endpoints.tsp b/typespec/docs/endpoints.tsp index 940d040b5..a493f9b86 100644 --- a/typespec/docs/endpoints.tsp +++ b/typespec/docs/endpoints.tsp @@ -18,10 +18,12 @@ namespace Docs; // interface UserEndpoints - extends ChildLimitOffsetPagination {} + extends ChildLimitOffsetPagination, + ChildCreateEndpoint {} interface AgentEndpoints - extends ChildLimitOffsetPagination {} + extends ChildLimitOffsetPagination, + ChildCreateEndpoint {} interface IndividualDocEndpoints extends GetEndpoint, @@ -45,7 +47,5 @@ interface SearchEndpoints Date: Sun, 4 Aug 2024 17:59:57 -0400 Subject: [PATCH 002/110] feat(agents-api): Reimplement agents routes Signed-off-by: Diwank Tomer --- .../agents_api/models/agent/create_agent.py | 12 ++------ agents-api/agents_api/models/utils.py | 17 ++++++----- .../agents_api/routers/agents/__init__.py | 22 +++++++++----- .../agents_api/routers/agents/create_agent.py | 24 +++++---------- ...te_agent_tools.py => create_agent_tool.py} | 21 ++++++------- .../routers/agents/create_or_update_agent.py | 19 +++++------- .../agents_api/routers/agents/delete_agent.py | 12 ++------ ...te_agent_tools.py => delete_agent_tool.py} | 2 +- .../routers/agents/get_agent_details.py | 12 ++------ .../routers/agents/list_agent_tools.py | 24 +++++++-------- .../agents_api/routers/agents/list_agents.py | 13 +++++--- .../agents_api/routers/agents/patch_agent.py | 28 ++++++----------- ...tch_agent_tools.py => patch_agent_tool.py} | 20 +++++-------- .../agents_api/routers/agents/update_agent.py | 30 ++++++------------- ...te_agent_tools.py => update_agent_tool.py} | 18 +++++------ 15 files changed, 111 insertions(+), 163 deletions(-) rename agents-api/agents_api/routers/agents/{create_agent_tools.py => create_agent_tool.py} (53%) rename agents-api/agents_api/routers/agents/{delete_agent_tools.py => delete_agent_tool.py} (95%) rename agents-api/agents_api/routers/agents/{patch_agent_tools.py => patch_agent_tool.py} (61%) rename agents-api/agents_api/routers/agents/{update_agent_tools.py => update_agent_tool.py} (65%) diff --git a/agents-api/agents_api/models/agent/create_agent.py b/agents-api/agents_api/models/agent/create_agent.py index 0e86c6634..404276a23 100644 --- a/agents-api/agents_api/models/agent/create_agent.py +++ b/agents-api/agents_api/models/agent/create_agent.py @@ -43,18 +43,12 @@ def create_agent( Constructs and executes a datalog query to create a new agent in the database. Parameters: - - agent_id (UUID): The unique identifier for the agent. + - agent_id (UUID | None): The unique identifier for the agent. - developer_id (UUID): The unique identifier for the developer creating the agent. - - name (str): The name of the agent. - - about (str): A description of the agent. - - instructions (list[str], optional): A list of instructions for using the agent. Defaults to an empty list. - - model (str, optional): The model identifier for the agent. Defaults to "julep-ai/samantha-1-turbo". - - metadata (dict, optional): A dictionary of metadata for the agent. Defaults to an empty dict. - - default_settings (dict, optional): A dictionary of default settings for the agent. Defaults to an empty dict. - - client (CozoClient, optional): The CozoDB client instance to use for the query. Defaults to a preconfigured client instance. + - data (CreateAgentRequest): The data for the new agent. Returns: - Agent: The newly created agent record. + - Agent: The newly created agent record. """ agent_id = agent_id or uuid4() diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index 1aea0e07e..5eafe4d78 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -1,7 +1,7 @@ import inspect import re from functools import partialmethod, wraps -from typing import Any, Callable, ParamSpec, Type +from typing import Any, Callable, ParamSpec, Type, TypeVar from uuid import UUID import pandas as pd @@ -11,6 +11,7 @@ from ..common.utils.cozo import uuid_int_list_to_uuid4 P = ParamSpec("P") +T = TypeVar("T") def fix_uuid( @@ -126,7 +127,9 @@ def cozo_query_dec(func: Callable[P, tuple[str | list[str], dict]]): from pprint import pprint @wraps(func) - def wrapper(*args, client=cozo_client, **kwargs) -> pd.DataFrame: + def wrapper( + *args: P.args, client=cozo_client, **kwargs: P.kwargs + ) -> pd.DataFrame: queries, variables = func(*args, **kwargs) if isinstance(queries, str): @@ -173,9 +176,9 @@ def wrap_in_class( one: bool = False, transform: Callable[[dict], dict] | None = None, ): - def decorator(func: Callable[..., pd.DataFrame]): + def decorator(func: Callable[P, pd.DataFrame]): @wraps(func) - def wrapper(*args, **kwargs): + def wrapper(*args: P.args, **kwargs: P.kwargs): df = func(*args, **kwargs) # Convert df to list of dicts @@ -206,13 +209,13 @@ def rewrap_exceptions( ], /, ): - def decorator(func: Callable[..., Any]): + def decorator(func: Callable[P, T]): @wraps(func) - def wrapper(*args, **kwargs): + def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: nonlocal mapping try: - result = func(*args, **kwargs) + result: T = func(*args, **kwargs) except BaseException as error: for check, transform in mapping.items(): diff --git a/agents-api/agents_api/routers/agents/__init__.py b/agents-api/agents_api/routers/agents/__init__.py index 54624e374..2eadecb3d 100644 --- a/agents-api/agents_api/routers/agents/__init__.py +++ b/agents-api/agents_api/routers/agents/__init__.py @@ -1,7 +1,15 @@ -from .create_agent import create_agent # noqa: F401 -from .delete_agent import delete_agent # noqa: F401 -from .get_agent_details import get_agent_details # noqa: F401 -from .list_agents import list_agents # noqa: F401 -from .patch_agent import patch_agent # noqa: F401 -from .router import router # noqa: F401 -from .update_agent import update_agent # noqa: F401 +# ruff: noqa: F401 + +from .create_agent import create_agent +from .create_agent_tool import create_agent_tool +from .create_or_update_agent import create_or_update_agent +from .delete_agent import delete_agent +from .delete_agent_tool import delete_agent_tool +from .get_agent_details import get_agent_details +from .list_agent_tools import list_agent_tools +from .list_agents import list_agents +from .patch_agent import patch_agent +from .patch_agent_tool import patch_agent_tool +from .router import router +from .update_agent import update_agent +from .update_agent_tool import update_agent_tool diff --git a/agents-api/agents_api/routers/agents/create_agent.py b/agents-api/agents_api/routers/agents/create_agent.py index d1b04e1f4..f6e60e728 100644 --- a/agents-api/agents_api/routers/agents/create_agent.py +++ b/agents-api/agents_api/routers/agents/create_agent.py @@ -1,37 +1,27 @@ from typing import Annotated -from uuid import uuid4 from fastapi import Depends from pydantic import UUID4 from starlette.status import HTTP_201_CREATED +import agents_api.models as models + from ...autogen.openapi_model import ( CreateAgentRequest, ResourceCreatedResponse, ) from ...dependencies.developer_id import get_developer_id -from ...models.agent.create_agent import create_agent as create_agent_query from .router import router @router.post("/agents", status_code=HTTP_201_CREATED, tags=["agents"]) async def create_agent( - request: CreateAgentRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + data: CreateAgentRequest, ) -> ResourceCreatedResponse: - new_agent_id = uuid4() - - _, resp = next( - create_agent_query( - developer_id=x_developer_id, - agent_id=new_agent_id, - name=request.name, - about=request.about, - instructions=request.instructions or [], - model=request.model, - default_settings=request.default_settings or {}, - metadata=request.metadata or {}, - ).iterrows() + agent = models.agent.create_agent( + developer_id=x_developer_id, + data=data, ) - return ResourceCreatedResponse(id=new_agent_id, created_at=resp["created_at"]) + return ResourceCreatedResponse(id=agent.id, created_at=agent.created_at) diff --git a/agents-api/agents_api/routers/agents/create_agent_tools.py b/agents-api/agents_api/routers/agents/create_agent_tool.py similarity index 53% rename from agents-api/agents_api/routers/agents/create_agent_tools.py rename to agents-api/agents_api/routers/agents/create_agent_tool.py index cafd6cb07..aaa434743 100644 --- a/agents-api/agents_api/routers/agents/create_agent_tools.py +++ b/agents-api/agents_api/routers/agents/create_agent_tool.py @@ -5,29 +5,26 @@ from pydantic import UUID4 from starlette.status import HTTP_201_CREATED +import agents_api.models as models + from ...autogen.openapi_model import ( CreateToolRequest, ResourceCreatedResponse, ) from ...dependencies.developer_id import get_developer_id -from ...models.tools.create_tools import create_tools as create_tools_query from .router import router @router.post("/agents/{agent_id}/tools", status_code=HTTP_201_CREATED, tags=["agents"]) -async def create_agent_tools( +async def create_agent_tool( agent_id: UUID, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], - data: list[CreateToolRequest], - ignore_existing: bool = False, + data: CreateToolRequest, ) -> ResourceCreatedResponse: - _, resp = next( - create_tools_query( - developer_id=x_developer_id, - agent_id=agent_id, - data=data, - ignore_existing=ignore_existing, - ).iterrows() + tool = models.tools.create_tools( + developer_id=x_developer_id, + agent_id=agent_id, + data=[data], ) - return ResourceCreatedResponse(id=resp["tool_id"], created_at=resp["created_at"]) + return ResourceCreatedResponse(id=tool.id, created_at=tool.created_at) diff --git a/agents-api/agents_api/routers/agents/create_or_update_agent.py b/agents-api/agents_api/routers/agents/create_or_update_agent.py index bcd53f800..0c40c98b8 100644 --- a/agents-api/agents_api/routers/agents/create_or_update_agent.py +++ b/agents-api/agents_api/routers/agents/create_or_update_agent.py @@ -5,29 +5,26 @@ from pydantic import UUID4 from starlette.status import HTTP_201_CREATED +import agents_api.models as models + from ...autogen.openapi_model import ( CreateOrUpdateAgentRequest, ResourceCreatedResponse, ) from ...dependencies.developer_id import get_developer_id -from ...models.agent.create_or_update_agent import ( - create_or_update_agent as create_or_update_agent_query, -) from .router import router @router.post("/agents/{agent_id}", status_code=HTTP_201_CREATED, tags=["agents"]) async def create_or_update_agent( agent_id: UUID, - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], data: CreateOrUpdateAgentRequest, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> ResourceCreatedResponse: - _, resp = next( - create_or_update_agent_query( - developer_id=x_developer_id, - agent_id=agent_id, - data=data, - ).iterrows() + agent = models.agent.create_or_update_agent( + developer_id=x_developer_id, + agent_id=agent_id, + data=data, ) - return ResourceCreatedResponse(id=agent_id, created_at=resp["created_at"]) + return ResourceCreatedResponse(id=agent.id, created_at=agent.created_at) diff --git a/agents-api/agents_api/routers/agents/delete_agent.py b/agents-api/agents_api/routers/agents/delete_agent.py index 8a94dda46..4603c2217 100644 --- a/agents-api/agents_api/routers/agents/delete_agent.py +++ b/agents-api/agents_api/routers/agents/delete_agent.py @@ -1,12 +1,10 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 -from starlette.status import HTTP_202_ACCEPTED, HTTP_404_NOT_FOUND +from starlette.status import HTTP_202_ACCEPTED from ...autogen.openapi_model import ResourceDeletedResponse -from ...common.exceptions.agents import AgentNotFoundError -from ...common.utils.datetime import utcnow from ...dependencies.developer_id import get_developer_id from ...models.agent.delete_agent import delete_agent as delete_agent_query from .router import router @@ -16,8 +14,4 @@ async def delete_agent( agent_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] ) -> ResourceDeletedResponse: - try: - delete_agent_query(x_developer_id, agent_id) - except AgentNotFoundError as e: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail=str(e)) - return ResourceDeletedResponse(id=agent_id, deleted_at=utcnow()) + return delete_agent_query(developer_id=x_developer_id, agent_id=agent_id) diff --git a/agents-api/agents_api/routers/agents/delete_agent_tools.py b/agents-api/agents_api/routers/agents/delete_agent_tool.py similarity index 95% rename from agents-api/agents_api/routers/agents/delete_agent_tools.py rename to agents-api/agents_api/routers/agents/delete_agent_tool.py index a2c5c147b..c220a2cf7 100644 --- a/agents-api/agents_api/routers/agents/delete_agent_tools.py +++ b/agents-api/agents_api/routers/agents/delete_agent_tool.py @@ -11,7 +11,7 @@ @router.delete("/agents/{agent_id}/tools/{tool_id}", tags=["agents"]) -async def delete_agent_tools( +async def delete_agent_tool( agent_id: UUID, tool_id: UUID, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], diff --git a/agents-api/agents_api/routers/agents/get_agent_details.py b/agents-api/agents_api/routers/agents/get_agent_details.py index d9c865cf0..04511527a 100644 --- a/agents-api/agents_api/routers/agents/get_agent_details.py +++ b/agents-api/agents_api/routers/agents/get_agent_details.py @@ -1,11 +1,9 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 -from starlette.status import HTTP_404_NOT_FOUND from ...autogen.openapi_model import Agent -from ...common.exceptions.agents import AgentNotFoundError from ...dependencies.developer_id import get_developer_id from ...models.agent.get_agent import get_agent as get_agent_query from .router import router @@ -16,10 +14,4 @@ async def get_agent_details( agent_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> Agent: - try: - agent = get_agent_query(developer_id=x_developer_id, agent_id=agent_id) - if not agent: - raise AgentNotFoundError(x_developer_id, agent_id) - return Agent(**agent) - except AgentNotFoundError as e: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail=str(e)) + return get_agent_query(developer_id=x_developer_id, agent_id=agent_id) diff --git a/agents-api/agents_api/routers/agents/list_agent_tools.py b/agents-api/agents_api/routers/agents/list_agent_tools.py index d4068d8a2..1b86f227a 100644 --- a/agents-api/agents_api/routers/agents/list_agent_tools.py +++ b/agents-api/agents_api/routers/agents/list_agent_tools.py @@ -4,6 +4,7 @@ from fastapi import Depends from pydantic import UUID4 +from ...autogen.openapi_model import Tool from ...dependencies.developer_id import get_developer_id from ...models.tools.list_tools import list_tools as list_tools_query from .router import router @@ -11,21 +12,18 @@ @router.get("/agents/{agent_id}/tools", tags=["agents"]) async def list_agent_tools( - agent_id: UUID, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + agent_id: UUID, limit: int = 100, offset: int = 0, sort_by: Literal["created_at", "updated_at"] = "created_at", direction: Literal["asc", "desc"] = "desc", -) -> list[tuple[str, dict]]: - return [ - row - for _, row in list_tools_query( - developer_id=x_developer_id, - agent_id=agent_id, - limit=limit, - offset=offset, - sort_by=sort_by, - direction=direction, - ).iterrows() - ] +) -> list[Tool]: + return list_tools_query( + agent_id=agent_id, + developer_id=x_developer_id, + limit=limit, + offset=offset, + sort_by=sort_by, + direction=direction, + ) diff --git a/agents-api/agents_api/routers/agents/list_agents.py b/agents-api/agents_api/routers/agents/list_agents.py index 652a26de7..c7a339bc9 100644 --- a/agents-api/agents_api/routers/agents/list_agents.py +++ b/agents-api/agents_api/routers/agents/list_agents.py @@ -1,11 +1,11 @@ -from typing import Annotated, List +from typing import Annotated, List, Literal from fastapi import Depends from pydantic import UUID4 from ...autogen.openapi_model import Agent from ...dependencies.developer_id import get_developer_id -from ...models.agent.list_agents import list_agents +from ...models.agent.list_agents import list_agents as list_agents_query from .router import router @@ -14,12 +14,17 @@ async def list_agents( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], limit: int = 100, offset: int = 0, + sort_by: Literal["created_at", "updated_at"] = "created_at", + direction: Literal["asc", "desc"] = "desc", metadata_filter: str = "{}", ) -> List[Agent]: - agents = list_agents( + agents = list_agents_query( developer_id=x_developer_id, limit=limit, offset=offset, + sort_by=sort_by, + direction=direction, metadata_filter=metadata_filter, ) - return [Agent(**agent) for agent in agents] + + return agents diff --git a/agents-api/agents_api/routers/agents/patch_agent.py b/agents-api/agents_api/routers/agents/patch_agent.py index 7731dda81..29596447e 100644 --- a/agents-api/agents_api/routers/agents/patch_agent.py +++ b/agents-api/agents_api/routers/agents/patch_agent.py @@ -1,11 +1,10 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 -from starlette.status import HTTP_200_OK, HTTP_404_NOT_FOUND +from starlette.status import HTTP_200_OK from ...autogen.openapi_model import PatchAgentRequest, ResourceUpdatedResponse -from ...common.exceptions.agents import AgentNotFoundError from ...dependencies.developer_id import get_developer_id from ...models.agent.patch_agent import patch_agent as patch_agent_query from .router import router @@ -18,21 +17,12 @@ tags=["agents"], ) async def patch_agent( - agent_id: UUID4, - request: PatchAgentRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + agent_id: UUID4, + data: PatchAgentRequest, ) -> ResourceUpdatedResponse: - try: - updated_agent = patch_agent_query( - agent_id=agent_id, - developer_id=x_developer_id, - default_settings=request.default_settings, - name=request.name, - about=request.about, - model=request.model, - metadata=request.metadata, - instructions=request.instructions, - ) - return ResourceUpdatedResponse(**updated_agent) - except AgentNotFoundError as e: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail=str(e)) + return patch_agent_query( + agent_id=agent_id, + developer_id=x_developer_id, + data=data, + ) diff --git a/agents-api/agents_api/routers/agents/patch_agent_tools.py b/agents-api/agents_api/routers/agents/patch_agent_tool.py similarity index 61% rename from agents-api/agents_api/routers/agents/patch_agent_tools.py rename to agents-api/agents_api/routers/agents/patch_agent_tool.py index ab8e0c206..843fa91eb 100644 --- a/agents-api/agents_api/routers/agents/patch_agent_tools.py +++ b/agents-api/agents_api/routers/agents/patch_agent_tool.py @@ -14,19 +14,15 @@ @router.patch("/agents/{agent_id}/tools/{tool_id}", tags=["agents"]) -async def patch_agent_tools( +async def patch_agent_tool( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], agent_id: UUID, tool_id: UUID, - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], - patch_tool: PatchToolRequest, + data: PatchToolRequest, ) -> ResourceUpdatedResponse: - _, resp = next( - patch_tool_query( - developer_id=x_developer_id, - agent_id=agent_id, - tool_id=tool_id, - patch_tool=patch_tool, - ).iterrows() + return patch_tool_query( + developer_id=x_developer_id, + agent_id=agent_id, + tool_id=tool_id, + data=data, ) - - return ResourceUpdatedResponse(id=resp["tool_id"], updated_at=resp["updated_at"]) diff --git a/agents-api/agents_api/routers/agents/update_agent.py b/agents-api/agents_api/routers/agents/update_agent.py index e37296ef3..865954a6d 100644 --- a/agents-api/agents_api/routers/agents/update_agent.py +++ b/agents-api/agents_api/routers/agents/update_agent.py @@ -1,11 +1,10 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 -from starlette.status import HTTP_200_OK, HTTP_404_NOT_FOUND +from starlette.status import HTTP_200_OK from ...autogen.openapi_model import ResourceUpdatedResponse, UpdateAgentRequest -from ...common.exceptions.agents import AgentNotFoundError from ...dependencies.developer_id import get_developer_id from ...models.agent.update_agent import update_agent as update_agent_query from .router import router @@ -18,23 +17,12 @@ tags=["agents"], ) async def update_agent( - agent_id: UUID4, - request: UpdateAgentRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + agent_id: UUID4, + data: UpdateAgentRequest, ) -> ResourceUpdatedResponse: - try: - _, updated_agent = next( - update_agent_query( - agent_id=agent_id, - developer_id=x_developer_id, - name=request.name, - about=request.about, - model=request.model, - default_settings=request.default_settings, - metadata=request.metadata, - instructions=request.instructions, - ).iterrows() - ) - return ResourceUpdatedResponse(**updated_agent) - except AgentNotFoundError as e: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail=str(e)) + return update_agent_query( + developer_id=x_developer_id, + agent_id=agent_id, + data=data, + ) diff --git a/agents-api/agents_api/routers/agents/update_agent_tools.py b/agents-api/agents_api/routers/agents/update_agent_tool.py similarity index 65% rename from agents-api/agents_api/routers/agents/update_agent_tools.py rename to agents-api/agents_api/routers/agents/update_agent_tool.py index ac51afd16..60e38ad76 100644 --- a/agents-api/agents_api/routers/agents/update_agent_tools.py +++ b/agents-api/agents_api/routers/agents/update_agent_tool.py @@ -14,19 +14,15 @@ @router.put("/agents/{agent_id}/tools/{tool_id}", tags=["agents"]) -async def update_agent_tools( +async def update_agent_tool( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], agent_id: UUID, tool_id: UUID, - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], data: UpdateToolRequest, ) -> ResourceUpdatedResponse: - _, resp = next( - update_tool_query( - developer_id=x_developer_id, - agent_id=agent_id, - tool_id=tool_id, - data=data, - ).iterrows() + return update_tool_query( + developer_id=x_developer_id, + agent_id=agent_id, + tool_id=tool_id, + data=data, ) - - return ResourceUpdatedResponse(id=resp["tool_id"], updated_at=resp["updated_at"]) From 65a0b991b969355676cbb67b85e6558c06ed72ef Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 4 Aug 2024 18:20:52 -0400 Subject: [PATCH 003/110] wip(agents-api): Session routes Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Chat.py | 2 +- agents-api/agents_api/autogen/Entries.py | 2 +- .../agents_api/autogen/openapi_model.py | 9 +- .../routers/agents/list_agent_tools.py | 8 +- .../agents_api/routers/agents/list_agents.py | 8 +- .../agents_api/routers/sessions/__init__.py | 17 +- .../sessions/create_or_update_session.py | 34 ++ .../routers/sessions/create_session.py | 20 +- .../routers/sessions/delete_session.py | 13 +- .../routers/sessions/get_session.py | 16 +- .../routers/sessions/list_sessions.py | 26 +- .../routers/sessions/patch_session.py | 37 +- .../agents_api/routers/sessions/protocol.py | 36 -- .../agents_api/routers/sessions/session.py | 550 ------------------ .../routers/sessions/update_session.py | 37 +- agents-api/poetry.lock | 199 ++++--- .../api/types/agents_route_list_response.py | 2 +- .../api/types/sessions_route_list_response.py | 2 +- .../api/types/users_route_list_response.py | 2 +- sdks/python/poetry.lock | 199 ++++--- sdks/ts/src/api/services/DefaultService.ts | 6 +- typespec/common/interfaces.tsp | 2 +- 22 files changed, 328 insertions(+), 899 deletions(-) create mode 100644 agents-api/agents_api/routers/sessions/create_or_update_session.py delete mode 100644 agents-api/agents_api/routers/sessions/protocol.py delete mode 100644 agents-api/agents_api/routers/sessions/session.py diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index c7c25c7ad..6669fbcec 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel from .Docs import DocReference from .Entries import ChatMLMessage diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index 6c8bf9ca4..d56b333d8 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, RootModel from .Tools import ChosenToolCall, Tool, ToolResponse diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index 3d5d3afaa..362384251 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -1,5 +1,5 @@ # ruff: noqa: F401, F403, F405 -from typing import Annotated +from typing import Annotated, Generic, TypeVar from uuid import UUID from pydantic import AwareDatetime, Field @@ -158,3 +158,10 @@ class UpdateTaskRequest(_UpdateTaskRequest): "extra": "allow", } ) + + +DataT = TypeVar("DataT", bound=BaseModel) + + +class ListResponse(BaseModel, Generic[DataT]): + items: list[DataT] diff --git a/agents-api/agents_api/routers/agents/list_agent_tools.py b/agents-api/agents_api/routers/agents/list_agent_tools.py index 1b86f227a..dff135920 100644 --- a/agents-api/agents_api/routers/agents/list_agent_tools.py +++ b/agents-api/agents_api/routers/agents/list_agent_tools.py @@ -4,7 +4,7 @@ from fastapi import Depends from pydantic import UUID4 -from ...autogen.openapi_model import Tool +from ...autogen.openapi_model import ListResponse, Tool from ...dependencies.developer_id import get_developer_id from ...models.tools.list_tools import list_tools as list_tools_query from .router import router @@ -18,8 +18,8 @@ async def list_agent_tools( offset: int = 0, sort_by: Literal["created_at", "updated_at"] = "created_at", direction: Literal["asc", "desc"] = "desc", -) -> list[Tool]: - return list_tools_query( +) -> ListResponse[Tool]: + tools = list_tools_query( agent_id=agent_id, developer_id=x_developer_id, limit=limit, @@ -27,3 +27,5 @@ async def list_agent_tools( sort_by=sort_by, direction=direction, ) + + return ListResponse[Tool](items=tools) diff --git a/agents-api/agents_api/routers/agents/list_agents.py b/agents-api/agents_api/routers/agents/list_agents.py index c7a339bc9..f894d9c36 100644 --- a/agents-api/agents_api/routers/agents/list_agents.py +++ b/agents-api/agents_api/routers/agents/list_agents.py @@ -1,9 +1,9 @@ -from typing import Annotated, List, Literal +from typing import Annotated, Literal from fastapi import Depends from pydantic import UUID4 -from ...autogen.openapi_model import Agent +from ...autogen.openapi_model import Agent, ListResponse from ...dependencies.developer_id import get_developer_id from ...models.agent.list_agents import list_agents as list_agents_query from .router import router @@ -17,7 +17,7 @@ async def list_agents( sort_by: Literal["created_at", "updated_at"] = "created_at", direction: Literal["asc", "desc"] = "desc", metadata_filter: str = "{}", -) -> List[Agent]: +) -> ListResponse[Agent]: agents = list_agents_query( developer_id=x_developer_id, limit=limit, @@ -27,4 +27,4 @@ async def list_agents( metadata_filter=metadata_filter, ) - return agents + return ListResponse[Agent](items=agents) diff --git a/agents-api/agents_api/routers/sessions/__init__.py b/agents-api/agents_api/routers/sessions/__init__.py index 3cea3eb2d..24ae2d9e8 100644 --- a/agents-api/agents_api/routers/sessions/__init__.py +++ b/agents-api/agents_api/routers/sessions/__init__.py @@ -1,7 +1,10 @@ -from .create_session import create_session # noqa: F401 -from .delete_session import delete_session # noqa: F401 -from .get_session import get_session # noqa: F401 -from .list_sessions import list_sessions # noqa: F401 -from .patch_session import patch_session # noqa: F401 -from .router import router # noqa: F401 -from .update_session import update_session # noqa: F401 +# ruff: noqa: F401 + +from .create_or_update_session import create_or_update_session +from .create_session import create_session +from .delete_session import delete_session +from .get_session import get_session +from .list_sessions import list_sessions +from .patch_session import patch_session +from .router import router +from .update_session import update_session diff --git a/agents-api/agents_api/routers/sessions/create_or_update_session.py b/agents-api/agents_api/routers/sessions/create_or_update_session.py new file mode 100644 index 000000000..8ed9ff7ba --- /dev/null +++ b/agents-api/agents_api/routers/sessions/create_or_update_session.py @@ -0,0 +1,34 @@ +from typing import Annotated +from uuid import UUID + +from fastapi import Depends +from pydantic import UUID4 +from starlette.status import HTTP_201_CREATED + +from ...autogen.openapi_model import ( + CreateOrUpdateSessionRequest, + ResourceCreatedResponse, +) +from ...dependencies.developer_id import get_developer_id +from ...models.session.create_or_update_session import ( + create_or_update_session as create_session_query, +) +from .router import router + + +@router.post("/sessions/{session_id}", status_code=HTTP_201_CREATED, tags=["sessions"]) +async def create_or_update_session( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + session_id: UUID, + data: CreateOrUpdateSessionRequest, +) -> ResourceCreatedResponse: + session = create_session_query( + developer_id=x_developer_id, + session_id=session_id, + data=data, + ) + + return ResourceCreatedResponse( + id=session.id, + created_at=session.created_at, + ) diff --git a/agents-api/agents_api/routers/sessions/create_session.py b/agents-api/agents_api/routers/sessions/create_session.py index 558a4ae22..cf4ba119d 100644 --- a/agents-api/agents_api/routers/sessions/create_session.py +++ b/agents-api/agents_api/routers/sessions/create_session.py @@ -1,7 +1,5 @@ from typing import Annotated -from uuid import uuid4 -import pandas as pd from fastapi import Depends from pydantic import UUID4 from starlette.status import HTTP_201_CREATED @@ -17,23 +15,15 @@ @router.post("/sessions", status_code=HTTP_201_CREATED, tags=["sessions"]) async def create_session( - request: CreateSessionRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + data: CreateSessionRequest, ) -> ResourceCreatedResponse: - session_id = uuid4() - resp: pd.DataFrame = create_session_query( - session_id=session_id, + session = create_session_query( developer_id=x_developer_id, - agent_id=request.agent_id, - user_id=request.user_id, - situation=request.situation, - metadata=request.metadata or {}, - render_templates=request.render_templates or False, - token_budget=request.token_budget, - context_overflow=request.context_overflow, + data=data, ) return ResourceCreatedResponse( - id=resp["session_id"][0], - created_at=resp["created_at"][0], + id=session.id, + created_at=session.created_at, ) diff --git a/agents-api/agents_api/routers/sessions/delete_session.py b/agents-api/agents_api/routers/sessions/delete_session.py index 969363d70..9645eb8de 100644 --- a/agents-api/agents_api/routers/sessions/delete_session.py +++ b/agents-api/agents_api/routers/sessions/delete_session.py @@ -1,12 +1,10 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 -from starlette.status import HTTP_202_ACCEPTED, HTTP_404_NOT_FOUND +from starlette.status import HTTP_202_ACCEPTED from ...autogen.openapi_model import ResourceDeletedResponse -from ...common.exceptions.sessions import SessionNotFoundError -from ...common.utils.datetime import utcnow from ...dependencies.developer_id import get_developer_id from ...models.session.delete_session import delete_session as delete_session_query from .router import router @@ -18,9 +16,4 @@ async def delete_session( session_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] ) -> ResourceDeletedResponse: - try: - delete_session_query(x_developer_id, session_id) - except SessionNotFoundError as e: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail=str(e)) - - return ResourceDeletedResponse(id=session_id, deleted_at=utcnow()) + return delete_session_query(developer_id=x_developer_id, session_id=session_id) diff --git a/agents-api/agents_api/routers/sessions/get_session.py b/agents-api/agents_api/routers/sessions/get_session.py index 5aed042dc..c357394fa 100644 --- a/agents-api/agents_api/routers/sessions/get_session.py +++ b/agents-api/agents_api/routers/sessions/get_session.py @@ -1,6 +1,6 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 from ...autogen.openapi_model import Session @@ -13,16 +13,4 @@ async def get_session( session_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] ) -> Session: - try: - res = [ - row.to_dict() - for _, row in get_session_query( - developer_id=x_developer_id, session_id=session_id - ).iterrows() - ][0] - return Session(**res) - except (IndexError, KeyError): - raise HTTPException( - status_code=404, - detail="Session not found", - ) + return get_session_query(developer_id=x_developer_id, session_id=session_id) diff --git a/agents-api/agents_api/routers/sessions/list_sessions.py b/agents-api/agents_api/routers/sessions/list_sessions.py index e93c22228..bf5458887 100644 --- a/agents-api/agents_api/routers/sessions/list_sessions.py +++ b/agents-api/agents_api/routers/sessions/list_sessions.py @@ -1,27 +1,25 @@ import json from json import JSONDecodeError -from typing import Annotated +from typing import Annotated, Literal from fastapi import Depends, HTTPException, status -from pydantic import UUID4, BaseModel +from pydantic import UUID4 -from ...autogen.openapi_model import Session +from ...autogen.openapi_model import ListResponse, Session from ...dependencies.developer_id import get_developer_id -from ...models.session.list_sessions import list_sessions +from ...models.session.list_sessions import list_sessions as list_sessions_query from .router import router -class SessionList(BaseModel): - items: list[Session] - - @router.get("/sessions", tags=["sessions"]) -async def list_sessions_route( +async def list_sessions( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], limit: int = 100, offset: int = 0, + sort_by: Literal["created_at", "updated_at"] = "created_at", + direction: Literal["asc", "desc"] = "desc", metadata_filter: str = "{}", -) -> SessionList: +) -> ListResponse[Session]: try: metadata_filter = json.loads(metadata_filter) except JSONDecodeError: @@ -30,13 +28,13 @@ async def list_sessions_route( detail="metadata_filter is not a valid JSON", ) - query_results = list_sessions( + sessions = list_sessions_query( developer_id=x_developer_id, limit=limit, offset=offset, + sort_by=sort_by, + direction=direction, metadata_filter=metadata_filter, ) - return SessionList( - items=[Session(**row.to_dict()) for _, row in query_results.iterrows()] - ) + return ListResponse[Session](items=sessions) diff --git a/agents-api/agents_api/routers/sessions/patch_session.py b/agents-api/agents_api/routers/sessions/patch_session.py index 992b272a8..365fa49ca 100644 --- a/agents-api/agents_api/routers/sessions/patch_session.py +++ b/agents-api/agents_api/routers/sessions/patch_session.py @@ -1,14 +1,12 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 -from starlette.status import HTTP_404_NOT_FOUND from ...autogen.openapi_model import ( PatchSessionRequest, ResourceUpdatedResponse, ) -from ...common.exceptions.sessions import SessionNotFoundError from ...dependencies.developer_id import get_developer_id from ...models.session.patch_session import patch_session as patch_session_query from .router import router @@ -16,31 +14,12 @@ @router.patch("/sessions/{session_id}", tags=["sessions"]) async def patch_session( - session_id: UUID4, - request: PatchSessionRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + session_id: UUID4, + data: PatchSessionRequest, ) -> ResourceUpdatedResponse: - try: - resp = patch_session_query( - session_id=session_id, - developer_id=x_developer_id, - situation=request.situation, - metadata=request.metadata, - token_budget=request.token_budget, - context_overflow=request.context_overflow, - ) - - return ResourceUpdatedResponse( - id=resp["session_id"][0], - updated_at=resp["updated_at"][0][0], - ) - except (IndexError, KeyError): - raise HTTPException( - status_code=HTTP_404_NOT_FOUND, - detail="Session not found", - ) - except SessionNotFoundError as e: - raise HTTPException( - status_code=HTTP_404_NOT_FOUND, - detail=str(e), - ) + return patch_session_query( + developer_id=x_developer_id, + session_id=session_id, + data=data, + ) diff --git a/agents-api/agents_api/routers/sessions/protocol.py b/agents-api/agents_api/routers/sessions/protocol.py deleted file mode 100644 index 6502e98a2..000000000 --- a/agents-api/agents_api/routers/sessions/protocol.py +++ /dev/null @@ -1,36 +0,0 @@ -from pydantic import BaseModel, ConfigDict, Field, field_validator - -from agents_api.autogen.openapi_model import Preset, ResponseFormat, Tool - - -class Settings(BaseModel): - model_config = ConfigDict(validate_assignment=True) - - model: str - frequency_penalty: float | None = Field(default=0) - length_penalty: float | None = Field(default=1.0) - logit_bias: float | None = None - max_tokens: int | None = Field(default=200) - presence_penalty: float | None = Field(default=0) - repetition_penalty: float | None = Field(default=1) - response_format: ResponseFormat | None - seed: int | None = Field(default=0) - stop: list[str] | None = None - stream: bool | None = Field(default=False) - temperature: float | None = Field(default=0.7) - top_p: float | None = Field(default=1) - remember: bool | None = Field(default=True) - recall: bool | None = Field(default=True) - min_p: float | None = Field(default=0.01) - preset: Preset | None = Field(default=None) - tools: list[Tool] | None = Field(default=None) - token_budget: int | None = Field(default=None) - context_overflow: str | None = Field(default=None) - - @field_validator("max_tokens") - def set_max_tokens(cls, max_tokens): - return max_tokens if max_tokens is not None else 200 - - @field_validator("stream") - def set_stream(cls, stream): - return stream or False diff --git a/agents-api/agents_api/routers/sessions/session.py b/agents-api/agents_api/routers/sessions/session.py deleted file mode 100644 index 5784f40af..000000000 --- a/agents-api/agents_api/routers/sessions/session.py +++ /dev/null @@ -1,550 +0,0 @@ -import json -from dataclasses import dataclass -from functools import partial, reduce -from json import JSONDecodeError -from typing import Callable -from uuid import uuid4 - -import litellm -import xxhash -from litellm import acompletion -from openai.types.chat.chat_completion import ChatCompletion -from pydantic import UUID4 - -from ...autogen.openapi_model import ( - CreateEntryRequest, - DocIds, - InputChatMLMessage, - Tool, -) -from ...clients.embed import embed -from ...clients.temporal import run_summarization_task, run_truncation_task -from ...clients.worker.types import ChatML -from ...common.exceptions.sessions import SessionNotFoundError -from ...common.protocol.entries import Entry -from ...common.protocol.sessions import SessionData -from ...common.utils.json import CustomJSONEncoder -from ...common.utils.messages import stringify_content -from ...common.utils.template import render_template -from ...env import ( - embedding_model_id, - embedding_service_url, - model_api_key, - model_inference_url, -) -from ...exceptions import PromptTooBigError -from ...model_registry import ( - LOCAL_MODELS, - LOCAL_MODELS_WITH_TOOL_CALLS, - OLLAMA_MODELS, - get_extra_settings, - load_context, - validate_and_extract_tool_calls, -) -from ...models.entry.create_entries import create_entries -from ...models.session.get_cached_response import get_cached_response -from ...models.session.prepare_session_data import prepare_session_data -from ...models.session.set_cached_response import set_cached_response -from .exceptions import InputTooBigError -from .protocol import Settings - -THOUGHTS_STRIP_LEN = 2 -MESSAGES_STRIP_LEN = 4 - - -tool_query_instruction = ( - "Transform this user request for fetching helpful tool descriptions: " -) -instruction_query_instruction = ( - "Embed this text chunk for finding useful historical chunks: " -) -doc_query_instruction = ( - "Encode this query and context for searching relevant passages: " -) - - -def cache(f): - async def wrapper(init_context: list[ChatML], settings: Settings) -> ChatCompletion: - key = xxhash.xxh64( - json.dumps( - { - "init_context": [c.model_dump() for c in init_context], - "settings": settings.model_dump(), - }, - cls=CustomJSONEncoder, - default_empty_value="", - ) - ).hexdigest() - result = get_cached_response(key=key) - if not result.size: - resp = await f(init_context, settings) - set_cached_response(key=key, value=resp.model_dump()) - return resp - choices = result.iloc[0].to_dict()["value"] - return ChatCompletion(**choices) - - return wrapper - - -# FIXME: Refactor llm_generate and cache for use inside tasks as well -# - these should probably be moved to a separate module -@cache -async def llm_generate( - init_context: list[ChatML], settings: Settings -) -> ChatCompletion: - init_context = load_context(init_context, settings.model) - tools = None - api_base = None - api_key = None - model = settings.model - if model in [*LOCAL_MODELS.keys(), *LOCAL_MODELS_WITH_TOOL_CALLS.keys()]: - api_base = model_inference_url - api_key = model_api_key - model = f"openai/{model}" - if model in OLLAMA_MODELS: - model = f"ollama/{model}" - - if settings.tools: - tools = [(tool.model_dump(exclude="id")) for tool in settings.tools] - - extra_body = get_extra_settings(settings) - - litellm.drop_params = True - litellm.add_function_to_prompt = True - - res = await acompletion( - model=model, - messages=init_context, - max_tokens=settings.max_tokens, - stop=settings.stop, - temperature=settings.temperature, - frequency_penalty=settings.frequency_penalty, - top_p=settings.top_p, - presence_penalty=settings.presence_penalty, - stream=settings.stream, - tools=tools, - response_format=settings.response_format, - api_base=api_base, - api_key=api_key, - **extra_body, - ) - - return res - - -@dataclass -class BaseSession: - session_id: UUID4 - developer_id: UUID4 - - def _remove_messages( - self, - messages: list[Entry], - start_idx: int | None, - end_idx: int | None, - token_count: int, - summarization_tokens_threshold: int, - predicate: Callable[[Entry], bool], - ) -> tuple[list[Entry], int]: - if len(messages) < abs((end_idx or len(messages)) - (start_idx or 0)): - return messages, token_count - - result: list[Entry] = messages[: start_idx or 0] - skip_check = False - for m in messages[start_idx:end_idx]: - if predicate(m) and not skip_check: - token_count -= m.token_count - if token_count <= summarization_tokens_threshold: - skip_check = True - - continue - - result.append(m) - - if end_idx is not None: - result += messages[end_idx:] - - return result, token_count - - def _truncate_context( - self, messages: list[Entry], summarization_tokens_threshold: int | None - ) -> list[Entry]: - def rm_thoughts(m): - return m.role == "system" and m.name == "thought" - - def rm_user_assistant(m): - return m.role in ("user", "assistant") - - if summarization_tokens_threshold is None: - return messages - - token_count = reduce(lambda c, e: (e.token_count or 0) + c, messages, 0) - - if token_count <= summarization_tokens_threshold: - return messages - - for start_idx, end_idx, cond in [ - (THOUGHTS_STRIP_LEN, -THOUGHTS_STRIP_LEN, rm_thoughts), - (None, None, rm_thoughts), - (MESSAGES_STRIP_LEN, -MESSAGES_STRIP_LEN, rm_user_assistant), - ]: - messages, token_count = self._remove_messages( - messages, - start_idx, - end_idx, - token_count, - summarization_tokens_threshold, - cond, - ) - - if token_count <= summarization_tokens_threshold and messages: - return messages - - # TODO: - # Compress info sections using LLM Lingua - # - If more space is still needed, remove info sections iteratively - - raise InputTooBigError(token_count, summarization_tokens_threshold) - - async def run( - self, new_input, settings: Settings - ) -> tuple[ChatCompletion, Entry, Callable | None, DocIds]: - # TODO: implement locking at some point - - # Get session data - session_data = prepare_session_data( - developer_id=self.developer_id, session_id=self.session_id - ) - if session_data is None: - raise SessionNotFoundError(self.developer_id, self.session_id) - - # Assemble context - init_context, final_settings, doc_ids = await self.forward( - session_data, new_input, settings - ) - - # Generate response - response = await self.generate( - self._truncate_context(init_context, final_settings.token_budget), - final_settings, - ) - - # Save response to session - # if final_settings.get("remember"): - # await self.add_to_session(new_input, response) - - # FIXME: Implement support for multiple choices, will need a revisit to the schema - message = response.choices[0].message - role = message.role - content = message.content - - # FIXME: Implement support for multiple tool calls - - # Unpack tool calls if present - # TODO: implement changes in the openapi spec - # Currently our function_call does the same job as openai's function role - # Need to add a new role for openai's paradigm of shoving function selected into assistant's context - # Ref: https://github.com/openai/openai-cookbook/blob/main/examples/How_to_call_functions_with_chat_models.ipynb - if not message.content and message.tool_calls: - role = "function_call" - content = message.tool_calls[0].function.model_dump_json() - - elif not message.content: - raise ValueError("No content in response") - - total_tokens = response.usage.total_tokens - completion_tokens = response.usage.completion_tokens - new_entry = Entry( - session_id=self.session_id, - role=role, - name=None if session_data is None else session_data.agent_name, - content=content, - token_count=completion_tokens, - ) - - # Return response and the backward pass as a background task (dont await here) - backward_pass = await self.backward( - new_input, total_tokens, new_entry, final_settings - ) - - return response, new_entry, backward_pass, doc_ids - - async def forward( - self, - session_data: SessionData | None, - new_input: list[Entry], - settings: Settings, - ) -> tuple[list[ChatML], Settings, DocIds]: - if session_data is not None: - settings.token_budget = session_data.token_budget - settings.context_overflow = session_data.context_overflow - - stringified_input = [] - for msg in new_input: - stringified_input.append( - ( - msg.role, - msg.name, - stringify_content(msg.content), - ) - ) - - # role, name, content, token_count, created_at - string_to_embed = "\n".join( - [ - f"{name or role}: {content}" - for (role, name, content) in stringified_input - if content - ] - ) - - # FIXME: bge-m3 does not require instructions - ( - tool_query_embedding, - doc_query_embedding, - ) = await embed( - [ - instruction + string_to_embed - for instruction in [ - tool_query_instruction, - doc_query_instruction, - ] - ], - join_inputs=False, - embedding_service_url=embedding_service_url, - embedding_model_name=embedding_model_id, - ) - - entries: list[Entry] = [] - instructions = "Instructions:\n\n" - first_instruction_idx = -1 - first_instruction_created_at = 0 - tools = [] - doc_ids = DocIds(agent_doc_ids=[], user_doc_ids=[]) - - for idx, row in proc_mem_context_query( - session_id=self.session_id, - tool_query_embedding=tool_query_embedding, - doc_query_embedding=doc_query_embedding, - ).iterrows(): - agent_doc_id = row.get("agent_doc_id") - user_doc_id = row.get("user_doc_id") - - if agent_doc_id is not None: - doc_ids.agent_doc_ids.append(agent_doc_id) - - if user_doc_id is not None: - doc_ids.user_doc_ids.append(user_doc_id) - - # If a `functions` message is encountered, extract into tools list - if row["name"] == "functions": - # FIXME: This might also break if {role: system, name: functions, content} but content not valid json object - try: - # FIXME: This is a hack for now, need to fix to support multiple function calls - assert ( - len(row["content"]) == 1 - ), "Only one function can be called at a time" - content = row["content"][0]["text"] - saved_function = json.loads(content) - except JSONDecodeError as e: - # FIXME: raise a proper error that can be caught by the router - raise ValueError(str(e)) - - tool = Tool(type="function", function=saved_function, id=str(uuid4())) - tools.append(tool) - - continue - - # If `instruction` encoountered, extract and compile together (because of a quirk in how cozo queries work) - if row["name"] == "instruction": - if first_instruction_idx < 0: - first_instruction_idx = idx - first_instruction_created_at = row["created_at"] - - instructions += f"{row['content'][0]['text']}" + "\n\n" - - continue - - # Else add to entries as is - entries.append( - Entry( - role=row["role"], - name=row["name"], - content=row["content"], - session_id=self.session_id, - created_at=row["created_at"], - ) - ) - - # If any instructions were found, add them as info block - if first_instruction_idx >= 0: - entries.insert( - first_instruction_idx, - Entry( - role="system", - name="information", - content=instructions, - session_id=self.session_id, - created_at=first_instruction_created_at, - ), - ) - - messages = [ - ChatML( - role=e.role.value if hasattr(e.role, "value") else e.role, - name=e.name, - content=e.content, - ) - for e in entries + new_input - if e.content - ] - - # Simplify messages if possible - for message in messages: - if ( - isinstance(message.content, list) - and len(message.content) == 1 - and message.content[0].type == "text" - ): - message.content = message.content[0].text - # Add tools to settings - if tools: - settings.tools = settings.tools or [] - settings.tools.extend(tools) - # If render_templates=True, render the templates - if session_data is not None and session_data.render_templates: - template_data = { - "session": { - "id": session_data.session_id, - "situation": session_data.situation, - "metadata": session_data.metadata, - }, - "user": { - "id": session_data.user_id, - "name": session_data.user_name, - "about": session_data.user_about, - "metadata": session_data.user_metadata, - }, - "agent": { - "id": session_data.agent_id, - "name": session_data.agent_name, - "about": session_data.agent_about, - "metadata": session_data.agent_metadata, - "tools": settings.tools, - }, - } - - for i, msg in enumerate(messages): - # Only render templates for system/assistant messages - if msg.role not in ["system", "assistant"]: - continue - - messages[i].content = await render_template(msg.content, template_data) - - # FIXME: This sometimes returns "The model `` does not exist." - if session_data is not None: - settings.model = session_data.model - - return messages, settings, doc_ids - - async def generate( - self, init_context: list[ChatML], settings: Settings - ) -> ChatCompletion: - # return await llm_generate(init_context, settings) - - init_context = load_context(init_context, settings.model) - tools = None - api_base = None - api_key = None - model = settings.model - if model in LOCAL_MODELS: - api_base = model_inference_url - api_key = model_api_key - model = f"openai/{model}" - - if settings.tools: - tools = [(tool.model_dump(exclude="id")) for tool in settings.tools] - - litellm.drop_params = True - litellm.add_function_to_prompt = True - res = await acompletion( - model=model, - messages=init_context, - max_tokens=settings.max_tokens, - stop=settings.stop, - temperature=settings.temperature, - frequency_penalty=settings.frequency_penalty, - top_p=settings.top_p, - presence_penalty=settings.presence_penalty, - stream=settings.stream, - tools=tools, - response_format=settings.response_format, - api_base=api_base, - api_key=api_key, - ) - if model in LOCAL_MODELS_WITH_TOOL_CALLS: - validation, tool_call, error_msg = validate_and_extract_tool_calls( - res.choices[0].message.content - ) - if validation: - res.choices[0].message.role = ( - "function_call" if tool_call else "assistant" - ) - res.choices[0].finish_reason = "tool_calls" - res.choices[0].message.tool_calls = tool_call - res.choices[0].message.content = json.dumps(tool_call) - return res - - async def backward( - self, - new_input: list[InputChatMLMessage], - total_tokens: int, - new_entry: Entry, - final_settings: Settings, - ) -> Callable | None: - if not final_settings.remember: - return - - entries: list[Entry] = [] - for m in new_input: - entries.append( - CreateEntryRequest( - role=m.role, - content=m.content, - name=m.name, - ) - ) - - entries.append( - CreateEntryRequest( - role=new_entry.role, - content=new_entry.content, - name=new_entry.name, - ) - ) - bg_task = None - - if ( - final_settings.token_budget is not None - and total_tokens >= final_settings.token_budget - ): - if final_settings.context_overflow == "truncate": - bg_task = partial(run_truncation_task, final_settings.token_budget) - elif final_settings.context_overflow == "adaptive": - bg_task = run_summarization_task - else: - raise PromptTooBigError(total_tokens, final_settings.token_budget) - - create_entries( - developer_id=self.developer_id, session_id=self.session_id, data=entries - ) - - return bg_task - - -class PlainCompletionSession(BaseSession): - pass - - -class RecursiveSummarizationSession(PlainCompletionSession): - pass diff --git a/agents-api/agents_api/routers/sessions/update_session.py b/agents-api/agents_api/routers/sessions/update_session.py index 1f2658525..8d7c28b33 100644 --- a/agents-api/agents_api/routers/sessions/update_session.py +++ b/agents-api/agents_api/routers/sessions/update_session.py @@ -1,14 +1,12 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 -from starlette.status import HTTP_404_NOT_FOUND from ...autogen.openapi_model import ( ResourceUpdatedResponse, UpdateSessionRequest, ) -from ...common.exceptions.sessions import SessionNotFoundError from ...dependencies.developer_id import get_developer_id from ...models.session.update_session import update_session as update_session_query from .router import router @@ -16,31 +14,12 @@ @router.put("/sessions/{session_id}", tags=["sessions"]) async def update_session( - session_id: UUID4, - request: UpdateSessionRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + session_id: UUID4, + data: UpdateSessionRequest, ) -> ResourceUpdatedResponse: - try: - resp = update_session_query( - session_id=session_id, - developer_id=x_developer_id, - situation=request.situation, - metadata=request.metadata, - token_budget=request.token_budget, - context_overflow=request.context_overflow, - ) - - return ResourceUpdatedResponse( - id=resp["session_id"][0], - updated_at=resp["updated_at"][0][0], - ) - except (IndexError, KeyError): - raise HTTPException( - status_code=HTTP_404_NOT_FOUND, - detail="Session not found", - ) - except SessionNotFoundError as e: - raise HTTPException( - status_code=HTTP_404_NOT_FOUND, - detail=str(e), - ) + return update_session_query( + developer_id=x_developer_id, + session_id=session_id, + data=data, + ) diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index cd7983feb..203e8b1ee 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -4127,99 +4127,120 @@ files = [ [[package]] name = "pyzmq" -version = "26.0.3" +version = "26.1.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:44dd6fc3034f1eaa72ece33588867df9e006a7303725a12d64c3dff92330f625"}, - {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:acb704195a71ac5ea5ecf2811c9ee19ecdc62b91878528302dd0be1b9451cc90"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbb9c997932473a27afa93954bb77a9f9b786b4ccf718d903f35da3232317de"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bcb34f869d431799c3ee7d516554797f7760cb2198ecaa89c3f176f72d062be"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ece17ec5f20d7d9b442e5174ae9f020365d01ba7c112205a4d59cf19dc38ee"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ba6e5e6588e49139a0979d03a7deb9c734bde647b9a8808f26acf9c547cab1bf"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3bf8b000a4e2967e6dfdd8656cd0757d18c7e5ce3d16339e550bd462f4857e59"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2136f64fbb86451dbbf70223635a468272dd20075f988a102bf8a3f194a411dc"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e8918973fbd34e7814f59143c5f600ecd38b8038161239fd1a3d33d5817a38b8"}, - {file = "pyzmq-26.0.3-cp310-cp310-win32.whl", hash = "sha256:0aaf982e68a7ac284377d051c742610220fd06d330dcd4c4dbb4cdd77c22a537"}, - {file = "pyzmq-26.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f1a9b7d00fdf60b4039f4455afd031fe85ee8305b019334b72dcf73c567edc47"}, - {file = "pyzmq-26.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:80b12f25d805a919d53efc0a5ad7c0c0326f13b4eae981a5d7b7cc343318ebb7"}, - {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:a72a84570f84c374b4c287183debc776dc319d3e8ce6b6a0041ce2e400de3f32"}, - {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ca684ee649b55fd8f378127ac8462fb6c85f251c2fb027eb3c887e8ee347bcd"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e222562dc0f38571c8b1ffdae9d7adb866363134299264a1958d077800b193b7"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f17cde1db0754c35a91ac00b22b25c11da6eec5746431d6e5092f0cd31a3fea9"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7c0c0b3244bb2275abe255d4a30c050d541c6cb18b870975553f1fb6f37527"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac97a21de3712afe6a6c071abfad40a6224fd14fa6ff0ff8d0c6e6cd4e2f807a"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88b88282e55fa39dd556d7fc04160bcf39dea015f78e0cecec8ff4f06c1fc2b5"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:72b67f966b57dbd18dcc7efbc1c7fc9f5f983e572db1877081f075004614fcdd"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4b6cecbbf3b7380f3b61de3a7b93cb721125dc125c854c14ddc91225ba52f83"}, - {file = "pyzmq-26.0.3-cp311-cp311-win32.whl", hash = "sha256:eed56b6a39216d31ff8cd2f1d048b5bf1700e4b32a01b14379c3b6dde9ce3aa3"}, - {file = "pyzmq-26.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:3191d312c73e3cfd0f0afdf51df8405aafeb0bad71e7ed8f68b24b63c4f36500"}, - {file = "pyzmq-26.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:b6907da3017ef55139cf0e417c5123a84c7332520e73a6902ff1f79046cd3b94"}, - {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:068ca17214038ae986d68f4a7021f97e187ed278ab6dccb79f837d765a54d753"}, - {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7821d44fe07335bea256b9f1f41474a642ca55fa671dfd9f00af8d68a920c2d4"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb438a26d87c123bb318e5f2b3d86a36060b01f22fbdffd8cf247d52f7c9a2b"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69ea9d6d9baa25a4dc9cef5e2b77b8537827b122214f210dd925132e34ae9b12"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7daa3e1369355766dea11f1d8ef829905c3b9da886ea3152788dc25ee6079e02"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6ca7a9a06b52d0e38ccf6bca1aeff7be178917893f3883f37b75589d42c4ac20"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1b7d0e124948daa4d9686d421ef5087c0516bc6179fdcf8828b8444f8e461a77"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e746524418b70f38550f2190eeee834db8850088c834d4c8406fbb9bc1ae10b2"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6b3146f9ae6af82c47a5282ac8803523d381b3b21caeae0327ed2f7ecb718798"}, - {file = "pyzmq-26.0.3-cp312-cp312-win32.whl", hash = "sha256:2b291d1230845871c00c8462c50565a9cd6026fe1228e77ca934470bb7d70ea0"}, - {file = "pyzmq-26.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:926838a535c2c1ea21c903f909a9a54e675c2126728c21381a94ddf37c3cbddf"}, - {file = "pyzmq-26.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:5bf6c237f8c681dfb91b17f8435b2735951f0d1fad10cc5dfd96db110243370b"}, - {file = "pyzmq-26.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c0991f5a96a8e620f7691e61178cd8f457b49e17b7d9cfa2067e2a0a89fc1d5"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dbf012d8fcb9f2cf0643b65df3b355fdd74fc0035d70bb5c845e9e30a3a4654b"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:01fbfbeb8249a68d257f601deb50c70c929dc2dfe683b754659569e502fbd3aa"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c8eb19abe87029c18f226d42b8a2c9efdd139d08f8bf6e085dd9075446db450"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5344b896e79800af86ad643408ca9aa303a017f6ebff8cee5a3163c1e9aec987"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:204e0f176fd1d067671157d049466869b3ae1fc51e354708b0dc41cf94e23a3a"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a42db008d58530efa3b881eeee4991146de0b790e095f7ae43ba5cc612decbc5"}, - {file = "pyzmq-26.0.3-cp37-cp37m-win32.whl", hash = "sha256:8d7a498671ca87e32b54cb47c82a92b40130a26c5197d392720a1bce1b3c77cf"}, - {file = "pyzmq-26.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:3b4032a96410bdc760061b14ed6a33613ffb7f702181ba999df5d16fb96ba16a"}, - {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2cc4e280098c1b192c42a849de8de2c8e0f3a84086a76ec5b07bfee29bda7d18"}, - {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bde86a2ed3ce587fa2b207424ce15b9a83a9fa14422dcc1c5356a13aed3df9d"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34106f68e20e6ff253c9f596ea50397dbd8699828d55e8fa18bd4323d8d966e6"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ebbbd0e728af5db9b04e56389e2299a57ea8b9dd15c9759153ee2455b32be6ad"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b1d1c631e5940cac5a0b22c5379c86e8df6a4ec277c7a856b714021ab6cfad"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e891ce81edd463b3b4c3b885c5603c00141151dd9c6936d98a680c8c72fe5c67"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9b273ecfbc590a1b98f014ae41e5cf723932f3b53ba9367cfb676f838038b32c"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b32bff85fb02a75ea0b68f21e2412255b5731f3f389ed9aecc13a6752f58ac97"}, - {file = "pyzmq-26.0.3-cp38-cp38-win32.whl", hash = "sha256:f6c21c00478a7bea93caaaef9e7629145d4153b15a8653e8bb4609d4bc70dbfc"}, - {file = "pyzmq-26.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:3401613148d93ef0fd9aabdbddb212de3db7a4475367f49f590c837355343972"}, - {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:2ed8357f4c6e0daa4f3baf31832df8a33334e0fe5b020a61bc8b345a3db7a606"}, - {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1c8f2a2ca45292084c75bb6d3a25545cff0ed931ed228d3a1810ae3758f975f"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b63731993cdddcc8e087c64e9cf003f909262b359110070183d7f3025d1c56b5"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b3cd31f859b662ac5d7f4226ec7d8bd60384fa037fc02aee6ff0b53ba29a3ba8"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115f8359402fa527cf47708d6f8a0f8234f0e9ca0cab7c18c9c189c194dbf620"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:715bdf952b9533ba13dfcf1f431a8f49e63cecc31d91d007bc1deb914f47d0e4"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e1258c639e00bf5e8a522fec6c3eaa3e30cf1c23a2f21a586be7e04d50c9acab"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:15c59e780be8f30a60816a9adab900c12a58d79c1ac742b4a8df044ab2a6d920"}, - {file = "pyzmq-26.0.3-cp39-cp39-win32.whl", hash = "sha256:d0cdde3c78d8ab5b46595054e5def32a755fc028685add5ddc7403e9f6de9879"}, - {file = "pyzmq-26.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ce828058d482ef860746bf532822842e0ff484e27f540ef5c813d516dd8896d2"}, - {file = "pyzmq-26.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:788f15721c64109cf720791714dc14afd0f449d63f3a5487724f024345067381"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c18645ef6294d99b256806e34653e86236eb266278c8ec8112622b61db255de"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e6bc96ebe49604df3ec2c6389cc3876cabe475e6bfc84ced1bf4e630662cb35"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:971e8990c5cc4ddcff26e149398fc7b0f6a042306e82500f5e8db3b10ce69f84"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8416c23161abd94cc7da80c734ad7c9f5dbebdadfdaa77dad78244457448223"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:082a2988364b60bb5de809373098361cf1dbb239623e39e46cb18bc035ed9c0c"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d57dfbf9737763b3a60d26e6800e02e04284926329aee8fb01049635e957fe81"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77a85dca4c2430ac04dc2a2185c2deb3858a34fe7f403d0a946fa56970cf60a1"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c82a6d952a1d555bf4be42b6532927d2a5686dd3c3e280e5f63225ab47ac1f5"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4496b1282c70c442809fc1b151977c3d967bfb33e4e17cedbf226d97de18f709"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e4946d6bdb7ba972dfda282f9127e5756d4f299028b1566d1245fa0d438847e6"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:03c0ae165e700364b266876d712acb1ac02693acd920afa67da2ebb91a0b3c09"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3e3070e680f79887d60feeda051a58d0ac36622e1759f305a41059eff62c6da7"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6ca08b840fe95d1c2bd9ab92dac5685f949fc6f9ae820ec16193e5ddf603c3b2"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e76654e9dbfb835b3518f9938e565c7806976c07b37c33526b574cc1a1050480"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:871587bdadd1075b112e697173e946a07d722459d20716ceb3d1bd6c64bd08ce"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d0a2d1bd63a4ad79483049b26514e70fa618ce6115220da9efdff63688808b17"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0270b49b6847f0d106d64b5086e9ad5dc8a902413b5dbbb15d12b60f9c1747a4"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:703c60b9910488d3d0954ca585c34f541e506a091a41930e663a098d3b794c67"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74423631b6be371edfbf7eabb02ab995c2563fee60a80a30829176842e71722a"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4adfbb5451196842a88fda3612e2c0414134874bffb1c2ce83ab4242ec9e027d"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3516119f4f9b8671083a70b6afaa0a070f5683e431ab3dc26e9215620d7ca1ad"}, - {file = "pyzmq-26.0.3.tar.gz", hash = "sha256:dba7d9f2e047dfa2bca3b01f4f84aa5246725203d6284e3790f2ca15fba6b40a"}, + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e"}, + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88"}, + {file = "pyzmq-26.1.0-cp310-cp310-win32.whl", hash = "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b"}, + {file = "pyzmq-26.1.0-cp311-cp311-win32.whl", hash = "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83"}, + {file = "pyzmq-26.1.0-cp312-cp312-win32.whl", hash = "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4"}, + {file = "pyzmq-26.1.0-cp313-cp313-win32.whl", hash = "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win32.whl", hash = "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c"}, + {file = "pyzmq-26.1.0-cp38-cp38-win32.whl", hash = "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741"}, + {file = "pyzmq-26.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8"}, + {file = "pyzmq-26.1.0-cp39-cp39-win32.whl", hash = "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c"}, + {file = "pyzmq-26.1.0.tar.gz", hash = "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f"}, ] [package.dependencies] diff --git a/sdks/python/julep/api/types/agents_route_list_response.py b/sdks/python/julep/api/types/agents_route_list_response.py index 98362162e..8802e37f5 100644 --- a/sdks/python/julep/api/types/agents_route_list_response.py +++ b/sdks/python/julep/api/types/agents_route_list_response.py @@ -9,7 +9,7 @@ class AgentsRouteListResponse(pydantic_v1.BaseModel): - results: typing.List[AgentsAgent] + items: typing.List[AgentsAgent] def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { diff --git a/sdks/python/julep/api/types/sessions_route_list_response.py b/sdks/python/julep/api/types/sessions_route_list_response.py index 874d1bd1e..ff1807961 100644 --- a/sdks/python/julep/api/types/sessions_route_list_response.py +++ b/sdks/python/julep/api/types/sessions_route_list_response.py @@ -9,7 +9,7 @@ class SessionsRouteListResponse(pydantic_v1.BaseModel): - results: typing.List[SessionsSession] + items: typing.List[SessionsSession] def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { diff --git a/sdks/python/julep/api/types/users_route_list_response.py b/sdks/python/julep/api/types/users_route_list_response.py index 7cb75ac18..77b2b9fb5 100644 --- a/sdks/python/julep/api/types/users_route_list_response.py +++ b/sdks/python/julep/api/types/users_route_list_response.py @@ -9,7 +9,7 @@ class UsersRouteListResponse(pydantic_v1.BaseModel): - results: typing.List[UsersUser] + items: typing.List[UsersUser] def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 02908773a..ae3a59f88 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -2402,99 +2402,120 @@ files = [ [[package]] name = "pyzmq" -version = "26.0.3" +version = "26.1.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:44dd6fc3034f1eaa72ece33588867df9e006a7303725a12d64c3dff92330f625"}, - {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:acb704195a71ac5ea5ecf2811c9ee19ecdc62b91878528302dd0be1b9451cc90"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbb9c997932473a27afa93954bb77a9f9b786b4ccf718d903f35da3232317de"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bcb34f869d431799c3ee7d516554797f7760cb2198ecaa89c3f176f72d062be"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ece17ec5f20d7d9b442e5174ae9f020365d01ba7c112205a4d59cf19dc38ee"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ba6e5e6588e49139a0979d03a7deb9c734bde647b9a8808f26acf9c547cab1bf"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3bf8b000a4e2967e6dfdd8656cd0757d18c7e5ce3d16339e550bd462f4857e59"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2136f64fbb86451dbbf70223635a468272dd20075f988a102bf8a3f194a411dc"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e8918973fbd34e7814f59143c5f600ecd38b8038161239fd1a3d33d5817a38b8"}, - {file = "pyzmq-26.0.3-cp310-cp310-win32.whl", hash = "sha256:0aaf982e68a7ac284377d051c742610220fd06d330dcd4c4dbb4cdd77c22a537"}, - {file = "pyzmq-26.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f1a9b7d00fdf60b4039f4455afd031fe85ee8305b019334b72dcf73c567edc47"}, - {file = "pyzmq-26.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:80b12f25d805a919d53efc0a5ad7c0c0326f13b4eae981a5d7b7cc343318ebb7"}, - {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:a72a84570f84c374b4c287183debc776dc319d3e8ce6b6a0041ce2e400de3f32"}, - {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ca684ee649b55fd8f378127ac8462fb6c85f251c2fb027eb3c887e8ee347bcd"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e222562dc0f38571c8b1ffdae9d7adb866363134299264a1958d077800b193b7"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f17cde1db0754c35a91ac00b22b25c11da6eec5746431d6e5092f0cd31a3fea9"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7c0c0b3244bb2275abe255d4a30c050d541c6cb18b870975553f1fb6f37527"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac97a21de3712afe6a6c071abfad40a6224fd14fa6ff0ff8d0c6e6cd4e2f807a"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88b88282e55fa39dd556d7fc04160bcf39dea015f78e0cecec8ff4f06c1fc2b5"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:72b67f966b57dbd18dcc7efbc1c7fc9f5f983e572db1877081f075004614fcdd"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4b6cecbbf3b7380f3b61de3a7b93cb721125dc125c854c14ddc91225ba52f83"}, - {file = "pyzmq-26.0.3-cp311-cp311-win32.whl", hash = "sha256:eed56b6a39216d31ff8cd2f1d048b5bf1700e4b32a01b14379c3b6dde9ce3aa3"}, - {file = "pyzmq-26.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:3191d312c73e3cfd0f0afdf51df8405aafeb0bad71e7ed8f68b24b63c4f36500"}, - {file = "pyzmq-26.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:b6907da3017ef55139cf0e417c5123a84c7332520e73a6902ff1f79046cd3b94"}, - {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:068ca17214038ae986d68f4a7021f97e187ed278ab6dccb79f837d765a54d753"}, - {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7821d44fe07335bea256b9f1f41474a642ca55fa671dfd9f00af8d68a920c2d4"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb438a26d87c123bb318e5f2b3d86a36060b01f22fbdffd8cf247d52f7c9a2b"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69ea9d6d9baa25a4dc9cef5e2b77b8537827b122214f210dd925132e34ae9b12"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7daa3e1369355766dea11f1d8ef829905c3b9da886ea3152788dc25ee6079e02"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6ca7a9a06b52d0e38ccf6bca1aeff7be178917893f3883f37b75589d42c4ac20"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1b7d0e124948daa4d9686d421ef5087c0516bc6179fdcf8828b8444f8e461a77"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e746524418b70f38550f2190eeee834db8850088c834d4c8406fbb9bc1ae10b2"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6b3146f9ae6af82c47a5282ac8803523d381b3b21caeae0327ed2f7ecb718798"}, - {file = "pyzmq-26.0.3-cp312-cp312-win32.whl", hash = "sha256:2b291d1230845871c00c8462c50565a9cd6026fe1228e77ca934470bb7d70ea0"}, - {file = "pyzmq-26.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:926838a535c2c1ea21c903f909a9a54e675c2126728c21381a94ddf37c3cbddf"}, - {file = "pyzmq-26.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:5bf6c237f8c681dfb91b17f8435b2735951f0d1fad10cc5dfd96db110243370b"}, - {file = "pyzmq-26.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c0991f5a96a8e620f7691e61178cd8f457b49e17b7d9cfa2067e2a0a89fc1d5"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dbf012d8fcb9f2cf0643b65df3b355fdd74fc0035d70bb5c845e9e30a3a4654b"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:01fbfbeb8249a68d257f601deb50c70c929dc2dfe683b754659569e502fbd3aa"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c8eb19abe87029c18f226d42b8a2c9efdd139d08f8bf6e085dd9075446db450"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5344b896e79800af86ad643408ca9aa303a017f6ebff8cee5a3163c1e9aec987"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:204e0f176fd1d067671157d049466869b3ae1fc51e354708b0dc41cf94e23a3a"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a42db008d58530efa3b881eeee4991146de0b790e095f7ae43ba5cc612decbc5"}, - {file = "pyzmq-26.0.3-cp37-cp37m-win32.whl", hash = "sha256:8d7a498671ca87e32b54cb47c82a92b40130a26c5197d392720a1bce1b3c77cf"}, - {file = "pyzmq-26.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:3b4032a96410bdc760061b14ed6a33613ffb7f702181ba999df5d16fb96ba16a"}, - {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2cc4e280098c1b192c42a849de8de2c8e0f3a84086a76ec5b07bfee29bda7d18"}, - {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bde86a2ed3ce587fa2b207424ce15b9a83a9fa14422dcc1c5356a13aed3df9d"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34106f68e20e6ff253c9f596ea50397dbd8699828d55e8fa18bd4323d8d966e6"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ebbbd0e728af5db9b04e56389e2299a57ea8b9dd15c9759153ee2455b32be6ad"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b1d1c631e5940cac5a0b22c5379c86e8df6a4ec277c7a856b714021ab6cfad"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e891ce81edd463b3b4c3b885c5603c00141151dd9c6936d98a680c8c72fe5c67"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9b273ecfbc590a1b98f014ae41e5cf723932f3b53ba9367cfb676f838038b32c"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b32bff85fb02a75ea0b68f21e2412255b5731f3f389ed9aecc13a6752f58ac97"}, - {file = "pyzmq-26.0.3-cp38-cp38-win32.whl", hash = "sha256:f6c21c00478a7bea93caaaef9e7629145d4153b15a8653e8bb4609d4bc70dbfc"}, - {file = "pyzmq-26.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:3401613148d93ef0fd9aabdbddb212de3db7a4475367f49f590c837355343972"}, - {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:2ed8357f4c6e0daa4f3baf31832df8a33334e0fe5b020a61bc8b345a3db7a606"}, - {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1c8f2a2ca45292084c75bb6d3a25545cff0ed931ed228d3a1810ae3758f975f"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b63731993cdddcc8e087c64e9cf003f909262b359110070183d7f3025d1c56b5"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b3cd31f859b662ac5d7f4226ec7d8bd60384fa037fc02aee6ff0b53ba29a3ba8"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115f8359402fa527cf47708d6f8a0f8234f0e9ca0cab7c18c9c189c194dbf620"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:715bdf952b9533ba13dfcf1f431a8f49e63cecc31d91d007bc1deb914f47d0e4"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e1258c639e00bf5e8a522fec6c3eaa3e30cf1c23a2f21a586be7e04d50c9acab"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:15c59e780be8f30a60816a9adab900c12a58d79c1ac742b4a8df044ab2a6d920"}, - {file = "pyzmq-26.0.3-cp39-cp39-win32.whl", hash = "sha256:d0cdde3c78d8ab5b46595054e5def32a755fc028685add5ddc7403e9f6de9879"}, - {file = "pyzmq-26.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ce828058d482ef860746bf532822842e0ff484e27f540ef5c813d516dd8896d2"}, - {file = "pyzmq-26.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:788f15721c64109cf720791714dc14afd0f449d63f3a5487724f024345067381"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c18645ef6294d99b256806e34653e86236eb266278c8ec8112622b61db255de"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e6bc96ebe49604df3ec2c6389cc3876cabe475e6bfc84ced1bf4e630662cb35"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:971e8990c5cc4ddcff26e149398fc7b0f6a042306e82500f5e8db3b10ce69f84"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8416c23161abd94cc7da80c734ad7c9f5dbebdadfdaa77dad78244457448223"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:082a2988364b60bb5de809373098361cf1dbb239623e39e46cb18bc035ed9c0c"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d57dfbf9737763b3a60d26e6800e02e04284926329aee8fb01049635e957fe81"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77a85dca4c2430ac04dc2a2185c2deb3858a34fe7f403d0a946fa56970cf60a1"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c82a6d952a1d555bf4be42b6532927d2a5686dd3c3e280e5f63225ab47ac1f5"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4496b1282c70c442809fc1b151977c3d967bfb33e4e17cedbf226d97de18f709"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e4946d6bdb7ba972dfda282f9127e5756d4f299028b1566d1245fa0d438847e6"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:03c0ae165e700364b266876d712acb1ac02693acd920afa67da2ebb91a0b3c09"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3e3070e680f79887d60feeda051a58d0ac36622e1759f305a41059eff62c6da7"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6ca08b840fe95d1c2bd9ab92dac5685f949fc6f9ae820ec16193e5ddf603c3b2"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e76654e9dbfb835b3518f9938e565c7806976c07b37c33526b574cc1a1050480"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:871587bdadd1075b112e697173e946a07d722459d20716ceb3d1bd6c64bd08ce"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d0a2d1bd63a4ad79483049b26514e70fa618ce6115220da9efdff63688808b17"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0270b49b6847f0d106d64b5086e9ad5dc8a902413b5dbbb15d12b60f9c1747a4"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:703c60b9910488d3d0954ca585c34f541e506a091a41930e663a098d3b794c67"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74423631b6be371edfbf7eabb02ab995c2563fee60a80a30829176842e71722a"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4adfbb5451196842a88fda3612e2c0414134874bffb1c2ce83ab4242ec9e027d"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3516119f4f9b8671083a70b6afaa0a070f5683e431ab3dc26e9215620d7ca1ad"}, - {file = "pyzmq-26.0.3.tar.gz", hash = "sha256:dba7d9f2e047dfa2bca3b01f4f84aa5246725203d6284e3790f2ca15fba6b40a"}, + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e"}, + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88"}, + {file = "pyzmq-26.1.0-cp310-cp310-win32.whl", hash = "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b"}, + {file = "pyzmq-26.1.0-cp311-cp311-win32.whl", hash = "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83"}, + {file = "pyzmq-26.1.0-cp312-cp312-win32.whl", hash = "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4"}, + {file = "pyzmq-26.1.0-cp313-cp313-win32.whl", hash = "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win32.whl", hash = "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c"}, + {file = "pyzmq-26.1.0-cp38-cp38-win32.whl", hash = "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741"}, + {file = "pyzmq-26.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8"}, + {file = "pyzmq-26.1.0-cp39-cp39-win32.whl", hash = "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c"}, + {file = "pyzmq-26.1.0.tar.gz", hash = "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f"}, ] [package.dependencies] diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index c642a99bd..8c7945853 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -88,7 +88,7 @@ export class DefaultService { */ metadataFilter?: string; }): CancelablePromise<{ - results: Array; + items: Array; }> { return this.httpRequest.request({ method: "GET", @@ -936,7 +936,7 @@ export class DefaultService { */ metadataFilter?: string; }): CancelablePromise<{ - results: Array; + items: Array; }> { return this.httpRequest.request({ method: "GET", @@ -1545,7 +1545,7 @@ export class DefaultService { */ metadataFilter?: string; }): CancelablePromise<{ - results: Array; + items: Array; }> { return this.httpRequest.request({ method: "GET", diff --git a/typespec/common/interfaces.tsp b/typespec/common/interfaces.tsp index 56beb423a..f6a6f2510 100644 --- a/typespec/common/interfaces.tsp +++ b/typespec/common/interfaces.tsp @@ -18,7 +18,7 @@ interface LimitOffsetPagination< @get @doc(DocString) list(...PaginationOptions): { - results: Type[]; + items: Type[]; }; } From 50c898e41ef8bd843057db90525598221db084f0 Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Mon, 5 Aug 2024 10:26:24 -0400 Subject: [PATCH 004/110] Reimplement tests for queries and operations (#443) * Reimplement tests for queries and operations Reimplement tests for queries and operations in `agents-api/agents_api/models` subdirectories using the new code. * **agents-api/agents_api/models/agent/test_agent_queries.py** - Uncomment import statements and test functions. - Update test functions to use new query functions. - Ensure tests pass with new code. * **agents-api/agents_api/models/docs/test_docs_queries.py** - Uncomment import statements and test functions. - Update test functions to use new query functions. - Ensure tests pass with new code. * **agents-api/agents_api/models/entry/test_entry_queries.py** - Uncomment import statements and test functions. - Update test functions to use new query functions. - Ensure tests pass with new code. * **agents-api/agents_api/models/execution/test_execution_queries.py** - Uncomment import statements and test functions. - Update test functions to use new query functions. - Ensure tests pass with new code. * **agents-api/agents_api/models/session/test_session_queries.py** - Uncomment import statements and test functions. - Update test functions to use new query functions. - Ensure tests pass with new code. * **agents-api/agents_api/models/task/test_task_queries.py** - Uncomment import statements and test functions. - Update test functions to use new query functions. - Ensure tests pass with new code. * **agents-api/agents_api/models/tools/test_tool_queries.py** - Uncomment import statements and test functions. - Update test functions to use new query functions. - Ensure tests pass with new code. * **agents-api/agents_api/models/user/test_user_queries.py** - Uncomment import statements and test functions. - Update test functions to use new query functions. - Ensure tests pass with new code. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/julep-ai/julep?shareId=XXXX-XXXX-XXXX-XXXX). * wip: reimplement model tests Signed-off-by: Diwank Tomer --------- Signed-off-by: Diwank Tomer Co-authored-by: Diwank Tomer --- agents-api/agents_api/common/utils/types.py | 22 + .../models/agent/test_agent_queries.py | 339 +++---- .../models/docs/test_docs_queries.py | 374 ++++---- .../models/entry/test_entry_queries.py | 425 +++++---- .../models/execution/create_execution.py | 12 +- .../execution/test_execution_queries.py | 590 +++++------- .../models/session/test_session_queries.py | 444 ++++----- .../models/task/test_task_queries.py | 266 ++++-- .../models/tools/test_tool_queries.py | 265 +++--- .../models/user/test_user_queries.py | 359 ++++---- agents-api/tests/test_activities.py | 158 ++-- agents-api/tests/test_agents.py | 866 +++++++++--------- agents-api/tests/test_messages_truncation.py | 572 ++++++------ agents-api/tests/test_sessions.py | 612 ++++++------- agents-api/tests/test_tasks.py | 318 +++---- agents-api/tests/test_users.py | 382 ++++---- 16 files changed, 2982 insertions(+), 3022 deletions(-) create mode 100644 agents-api/agents_api/common/utils/types.py diff --git a/agents-api/agents_api/common/utils/types.py b/agents-api/agents_api/common/utils/types.py new file mode 100644 index 000000000..6bf9cd502 --- /dev/null +++ b/agents-api/agents_api/common/utils/types.py @@ -0,0 +1,22 @@ +from typing import Type + +from beartype.vale import Is +from beartype.vale._core._valecore import BeartypeValidator +from pydantic import BaseModel + + +def dict_like(pydantic_model_class: Type[BaseModel]) -> BeartypeValidator: + required_fields_set: set[str] = set( + [ + field + for field, info in pydantic_model_class.model_fields.items() + if info.is_required() + ] + ) + + validator = Is[ + lambda x: isinstance(x, pydantic_model_class) + or required_fields_set.issubset(set(x.keys())) + ] + + return validator diff --git a/agents-api/agents_api/models/agent/test_agent_queries.py b/agents-api/agents_api/models/agent/test_agent_queries.py index b416b3628..f293c0700 100644 --- a/agents-api/agents_api/models/agent/test_agent_queries.py +++ b/agents-api/agents_api/models/agent/test_agent_queries.py @@ -1,167 +1,172 @@ -# # Tests for agent queries -# from uuid import uuid4 - -# from cozo_migrate.api import init, apply -# from pycozo import Client -# from ward import test - -# from .create_agent import create_agent_query -# from .delete_agent import delete_agent_query -# from .get_agent import get_agent_query -# from .list_agents import list_agents_query -# from .update_agent import update_agent_query - -# MODEL = "julep-ai/samantha-1-turbo" - - -# def cozo_client(migrations_dir: str = "./migrations"): -# # Create a new client for each test -# # and initialize the schema. -# client = Client() - -# init(client) -# apply(client, migrations_dir=migrations_dir, all_=True) - -# return client - - -# @test("model: create agent") -# def _(): -# client = cozo_client() -# agent_id = uuid4() -# developer_id = uuid4() - -# create_agent_query( -# agent_id=agent_id, -# model=MODEL, -# developer_id=developer_id, -# name="test agent", -# about="test agent about", -# client=client, -# ) - - -# @test("model: create agent with instructions") -# def _(): -# client = cozo_client() -# agent_id = uuid4() -# developer_id = uuid4() - -# create_agent_query( -# agent_id=agent_id, -# model=MODEL, -# developer_id=developer_id, -# name="test agent", -# about="test agent about", -# instructions=[ -# "test instruction", -# ], -# client=client, -# ) - - -# @test("model: get agent not exists") -# def _(): -# client = cozo_client() -# agent_id = uuid4() -# developer_id = uuid4() - -# result = get_agent_query( -# agent_id=agent_id, developer_id=developer_id, client=client -# ) - -# assert len(result["id"]) == 0 - - -# @test("model: get agent exists") -# def _(): -# client = cozo_client() -# agent_id = uuid4() -# developer_id = uuid4() - -# result = create_agent_query( -# agent_id=agent_id, -# model=MODEL, -# developer_id=developer_id, -# name="test agent", -# about="test agent about", -# default_settings={"temperature": 1.5}, -# client=client, -# ) - -# result = get_agent_query( -# agent_id=agent_id, developer_id=developer_id, client=client -# ) - -# assert len(result["id"]) == 1 -# assert "temperature" in result["default_settings"][0] -# assert result["default_settings"][0]["temperature"] == 1.5 - - -# @test("model: delete agent") -# def _(): -# client = cozo_client() -# agent_id = uuid4() -# developer_id = uuid4() - -# # Create the agent -# result = create_agent_query( -# agent_id=agent_id, -# model=MODEL, -# developer_id=developer_id, -# name="test agent", -# about="test agent about", -# client=client, -# ) - -# # Delete the agent -# result = delete_agent_query( -# agent_id=agent_id, developer_id=developer_id, client=client -# ) - -# # Check that the agent is deleted -# result = get_agent_query( -# agent_id=agent_id, developer_id=developer_id, client=client -# ) - -# assert len(result["id"]) == 0 - - -# @test("model: update agent") -# def _(): -# client = cozo_client() -# agent_id = uuid4() -# developer_id = uuid4() - -# create_agent_query( -# agent_id=agent_id, -# model=MODEL, -# developer_id=developer_id, -# name="test agent", -# about="test agent about", -# client=client, -# ) - -# result = update_agent_query( -# agent_id=agent_id, -# developer_id=developer_id, -# name="updated agent", -# about="updated agent about", -# default_settings={"temperature": 1.5}, -# client=client, -# ) - -# data = result.iloc[0].to_dict() - -# assert data["updated_at"] > data["created_at"] - - -# @test("model: list agents") -# def _(): -# """Tests listing all agents associated with a developer in the database. Verifies that the correct list of agents is retrieved.""" -# client = cozo_client() -# developer_id = uuid4() - -# result = list_agents_query(developer_id=developer_id, client=client) - -# assert len(result["id"]) == 0 +# Tests for agent queries +from uuid import uuid4 + +from cozo_migrate.api import apply, init +from pycozo import Client +from ward import test + +from agents_api.autogen.openapi_model import Agent + +from .create_agent import create_agent +from .delete_agent import delete_agent +from .get_agent import get_agent +from .list_agents import list_agents +from .update_agent import update_agent + +MODEL = "julep-ai/samantha-1-turbo" + + +def cozo_client(migrations_dir: str = "./migrations"): + # Create a new client for each test + # and initialize the schema. + client = Client() + + init(client) + apply(client, migrations_dir=migrations_dir, all_=True) + + return client + + +@test("model: create agent") +def _(): + client = cozo_client() + agent_id = uuid4() + developer_id = uuid4() + + create_agent( + agent_id=agent_id, + developer_id=developer_id, + data={ + "model": MODEL, + "name": "test agent", + "about": "test agent about", + }, + client=client, + ) + + +@test("model: create agent with instructions") +def _(): + client = cozo_client() + agent_id = uuid4() + developer_id = uuid4() + + create_agent( + agent_id=agent_id, + developer_id=developer_id, + data={ + "model": MODEL, + "name": "test agent", + "about": "test agent about", + "instructions": ["test instruction"], + }, + client=client, + ) + + +@test("model: get agent not exists") +def _(): + client = cozo_client() + agent_id = uuid4() + developer_id = uuid4() + + result = get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + + assert result is None + + +@test("model: get agent exists") +def _(): + client = cozo_client() + agent_id = uuid4() + developer_id = uuid4() + + create_agent( + agent_id=agent_id, + developer_id=developer_id, + data={ + "model": MODEL, + "name": "test agent", + "about": "test agent about", + "default_settings": {"temperature": 1.5}, + }, + client=client, + ) + + result = get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + + assert result is not None + assert isinstance(result, Agent) + assert result.default_settings.temperature == 1.5 + + +@test("model: delete agent") +def _(): + client = cozo_client() + agent_id = uuid4() + developer_id = uuid4() + + # Create the agent + create_agent( + agent_id=agent_id, + developer_id=developer_id, + data={ + "model": MODEL, + "name": "test agent", + "about": "test agent about", + }, + client=client, + ) + + # Delete the agent + delete_agent(agent_id=agent_id, developer_id=developer_id, client=client) + + # Check that the agent is deleted + result = get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + + assert result is None + + +@test("model: update agent") +def _(): + client = cozo_client() + agent_id = uuid4() + developer_id = uuid4() + + create_agent( + agent_id=agent_id, + developer_id=developer_id, + data={ + "model": MODEL, + "name": "test agent", + "about": "test agent about", + }, + client=client, + ) + + result = update_agent( + agent_id=agent_id, + developer_id=developer_id, + data={ + "name": "updated agent", + "about": "updated agent about", + "default_settings": {"temperature": 1.5}, + }, + client=client, + ) + + assert result is not None + assert isinstance(result, Agent) + assert result.default_settings.temperature == 1.5 + + +@test("model: list agents") +def _(): + """Tests listing all agents associated with a developer in the database. Verifies that the correct list of agents is retrieved.""" + client = cozo_client() + developer_id = uuid4() + + result = list_agents(developer_id=developer_id, client=client) + + assert isinstance(result, list) + assert all(isinstance(agent, Agent) for agent in result) diff --git a/agents-api/agents_api/models/docs/test_docs_queries.py b/agents-api/agents_api/models/docs/test_docs_queries.py index 6b5528b40..3a0198601 100644 --- a/agents-api/agents_api/models/docs/test_docs_queries.py +++ b/agents-api/agents_api/models/docs/test_docs_queries.py @@ -1,158 +1,216 @@ -# # Tests for entry queries -# from uuid import uuid4 - -# from cozo_migrate.api import init, apply -# from pycozo import Client -# from ward import test - - -# from .create_docs import create_docs_query -# from .delete_docs import delete_docs_by_id_query -# from .get_docs import get_docs_snippets_by_id_query -# from .list_docs import list_docs_snippets_by_owner_query -# from .embed_docs import embed_docs_snippets_query -# from .search_docs import search_docs_snippets_by_embedding_query - - -# EMBEDDING_SIZE: int = 1024 - - -# def cozo_client(migrations_dir: str = "./migrations"): -# # Create a new client for each test -# # and initialize the schema. -# client = Client() - -# init(client) -# apply(client, migrations_dir=migrations_dir, all_=True) - -# return client - - -# @test("model: create docs") -# def _(): -# client = cozo_client() - -# for owner_type in ("user", "agent"): -# owner_id = uuid4() -# id = uuid4() - -# result = create_docs_query( -# owner_type, owner_id, id, title="Hello", content="World", client=client -# ) - -# assert result["created_at"][0] - - -# @test("model: get docs") -# def _(): -# client = cozo_client() - -# for owner_type in ("user", "agent"): -# owner_id = uuid4() -# id = uuid4() - -# create_docs_query( -# owner_type, owner_id, id, title="Hello", content="World", client=client -# ) - -# result = get_docs_snippets_by_id_query(owner_type, id, client=client) - -# assert len(result) == 1, "Only 1 should have been found" - - -# @test("model: delete docs") -# def _(): -# client = cozo_client() - -# for owner_type in ("user", "agent"): -# owner_id = uuid4() -# id = uuid4() - -# create_docs_query( -# owner_type, owner_id, id, title="Hello", content="World", client=client -# ) - -# result = delete_docs_by_id_query(owner_type, owner_id, id, client=client) - -# delete_info = next( -# (row for row in result.to_dict("records") if row["_kind"] == "deleted"), -# None, -# ) - -# assert delete_info is not None, "Delete operation found the row" - - -# @test("model: list docs") -# def _(): -# client = cozo_client() - -# for owner_type in ("user", "agent"): -# owner_id = uuid4() -# id = uuid4() - -# create_docs_query( -# owner_type, owner_id, id, title="Hello", content="World", client=client -# ) - -# result = list_docs_snippets_by_owner_query(owner_type, owner_id, client=client) - -# assert len(result) == 1, "Only 1 should have been found" - - -# @test("model: search docs") -# def _(): -# client = cozo_client() - -# for owner_type in ("user", "agent"): -# owner_id = uuid4() -# id = uuid4() - -# create_docs_query( -# owner_type, owner_id, id, title="Hello", content="World", client=client -# ) - -# ### Add embedding to the snippet -# client.update( -# "information_snippets", -# dict(doc_id=str(id), snippet_idx=0, embedding=[1.0] * EMBEDDING_SIZE), -# ) - -# ### Search -# query_embedding = [0.99] * EMBEDDING_SIZE - -# result = search_docs_snippets_by_embedding_query( -# owner_type, owner_id, query_embedding, client=client -# ) - -# assert len(result) == 1, "Only 1 should have been found" - - -# @test("model: embed docs") -# def _(): -# client = cozo_client() - -# for owner_type in ("user", "agent"): -# owner_id = uuid4() -# id = uuid4() - -# snippets = [ -# "Hello World", -# "Hello Banana", -# "Hello Apple", -# ] - -# create_docs_query( -# owner_type, -# owner_id, -# id, -# title="Hi", -# content=snippets, -# client=client, -# ) - -# ### Add embedding to the snippet -# snippet_indices = [*range(len(snippets))] - -# embeddings = [[1.0] * EMBEDDING_SIZE for _ in snippets] - -# embed_docs_snippets_query(id, snippet_indices, embeddings, client=client) +# Tests for entry queries +from uuid import uuid4 + +from cozo_migrate.api import apply, init +from pycozo import Client +from ward import test + +from .create_doc import create_doc +from .delete_doc import delete_doc +from .embed_snippets import embed_snippets +from .get_doc import get_doc +from .list_docs import list_docs +from .search_docs import search_docs_by_embedding + +EMBEDDING_SIZE: int = 1024 + + +def cozo_client(migrations_dir: str = "./migrations"): + # Create a new client for each test + # and initialize the schema. + client = Client() + + init(client) + apply(client, migrations_dir=migrations_dir, all_=True) + + return client + + +@test("model: create docs") +def _(): + client = cozo_client() + + for owner_type in ("user", "agent"): + owner_id = uuid4() + doc_id = uuid4() + developer_id = uuid4() + + create_doc( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + doc_id=doc_id, + data={"title": "Hello", "content": ["World"]}, + client=client, + ) + + +@test("model: get docs") +def _(): + client = cozo_client() + + for owner_type in ("user", "agent"): + owner_id = uuid4() + doc_id = uuid4() + developer_id = uuid4() + + create_doc( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + doc_id=doc_id, + data={"title": "Hello", "content": ["World"]}, + client=client, + ) + + result = get_doc( + developer_id=developer_id, + owner_type=owner_type, + doc_id=doc_id, + client=client, + ) + + assert len(result["id"]) == 1, "Only 1 should have been found" + + +@test("model: delete docs") +def _(): + client = cozo_client() + + for owner_type in ("user", "agent"): + owner_id = uuid4() + doc_id = uuid4() + developer_id = uuid4() + + create_doc( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + doc_id=doc_id, + data={"title": "Hello", "content": ["World"]}, + client=client, + ) + + delete_doc( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + doc_id=doc_id, + client=client, + ) + + result = get_doc( + developer_id=developer_id, + owner_type=owner_type, + doc_id=doc_id, + client=client, + ) + + assert len(result["id"]) == 0, "Delete operation found the row" + + +@test("model: list docs") +def _(): + client = cozo_client() + + for owner_type in ("user", "agent"): + owner_id = uuid4() + doc_id = uuid4() + developer_id = uuid4() + + create_doc( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + doc_id=doc_id, + data={"title": "Hello", "content": ["World"]}, + client=client, + ) + + result = list_docs( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + client=client, + ) + + assert len(result["id"]) == 1, "Only 1 should have been found" + + +@test("model: search docs") +def _(): + client = cozo_client() + + for owner_type in ("user", "agent"): + owner_id = uuid4() + doc_id = uuid4() + developer_id = uuid4() + + create_doc( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + doc_id=doc_id, + data={"title": "Hello", "content": ["World"]}, + client=client, + ) + + ### Add embedding to the snippet + embed_snippets( + developer_id=developer_id, + doc_id=doc_id, + snippet_indices=[0], + embeddings=[[1.0] * EMBEDDING_SIZE], + client=client, + ) + + ### Search + query_embedding = [0.99] * EMBEDDING_SIZE + + result = search_docs_by_embedding( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + query_embedding=query_embedding, + client=client, + ) + + assert len(result["id"]) == 1, "Only 1 should have been found" + + +@test("model: embed docs") +def _(): + client = cozo_client() + + for owner_type in ("user", "agent"): + owner_id = uuid4() + doc_id = uuid4() + developer_id = uuid4() + + snippets = [ + "Hello World", + "Hello Banana", + "Hello Apple", + ] + + create_doc( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + doc_id=doc_id, + data={"title": "Hi", "content": snippets}, + client=client, + ) + + ### Add embedding to the snippet + snippet_indices = [*range(len(snippets))] + + embeddings = [[1.0] * EMBEDDING_SIZE for _ in snippets] + + embed_snippets( + developer_id=developer_id, + doc_id=doc_id, + snippet_indices=snippet_indices, + embeddings=embeddings, + client=client, + ) diff --git a/agents-api/agents_api/models/entry/test_entry_queries.py b/agents-api/agents_api/models/entry/test_entry_queries.py index d4549898d..70823a0b6 100644 --- a/agents-api/agents_api/models/entry/test_entry_queries.py +++ b/agents-api/agents_api/models/entry/test_entry_queries.py @@ -1,192 +1,233 @@ -# """ -# This module contains tests for entry queries against the CozoDB database. -# It verifies the functionality of adding, retrieving, and processing entries as defined in the schema. -# """ - -# # Tests for entry queries -# from uuid import uuid4 - -# from cozo_migrate.api import init, apply -# from pycozo import Client -# from ward import test - -# from ...autogen.openapi_model import FunctionDef -# from ...common.protocol.entries import Entry -# from ..docs.create_docs import create_docs_query -# from ..docs.embed_docs import embed_docs_snippets_query -# from ..agent.create_agent import create_agent_query -# from ..session.create_session import create_session_query -# from ..tools.create_tools import create_function_query -# from ..tools.embed_tools import embed_functions_query -# from ..user.create_user import create_user_query -# from .add_entries import add_entries_query -# from .get_entries import get_entries_query -# from .proc_mem_context import proc_mem_context_query - -# MODEL = "julep-ai/samantha-1-turbo" - - -# # Initializes a new CozoDB client for testing, applying all migrations. -# def cozo_client(migrations_dir: str = "./migrations"): -# # Create a new client for each test -# # and initialize the schema. -# client = Client() - -# init(client) -# apply(client, migrations_dir=migrations_dir, all_=True) - -# return client - - -# @test("model: create entry") -# def _(): -# """ -# Tests the addition of a new entry to the database. -# Verifies that the entry can be successfully added using the add_entries_query function. -# """ -# client = cozo_client() -# session_id = uuid4() - -# test_entry = Entry( -# session_id=session_id, -# role="user", -# content="test entry content", -# ) - -# add_entries_query(entries=[test_entry], client=client) - - -# @test("model: get entries") -# def _(): -# """ -# Tests the retrieval of entries from the database. -# Verifies that entries matching specific criteria can be successfully retrieved. -# """ -# client = cozo_client() -# session_id = uuid4() - -# test_entry = Entry( -# session_id=session_id, -# role="user", -# content="test entry content", -# ) - -# internal_entry = Entry( -# session_id=session_id, -# role="user", -# content="test entry content", -# source="internal", -# ) - -# add_entries_query(entries=[test_entry, internal_entry], client=client) - -# result = get_entries_query(session_id=session_id, client=client) - -# # Asserts that only one entry is retrieved, matching the session_id. -# assert len(result["entry_id"]) == 1 - - -# @test("model: procedural memory context") -# def _(): -# """ -# Tests the procedural memory context in the database. -# Verifies the functionality of retrieving relevant memory context based on embeddings. -# """ -# client = cozo_client() -# developer_id = uuid4() -# user_id = uuid4() -# agent_id = uuid4() -# session_id = uuid4() -# tool_id = uuid4() -# user_doc_id = uuid4() -# agent_doc_id = uuid4() - -# # Setup: Creates a user, agent, session, function, and documents, then embeds tools and document snippets. -# # Create stuff -# test_entry = Entry( -# session_id=session_id, -# role="user", -# content="test entry content", -# source="api_request", -# ) - -# test_instruction1 = "test instruction" -# test_instruction2 = "test instruction" -# test_function = FunctionDef( -# name="test function", -# description="test function description", -# parameters={"type": "object", "properties": {}}, -# ) - -# test_user_doc = "test user doc" -# test_agent_doc = "test agent doc" - -# [ -# add_entries_query(entries=[test_entry], client=client), -# create_user_query( -# user_id=user_id, -# developer_id=developer_id, -# name="test user", -# about="test user about", -# client=client, -# ), -# create_agent_query( -# agent_id=agent_id, -# model=MODEL, -# developer_id=developer_id, -# name="test agent", -# about="test agent about", -# instructions=[test_instruction1, test_instruction2], -# client=client, -# ), -# create_session_query( -# developer_id=developer_id, -# session_id=session_id, -# user_id=user_id, -# agent_id=agent_id, -# situation="test situation", -# client=client, -# ), -# create_function_query( -# agent_id=agent_id, id=tool_id, function=test_function, client=client -# ), -# create_docs_query( -# owner_type="agent", -# owner_id=agent_id, -# id=agent_doc_id, -# title=test_agent_doc, -# content=test_agent_doc, -# client=client, -# ), -# create_docs_query( -# owner_type="user", -# owner_id=user_id, -# id=user_doc_id, -# title=test_user_doc, -# content=test_user_doc, -# client=client, -# ), -# embed_functions_query( -# agent_id=agent_id, -# tool_ids=[tool_id], -# embeddings=[[1.0] * 768], -# client=client, -# ), -# embed_docs_snippets_query( -# agent_doc_id, snippet_indices=[0], embeddings=[[1.0] * 1024], client=client -# ), -# embed_docs_snippets_query( -# user_doc_id, snippet_indices=[0], embeddings=[[1.0] * 1024], client=client -# ), -# ] - -# # Executes the procedural memory context query to retrieve relevant memory context based on embeddings. -# # Run the query -# result = proc_mem_context_query( -# session_id=session_id, -# tool_query_embedding=[0.9] * 768, -# doc_query_embedding=[0.9] * 1024, -# client=client, -# ) - -# assert len(result) == 8 +""" +This module contains tests for entry queries against the CozoDB database. +It verifies the functionality of adding, retrieving, and processing entries as defined in the schema. +""" + +# Tests for entry queries +from uuid import uuid4 + +from cozo_migrate.api import apply, init +from pycozo import Client +from ward import test + +from ...autogen.openapi_model import Entry, FunctionDef +from ..agent.create_agent import create_agent +from ..docs.create_doc import create_doc +from ..docs.embed_snippets import embed_snippets +from ..session.create_session import create_session +from ..tools.create_tools import create_tools +from ..user.create_user import create_user +from .create_entries import create_entries +from .get_history import get_history +from .list_entries import list_entries + +MODEL = "julep-ai/samantha-1-turbo" + + +# Initializes a new CozoDB client for testing, applying all migrations. +def cozo_client(migrations_dir: str = "./migrations"): + # Create a new client for each test + # and initialize the schema. + client = Client() + + init(client) + apply(client, migrations_dir=migrations_dir, all_=True) + + return client + + +@test("model: create entry") +def _(): + """ + Tests the addition of a new entry to the database. + Verifies that the entry can be successfully added using the create_entries function. + """ + client = cozo_client() + session_id = uuid4() + developer_id = uuid4() + + test_entry = Entry( + session_id=session_id, + role="user", + content="test entry content", + ) + + create_entries( + developer_id=developer_id, + session_id=session_id, + data=[test_entry], + client=client, + ) + + +@test("model: get entries") +def _(): + """ + Tests the retrieval of entries from the database. + Verifies that entries matching specific criteria can be successfully retrieved. + """ + client = cozo_client() + session_id = uuid4() + developer_id = uuid4() + + test_entry = Entry( + session_id=session_id, + role="user", + content="test entry content", + ) + + internal_entry = Entry( + session_id=session_id, + role="user", + content="test entry content", + source="internal", + ) + + create_entries( + developer_id=developer_id, + session_id=session_id, + data=[test_entry, internal_entry], + client=client, + ) + + result = list_entries( + developer_id=developer_id, + session_id=session_id, + client=client, + ) + + # Asserts that only one entry is retrieved, matching the session_id. + assert len(result["id"]) == 1 + + +@test("model: procedural memory context") +def _(): + """ + Tests the procedural memory context in the database. + Verifies the functionality of retrieving relevant memory context based on embeddings. + """ + client = cozo_client() + developer_id = uuid4() + user_id = uuid4() + agent_id = uuid4() + session_id = uuid4() + tool_id = uuid4() + user_doc_id = uuid4() + agent_doc_id = uuid4() + + # Setup: Creates a user, agent, session, function, and documents, then embeds tools and document snippets. + # Create stuff + test_entry = Entry( + session_id=session_id, + role="user", + content="test entry content", + source="api_request", + ) + + test_instruction1 = "test instruction" + test_instruction2 = "test instruction" + test_function = FunctionDef( + name="test function", + description="test function description", + parameters={"type": "object", "properties": {}}, + ) + + test_user_doc = "test user doc" + test_agent_doc = "test agent doc" + + [ + create_entries( + developer_id=developer_id, + session_id=session_id, + data=[test_entry], + client=client, + ), + create_user( + developer_id=developer_id, + user_id=user_id, + data={ + "name": "test user", + "about": "test user about", + }, + client=client, + ), + create_agent( + developer_id=developer_id, + agent_id=agent_id, + data={ + "model": MODEL, + "name": "test agent", + "about": "test agent about", + "instructions": [test_instruction1, test_instruction2], + }, + client=client, + ), + create_session( + developer_id=developer_id, + session_id=session_id, + data={ + "users": [user_id], + "agents": [agent_id], + "situation": "test situation", + }, + client=client, + ), + create_tools( + developer_id=developer_id, + agent_id=agent_id, + data=[ + { + "name": test_function.name, + "description": test_function.description, + "parameters": test_function.parameters, + } + ], + client=client, + ), + create_doc( + developer_id=developer_id, + owner_type="agent", + owner_id=agent_id, + doc_id=agent_doc_id, + data={ + "title": test_agent_doc, + "content": [test_agent_doc], + }, + client=client, + ), + create_doc( + developer_id=developer_id, + owner_type="user", + owner_id=user_id, + doc_id=user_doc_id, + data={ + "title": test_user_doc, + "content": [test_user_doc], + }, + client=client, + ), + embed_snippets( + developer_id=developer_id, + doc_id=agent_doc_id, + snippet_indices=[0], + embeddings=[[1.0] * 1024], + client=client, + ), + embed_snippets( + developer_id=developer_id, + doc_id=user_doc_id, + snippet_indices=[0], + embeddings=[[1.0] * 1024], + client=client, + ), + ] + + # Executes the procedural memory context query to retrieve relevant memory context based on embeddings. + # Run the query + result = get_history( + developer_id=developer_id, + session_id=session_id, + client=client, + ) + + assert len(result["entries"]) == 1 diff --git a/agents-api/agents_api/models/execution/create_execution.py b/agents-api/agents_api/models/execution/create_execution.py index 959bb92de..90da93fc5 100644 --- a/agents-api/agents_api/models/execution/create_execution.py +++ b/agents-api/agents_api/models/execution/create_execution.py @@ -1,3 +1,4 @@ +from typing import Annotated from uuid import UUID, uuid4 from beartype import beartype @@ -7,6 +8,7 @@ from ...autogen.openapi_model import CreateExecutionRequest, Execution from ...common.utils.cozo import cozo_process_mutate_data +from ...common.utils.types import dict_like from ..utils import ( cozo_query, partialclass, @@ -36,7 +38,7 @@ def create_execution( developer_id: UUID, task_id: UUID, execution_id: UUID | None = None, - data: CreateExecutionRequest, + data: Annotated[CreateExecutionRequest | dict, dict_like(CreateExecutionRequest)], ) -> tuple[list[str], dict]: execution_id = execution_id or uuid4() @@ -44,8 +46,12 @@ def create_execution( task_id = str(task_id) execution_id = str(execution_id) - data.metadata = data.metadata or {} - execution_data = data.model_dump() + if isinstance(data, CreateExecutionRequest): + data.metadata = data.metadata or {} + execution_data = data.model_dump() + else: + data["metadata"] = data.get("metadata", {}) + execution_data = data columns, values = cozo_process_mutate_data( { diff --git a/agents-api/agents_api/models/execution/test_execution_queries.py b/agents-api/agents_api/models/execution/test_execution_queries.py index eade4329c..6ca5e3a2f 100644 --- a/agents-api/agents_api/models/execution/test_execution_queries.py +++ b/agents-api/agents_api/models/execution/test_execution_queries.py @@ -1,377 +1,213 @@ -# # Tests for execution queries -# from uuid import uuid4 -# -# from cozo_migrate.api import init, apply -# from pycozo import Client -# from ward import test - -# from ..agent.create_agent import create_agent_query -# from ..task.create_task import create_task_query -# from .create_execution import create_execution_query -# from .get_execution import get_execution_query -# from .get_execution_status import get_execution_status_query -# from .get_execution_input import get_execution_input_query -# from .list_executions import list_task_executions_query -# from .update_execution_status import update_execution_status_query -# from .create_execution_transition import create_execution_transition_query -# from .get_execution_transition import get_execution_transition_query -# from .list_execution_transitions import list_execution_transitions_query -# from .update_execution_transition import update_execution_transition_query - -# from ...common.protocol.tasks import ExecutionInput - - -# def cozo_client(migrations_dir: str = "./migrations"): -# # Create a new client for each test -# # and initialize the schema. -# client = Client() - -# init(client) -# apply(client, migrations_dir=migrations_dir, all_=True) - -# return client - - -# @test("model: create execution") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() -# execution_id = uuid4() - -# create_execution_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# execution_id=execution_id, -# arguments={"input": "test"}, -# client=client, -# ) - - -# @test("model: create execution with session") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() -# execution_id = uuid4() -# session_id = uuid4() - -# create_execution_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# execution_id=execution_id, -# session_id=session_id, -# arguments={"input": "test"}, -# client=client, -# ) - - -# @test("model: get execution") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() -# execution_id = uuid4() - -# create_execution_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# execution_id=execution_id, -# arguments={"input": "test"}, -# client=client, -# ) - -# result = get_execution_query( -# task_id=task_id, execution_id=execution_id, client=client -# ) - -# assert len(result["status"]) == 1 -# assert result["status"][0] == "queued" - - -# @test("model: get execution status") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() -# execution_id = uuid4() - -# create_execution_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# execution_id=execution_id, -# arguments={"input": "test"}, -# client=client, -# ) - -# result = get_execution_status_query( -# task_id=task_id, execution_id=execution_id, client=client -# ) - -# assert len(result["status"]) == 1 -# assert result["status"][0] == "queued" - - -# @test("model: get execution input") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() -# execution_id = uuid4() - -# create_agent_query( -# agent_id=agent_id, -# developer_id=developer_id, -# name="test", -# about="test", -# model="gpt-4", -# metadata={"test": "test"}, -# client=client, -# ) - -# create_task_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# name="test", -# description="test", -# input_schema={"test": "test"}, -# tools_available=[], -# workflows=[], -# client=client, -# ) - -# create_execution_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# execution_id=execution_id, -# arguments={"input": "test"}, -# client=client, -# ) - -# result = get_execution_input_query( -# task_id=task_id, execution_id=execution_id, client=client -# ) - -# assert len(result["execution"]) == 1 - - -# @test("model: fetch execution input") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() -# execution_id = uuid4() - -# create_agent_query( -# agent_id=agent_id, -# developer_id=developer_id, -# name="test", -# about="test", -# model="gpt-4", -# metadata={"test": "test"}, -# client=client, -# ) - -# create_task_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# name="test", -# description="test", -# input_schema={"test": "test"}, -# tools_available=[], -# workflows=[{"name": "main", "steps": []}], -# client=client, -# ) - -# create_execution_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# execution_id=execution_id, -# arguments={"input": "test"}, -# client=client, -# ) - -# result = ExecutionInput.fetch( -# developer_id=developer_id, -# task_id=task_id, -# execution_id=execution_id, -# client=client, -# ) - -# assert result.execution.id == execution_id - - -# @test("model: list executions empty") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() - -# result = list_task_executions_query( -# task_id=task_id, agent_id=agent_id, developer_id=developer_id, client=client -# ) - -# assert len(result) == 0 - - -# @test("model: list executions") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() -# execution_id = uuid4() - -# create_execution_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# execution_id=execution_id, -# arguments={"input": "test"}, -# client=client, -# ) - -# result = list_task_executions_query( -# task_id=task_id, agent_id=agent_id, developer_id=developer_id, client=client -# ) - -# assert len(result["status"]) == 1 -# assert result["status"][0] == "queued" - - -# @test("model: update execution status") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() -# execution_id = uuid4() - -# create_execution_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# execution_id=execution_id, -# arguments={"input": "test"}, -# client=client, -# ) - -# result = update_execution_status_query( -# task_id=task_id, execution_id=execution_id, status="running", client=client -# ) - -# updated_rows = result[result["_kind"] == "inserted"].reset_index() -# assert len(updated_rows) == 1 -# assert updated_rows["status"][0] == "running" - - -# @test("model: create execution transition") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# execution_id = uuid4() -# transition_id = uuid4() - -# create_execution_transition_query( -# developer_id=developer_id, -# execution_id=execution_id, -# transition_id=transition_id, -# type="step", -# from_=("test", 1), -# to=("test", 2), -# outputs={"input": "test"}, -# client=client, -# ) - - -# @test("model: get execution transition") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# execution_id = uuid4() -# transition_id = uuid4() - -# create_execution_transition_query( -# developer_id=developer_id, -# execution_id=execution_id, -# transition_id=transition_id, -# type="step", -# from_=("test", 1), -# to=("test", 2), -# outputs={"input": "test"}, -# client=client, -# ) - -# result = get_execution_transition_query( -# execution_id=execution_id, transition_id=transition_id, client=client -# ) - -# assert len(result["type"]) == 1 - - -# @test("model: list execution transitions") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# execution_id = uuid4() -# transition_id = uuid4() - -# create_execution_transition_query( -# developer_id=developer_id, -# execution_id=execution_id, -# transition_id=transition_id, -# type="step", -# from_=("test", 1), -# to=("test", 2), -# outputs={"input": "test"}, -# client=client, -# ) - -# result = list_execution_transitions_query(execution_id=execution_id, client=client) - -# assert len(result["type"]) == 1 - - -# @test("model: update execution transitions") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# execution_id = uuid4() -# transition_id = uuid4() - -# create_execution_transition_query( -# developer_id=developer_id, -# execution_id=execution_id, -# transition_id=transition_id, -# type="step", -# from_=("test", 1), -# to=("test", 2), -# outputs={"input": "test"}, -# client=client, -# ) - -# result = update_execution_transition_query( -# execution_id=execution_id, -# transition_id=transition_id, -# type="finished", -# client=client, -# ) - -# updated_rows = result[result["_kind"] == "inserted"].reset_index() -# assert len(updated_rows) == 1 -# assert updated_rows["type"][0] == "finished" +# Tests for execution queries +from uuid import uuid4 + +from cozo_migrate.api import apply, init +from pycozo import Client +from ward import test + +from agents_api.autogen.openapi_model import Execution, Transition + +from ..agent.create_agent import create_agent +from ..task.create_task import create_task +from .create_execution import create_execution +from .create_execution_transition import create_execution_transition +from .get_execution import get_execution +from .get_execution_transition import get_execution_transition +from .list_execution_transitions import list_execution_transitions +from .list_executions import list_executions + +MODEL = "julep-ai/samantha-1-turbo" + + +def cozo_client(migrations_dir: str = "./migrations"): + # Create a new client for each test + # and initialize the schema. + client = Client() + + init(client) + apply(client, migrations_dir=migrations_dir, all_=True) + + return client + + +@test("model: create execution") +def _(): + client = cozo_client() + developer_id = uuid4() + task_id = uuid4() + execution_id = uuid4() + + create_execution( + developer_id=developer_id, + task_id=task_id, + execution_id=execution_id, + data={"input": "test"}, + client=client, + ) + + +@test("model: create execution with session") +def _(): + client = cozo_client() + developer_id = uuid4() + task_id = uuid4() + execution_id = uuid4() + session_id = uuid4() + + create_execution( + developer_id=developer_id, + task_id=task_id, + execution_id=execution_id, + data={"input": "test", "session_id": session_id}, + client=client, + ) + + +@test("model: get execution") +def _(): + client = cozo_client() + developer_id = uuid4() + task_id = uuid4() + execution_id = uuid4() + + create_execution( + developer_id=developer_id, + task_id=task_id, + execution_id=execution_id, + data={"input": "test"}, + client=client, + ) + + result = get_execution( + execution_id=execution_id, + client=client, + ) + + assert result is not None + assert isinstance(result, Execution) + assert result.status == "queued" + + +@test("model: list executions empty") +def _(): + client = cozo_client() + developer_id = uuid4() + task_id = uuid4() + + result = list_executions( + developer_id=developer_id, + task_id=task_id, + client=client, + ) + + assert isinstance(result, list) + assert len(result) == 0 + + +@test("model: list executions") +def _(): + client = cozo_client() + developer_id = uuid4() + task_id = uuid4() + execution_id = uuid4() + + create_execution( + developer_id=developer_id, + task_id=task_id, + execution_id=execution_id, + data={"input": "test"}, + client=client, + ) + + result = list_executions( + developer_id=developer_id, + task_id=task_id, + client=client, + ) + + assert isinstance(result, list) + assert len(result) == 1 + assert result[0].status == "queued" + + +@test("model: create execution transition") +def _(): + client = cozo_client() + developer_id = uuid4() + execution_id = uuid4() + transition_id = uuid4() + + create_execution_transition( + developer_id=developer_id, + execution_id=execution_id, + transition_id=transition_id, + data={ + "type": "step", + "from": "test", + "to": "test", + "outputs": {"input": "test"}, + }, + client=client, + ) + + +@test("model: get execution transition") +def _(): + client = cozo_client() + developer_id = uuid4() + execution_id = uuid4() + transition_id = uuid4() + + create_execution_transition( + developer_id=developer_id, + execution_id=execution_id, + transition_id=transition_id, + data={ + "type": "step", + "from": "test", + "to": "test", + "outputs": {"input": "test"}, + }, + client=client, + ) + + result = get_execution_transition( + developer_id=developer_id, + transition_id=transition_id, + client=client, + ) + + assert result is not None + assert isinstance(result, Transition) + assert result.type == "step" + + +@test("model: list execution transitions") +def _(): + client = cozo_client() + developer_id = uuid4() + execution_id = uuid4() + transition_id = uuid4() + + create_execution_transition( + developer_id=developer_id, + execution_id=execution_id, + transition_id=transition_id, + data={ + "type": "step", + "from": "test", + "to": "test", + "outputs": {"input": "test"}, + }, + client=client, + ) + + result = list_execution_transitions( + developer_id=developer_id, + execution_id=execution_id, + client=client, + ) + + assert isinstance(result, list) + assert len(result) == 1 + assert result[0].type == "step" diff --git a/agents-api/agents_api/models/session/test_session_queries.py b/agents-api/agents_api/models/session/test_session_queries.py index 8bc750359..b878e8c54 100644 --- a/agents-api/agents_api/models/session/test_session_queries.py +++ b/agents-api/agents_api/models/session/test_session_queries.py @@ -1,284 +1,160 @@ -# """ -# This module contains tests for session-related queries against the 'cozodb' database. It verifies the creation, retrieval, and deletion of session records as defined in the schema provided in agents-api/README.md. -# """ - -# # Tests for session queries -# from uuid import uuid4 - -# from cozo_migrate.api import init, apply -# from pycozo import Client -# from ward import test, skip - -# from ..agent.create_agent import create_agent_query -# from ..user.create_user import create_user_query - -# from .create_session import create_session_query -# from .delete_session import delete_session_query -# from .get_session import get_session_query -# from .list_sessions import list_sessions_query -# from .session_data import get_session_data, session_data_query - - -# MODEL = "julep-ai/samantha-1-turbo" - - -# def cozo_client(migrations_dir: str = "./migrations"): -# # Create a new client for each test -# # and initialize the schema. -# client = Client() - -# init(client) -# apply(client, migrations_dir=migrations_dir, all_=True) - -# return client - - -# @test("model: create session") -# def _(): -# """Test session creation with a valid session, user, agent, and developer IDs.""" -# client = cozo_client() -# session_id = uuid4() -# agent_id = uuid4() -# user_id = uuid4() -# developer_id = uuid4() - -# create_session_query( -# session_id=session_id, -# user_id=user_id, -# developer_id=developer_id, -# agent_id=agent_id, -# situation="test session about", -# client=client, -# ) - - -# @test("model: create session no user") -# def _(): -# """Test session creation without a user ID.""" -# client = cozo_client() -# session_id = uuid4() -# agent_id = uuid4() -# developer_id = uuid4() - -# create_session_query( -# session_id=session_id, -# user_id=None, -# developer_id=developer_id, -# agent_id=agent_id, -# situation="test session about", -# client=client, -# ) - - -# @test("model: get session not exists") -# def _(): -# """Verify that querying a non-existent session returns an empty result.""" -# client = cozo_client() -# session_id = uuid4() -# developer_id = uuid4() - -# result = get_session_query( -# session_id=session_id, -# developer_id=developer_id, -# client=client, -# ) - -# assert len(result["id"]) == 0 - - -# @test("model: get session exists") -# def _(): -# """Verify that a created session can be successfully retrieved.""" -# client = cozo_client() -# session_id = uuid4() -# agent_id = uuid4() -# user_id = uuid4() -# developer_id = uuid4() - -# result = create_session_query( -# session_id=session_id, -# user_id=user_id, -# agent_id=agent_id, -# developer_id=developer_id, -# situation="test session about", -# client=client, -# ) - -# result = get_session_query( -# session_id=session_id, -# developer_id=developer_id, -# client=client, -# ) - -# assert len(result["id"]) == 1 - - -# @test("model: get session data") -# def _(): -# """Test retrieval of session data for an existing session.""" -# # Setup client for user and agent -# client = cozo_client() - -# session_id = uuid4() -# agent_id = uuid4() -# user_id = uuid4() -# developer_id = uuid4() - -# # Create a user -# create_user_query( -# user_id=user_id, -# developer_id=developer_id, -# about="test user about", -# name="test user name", -# client=client, -# ) - -# # Create an agent -# create_agent_query( -# agent_id=agent_id, -# model=MODEL, -# developer_id=developer_id, -# about="test agent about", -# name="test agent name", -# client=client, -# ) - -# # Create a session - -# result = create_session_query( -# session_id=session_id, -# user_id=user_id, -# agent_id=agent_id, -# developer_id=developer_id, -# situation="test session about", -# client=client, -# ) - -# result = session_data_query( -# session_id=session_id, -# developer_id=developer_id, -# client=client, -# ) - -# assert len(result["user_about"]) == 1 - - -# @test("model: delete session") -# def _(): -# """Test the deletion of a session and verify it cannot be retrieved afterwards.""" -# # Setup client for user and agent -# client = cozo_client() - -# session_id = uuid4() -# agent_id = uuid4() -# user_id = uuid4() -# developer_id = uuid4() - -# # Create a user -# create_user_query( -# user_id=user_id, -# developer_id=developer_id, -# about="test user about", -# name="test user name", -# client=client, -# ) - -# # Create an agent -# create_agent_query( -# agent_id=agent_id, -# model=MODEL, -# developer_id=developer_id, -# about="test agent about", -# name="test agent name", -# client=client, -# ) - -# # Create a session -# result = create_session_query( -# session_id=session_id, -# user_id=user_id, -# agent_id=agent_id, -# developer_id=developer_id, -# situation="test session about", -# client=client, -# ) - -# # Delete the session -# result = delete_session_query( -# session_id=session_id, -# developer_id=developer_id, -# client=client, -# ) - -# # Check that the session is deleted -# result = get_session_query( -# session_id=session_id, -# developer_id=developer_id, -# client=client, -# ) - -# assert len(result["id"]) == 0 - - -# @skip("get session data using get_session_data") -# def _(): -# # Setup client for user and agent -# client = cozo_client() - -# developer_id = uuid4() -# session_id = uuid4() -# agent_id = uuid4() -# user_id = uuid4() - -# # Setup: Create a user, agent, and session for testing session data retrieval using get_session_data. -# # Create a user -# create_user_query( -# user_id=user_id, -# developer_id=developer_id, -# about="test user about", -# name="test user name", -# client=client, -# ) - -# # Create an agent -# create_agent_query( -# developer_id=developer_id, -# model=MODEL, -# agent_id=agent_id, -# about="test agent about", -# name="test agent name", -# client=client, -# ) - -# # Create a session - -# create_session_query( -# developer_id=developer_id, -# session_id=session_id, -# user_id=user_id, -# agent_id=agent_id, -# situation="test session about", -# client=client, -# ) - -# session_data = get_session_data( -# developer_id=developer_id, -# session_id=session_id, -# client=client, -# ) - -# assert session_data is not None -# assert session_data.user_about == "test user about" - - -# @skip("list sessions") -# def _(): -# client = cozo_client() -# developer_id = uuid4() - -# result = list_sessions_query( -# developer_id=developer_id, -# client=client, -# ) - -# assert len(result["id"]) == 0 +# Tests for session queries +from uuid import uuid4 + +from cozo_migrate.api import apply, init +from pycozo import Client +from ward import test + +from agents_api.autogen.openapi_model import Session + +from ..agent.create_agent import create_agent +from ..user.create_user import create_user +from .create_session import create_session +from .delete_session import delete_session +from .get_session import get_session +from .list_sessions import list_sessions + +MODEL = "julep-ai/samantha-1-turbo" + + +def cozo_client(migrations_dir: str = "./migrations"): + # Create a new client for each test + # and initialize the schema. + client = Client() + + init(client) + apply(client, migrations_dir=migrations_dir, all_=True) + + return client + + +@test("model: create session") +def _(): + client = cozo_client() + session_id = uuid4() + agent_id = uuid4() + user_id = uuid4() + developer_id = uuid4() + + create_session( + session_id=session_id, + developer_id=developer_id, + data={ + "users": [user_id], + "agents": [agent_id], + "situation": "test session about", + }, + client=client, + ) + + +@test("model: create session no user") +def _(): + client = cozo_client() + session_id = uuid4() + agent_id = uuid4() + developer_id = uuid4() + + create_session( + session_id=session_id, + developer_id=developer_id, + data={ + "agents": [agent_id], + "situation": "test session about", + }, + client=client, + ) + + +@test("model: get session not exists") +def _(): + client = cozo_client() + session_id = uuid4() + developer_id = uuid4() + + try: + get_session( + session_id=session_id, + developer_id=developer_id, + client=client, + ) + except Exception as e: + assert str(e) == "Session not found" + + +@test("model: get session exists") +def _(): + client = cozo_client() + session_id = uuid4() + agent_id = uuid4() + user_id = uuid4() + developer_id = uuid4() + + create_session( + session_id=session_id, + developer_id=developer_id, + data={ + "users": [user_id], + "agents": [agent_id], + "situation": "test session about", + }, + client=client, + ) + + result = get_session( + session_id=session_id, + developer_id=developer_id, + client=client, + ) + + assert result is not None + assert isinstance(result, Session) + + +@test("model: delete session") +def _(): + client = cozo_client() + session_id = uuid4() + agent_id = uuid4() + user_id = uuid4() + developer_id = uuid4() + + create_session( + session_id=session_id, + developer_id=developer_id, + data={ + "users": [user_id], + "agents": [agent_id], + "situation": "test session about", + }, + client=client, + ) + + delete_session( + session_id=session_id, + developer_id=developer_id, + client=client, + ) + + try: + get_session( + session_id=session_id, + developer_id=developer_id, + client=client, + ) + except Exception as e: + assert str(e) == "Session not found" + + +@test("model: list sessions") +def _(): + client = cozo_client() + developer_id = uuid4() + + result = list_sessions( + developer_id=developer_id, + client=client, + ) + + assert isinstance(result, list) + assert len(result) == 0 diff --git a/agents-api/agents_api/models/task/test_task_queries.py b/agents-api/agents_api/models/task/test_task_queries.py index ff9150f99..4ec5ae875 100644 --- a/agents-api/agents_api/models/task/test_task_queries.py +++ b/agents-api/agents_api/models/task/test_task_queries.py @@ -1,90 +1,176 @@ -# # Tests for task queries -# from uuid import uuid4 - -# from cozo_migrate.api import init, apply -# from pycozo import Client -# from ward import test - -# from .create_task import create_task_query -# from .get_task import get_task_query -# from .list_tasks import list_tasks_query - - -# def cozo_client(migrations_dir: str = "./migrations"): -# # Create a new client for each test -# # and initialize the schema. -# client = Client() - -# init(client) -# apply(client, migrations_dir=migrations_dir, all_=True) - -# return client - - -# @test("model: create task") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() - -# create_task_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# name="test task", -# description="test task about", -# input_schema={"type": "object", "additionalProperties": True}, -# client=client, -# ) - - -# @test("model: list tasks") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() - -# result = list_tasks_query( -# developer_id=developer_id, -# agent_id=agent_id, -# client=client, -# ) - -# assert len(result["id"]) == 0 - - -# @test("model: get task exists") -# def _(): -# client = cozo_client() -# developer_id = uuid4() -# agent_id = uuid4() -# task_id = uuid4() - -# create_task_query( -# developer_id=developer_id, -# agent_id=agent_id, -# task_id=task_id, -# name="test task", -# description="test task about", -# input_schema={"type": "object", "additionalProperties": True}, -# client=client, -# ) - -# result = get_task_query( -# agent_id=agent_id, task_id=task_id, developer_id=developer_id, client=client -# ) - -# assert len(result["id"]) == 1 - - -# # @test("model: delete task") -# # def _(): -# # TODO: Implement this test -# # raise NotImplementedError - - -# # @test("model: update task") -# # def _(): -# # TODO: Implement this test -# # raise NotImplementedError +# Tests for task queries +from uuid import uuid4 + +from cozo_migrate.api import apply, init +from pycozo import Client +from ward import test + +from agents_api.autogen.openapi_model import Task + +from .create_task import create_task +from .delete_task import delete_task +from .get_task import get_task +from .list_tasks import list_tasks +from .update_task import update_task + + +def cozo_client(migrations_dir: str = "./migrations"): + # Create a new client for each test + # and initialize the schema. + client = Client() + + init(client) + apply(client, migrations_dir=migrations_dir, all_=True) + + return client + + +@test("model: create task") +def _(): + client = cozo_client() + developer_id = uuid4() + agent_id = uuid4() + task_id = uuid4() + + create_task( + developer_id=developer_id, + agent_id=agent_id, + task_id=task_id, + data={ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, + client=client, + ) + + +@test("model: get task not exists") +def _(): + client = cozo_client() + developer_id = uuid4() + task_id = uuid4() + + try: + get_task( + developer_id=developer_id, + task_id=task_id, + client=client, + ) + except Exception as e: + assert str(e) == "Task not found" + + +@test("model: get task exists") +def _(): + client = cozo_client() + developer_id = uuid4() + agent_id = uuid4() + task_id = uuid4() + + create_task( + developer_id=developer_id, + agent_id=agent_id, + task_id=task_id, + data={ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, + client=client, + ) + + result = get_task( + developer_id=developer_id, + task_id=task_id, + client=client, + ) + + assert result is not None + assert isinstance(result, Task) + + +@test("model: delete task") +def _(): + client = cozo_client() + developer_id = uuid4() + agent_id = uuid4() + task_id = uuid4() + + create_task( + developer_id=developer_id, + agent_id=agent_id, + task_id=task_id, + data={ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, + client=client, + ) + + delete_task( + developer_id=developer_id, + task_id=task_id, + client=client, + ) + + try: + get_task( + developer_id=developer_id, + task_id=task_id, + client=client, + ) + except Exception as e: + assert str(e) == "Task not found" + + +@test("model: update task") +def _(): + client = cozo_client() + developer_id = uuid4() + agent_id = uuid4() + task_id = uuid4() + + create_task( + developer_id=developer_id, + agent_id=agent_id, + task_id=task_id, + data={ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, + client=client, + ) + + result = update_task( + developer_id=developer_id, + task_id=task_id, + data={ + "name": "updated task", + "description": "updated task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, + client=client, + ) + + assert result is not None + assert isinstance(result, Task) + assert result.name == "updated task" + + +@test("model: list tasks") +def _(): + client = cozo_client() + developer_id = uuid4() + agent_id = uuid4() + + result = list_tasks( + developer_id=developer_id, + agent_id=agent_id, + client=client, + ) + + assert isinstance(result, list) + assert all(isinstance(task, Task) for task in result) diff --git a/agents-api/agents_api/models/tools/test_tool_queries.py b/agents-api/agents_api/models/tools/test_tool_queries.py index 058b36eba..6032abd7c 100644 --- a/agents-api/agents_api/models/tools/test_tool_queries.py +++ b/agents-api/agents_api/models/tools/test_tool_queries.py @@ -1,127 +1,138 @@ -# # Tests for entry queries -# from uuid import uuid4 - -# from cozo_migrate.api import init, apply -# from pycozo import Client -# from ward import test - -# from ...autogen.openapi_model import FunctionDef -# from .create_tools import create_function_query, create_multiple_functions_query -# from .delete_tools import delete_function_by_id_query -# from .get_tool import get_function_by_id_query -# from .list_tools import list_functions_by_agent_query - - -# def cozo_client(migrations_dir: str = "./migrations"): -# # Create a new client for each test -# # and initialize the schema. -# client = Client() - -# init(client) -# apply(client, migrations_dir=migrations_dir, all_=True) - -# return client - - -# @test("model: create function") -# def _(): -# client = cozo_client() - -# agent_id = uuid4() -# tool_id = uuid4() -# function = FunctionDef( -# name="hello_world", -# description="A function that prints hello world", -# parameters={"type": "object", "properties": {}}, -# ) - -# result = create_function_query(agent_id, tool_id, function, client=client) - -# assert result["created_at"][0] - - -# @test("model: create multiple functions") -# def _(): -# client = cozo_client() - -# agent_id = uuid4() -# function = FunctionDef( -# name="hello_world", -# description="A function that prints hello world", -# parameters={"type": "object", "properties": {}}, -# ) -# num_functions = 10 - -# result = create_multiple_functions_query( -# agent_id, [function] * num_functions, client=client -# ) - -# assert result["created_at"][0] -# assert len(result["tool_id"]) == num_functions - - -# @test("model: delete function") -# def _(): -# client = cozo_client() - -# # Create function -# agent_id = uuid4() -# tool_id = uuid4() -# function = FunctionDef( -# name="hello_world", -# description="A function that prints hello world", -# parameters={"type": "object", "properties": {}}, -# ) - -# create_function_query(agent_id, tool_id, function, client=client) - -# # Delete function -# result = delete_function_by_id_query(agent_id, tool_id, client=client) - -# delete_info = next( -# (row for row in result.to_dict("records") if row["_kind"] == "deleted"), None -# ) - -# assert delete_info is not None, "Delete operation did not find the row" - - -# @test("model: get function") -# def _(): -# client = cozo_client() - -# # Create function -# agent_id = uuid4() -# tool_id = uuid4() -# function = FunctionDef( -# name="hello_world", -# description="A function that prints hello world", -# parameters={"type": "object", "properties": {}}, -# ) - -# create_function_query(agent_id, tool_id, function, client=client) - -# # Get function -# result = get_function_by_id_query(agent_id, tool_id, client=client) - -# assert len(result["tool_id"]) == 1, "Get operation did not find the row" - - -# @test("model: list functions") -# def _(): -# client = cozo_client() - -# agent_id = uuid4() -# function = FunctionDef( -# name="hello_world", -# description="A function that prints hello world", -# parameters={"type": "object", "properties": {}}, -# ) -# num_functions = 10 - -# # Create functions -# create_multiple_functions_query(agent_id, [function] * num_functions, client=client) - -# # List functions -# result = list_functions_by_agent_query(agent_id, client=client) - -# assert len(result["tool_id"]) == num_functions +# Tests for tool queries +from uuid import uuid4 + +from cozo_migrate.api import apply, init +from pycozo import Client +from ward import test + +from agents_api.autogen.openapi_model import FunctionDef, Tool + +from .create_tools import create_tools +from .delete_tool import delete_tool +from .get_tool import get_tool +from .list_tools import list_tools + + +def cozo_client(migrations_dir: str = "./migrations"): + # Create a new client for each test + # and initialize the schema. + client = Client() + + init(client) + apply(client, migrations_dir=migrations_dir, all_=True) + + return client + + +@test("model: create tool") +def _(): + client = cozo_client() + developer_id = uuid4() + agent_id = uuid4() + + tool = FunctionDef( + name="hello_world", + description="A function that prints hello world", + parameters={"type": "object", "properties": {}}, + ) + + result = create_tools( + developer_id=developer_id, + agent_id=agent_id, + data=[tool], + client=client, + ) + + assert result is not None + assert isinstance(result[0], Tool) + + +@test("model: delete tool") +def _(): + client = cozo_client() + developer_id = uuid4() + agent_id = uuid4() + tool_id = uuid4() + + tool = FunctionDef( + name="hello_world", + description="A function that prints hello world", + parameters={"type": "object", "properties": {}}, + ) + + create_tools( + developer_id=developer_id, + agent_id=agent_id, + data=[tool], + client=client, + ) + + result = delete_tool( + developer_id=developer_id, + agent_id=agent_id, + tool_id=tool_id, + client=client, + ) + + assert result is not None + assert result.id == tool_id + + +@test("model: get tool") +def _(): + client = cozo_client() + developer_id = uuid4() + agent_id = uuid4() + tool_id = uuid4() + + tool = FunctionDef( + name="hello_world", + description="A function that prints hello world", + parameters={"type": "object", "properties": {}}, + ) + + create_tools( + developer_id=developer_id, + agent_id=agent_id, + data=[tool], + client=client, + ) + + result = get_tool( + developer_id=developer_id, + agent_id=agent_id, + tool_id=tool_id, + client=client, + ) + + assert result is not None + assert isinstance(result, Tool) + + +@test("model: list tools") +def _(): + client = cozo_client() + developer_id = uuid4() + agent_id = uuid4() + + tool = FunctionDef( + name="hello_world", + description="A function that prints hello world", + parameters={"type": "object", "properties": {}}, + ) + + create_tools( + developer_id=developer_id, + agent_id=agent_id, + data=[tool], + client=client, + ) + + result = list_tools( + developer_id=developer_id, + agent_id=agent_id, + client=client, + ) + + assert result is not None + assert all(isinstance(tool, Tool) for tool in result) diff --git a/agents-api/agents_api/models/user/test_user_queries.py b/agents-api/agents_api/models/user/test_user_queries.py index e33d044e1..385ba9ab0 100644 --- a/agents-api/agents_api/models/user/test_user_queries.py +++ b/agents-api/agents_api/models/user/test_user_queries.py @@ -1,170 +1,189 @@ -# """This module contains tests for user-related queries against the 'cozodb' database. It includes tests for creating, updating, and retrieving user information.""" - -# # Tests for user queries -# from uuid import uuid4 - -# from cozo_migrate.api import init, apply -# from pycozo import Client -# from ward import raises, test - -# from .create_user import create_user_query -# from .get_user import get_user_query -# from .list_users import list_users_query -# from .update_user import update_user_query - - -# def cozo_client(migrations_dir: str = "./migrations"): -# """Initializes a new Cozo client for testing, applying all migrations to ensure the database schema is up to date.""" -# # Create a new client for each test -# # and initialize the schema. -# client = Client() - -# init(client) -# apply(client, migrations_dir=migrations_dir, all_=True) - -# return client - - -# @test("model: create user") -# def _(): -# """Test that a user can be successfully created.""" -# client = cozo_client() -# user_id = uuid4() -# developer_id = uuid4() - -# create_user_query( -# user_id=user_id, -# developer_id=developer_id, -# name="test user", -# about="test user about", -# client=client, -# ) - - -# @test("model: create user twice should fail") -# def _(): -# """Test that attempting to create the same user twice results in a failure.""" -# client = cozo_client() -# user_id = uuid4() -# developer_id = uuid4() - -# # Expect an exception to be raised as creating the same user twice should not be allowed. -# # Should fail because the user already exists. -# with raises(Exception): -# create_user_query( -# user_id=user_id, -# developer_id=developer_id, -# name="test user", -# about="test user about", -# client=client, -# ) - -# create_user_query( -# user_id=user_id, -# developer_id=developer_id, -# name="test user", -# about="test user about", -# client=client, -# ) - - -# @test("model: update non-existent user should fail") -# def _(): -# """Test that attempting to update a non-existent user results in a failure.""" -# client = cozo_client() -# user_id = uuid4() -# developer_id = uuid4() - -# # Should fail because the user doecn't exists. -# with raises(Exception): -# update_user_query( -# user_id=user_id, -# developer_id=developer_id, -# name="test user", -# about="test user about", -# client=client, -# ) - - -# @test("model: update user") -# def _(): -# """Test that an existing user's information can be successfully updated.""" -# client = cozo_client() -# user_id = uuid4() -# developer_id = uuid4() - -# create_user_query( -# user_id=user_id, -# developer_id=developer_id, -# name="test user", -# about="test user about", -# client=client, -# ) - -# # Verify that the 'updated_at' timestamp is greater than the 'created_at' timestamp, indicating a successful update. -# update_result = update_user_query( -# user_id=user_id, -# developer_id=developer_id, -# name="updated user", -# about="updated user about", -# client=client, -# ) - -# data = update_result.iloc[0].to_dict() - -# assert data["updated_at"] > data["created_at"] - - -# @test("model: get user not exists") -# def _(): -# """Test that retrieving a non-existent user returns an empty result.""" -# client = cozo_client() -# user_id = uuid4() -# developer_id = uuid4() - -# # Ensure that the query for an existing user returns exactly one result. -# result = get_user_query( -# user_id=user_id, -# developer_id=developer_id, -# client=client, -# ) - -# assert len(result["id"]) == 0 - - -# @test("model: get user exists") -# def _(): -# """Test that retrieving an existing user returns the correct user information.""" -# client = cozo_client() -# user_id = uuid4() -# developer_id = uuid4() - -# result = create_user_query( -# user_id=user_id, -# developer_id=developer_id, -# name="test user", -# about="test user about", -# client=client, -# ) - -# result = get_user_query( -# user_id=user_id, -# developer_id=developer_id, -# client=client, -# ) - -# assert len(result["id"]) == 1 - - -# @test("model: list users") -# def _(): -# """Test that listing users returns a collection of user information.""" -# client = cozo_client() -# developer_id = uuid4() - -# result = list_users_query( -# developer_id=developer_id, -# client=client, -# ) - -# assert len(result["id"]) == 0 +# This module contains tests for user-related queries against the 'cozodb' database. It includes tests for creating, updating, and retrieving user information. + +# Tests for user queries +from uuid import uuid4 + +from cozo_migrate.api import apply, init +from pycozo import Client +from ward import raises, test + +from agents_api.autogen.openapi_model import User + +from .create_user import create_user +from .get_user import get_user +from .list_users import list_users +from .update_user import update_user + + +def cozo_client(migrations_dir: str = "./migrations"): + """Initializes a new Cozo client for testing, applying all migrations to ensure the database schema is up to date.""" + # Create a new client for each test + # and initialize the schema. + client = Client() + + init(client) + apply(client, migrations_dir=migrations_dir, all_=True) + + return client + + +@test("model: create user") +def _(): + """Test that a user can be successfully created.""" + client = cozo_client() + user_id = uuid4() + developer_id = uuid4() + + create_user( + user_id=user_id, + developer_id=developer_id, + data={ + "name": "test user", + "about": "test user about", + }, + client=client, + ) + + +@test("model: create user twice should fail") +def _(): + """Test that attempting to create the same user twice results in a failure.""" + client = cozo_client() + user_id = uuid4() + developer_id = uuid4() + + # Expect an exception to be raised as creating the same user twice should not be allowed. + # Should fail because the user already exists. + with raises(Exception): + create_user( + user_id=user_id, + developer_id=developer_id, + data={ + "name": "test user", + "about": "test user about", + }, + client=client, + ) + + create_user( + user_id=user_id, + developer_id=developer_id, + data={ + "name": "test user", + "about": "test user about", + }, + client=client, + ) + + +@test("model: update non-existent user should fail") +def _(): + """Test that attempting to update a non-existent user results in a failure.""" + client = cozo_client() + user_id = uuid4() + developer_id = uuid4() + + # Should fail because the user doesn't exist. + with raises(Exception): + update_user( + user_id=user_id, + developer_id=developer_id, + data={ + "name": "test user", + "about": "test user about", + }, + client=client, + ) + + +@test("model: update user") +def _(): + """Test that an existing user's information can be successfully updated.""" + client = cozo_client() + user_id = uuid4() + developer_id = uuid4() + + create_user( + user_id=user_id, + developer_id=developer_id, + data={ + "name": "test user", + "about": "test user about", + }, + client=client, + ) + + # Verify that the 'updated_at' timestamp is greater than the 'created_at' timestamp, indicating a successful update. + update_result = update_user( + user_id=user_id, + developer_id=developer_id, + data={ + "name": "updated user", + "about": "updated user about", + }, + client=client, + ) + + assert update_result is not None + assert isinstance(update_result, User) + assert update_result.updated_at > update_result.created_at + + +@test("model: get user not exists") +def _(): + """Test that retrieving a non-existent user returns an empty result.""" + client = cozo_client() + user_id = uuid4() + developer_id = uuid4() + + # Ensure that the query for an existing user returns exactly one result. + try: + get_user( + user_id=user_id, + developer_id=developer_id, + client=client, + ) + except Exception as e: + assert str(e) == "User not found" + + +@test("model: get user exists") +def _(): + """Test that retrieving an existing user returns the correct user information.""" + client = cozo_client() + user_id = uuid4() + developer_id = uuid4() + + create_user( + user_id=user_id, + developer_id=developer_id, + data={ + "name": "test user", + "about": "test user about", + }, + client=client, + ) + + result = get_user( + user_id=user_id, + developer_id=developer_id, + client=client, + ) + + assert result is not None + assert isinstance(result, User) + + +@test("model: list users") +def _(): + """Test that listing users returns a collection of user information.""" + client = cozo_client() + developer_id = uuid4() + + result = list_users( + developer_id=developer_id, + client=client, + ) + + assert isinstance(result, list) + assert all(isinstance(user, User) for user in result) diff --git a/agents-api/tests/test_activities.py b/agents-api/tests/test_activities.py index 5ebf49d5b..2723911cc 100644 --- a/agents-api/tests/test_activities.py +++ b/agents-api/tests/test_activities.py @@ -1,92 +1,92 @@ -import time -import uuid +# import time +# import uuid -from ward import test +# from ward import test -from agents_api.activities.truncation import get_extra_entries -from agents_api.autogen.openapi_model import Role -from agents_api.common.protocol.entries import Entry +# from agents_api.activities.truncation import get_extra_entries +# from agents_api.autogen.openapi_model import Role +# from agents_api.common.protocol.entries import Entry -@test("get extra entries, do not strip system message") -def _(): - session_ids = [uuid.uuid4()] * 3 - entry_ids = [uuid.uuid4()] * 3 - now = time.time() - messages = [ - Entry( - entry_id=entry_ids[0], - session_id=session_ids[0], - role=Role.system, - content="content 1", - created_at=now, - timestamp=now, - ), - Entry( - entry_id=entry_ids[1], - session_id=session_ids[1], - role=Role.assistant, - content="content 2", - created_at=now, - timestamp=now, - ), - Entry( - entry_id=entry_ids[2], - session_id=session_ids[2], - role=Role.user, - content="content 3", - created_at=now, - timestamp=now, - ), - ] +# @test("get extra entries, do not strip system message") +# def _(): +# session_ids = [uuid.uuid4()] * 3 +# entry_ids = [uuid.uuid4()] * 3 +# now = time.time() +# messages = [ +# Entry( +# entry_id=entry_ids[0], +# session_id=session_ids[0], +# role=Role.system, +# content="content 1", +# created_at=now, +# timestamp=now, +# ), +# Entry( +# entry_id=entry_ids[1], +# session_id=session_ids[1], +# role=Role.assistant, +# content="content 2", +# created_at=now, +# timestamp=now, +# ), +# Entry( +# entry_id=entry_ids[2], +# session_id=session_ids[2], +# role=Role.user, +# content="content 3", +# created_at=now, +# timestamp=now, +# ), +# ] - threshold = sum([m.token_count for m in messages]) - 1 - result = get_extra_entries(messages, threshold) +# threshold = sum([m.token_count for m in messages]) - 1 +# result = get_extra_entries(messages, threshold) - assert result == [messages[1].id] +# assert result == [messages[1].id] -@test("get extra entries") -def _(): - session_ids = [uuid.uuid4()] * 3 - entry_ids = [uuid.uuid4()] * 3 - now = time.time() - messages = [ - Entry( - entry_id=entry_ids[0], - session_id=session_ids[0], - role=Role.user, - content="content 1", - created_at=now, - timestamp=now, - ), - Entry( - entry_id=entry_ids[1], - session_id=session_ids[1], - role=Role.assistant, - content="content 2", - created_at=now, - timestamp=now, - ), - Entry( - entry_id=entry_ids[2], - session_id=session_ids[2], - role=Role.user, - content="content 3", - created_at=now, - timestamp=now, - ), - ] +# @test("get extra entries") +# def _(): +# session_ids = [uuid.uuid4()] * 3 +# entry_ids = [uuid.uuid4()] * 3 +# now = time.time() +# messages = [ +# Entry( +# entry_id=entry_ids[0], +# session_id=session_ids[0], +# role=Role.user, +# content="content 1", +# created_at=now, +# timestamp=now, +# ), +# Entry( +# entry_id=entry_ids[1], +# session_id=session_ids[1], +# role=Role.assistant, +# content="content 2", +# created_at=now, +# timestamp=now, +# ), +# Entry( +# entry_id=entry_ids[2], +# session_id=session_ids[2], +# role=Role.user, +# content="content 3", +# created_at=now, +# timestamp=now, +# ), +# ] - threshold = sum([m.token_count for m in messages]) - 1 - result = get_extra_entries(messages, threshold) +# threshold = sum([m.token_count for m in messages]) - 1 +# result = get_extra_entries(messages, threshold) - assert result == [messages[0].id] +# assert result == [messages[0].id] -@test("get extra entries, no change if empty") -def _(): - messages = [] - result = get_extra_entries(messages, 1) +# @test("get extra entries, no change if empty") +# def _(): +# messages = [] +# result = get_extra_entries(messages, 1) - assert result == [] +# assert result == [] diff --git a/agents-api/tests/test_agents.py b/agents-api/tests/test_agents.py index e5076e37d..046aa188d 100644 --- a/agents-api/tests/test_agents.py +++ b/agents-api/tests/test_agents.py @@ -1,433 +1,433 @@ -import uuid - -from julep.api import Agent, ResourceCreatedResponse, ResourceUpdatedResponse -from julep.api.core import ApiError -from ward import test - -from tests.fixtures import agent, async_client, client - - -@test("create new agent with tools") -def _(client=client): - agent = client.agents.create( - name="Samantha", - about="about Samantha", - instructions=[ - "non-important content", - "important content", - ], - tools=[ - { - "type": "function", - "function": { - "description": "func desc", - "name": "some_func", - "parameters": {"param1": "string"}, - }, - } - ], - default_settings={ - "frequency_penalty": 0.1, - "length_penalty": 0.9, - "presence_penalty": 0.8, - "repetition_penalty": 0.7, - "temperature": 0.6, - "top_p": 0.5, - }, - model="julep-ai/samantha-1-turbo", - docs=[ - { - "title": "some titie", - "content": "some content", - }, - ], - ) - - assert isinstance(agent, ResourceCreatedResponse) - assert agent.created_at - assert bool(uuid.UUID(str(agent.id), version=4)) - - -@test("async create new agent with tools") -async def _(client=async_client): - agent = await client.agents.create( - name="Samantha", - about="about Samantha", - instructions=[ - "non-important content", - "important content", - ], - tools=[ - { - "type": "function", - "function": { - "description": "func desc", - "name": "some_func", - "parameters": {"param1": "string"}, - }, - } - ], - default_settings={ - "frequency_penalty": 0.1, - "length_penalty": 0.9, - "presence_penalty": 0.8, - "repetition_penalty": 0.7, - "temperature": 0.6, - "top_p": 0.5, - }, - model="julep-ai/samantha-1-turbo", - docs=[ - { - "title": "some titie", - "content": "some content", - }, - ], - ) - - assert isinstance(agent, ResourceCreatedResponse) - assert agent.created_at - assert bool(uuid.UUID(str(agent.id), version=4)) - - -@test("create new agent with functions") -def _(client=client): - agent = client.agents.create( - name="Samantha", - about="about Samantha", - instructions=[ - "non-important content", - "important content", - ], - functions=[ - { - "description": "func desc", - "name": "some_func", - "parameters": {"param1": "string"}, - } - ], - default_settings={ - "frequency_penalty": 0.1, - "length_penalty": 0.9, - "presence_penalty": 0.8, - "repetition_penalty": 0.7, - "temperature": 0.6, - "top_p": 0.5, - }, - model="julep-ai/samantha-1-turbo", - docs=[ - { - "title": "some titie", - "content": "some content", - }, - ], - ) - - assert isinstance(agent, ResourceCreatedResponse) - assert agent.created_at - assert bool(uuid.UUID(str(agent.id), version=4)) - - -@test("async create new agent with functions") -async def _(client=async_client): - agent = await client.agents.create( - name="Samantha", - about="about Samantha", - instructions=[ - "non-important content", - "important content", - ], - functions=[ - { - "description": "func desc", - "name": "some_func", - "parameters": {"param1": "string"}, - } - ], - default_settings={ - "frequency_penalty": 0.1, - "length_penalty": 0.9, - "presence_penalty": 0.8, - "repetition_penalty": 0.7, - "temperature": 0.6, - "top_p": 0.5, - }, - model="julep-ai/samantha-1-turbo", - docs=[ - { - "title": "some titie", - "content": "some content", - }, - ], - ) - - assert isinstance(agent, ResourceCreatedResponse) - assert agent.created_at - assert bool(uuid.UUID(str(agent.id), version=4)) - - -@test("create new agent with functions and tools") -def _(client=client): - try: - client.agents.create( - name="Samantha", - about="about Samantha", - instructions=[ - "non-important content", - "important content", - ], - tools=[ - { - "type": "function", - "function": { - "description": "func desc", - "name": "some_func", - "parameters": {"param1": "string"}, - }, - } - ], - functions=[ - { - "description": "func desc", - "name": "some_func", - "parameters": {"param1": "string"}, - } - ], - default_settings={ - "frequency_penalty": 0.1, - "length_penalty": 0.9, - "presence_penalty": 0.8, - "repetition_penalty": 0.7, - "temperature": 0.6, - "top_p": 0.5, - }, - model="julep-ai/samantha-1-turbo", - docs=[ - { - "title": "some titie", - "content": "some content", - }, - ], - ) - except Exception: - assert True - else: - assert False - - -@test("async create new agent with functions and tools") -async def _(client=async_client): - try: - await client.agents.create( - name="Samantha", - about="about Samantha", - instructions=[ - "non-important content", - "important content", - ], - tools=[ - { - "type": "function", - "function": { - "description": "func desc", - "name": "some_func", - "parameters": {"param1": "string"}, - }, - } - ], - functions=[ - { - "description": "func desc", - "name": "some_func", - "parameters": {"param1": "string"}, - } - ], - default_settings={ - "frequency_penalty": 0.1, - "length_penalty": 0.9, - "presence_penalty": 0.8, - "repetition_penalty": 0.7, - "temperature": 0.6, - "top_p": 0.5, - }, - model="julep-ai/samantha-1-turbo", - docs=[ - { - "title": "some titie", - "content": "some content", - }, - ], - ) - except Exception: - assert True - else: - assert False - - -@test("update existing agent") -def _(client=client, existing_agent=agent): - response = client.agents.update( - agent_id=agent.id, - name="test user", - about="test user about", - instructions=["test agent instructions"], - default_settings={"temperature": 0.5}, - model="some model", - ) - - assert isinstance(response, ResourceUpdatedResponse) - assert response.updated_at != existing_agent.updated_at - assert response.id == existing_agent.id - - -@test("async update existing agent") -async def _(client=async_client, existing_agent=agent): - response = await client.agents.update( - agent_id=agent.id, - name="test user", - about="test user about", - instructions=["test agent instructions"], - default_settings={"temperature": 0.5}, - model="some model", - ) - - assert isinstance(response, ResourceUpdatedResponse) - assert response.updated_at != existing_agent.updated_at - assert response.id == existing_agent.id - - -@test("update non-existing agent") -def _(client=client): - try: - client.agents.update( - agent_id=uuid.uuid4(), - name="test user", - about="test user about", - instructions=["test agent instructions"], - default_settings={"temperature": 0.5}, - model="some model", - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("async update non-existing agent") -async def _(client=async_client): - try: - await client.agents.update( - agent_id=uuid.uuid4(), - name="test user", - about="test user about", - instructions=["test agent instructions"], - default_settings={"temperature": 0.5}, - model="some model", - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("delete existing agent") -def _(client=client, existing_agent=agent): - response = client.agents.delete( - existing_agent.id, - ) - - assert response is None - - -@test("async delete existing agent") -async def _(client=async_client, existing_agent=agent): - response = await client.agents.delete( - existing_agent.id, - ) - - assert response is None - - -@test("delete non-existing agent") -def _(client=client): - try: - client.agents.delete( - uuid.uuid4(), - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("async delete non-existing agent") -async def _(client=async_client): - try: - await client.agents.delete( - uuid.uuid4(), - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("get existing agent") -def _(client=client, existing_agent=agent): - response = client.agents.get(existing_agent.id) - assert isinstance(response, Agent) - assert response.id == existing_agent.id - - -@test("async get existing agent") -async def _(client=async_client, existing_agent=agent): - response = await client.agents.get(existing_agent.id) - assert isinstance(response, Agent) - assert response.id == existing_agent.id - - -@test("get non-existing agent") -def _(client=client): - try: - client.agents.get(uuid.uuid4()) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("async get non-existing agent") -async def _(client=async_client): - try: - await client.agents.get(uuid.uuid4()) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("list agents") -def _(client=client, existing_agent=agent): - response = client.agents.list() - assert len(response) > 0 - assert isinstance(response[0], Agent) - assert response[0].id == existing_agent.id - - -@test("async list agents") -async def _(client=async_client, existing_agent=agent): - response = await client.agents.list() - assert len(response) > 0 - assert isinstance(response[0], Agent) - assert response[0].id == existing_agent.id +# import uuid + +# from julep.api import Agent, ResourceCreatedResponse, ResourceUpdatedResponse +# from julep.api.core import ApiError +# from ward import test + +# from tests.fixtures import agent, async_client, client + + +# @test("create new agent with tools") +# def _(client=client): +# agent = client.agents.create( +# name="Samantha", +# about="about Samantha", +# instructions=[ +# "non-important content", +# "important content", +# ], +# tools=[ +# { +# "type": "function", +# "function": { +# "description": "func desc", +# "name": "some_func", +# "parameters": {"param1": "string"}, +# }, +# } +# ], +# default_settings={ +# "frequency_penalty": 0.1, +# "length_penalty": 0.9, +# "presence_penalty": 0.8, +# "repetition_penalty": 0.7, +# "temperature": 0.6, +# "top_p": 0.5, +# }, +# model="julep-ai/samantha-1-turbo", +# docs=[ +# { +# "title": "some titie", +# "content": "some content", +# }, +# ], +# ) + +# assert isinstance(agent, ResourceCreatedResponse) +# assert agent.created_at +# assert bool(uuid.UUID(str(agent.id), version=4)) + + +# @test("async create new agent with tools") +# async def _(client=async_client): +# agent = await client.agents.create( +# name="Samantha", +# about="about Samantha", +# instructions=[ +# "non-important content", +# "important content", +# ], +# tools=[ +# { +# "type": "function", +# "function": { +# "description": "func desc", +# "name": "some_func", +# "parameters": {"param1": "string"}, +# }, +# } +# ], +# default_settings={ +# "frequency_penalty": 0.1, +# "length_penalty": 0.9, +# "presence_penalty": 0.8, +# "repetition_penalty": 0.7, +# "temperature": 0.6, +# "top_p": 0.5, +# }, +# model="julep-ai/samantha-1-turbo", +# docs=[ +# { +# "title": "some titie", +# "content": "some content", +# }, +# ], +# ) + +# assert isinstance(agent, ResourceCreatedResponse) +# assert agent.created_at +# assert bool(uuid.UUID(str(agent.id), version=4)) + + +# @test("create new agent with functions") +# def _(client=client): +# agent = client.agents.create( +# name="Samantha", +# about="about Samantha", +# instructions=[ +# "non-important content", +# "important content", +# ], +# functions=[ +# { +# "description": "func desc", +# "name": "some_func", +# "parameters": {"param1": "string"}, +# } +# ], +# default_settings={ +# "frequency_penalty": 0.1, +# "length_penalty": 0.9, +# "presence_penalty": 0.8, +# "repetition_penalty": 0.7, +# "temperature": 0.6, +# "top_p": 0.5, +# }, +# model="julep-ai/samantha-1-turbo", +# docs=[ +# { +# "title": "some titie", +# "content": "some content", +# }, +# ], +# ) + +# assert isinstance(agent, ResourceCreatedResponse) +# assert agent.created_at +# assert bool(uuid.UUID(str(agent.id), version=4)) + + +# @test("async create new agent with functions") +# async def _(client=async_client): +# agent = await client.agents.create( +# name="Samantha", +# about="about Samantha", +# instructions=[ +# "non-important content", +# "important content", +# ], +# functions=[ +# { +# "description": "func desc", +# "name": "some_func", +# "parameters": {"param1": "string"}, +# } +# ], +# default_settings={ +# "frequency_penalty": 0.1, +# "length_penalty": 0.9, +# "presence_penalty": 0.8, +# "repetition_penalty": 0.7, +# "temperature": 0.6, +# "top_p": 0.5, +# }, +# model="julep-ai/samantha-1-turbo", +# docs=[ +# { +# "title": "some titie", +# "content": "some content", +# }, +# ], +# ) + +# assert isinstance(agent, ResourceCreatedResponse) +# assert agent.created_at +# assert bool(uuid.UUID(str(agent.id), version=4)) + + +# @test("create new agent with functions and tools") +# def _(client=client): +# try: +# client.agents.create( +# name="Samantha", +# about="about Samantha", +# instructions=[ +# "non-important content", +# "important content", +# ], +# tools=[ +# { +# "type": "function", +# "function": { +# "description": "func desc", +# "name": "some_func", +# "parameters": {"param1": "string"}, +# }, +# } +# ], +# functions=[ +# { +# "description": "func desc", +# "name": "some_func", +# "parameters": {"param1": "string"}, +# } +# ], +# default_settings={ +# "frequency_penalty": 0.1, +# "length_penalty": 0.9, +# "presence_penalty": 0.8, +# "repetition_penalty": 0.7, +# "temperature": 0.6, +# "top_p": 0.5, +# }, +# model="julep-ai/samantha-1-turbo", +# docs=[ +# { +# "title": "some titie", +# "content": "some content", +# }, +# ], +# ) +# except Exception: +# assert True +# else: +# assert False + + +# @test("async create new agent with functions and tools") +# async def _(client=async_client): +# try: +# await client.agents.create( +# name="Samantha", +# about="about Samantha", +# instructions=[ +# "non-important content", +# "important content", +# ], +# tools=[ +# { +# "type": "function", +# "function": { +# "description": "func desc", +# "name": "some_func", +# "parameters": {"param1": "string"}, +# }, +# } +# ], +# functions=[ +# { +# "description": "func desc", +# "name": "some_func", +# "parameters": {"param1": "string"}, +# } +# ], +# default_settings={ +# "frequency_penalty": 0.1, +# "length_penalty": 0.9, +# "presence_penalty": 0.8, +# "repetition_penalty": 0.7, +# "temperature": 0.6, +# "top_p": 0.5, +# }, +# model="julep-ai/samantha-1-turbo", +# docs=[ +# { +# "title": "some titie", +# "content": "some content", +# }, +# ], +# ) +# except Exception: +# assert True +# else: +# assert False + + +# @test("update existing agent") +# def _(client=client, existing_agent=agent): +# response = client.agents.update( +# agent_id=agent.id, +# name="test user", +# about="test user about", +# instructions=["test agent instructions"], +# default_settings={"temperature": 0.5}, +# model="some model", +# ) + +# assert isinstance(response, ResourceUpdatedResponse) +# assert response.updated_at != existing_agent.updated_at +# assert response.id == existing_agent.id + + +# @test("async update existing agent") +# async def _(client=async_client, existing_agent=agent): +# response = await client.agents.update( +# agent_id=agent.id, +# name="test user", +# about="test user about", +# instructions=["test agent instructions"], +# default_settings={"temperature": 0.5}, +# model="some model", +# ) + +# assert isinstance(response, ResourceUpdatedResponse) +# assert response.updated_at != existing_agent.updated_at +# assert response.id == existing_agent.id + + +# @test("update non-existing agent") +# def _(client=client): +# try: +# client.agents.update( +# agent_id=uuid.uuid4(), +# name="test user", +# about="test user about", +# instructions=["test agent instructions"], +# default_settings={"temperature": 0.5}, +# model="some model", +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("async update non-existing agent") +# async def _(client=async_client): +# try: +# await client.agents.update( +# agent_id=uuid.uuid4(), +# name="test user", +# about="test user about", +# instructions=["test agent instructions"], +# default_settings={"temperature": 0.5}, +# model="some model", +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("delete existing agent") +# def _(client=client, existing_agent=agent): +# response = client.agents.delete( +# existing_agent.id, +# ) + +# assert response is None + + +# @test("async delete existing agent") +# async def _(client=async_client, existing_agent=agent): +# response = await client.agents.delete( +# existing_agent.id, +# ) + +# assert response is None + + +# @test("delete non-existing agent") +# def _(client=client): +# try: +# client.agents.delete( +# uuid.uuid4(), +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("async delete non-existing agent") +# async def _(client=async_client): +# try: +# await client.agents.delete( +# uuid.uuid4(), +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("get existing agent") +# def _(client=client, existing_agent=agent): +# response = client.agents.get(existing_agent.id) +# assert isinstance(response, Agent) +# assert response.id == existing_agent.id + + +# @test("async get existing agent") +# async def _(client=async_client, existing_agent=agent): +# response = await client.agents.get(existing_agent.id) +# assert isinstance(response, Agent) +# assert response.id == existing_agent.id + + +# @test("get non-existing agent") +# def _(client=client): +# try: +# client.agents.get(uuid.uuid4()) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("async get non-existing agent") +# async def _(client=async_client): +# try: +# await client.agents.get(uuid.uuid4()) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("list agents") +# def _(client=client, existing_agent=agent): +# response = client.agents.list() +# assert len(response) > 0 +# assert isinstance(response[0], Agent) +# assert response[0].id == existing_agent.id + + +# @test("async list agents") +# async def _(client=async_client, existing_agent=agent): +# response = await client.agents.list() +# assert len(response) > 0 +# assert isinstance(response[0], Agent) +# assert response[0].id == existing_agent.id diff --git a/agents-api/tests/test_messages_truncation.py b/agents-api/tests/test_messages_truncation.py index 2edd5610e..97516617a 100644 --- a/agents-api/tests/test_messages_truncation.py +++ b/agents-api/tests/test_messages_truncation.py @@ -1,316 +1,316 @@ -from uuid import uuid4 +# from uuid import uuid4 -from ward import raises, test +# from ward import raises, test -from agents_api.autogen.openapi_model import Role -from agents_api.common.protocol.entries import Entry -from agents_api.routers.sessions.exceptions import InputTooBigError -from tests.fixtures import base_session +# from agents_api.autogen.openapi_model import Role +# from agents_api.common.protocol.entries import Entry +# from agents_api.routers.sessions.exceptions import InputTooBigError +# from tests.fixtures import base_session -@test("truncate empty messages list", tags=["messages_truncate"]) -def _(session=base_session): - messages: list[Entry] = [] - result = session.truncate(messages, 10) +# @test("truncate empty messages list", tags=["messages_truncate"]) +# def _(session=base_session): +# messages: list[Entry] = [] +# result = session.truncate(messages, 10) - assert messages == result +# assert messages == result -@test("do not truncate", tags=["messages_truncate"]) -def _(session=base_session): - contents = [ - "content1", - "content2", - "content3", - ] - threshold = sum([len(c) // 3.5 for c in contents]) +# @test("do not truncate", tags=["messages_truncate"]) +# def _(session=base_session): +# contents = [ +# "content1", +# "content2", +# "content3", +# ] +# threshold = sum([len(c) // 3.5 for c in contents]) - messages: list[Entry] = [ - Entry(session_id=uuid4(), role=Role.user, content=contents[0][0]), - Entry(session_id=uuid4(), role=Role.assistant, content=contents[1][0]), - Entry(session_id=uuid4(), role=Role.user, content=contents[2][0]), - ] - result = session.truncate(messages, threshold) +# messages: list[Entry] = [ +# Entry(session_id=uuid4(), role=Role.user, content=contents[0][0]), +# Entry(session_id=uuid4(), role=Role.assistant, content=contents[1][0]), +# Entry(session_id=uuid4(), role=Role.user, content=contents[2][0]), +# ] +# result = session.truncate(messages, threshold) - assert messages == result +# assert messages == result -@test("truncate thoughts partially", tags=["messages_truncate"]) -def _(session=base_session): - contents = [ - ("content1", True), - ("content2", True), - ("content3", False), - ("content4", True), - ("content5", True), - ("content6", True), - ] - session_ids = [uuid4()] * len(contents) - threshold = sum([len(c) // 3.5 for c, i in contents if i]) +# @test("truncate thoughts partially", tags=["messages_truncate"]) +# def _(session=base_session): +# contents = [ +# ("content1", True), +# ("content2", True), +# ("content3", False), +# ("content4", True), +# ("content5", True), +# ("content6", True), +# ] +# session_ids = [uuid4()] * len(contents) +# threshold = sum([len(c) // 3.5 for c, i in contents if i]) - messages: list[Entry] = [ - Entry( - session_id=session_ids[0], - role=Role.system, - name="thought", - content=contents[0][0], - ), - Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), - Entry( - session_id=session_ids[2], - role=Role.system, - name="thought", - content=contents[2][0], - ), - Entry( - session_id=session_ids[3], - role=Role.system, - name="thought", - content=contents[3][0], - ), - Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), - Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), - ] - result = session.truncate(messages, threshold) - [ - messages[0], - messages[1], - messages[3], - messages[4], - messages[5], - ] +# messages: list[Entry] = [ +# Entry( +# session_id=session_ids[0], +# role=Role.system, +# name="thought", +# content=contents[0][0], +# ), +# Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), +# Entry( +# session_id=session_ids[2], +# role=Role.system, +# name="thought", +# content=contents[2][0], +# ), +# Entry( +# session_id=session_ids[3], +# role=Role.system, +# name="thought", +# content=contents[3][0], +# ), +# Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), +# Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), +# ] +# result = session.truncate(messages, threshold) +# [ +# messages[0], +# messages[1], +# messages[3], +# messages[4], +# messages[5], +# ] - assert result == [ - messages[0], - messages[1], - messages[3], - messages[4], - messages[5], - ] +# assert result == [ +# messages[0], +# messages[1], +# messages[3], +# messages[4], +# messages[5], +# ] -@test("truncate thoughts partially 2", tags=["messages_truncate"]) -def _(session=base_session): - contents = [ - ("content1", True), - ("content2", True), - ("content3", False), - ("content4", False), - ("content5", True), - ("content6", True), - ] - session_ids = [uuid4()] * len(contents) - threshold = sum([len(c) // 3.5 for c, i in contents if i]) +# @test("truncate thoughts partially 2", tags=["messages_truncate"]) +# def _(session=base_session): +# contents = [ +# ("content1", True), +# ("content2", True), +# ("content3", False), +# ("content4", False), +# ("content5", True), +# ("content6", True), +# ] +# session_ids = [uuid4()] * len(contents) +# threshold = sum([len(c) // 3.5 for c, i in contents if i]) - messages: list[Entry] = [ - Entry( - session_id=session_ids[0], - role=Role.system, - name="thought", - content=contents[0][0], - ), - Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), - Entry( - session_id=session_ids[2], - role=Role.system, - name="thought", - content=contents[2][0], - ), - Entry( - session_id=session_ids[3], - role=Role.system, - name="thought", - content=contents[3][0], - ), - Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), - Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), - ] - result = session.truncate(messages, threshold) +# messages: list[Entry] = [ +# Entry( +# session_id=session_ids[0], +# role=Role.system, +# name="thought", +# content=contents[0][0], +# ), +# Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), +# Entry( +# session_id=session_ids[2], +# role=Role.system, +# name="thought", +# content=contents[2][0], +# ), +# Entry( +# session_id=session_ids[3], +# role=Role.system, +# name="thought", +# content=contents[3][0], +# ), +# Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), +# Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), +# ] +# result = session.truncate(messages, threshold) - assert result == [ - messages[0], - messages[1], - messages[4], - messages[5], - ] +# assert result == [ +# messages[0], +# messages[1], +# messages[4], +# messages[5], +# ] -@test("truncate all thoughts", tags=["messages_truncate"]) -def _(session=base_session): - contents = [ - ("content1", False), - ("content2", True), - ("content3", False), - ("content4", False), - ("content5", True), - ("content6", True), - ("content7", False), - ] - session_ids = [uuid4()] * len(contents) - threshold = sum([len(c) // 3.5 for c, i in contents if i]) +# @test("truncate all thoughts", tags=["messages_truncate"]) +# def _(session=base_session): +# contents = [ +# ("content1", False), +# ("content2", True), +# ("content3", False), +# ("content4", False), +# ("content5", True), +# ("content6", True), +# ("content7", False), +# ] +# session_ids = [uuid4()] * len(contents) +# threshold = sum([len(c) // 3.5 for c, i in contents if i]) - messages: list[Entry] = [ - Entry( - session_id=session_ids[0], - role=Role.system, - name="thought", - content=contents[0][0], - ), - Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), - Entry( - session_id=session_ids[2], - role=Role.system, - name="thought", - content=contents[2][0], - ), - Entry( - session_id=session_ids[3], - role=Role.system, - name="thought", - content=contents[3][0], - ), - Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), - Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), - Entry( - session_id=session_ids[6], - role=Role.system, - name="thought", - content=contents[6][0], - ), - ] - result = session.truncate(messages, threshold) +# messages: list[Entry] = [ +# Entry( +# session_id=session_ids[0], +# role=Role.system, +# name="thought", +# content=contents[0][0], +# ), +# Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), +# Entry( +# session_id=session_ids[2], +# role=Role.system, +# name="thought", +# content=contents[2][0], +# ), +# Entry( +# session_id=session_ids[3], +# role=Role.system, +# name="thought", +# content=contents[3][0], +# ), +# Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), +# Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), +# Entry( +# session_id=session_ids[6], +# role=Role.system, +# name="thought", +# content=contents[6][0], +# ), +# ] +# result = session.truncate(messages, threshold) - assert result == [ - messages[1], - messages[4], - messages[5], - ] +# assert result == [ +# messages[1], +# messages[4], +# messages[5], +# ] -@test("truncate user assistant pairs", tags=["messages_truncate"]) -def _(session=base_session): - contents = [ - ("content1", False), - ("content2", True), - ("content3", False), - ("content4", False), - ("content5", True), - ("content6", True), - ("content7", True), - ("content8", False), - ("content9", True), - ("content10", True), - ("content11", True), - ("content12", True), - ("content13", False), - ] - session_ids = [uuid4()] * len(contents) - threshold = sum([len(c) // 3.5 for c, i in contents if i]) +# @test("truncate user assistant pairs", tags=["messages_truncate"]) +# def _(session=base_session): +# contents = [ +# ("content1", False), +# ("content2", True), +# ("content3", False), +# ("content4", False), +# ("content5", True), +# ("content6", True), +# ("content7", True), +# ("content8", False), +# ("content9", True), +# ("content10", True), +# ("content11", True), +# ("content12", True), +# ("content13", False), +# ] +# session_ids = [uuid4()] * len(contents) +# threshold = sum([len(c) // 3.5 for c, i in contents if i]) - messages: list[Entry] = [ - Entry( - session_id=session_ids[0], - role=Role.system, - name="thought", - content=contents[0][0], - ), - Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), - Entry( - session_id=session_ids[2], - role=Role.system, - name="thought", - content=contents[2][0], - ), - Entry( - session_id=session_ids[3], - role=Role.system, - name="thought", - content=contents[3][0], - ), - Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), - Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), - Entry(session_id=session_ids[6], role=Role.user, content=contents[6][0]), - Entry(session_id=session_ids[7], role=Role.assistant, content=contents[7][0]), - Entry(session_id=session_ids[8], role=Role.user, content=contents[8][0]), - Entry(session_id=session_ids[9], role=Role.assistant, content=contents[9][0]), - Entry(session_id=session_ids[10], role=Role.user, content=contents[10][0]), - Entry(session_id=session_ids[11], role=Role.assistant, content=contents[11][0]), - Entry( - session_id=session_ids[12], - role=Role.system, - name="thought", - content=contents[12][0], - ), - ] +# messages: list[Entry] = [ +# Entry( +# session_id=session_ids[0], +# role=Role.system, +# name="thought", +# content=contents[0][0], +# ), +# Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), +# Entry( +# session_id=session_ids[2], +# role=Role.system, +# name="thought", +# content=contents[2][0], +# ), +# Entry( +# session_id=session_ids[3], +# role=Role.system, +# name="thought", +# content=contents[3][0], +# ), +# Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), +# Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), +# Entry(session_id=session_ids[6], role=Role.user, content=contents[6][0]), +# Entry(session_id=session_ids[7], role=Role.assistant, content=contents[7][0]), +# Entry(session_id=session_ids[8], role=Role.user, content=contents[8][0]), +# Entry(session_id=session_ids[9], role=Role.assistant, content=contents[9][0]), +# Entry(session_id=session_ids[10], role=Role.user, content=contents[10][0]), +# Entry(session_id=session_ids[11], role=Role.assistant, content=contents[11][0]), +# Entry( +# session_id=session_ids[12], +# role=Role.system, +# name="thought", +# content=contents[12][0], +# ), +# ] - result = session.truncate(messages, threshold) +# result = session.truncate(messages, threshold) - assert result == [ - messages[1], - messages[4], - messages[5], - messages[6], - messages[8], - messages[9], - messages[10], - messages[11], - ] +# assert result == [ +# messages[1], +# messages[4], +# messages[5], +# messages[6], +# messages[8], +# messages[9], +# messages[10], +# messages[11], +# ] -@test("unable to truncate", tags=["messages_truncate"]) -def _(session=base_session): - contents = [ - ("content1", False), - ("content2", True), - ("content3", False), - ("content4", False), - ("content5", False), - ("content6", False), - ("content7", True), - ("content8", False), - ("content9", True), - ("content10", False), - ] - session_ids = [uuid4()] * len(contents) - threshold = sum([len(c) // 3.5 for c, i in contents if i]) - all_tokens = sum([len(c) // 3.5 for c, _ in contents]) +# @test("unable to truncate", tags=["messages_truncate"]) +# def _(session=base_session): +# contents = [ +# ("content1", False), +# ("content2", True), +# ("content3", False), +# ("content4", False), +# ("content5", False), +# ("content6", False), +# ("content7", True), +# ("content8", False), +# ("content9", True), +# ("content10", False), +# ] +# session_ids = [uuid4()] * len(contents) +# threshold = sum([len(c) // 3.5 for c, i in contents if i]) +# all_tokens = sum([len(c) // 3.5 for c, _ in contents]) - messages: list[Entry] = [ - Entry( - session_id=session_ids[0], - role=Role.system, - name="thought", - content=contents[0][0], - ), - Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), - Entry( - session_id=session_ids[2], - role=Role.system, - name="thought", - content=contents[2][0], - ), - Entry( - session_id=session_ids[3], - role=Role.system, - name="thought", - content=contents[3][0], - ), - Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), - Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), - Entry(session_id=session_ids[6], role=Role.user, content=contents[6][0]), - Entry(session_id=session_ids[7], role=Role.assistant, content=contents[7][0]), - Entry(session_id=session_ids[8], role=Role.user, content=contents[8][0]), - Entry( - session_id=session_ids[9], - role=Role.system, - name="thought", - content=contents[9][0], - ), - ] - with raises(InputTooBigError) as ex: - session.truncate(messages, threshold) +# messages: list[Entry] = [ +# Entry( +# session_id=session_ids[0], +# role=Role.system, +# name="thought", +# content=contents[0][0], +# ), +# Entry(session_id=session_ids[1], role=Role.assistant, content=contents[1][0]), +# Entry( +# session_id=session_ids[2], +# role=Role.system, +# name="thought", +# content=contents[2][0], +# ), +# Entry( +# session_id=session_ids[3], +# role=Role.system, +# name="thought", +# content=contents[3][0], +# ), +# Entry(session_id=session_ids[4], role=Role.user, content=contents[4][0]), +# Entry(session_id=session_ids[5], role=Role.assistant, content=contents[5][0]), +# Entry(session_id=session_ids[6], role=Role.user, content=contents[6][0]), +# Entry(session_id=session_ids[7], role=Role.assistant, content=contents[7][0]), +# Entry(session_id=session_ids[8], role=Role.user, content=contents[8][0]), +# Entry( +# session_id=session_ids[9], +# role=Role.system, +# name="thought", +# content=contents[9][0], +# ), +# ] +# with raises(InputTooBigError) as ex: +# session.truncate(messages, threshold) - assert ( - str(ex.raised) - == f"input is too big, {threshold} tokens required, but you got {all_tokens} tokens" - ) +# assert ( +# str(ex.raised) +# == f"input is too big, {threshold} tokens required, but you got {all_tokens} tokens" +# ) diff --git a/agents-api/tests/test_sessions.py b/agents-api/tests/test_sessions.py index 74ad0151c..e036f75a2 100644 --- a/agents-api/tests/test_sessions.py +++ b/agents-api/tests/test_sessions.py @@ -1,307 +1,307 @@ -import uuid - -from julep.api import ( - ChatMlMessage, - ChatResponse, - ChatSettingsResponseFormat, - ChatSettingsResponseFormatType, - InputChatMlMessage, - InputChatMlMessageRole, - ResourceCreatedResponse, - ResourceUpdatedResponse, - Session, - Suggestion, - Tool, - ToolChoiceOption, -) -from julep.api.core import ApiError -from ward import test +# import uuid + +# from julep.api import ( +# ChatMlMessage, +# ChatResponse, +# ChatSettingsResponseFormat, +# ChatSettingsResponseFormatType, +# InputChatMlMessage, +# InputChatMlMessageRole, +# ResourceCreatedResponse, +# ResourceUpdatedResponse, +# Session, +# Suggestion, +# Tool, +# ToolChoiceOption, +# ) +# from julep.api.core import ApiError +# from ward import test -from tests.fixtures import agent, async_client, client, session, user - - -@test("get existing session") -def _(existing_session=session, client=client): - response = client.sessions.get(id=existing_session.id) - - assert isinstance(response, Session) - assert response.id == existing_session.id - - -@test("async get existing sessions") -async def _(existing_session=session, client=async_client): - response = await client.sessions.get(id=existing_session.id) - - assert isinstance(response, Session) - assert response.id == existing_session.id - - -@test("get non-existing session") -def _(client=client): - try: - client.sessions.get(id=uuid.uuid4()) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("async get non-existing sessions") -async def _(existing_session=session, client=async_client): - try: - await client.sessions.get(id=uuid.uuid4()) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("create sessions") -def _(user=user, agent=agent, client=client): - response = client.sessions.create( - user_id=user.id, - agent_id=agent.id, - situation="test situation", - ) - - assert isinstance(response, ResourceCreatedResponse) - assert response.created_at - bool(uuid.UUID(str(response.id), version=4)) - - -@test("async create sessions") -async def _(user=user, agent=agent, client=async_client): - response = await client.sessions.create( - user_id=user.id, - agent_id=agent.id, - situation="test situation", - ) - - assert isinstance(response, ResourceCreatedResponse) - assert response.created_at - bool(uuid.UUID(str(response.id), version=4)) - - -@test("list sessions") -def _(existing_session=session, client=client): - response = client.sessions.list() - - assert len(response) > 0 - assert isinstance(response[0], Session) - assert response[0].id == existing_session.id - - -@test("async list sessions") -async def _(existing_session=session, client=async_client): - response = await client.sessions.list() - - assert len(response) > 0 - assert isinstance(response[0], Session) - assert response[0].id == existing_session.id - - -@test("update existing session") -def _(existing_session=session, client=client): - response = client.sessions.update( - session_id=existing_session.id, - situation="test situation", - ) - - assert isinstance(response, ResourceUpdatedResponse) - assert response.updated_at - assert response.updated_at != existing_session.updated_at - assert response.id == existing_session.id - - -@test("async update existing session") -async def _(existing_session=session, client=async_client): - response = await client.sessions.update( - session_id=existing_session.id, - situation="test situation", - ) - - assert isinstance(response, ResourceUpdatedResponse) - assert response.updated_at - assert response.updated_at != existing_session.updated_at - assert response.id == existing_session.id - - -@test("update non-existing session") -def _(client=client): - try: - client.sessions.update( - session_id=uuid.uuid4(), - situation="test situation", - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("async update non-existing session") -async def _(client=async_client): - try: - await client.sessions.update( - session_id=uuid.uuid4(), - situation="test situation", - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("delete existing sessions") -def _(existing_session=session, client=client): - response = client.sessions.delete( - session_id=existing_session.id, - ) - - assert response is None - - -@test("async delete existing sessions") -async def _(existing_session=session, client=client): - response = await client.sessions.delete( - session_id=existing_session.id, - ) - - assert response is None - - -# TODO: implement below tests properly -@test("sessions.chat") -def _(client=client): - response = client.sessions.chat( - session_id=str(uuid.uuid4()), - messages=[ - InputChatMlMessage( - role=InputChatMlMessageRole.USER, - content="test content", - name="tets name", - ) - ], - tools=[ - Tool( - **{ - "type": "function", - "function": { - "description": "test description", - "name": "test name", - "parameters": {"test_arg": "test val"}, - }, - "id": str(uuid.uuid4()), - }, - ) - ], - tool_choice=ToolChoiceOption("auto"), - frequency_penalty=0.5, - length_penalty=0.5, - logit_bias={"test": 1}, - max_tokens=120, - presence_penalty=0.5, - repetition_penalty=0.5, - response_format=ChatSettingsResponseFormat( - type=ChatSettingsResponseFormatType.TEXT, - ), - seed=1, - stop=["<"], - stream=False, - temperature=0.7, - top_p=0.9, - recall=False, - remember=False, - ) - - assert isinstance(response, ChatResponse) - - -@test("async sessions.chat") -async def _(client=async_client): - response = await client.sessions.chat( - session_id=str(uuid.uuid4()), - messages=[ - InputChatMlMessage( - role=InputChatMlMessageRole.USER, - content="test content", - name="tets name", - ) - ], - tools=[ - Tool( - **{ - "type": "function", - "function": { - "description": "test description", - "name": "test name", - "parameters": {"test_arg": "test val"}, - }, - "id": str(uuid.uuid4()), - }, - ) - ], - tool_choice=ToolChoiceOption("auto"), - frequency_penalty=0.5, - length_penalty=0.5, - logit_bias={"test": 1}, - max_tokens=120, - presence_penalty=0.5, - repetition_penalty=0.5, - response_format=ChatSettingsResponseFormat( - type=ChatSettingsResponseFormatType.TEXT, - ), - seed=1, - stop=["<"], - stream=False, - temperature=0.7, - top_p=0.9, - recall=False, - remember=False, - ) - - assert isinstance(response, ChatResponse) - - -@test("sessions.suggestions") -def _(client=client): - response = client.sessions.suggestions( - session_id=uuid.uuid4(), - ) - assert len(response) > 0 - assert isinstance(response[0], Suggestion) - - -@test("async sessions.suggestions") -async def _(client=async_client): - response = await client.sessions.suggestions( - session_id=uuid.uuid4(), - ) - assert len(response) > 0 - assert isinstance(response[0], Suggestion) - - -@test("sessions.history") -def _(client=client): - response = client.sessions.history( - session_id=uuid.uuid4(), - ) - assert len(response) > 0 - assert isinstance(response[0], ChatMlMessage) - - -@test("async sessions.list") -async def _(client=async_client): - response = await client.sessions.history( - session_id=uuid.uuid4(), - ) - assert len(response) > 0 - assert isinstance(response[0], ChatMlMessage) +# from tests.fixtures import agent, async_client, client, session, user + + +# @test("get existing session") +# def _(existing_session=session, client=client): +# response = client.sessions.get(id=existing_session.id) + +# assert isinstance(response, Session) +# assert response.id == existing_session.id + + +# @test("async get existing sessions") +# async def _(existing_session=session, client=async_client): +# response = await client.sessions.get(id=existing_session.id) + +# assert isinstance(response, Session) +# assert response.id == existing_session.id + + +# @test("get non-existing session") +# def _(client=client): +# try: +# client.sessions.get(id=uuid.uuid4()) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("async get non-existing sessions") +# async def _(existing_session=session, client=async_client): +# try: +# await client.sessions.get(id=uuid.uuid4()) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("create sessions") +# def _(user=user, agent=agent, client=client): +# response = client.sessions.create( +# user_id=user.id, +# agent_id=agent.id, +# situation="test situation", +# ) + +# assert isinstance(response, ResourceCreatedResponse) +# assert response.created_at +# bool(uuid.UUID(str(response.id), version=4)) + + +# @test("async create sessions") +# async def _(user=user, agent=agent, client=async_client): +# response = await client.sessions.create( +# user_id=user.id, +# agent_id=agent.id, +# situation="test situation", +# ) + +# assert isinstance(response, ResourceCreatedResponse) +# assert response.created_at +# bool(uuid.UUID(str(response.id), version=4)) + + +# @test("list sessions") +# def _(existing_session=session, client=client): +# response = client.sessions.list() + +# assert len(response) > 0 +# assert isinstance(response[0], Session) +# assert response[0].id == existing_session.id + + +# @test("async list sessions") +# async def _(existing_session=session, client=async_client): +# response = await client.sessions.list() + +# assert len(response) > 0 +# assert isinstance(response[0], Session) +# assert response[0].id == existing_session.id + + +# @test("update existing session") +# def _(existing_session=session, client=client): +# response = client.sessions.update( +# session_id=existing_session.id, +# situation="test situation", +# ) + +# assert isinstance(response, ResourceUpdatedResponse) +# assert response.updated_at +# assert response.updated_at != existing_session.updated_at +# assert response.id == existing_session.id + + +# @test("async update existing session") +# async def _(existing_session=session, client=async_client): +# response = await client.sessions.update( +# session_id=existing_session.id, +# situation="test situation", +# ) + +# assert isinstance(response, ResourceUpdatedResponse) +# assert response.updated_at +# assert response.updated_at != existing_session.updated_at +# assert response.id == existing_session.id + + +# @test("update non-existing session") +# def _(client=client): +# try: +# client.sessions.update( +# session_id=uuid.uuid4(), +# situation="test situation", +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("async update non-existing session") +# async def _(client=async_client): +# try: +# await client.sessions.update( +# session_id=uuid.uuid4(), +# situation="test situation", +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("delete existing sessions") +# def _(existing_session=session, client=client): +# response = client.sessions.delete( +# session_id=existing_session.id, +# ) + +# assert response is None + + +# @test("async delete existing sessions") +# async def _(existing_session=session, client=client): +# response = await client.sessions.delete( +# session_id=existing_session.id, +# ) + +# assert response is None + + +# # TODO: implement below tests properly +# @test("sessions.chat") +# def _(client=client): +# response = client.sessions.chat( +# session_id=str(uuid.uuid4()), +# messages=[ +# InputChatMlMessage( +# role=InputChatMlMessageRole.USER, +# content="test content", +# name="tets name", +# ) +# ], +# tools=[ +# Tool( +# **{ +# "type": "function", +# "function": { +# "description": "test description", +# "name": "test name", +# "parameters": {"test_arg": "test val"}, +# }, +# "id": str(uuid.uuid4()), +# }, +# ) +# ], +# tool_choice=ToolChoiceOption("auto"), +# frequency_penalty=0.5, +# length_penalty=0.5, +# logit_bias={"test": 1}, +# max_tokens=120, +# presence_penalty=0.5, +# repetition_penalty=0.5, +# response_format=ChatSettingsResponseFormat( +# type=ChatSettingsResponseFormatType.TEXT, +# ), +# seed=1, +# stop=["<"], +# stream=False, +# temperature=0.7, +# top_p=0.9, +# recall=False, +# remember=False, +# ) + +# assert isinstance(response, ChatResponse) + + +# @test("async sessions.chat") +# async def _(client=async_client): +# response = await client.sessions.chat( +# session_id=str(uuid.uuid4()), +# messages=[ +# InputChatMlMessage( +# role=InputChatMlMessageRole.USER, +# content="test content", +# name="tets name", +# ) +# ], +# tools=[ +# Tool( +# **{ +# "type": "function", +# "function": { +# "description": "test description", +# "name": "test name", +# "parameters": {"test_arg": "test val"}, +# }, +# "id": str(uuid.uuid4()), +# }, +# ) +# ], +# tool_choice=ToolChoiceOption("auto"), +# frequency_penalty=0.5, +# length_penalty=0.5, +# logit_bias={"test": 1}, +# max_tokens=120, +# presence_penalty=0.5, +# repetition_penalty=0.5, +# response_format=ChatSettingsResponseFormat( +# type=ChatSettingsResponseFormatType.TEXT, +# ), +# seed=1, +# stop=["<"], +# stream=False, +# temperature=0.7, +# top_p=0.9, +# recall=False, +# remember=False, +# ) + +# assert isinstance(response, ChatResponse) + + +# @test("sessions.suggestions") +# def _(client=client): +# response = client.sessions.suggestions( +# session_id=uuid.uuid4(), +# ) +# assert len(response) > 0 +# assert isinstance(response[0], Suggestion) + + +# @test("async sessions.suggestions") +# async def _(client=async_client): +# response = await client.sessions.suggestions( +# session_id=uuid.uuid4(), +# ) +# assert len(response) > 0 +# assert isinstance(response[0], Suggestion) + + +# @test("sessions.history") +# def _(client=client): +# response = client.sessions.history( +# session_id=uuid.uuid4(), +# ) +# assert len(response) > 0 +# assert isinstance(response[0], ChatMlMessage) + + +# @test("async sessions.list") +# async def _(client=async_client): +# response = await client.sessions.history( +# session_id=uuid.uuid4(), +# ) +# assert len(response) > 0 +# assert isinstance(response[0], ChatMlMessage) diff --git a/agents-api/tests/test_tasks.py b/agents-api/tests/test_tasks.py index 24562282b..6992ba327 100644 --- a/agents-api/tests/test_tasks.py +++ b/agents-api/tests/test_tasks.py @@ -1,159 +1,159 @@ -import uuid -from typing import List - -from julep.api.types import Execution, Task -from ward import test - -from tests.fixtures import agent, async_client, client, task - - -@test("create task") -def _(client=client, agent=agent): - task = client.tasks.create( - agent_id=agent.id, - name="task1", - description="task 1", - tools_available=["tool1"], - input_schema={}, - main=[], - ) - - assert isinstance(task, Task) - assert task.created_at - assert bool(uuid.UUID(str(task.id), version=4)) - - assert task.agent_id == agent.id - assert task.name == "task1" - assert task.description == "task 1" - assert task.tools_available == ["tool1"] - assert task.input_schema == {} - assert task.main == [] - - -@test("get task") -def _(client=client, agent=agent, task=task): - task = client.tasks.get( - agent_id=agent.id, - task_id=task.id, - ) - - assert isinstance(task, Task) - assert task.created_at - assert bool(uuid.UUID(str(task.id), version=4)) - - assert task.agent_id == agent.id - assert task.name == "task1" - assert task.description == "task 1" - assert task.tools_available == ["tool1"] - assert task.input_schema == {} - assert task.main == [] - - -@test("list task") -def _(client=client, agent=agent): - tasks = client.tasks.list( - agent_id=agent.id, - ) - - assert isinstance(tasks, List[Task]) - assert len(tasks) > 0 - - task = tasks[0] - - assert task.created_at - assert bool(uuid.UUID(str(task.id), version=4)) - - assert task.agent_id == agent.id - assert task.name == "task1" - assert task.description == "task 1" - assert task.tools_available == ["tool1"] - assert task.input_schema == {} - assert task.main == [] - - -@test("start task execution") -def _(client=client, agent=agent, task=task): - execution = client.tasks.start_task_execution( - agent_id=agent.id, - task_id=task.id, - arguments={}, - status="enqueued", - ) - - assert isinstance(execution, Execution) - - -@test("create task") -async def _(client=async_client, agent=agent): - task = await client.tasks.create( - agent_id=agent.id, - name="task1", - description="task 1", - tools_available=["tool1"], - input_schema={}, - main=[], - ) - - assert isinstance(task, Task) - assert task.created_at - assert bool(uuid.UUID(str(task.id), version=4)) - - assert task.agent_id == agent.id - assert task.name == "task1" - assert task.description == "task 1" - assert task.tools_available == ["tool1"] - assert task.input_schema == {} - assert task.main == [] - - -@test("get task") -async def _(client=async_client, agent=agent, task=task): - task = await client.tasks.get( - agent_id=agent.id, - task_id=task.id, - ) - - assert isinstance(task, Task) - assert task.created_at - assert bool(uuid.UUID(str(task.id), version=4)) - - assert task.agent_id == agent.id - assert task.name == "task1" - assert task.description == "task 1" - assert task.tools_available == ["tool1"] - assert task.input_schema == {} - assert task.main == [] - - -@test("list task") -async def _(client=async_client, agent=agent): - tasks = await client.tasks.list( - agent_id=agent.id, - ) - - assert isinstance(tasks, List[Task]) - assert len(tasks) > 0 - - task = tasks[0] - - assert task.created_at - assert bool(uuid.UUID(str(task.id), version=4)) - - assert task.agent_id == agent.id - assert task.name == "task1" - assert task.description == "task 1" - assert task.tools_available == ["tool1"] - assert task.input_schema == {} - assert task.main == [] - - -@test("start task execution") -async def _(client=async_client, agent=agent, task=task): - execution = await client.tasks.start_task_execution( - agent_id=agent.id, - task_id=task.id, - arguments={}, - status="enqueued", - ) - - assert isinstance(execution, Execution) +# import uuid +# from typing import List + +# from julep.api.types import Execution, Task +# from ward import test + +# from tests.fixtures import agent, async_client, client, task + + +# @test("create task") +# def _(client=client, agent=agent): +# task = client.tasks.create( +# agent_id=agent.id, +# name="task1", +# description="task 1", +# tools_available=["tool1"], +# input_schema={}, +# main=[], +# ) + +# assert isinstance(task, Task) +# assert task.created_at +# assert bool(uuid.UUID(str(task.id), version=4)) + +# assert task.agent_id == agent.id +# assert task.name == "task1" +# assert task.description == "task 1" +# assert task.tools_available == ["tool1"] +# assert task.input_schema == {} +# assert task.main == [] + + +# @test("get task") +# def _(client=client, agent=agent, task=task): +# task = client.tasks.get( +# agent_id=agent.id, +# task_id=task.id, +# ) + +# assert isinstance(task, Task) +# assert task.created_at +# assert bool(uuid.UUID(str(task.id), version=4)) + +# assert task.agent_id == agent.id +# assert task.name == "task1" +# assert task.description == "task 1" +# assert task.tools_available == ["tool1"] +# assert task.input_schema == {} +# assert task.main == [] + + +# @test("list task") +# def _(client=client, agent=agent): +# tasks = client.tasks.list( +# agent_id=agent.id, +# ) + +# assert isinstance(tasks, List[Task]) +# assert len(tasks) > 0 + +# task = tasks[0] + +# assert task.created_at +# assert bool(uuid.UUID(str(task.id), version=4)) + +# assert task.agent_id == agent.id +# assert task.name == "task1" +# assert task.description == "task 1" +# assert task.tools_available == ["tool1"] +# assert task.input_schema == {} +# assert task.main == [] + + +# @test("start task execution") +# def _(client=client, agent=agent, task=task): +# execution = client.tasks.start_task_execution( +# agent_id=agent.id, +# task_id=task.id, +# arguments={}, +# status="enqueued", +# ) + +# assert isinstance(execution, Execution) + + +# @test("create task") +# async def _(client=async_client, agent=agent): +# task = await client.tasks.create( +# agent_id=agent.id, +# name="task1", +# description="task 1", +# tools_available=["tool1"], +# input_schema={}, +# main=[], +# ) + +# assert isinstance(task, Task) +# assert task.created_at +# assert bool(uuid.UUID(str(task.id), version=4)) + +# assert task.agent_id == agent.id +# assert task.name == "task1" +# assert task.description == "task 1" +# assert task.tools_available == ["tool1"] +# assert task.input_schema == {} +# assert task.main == [] + + +# @test("get task") +# async def _(client=async_client, agent=agent, task=task): +# task = await client.tasks.get( +# agent_id=agent.id, +# task_id=task.id, +# ) + +# assert isinstance(task, Task) +# assert task.created_at +# assert bool(uuid.UUID(str(task.id), version=4)) + +# assert task.agent_id == agent.id +# assert task.name == "task1" +# assert task.description == "task 1" +# assert task.tools_available == ["tool1"] +# assert task.input_schema == {} +# assert task.main == [] + + +# @test("list task") +# async def _(client=async_client, agent=agent): +# tasks = await client.tasks.list( +# agent_id=agent.id, +# ) + +# assert isinstance(tasks, List[Task]) +# assert len(tasks) > 0 + +# task = tasks[0] + +# assert task.created_at +# assert bool(uuid.UUID(str(task.id), version=4)) + +# assert task.agent_id == agent.id +# assert task.name == "task1" +# assert task.description == "task 1" +# assert task.tools_available == ["tool1"] +# assert task.input_schema == {} +# assert task.main == [] + + +# @test("start task execution") +# async def _(client=async_client, agent=agent, task=task): +# execution = await client.tasks.start_task_execution( +# agent_id=agent.id, +# task_id=task.id, +# arguments={}, +# status="enqueued", +# ) + +# assert isinstance(execution, Execution) diff --git a/agents-api/tests/test_users.py b/agents-api/tests/test_users.py index 6dc2b0789..8cd074bdb 100644 --- a/agents-api/tests/test_users.py +++ b/agents-api/tests/test_users.py @@ -1,191 +1,191 @@ -import uuid - -from julep.api import ResourceCreatedResponse, ResourceUpdatedResponse, User -from julep.api.core import ApiError -from ward import test - -from tests.fixtures import async_client, client, user - - -@test("create user") -def _(client=client): - response = client.users.create( - name="test user", - about="test user about", - ) - - assert isinstance(response, ResourceCreatedResponse) - assert response.created_at - assert bool(uuid.UUID(str(response.id), version=4)) - - -@test("async create user") -async def _(client=async_client): - response = await client.users.create( - name="test user", - about="test user about", - ) - - assert isinstance(response, ResourceCreatedResponse) - assert response.created_at - assert bool(uuid.UUID(str(response.id), version=4)) - - -@test("get existing user") -def _(existing_user=user, client=client): - response = client.users.get(existing_user.id) - assert isinstance(response, User) - assert existing_user.id == response.id - - -@test("async get existing user") -async def _(existing_user=user, client=async_client): - response = await client.users.get(existing_user.id) - assert isinstance(response, User) - assert existing_user.id == response.id - - -@test("get non-existing user") -def _(client=client): - try: - client.users.get(uuid.uuid4()) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("async get non-existing user") -async def _(client=async_client): - try: - await client.users.get(uuid.uuid4()) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("update existing user") -def _(existing_user=user, client=client): - response = client.users.update( - user_id=existing_user.id, - name="test user", - about="test user about", - ) - - assert isinstance(response, ResourceUpdatedResponse) - assert response.updated_at - assert response.updated_at != existing_user.updated_at - assert response.id == existing_user.id - - -@test("async update existing user") -async def _(existing_user=user, async_client=client): - response = await client.users.update( - user_id=existing_user.id, - name="test user", - about="test user about", - ) - - assert isinstance(response, ResourceUpdatedResponse) - assert response.updated_at - assert response.updated_at != existing_user.updated_at - assert response.id == existing_user.id - - -@test("update non-existing user") -def _(client=client): - try: - client.users.update( - user_id=uuid.uuid4(), - name="test user", - about="test user about", - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("async update non-existing user") -async def _(client=async_client): - try: - await client.users.update( - user_id=uuid.uuid4(), - name="test user", - about="test user about", - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("delete existing user") -def _(existing_user=user, client=client): - response = client.users.delete( - user_id=existing_user.id, - ) - - assert response is None - - -@test("async delete existing user") -async def _(existing_user=user, client=async_client): - response = await client.users.delete( - user_id=existing_user.id, - ) - - assert response is None - - -@test("delete non-existing user") -def _(client=client): - try: - client.users.delete( - user_id=uuid.uuid4(), - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("async delete non-existing user") -async def _(client=async_client): - try: - await client.users.delete( - user_id=uuid.uuid4(), - ) - except ApiError as e: - assert e.status_code == 404 - except Exception: - assert False - else: - assert False - - -@test("list users") -def _(existing_user=user, client=client): - response = client.users.list() - assert len(response) > 0 - assert isinstance(response[0], User) - assert response[0].id == existing_user.id - - -@test("async list users") -async def _(existing_user=user, client=async_client): - response = await client.users.list() - assert len(response) > 0 - assert isinstance(response[0], User) - assert response[0].id == existing_user.id +# import uuid + +# from julep.api import ResourceCreatedResponse, ResourceUpdatedResponse, User +# from julep.api.core import ApiError +# from ward import test + +# from tests.fixtures import async_client, client, user + + +# @test("create user") +# def _(client=client): +# response = client.users.create( +# name="test user", +# about="test user about", +# ) + +# assert isinstance(response, ResourceCreatedResponse) +# assert response.created_at +# assert bool(uuid.UUID(str(response.id), version=4)) + + +# @test("async create user") +# async def _(client=async_client): +# response = await client.users.create( +# name="test user", +# about="test user about", +# ) + +# assert isinstance(response, ResourceCreatedResponse) +# assert response.created_at +# assert bool(uuid.UUID(str(response.id), version=4)) + + +# @test("get existing user") +# def _(existing_user=user, client=client): +# response = client.users.get(existing_user.id) +# assert isinstance(response, User) +# assert existing_user.id == response.id + + +# @test("async get existing user") +# async def _(existing_user=user, client=async_client): +# response = await client.users.get(existing_user.id) +# assert isinstance(response, User) +# assert existing_user.id == response.id + + +# @test("get non-existing user") +# def _(client=client): +# try: +# client.users.get(uuid.uuid4()) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("async get non-existing user") +# async def _(client=async_client): +# try: +# await client.users.get(uuid.uuid4()) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("update existing user") +# def _(existing_user=user, client=client): +# response = client.users.update( +# user_id=existing_user.id, +# name="test user", +# about="test user about", +# ) + +# assert isinstance(response, ResourceUpdatedResponse) +# assert response.updated_at +# assert response.updated_at != existing_user.updated_at +# assert response.id == existing_user.id + + +# @test("async update existing user") +# async def _(existing_user=user, async_client=client): +# response = await client.users.update( +# user_id=existing_user.id, +# name="test user", +# about="test user about", +# ) + +# assert isinstance(response, ResourceUpdatedResponse) +# assert response.updated_at +# assert response.updated_at != existing_user.updated_at +# assert response.id == existing_user.id + + +# @test("update non-existing user") +# def _(client=client): +# try: +# client.users.update( +# user_id=uuid.uuid4(), +# name="test user", +# about="test user about", +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("async update non-existing user") +# async def _(client=async_client): +# try: +# await client.users.update( +# user_id=uuid.uuid4(), +# name="test user", +# about="test user about", +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("delete existing user") +# def _(existing_user=user, client=client): +# response = client.users.delete( +# user_id=existing_user.id, +# ) + +# assert response is None + + +# @test("async delete existing user") +# async def _(existing_user=user, client=async_client): +# response = await client.users.delete( +# user_id=existing_user.id, +# ) + +# assert response is None + + +# @test("delete non-existing user") +# def _(client=client): +# try: +# client.users.delete( +# user_id=uuid.uuid4(), +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("async delete non-existing user") +# async def _(client=async_client): +# try: +# await client.users.delete( +# user_id=uuid.uuid4(), +# ) +# except ApiError as e: +# assert e.status_code == 404 +# except Exception: +# assert False +# else: +# assert False + + +# @test("list users") +# def _(existing_user=user, client=client): +# response = client.users.list() +# assert len(response) > 0 +# assert isinstance(response[0], User) +# assert response[0].id == existing_user.id + + +# @test("async list users") +# async def _(existing_user=user, client=async_client): +# response = await client.users.list() +# assert len(response) > 0 +# assert isinstance(response[0], User) +# assert response[0].id == existing_user.id From 33e86a2c9148fcb31197c880d118ce0c9573157e Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Mon, 5 Aug 2024 10:26:40 -0400 Subject: [PATCH 005/110] feat(agents-api): Hybrid docs search (#444) Signed-off-by: Diwank Tomer Co-authored-by: Diwank Tomer --- agents-api/agents_api/models/docs/__init__.py | 3 +- ...ch_docs.py => search_docs_by_embedding.py} | 4 + .../models/docs/search_docs_by_text.py | 153 ++++++++++++++++++ .../models/docs/search_docs_hybrid.py | 121 ++++++++++++++ .../models/session/prepare_chat_context.py | 13 +- 5 files changed, 281 insertions(+), 13 deletions(-) rename agents-api/agents_api/models/docs/{search_docs.py => search_docs_by_embedding.py} (98%) create mode 100644 agents-api/agents_api/models/docs/search_docs_by_text.py create mode 100644 agents-api/agents_api/models/docs/search_docs_hybrid.py diff --git a/agents-api/agents_api/models/docs/__init__.py b/agents-api/agents_api/models/docs/__init__.py index f668e048d..0ba3db0d4 100644 --- a/agents-api/agents_api/models/docs/__init__.py +++ b/agents-api/agents_api/models/docs/__init__.py @@ -21,4 +21,5 @@ from .embed_snippets import embed_snippets from .get_doc import get_doc from .list_docs import list_docs -from .search_docs import search_docs_by_embedding +from .search_docs_by_embedding import search_docs_by_embedding +from .search_docs_by_text import search_docs_by_text diff --git a/agents-api/agents_api/models/docs/search_docs.py b/agents-api/agents_api/models/docs/search_docs_by_embedding.py similarity index 98% rename from agents-api/agents_api/models/docs/search_docs.py rename to agents-api/agents_api/models/docs/search_docs_by_embedding.py index d5903bfe7..0acbf8f6a 100644 --- a/agents-api/agents_api/models/docs/search_docs.py +++ b/agents-api/agents_api/models/docs/search_docs_by_embedding.py @@ -48,6 +48,7 @@ def search_docs_by_embedding( confidence: float = 0.7, ef: int = 128, mmr_lambda: float = 0.25, + embedding_size: int = 1024, ) -> tuple[list[str], dict]: """ Searches for document snippets in CozoDB by embedding query. @@ -61,6 +62,9 @@ def search_docs_by_embedding( - mmr_lambda (float, optional): The lambda parameter for MMR. Defaults to 0.25. """ + assert len(query_embedding) == embedding_size + assert sum(query_embedding) + owner_id = str(owner_id) # Calculate the search radius based on confidence level diff --git a/agents-api/agents_api/models/docs/search_docs_by_text.py b/agents-api/agents_api/models/docs/search_docs_by_text.py new file mode 100644 index 000000000..901e3ea31 --- /dev/null +++ b/agents-api/agents_api/models/docs/search_docs_by_text.py @@ -0,0 +1,153 @@ +"""This module contains functions for searching documents in the CozoDB based on embedding queries.""" + +from typing import Literal +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...autogen.openapi_model import DocReference +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + DocReference, + transform=lambda d: { + "owner": { + "id": d["owner_id"], + "role": d["owner_type"], + }, + **d, + }, +) +@cozo_query +@beartype +def search_docs_by_text( + *, + developer_id: UUID, + owner_type: Literal["user", "agent"], + owner_id: UUID, + query: str, + k: int = 3, +) -> tuple[list[str], dict]: + """ + Searches for document snippets in CozoDB by embedding query. + + Parameters: + - owner_type (Literal["user", "agent"]): The type of the owner of the documents. + - owner_id (UUID): The unique identifier of the owner. + - query (str): The query string. + - k (int, optional): The number of nearest neighbors to retrieve. Defaults to 3. + """ + + owner_id = str(owner_id) + + # Construct the datalog query for searching document snippets + search_query = f""" + input[ + owner_id, + query, + ] <- [[ + to_uuid($owner_id), + $query, + ]] + + candidate[doc_id] := + input[owner_id, _], + *docs {{ + owner_type: $owner_type, + owner_id, + doc_id + }} + + search_result[ + doc_id, + snippet_data, + distance, + ] := + input[owner_id, query], + candidate[doc_id], + ~snippets:fts {{ + doc_id, + index, + content + | + query: query, + k: {k}, + score_kind: 'tf_idf', + bind_score: score, + }}, + distance = -score, + snippet_data = [index, content] + + m[ + doc_id, + collect(snippet), + distance, + title, + ] := + candidate[doc_id], + *docs {{ + owner_type: $owner_type, + owner_id, + doc_id, + title, + }}, + search_result [ + doc_id, + snippet_data, + distance, + ], + snippet = {{ + "index": snippet_data->0, + "content": snippet_data->1, + }} + + + ?[ + id, + owner_type, + owner_id, + snippets, + distance, + title, + ] := m[ + id, + snippets, + distance, + title, + ], owner_type = $owner_type, owner_id = $owner_id + + # Sort the results by distance to find the closest matches + :sort distance + :limit {k} + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query( + developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} + ), + search_query, + ] + + return ( + queries, + {"owner_type": owner_type, "owner_id": owner_id, "query": query}, + ) diff --git a/agents-api/agents_api/models/docs/search_docs_hybrid.py b/agents-api/agents_api/models/docs/search_docs_hybrid.py new file mode 100644 index 000000000..1595b3ca5 --- /dev/null +++ b/agents-api/agents_api/models/docs/search_docs_hybrid.py @@ -0,0 +1,121 @@ +"""This module contains functions for searching documents in the CozoDB based on embedding queries.""" + +from statistics import mean, stdev +from typing import Literal +from uuid import UUID + +from beartype import beartype + +from ...autogen.openapi_model import DocReference +from .search_docs_by_embedding import search_docs_by_embedding +from .search_docs_by_text import search_docs_by_text + + +# Distribution based score normalization +# https://medium.com/plain-simple-software/distribution-based-score-fusion-dbsf-a-new-approach-to-vector-search-ranking-f87c37488b18 +def dbsf_normalize(scores: list[float]) -> list[float]: + """ + Scores scaled using minmax scaler with our custom feature range + (extremes indicated as 3 standard deviations from the mean) + """ + sd = stdev(scores) + if sd == 0: + return scores + + m = mean(scores) + m3d = 3 * sd + m + m_3d = m - 3 * sd + + return [(s - m_3d) / (m3d - m_3d) for s in scores] + + +def dbsf_fuse( + text_results: list[DocReference], + embedding_results: list[DocReference], + alpha: float = 0.7, # Weight of the embedding search results (this is a good default) +) -> list[DocReference]: + """ + Weighted reciprocal-rank fusion of text and embedding search results + """ + all_docs = {doc.id: doc for doc in text_results + embedding_results} + + text_scores: dict[UUID, float] = {doc.id: -doc.distance for doc in text_results} + + # Because these are cosine distances, we need to invert them + embedding_scores: dict[UUID, float] = { + doc.id: 1.0 - doc.distance for doc in embedding_results + } + + # normalize the scores + text_scores_normalized = dbsf_normalize(list(text_scores.values())) + text_scores = { + doc_id: score + for doc_id, score in zip(text_scores.keys(), text_scores_normalized) + } + + embedding_scores_normalized = dbsf_normalize(list(embedding_scores.values())) + embedding_scores = { + doc_id: score + for doc_id, score in zip(embedding_scores.keys(), embedding_scores_normalized) + } + + # Combine the scores + text_weight: float = 1 - alpha + embedding_weight: float = alpha + + combined_scores = [] + + for id in all_docs.keys(): + text_score = text_weight * text_scores.get(id, 0) + embedding_score = embedding_weight * embedding_scores.get(id, 0) + + combined_scores.append((id, text_score + embedding_score)) + + # Sort by the combined score + combined_scores = sorted(combined_scores, key=lambda x: x[1], reverse=True) + + # Rank the results + ranked_results = [] + for id, score in combined_scores: + doc = all_docs[id].model_copy() + doc.distance = 1.0 - score + ranked_results.append(doc) + + return ranked_results + + +@beartype +def search_docs_hybrid( + *, + developer_id: UUID, + owner_type: Literal["user", "agent"], + owner_id: UUID, + query: str, + query_embedding: list[float], + k: int = 3, + embed_search_options: dict = {}, + text_search_options: dict = {}, + **kwargs, +) -> list[DocReference]: + # TODO: We should probably parallelize these queries + text_results = search_docs_by_text( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + query=query, + k=2 * k, + **text_search_options, + **kwargs, + ) + + embedding_results = search_docs_by_embedding( + developer_id=developer_id, + owner_type=owner_type, + owner_id=owner_id, + query_embedding=query_embedding, + k=2 * k, + **embed_search_options, + **kwargs, + ) + + return dbsf_fuse(text_results, embedding_results)[:k] diff --git a/agents-api/agents_api/models/session/prepare_chat_context.py b/agents-api/agents_api/models/session/prepare_chat_context.py index f599a8d25..ccd49a36a 100644 --- a/agents-api/agents_api/models/session/prepare_chat_context.py +++ b/agents-api/agents_api/models/session/prepare_chat_context.py @@ -47,15 +47,10 @@ def prepare_chat_context( developer_id: UUID, agent_id: UUID, session_id: UUID, - # doc_query_embedding: list[float], - # docs_confidence: float = 0.4, - # k_docs: int = 3, ) -> tuple[list[str], dict]: """ - Executes a complex query to retrieve memory context based on session ID, tool and document embeddings. + Executes a complex query to retrieve memory context based on session ID. """ - # VECTOR_SIZE = 1024 - # docs_radius: float = 1.0 - docs_confidence session_data_query, sd_vars = prepare_session_data.__wrapped__( developer_id=developer_id, session_id=session_id @@ -89,9 +84,6 @@ def prepare_chat_context( }} """ - # TODO: Implement the following queries - # docs_query = ... - entries_query, e_vars = list_entries.__wrapped__( developer_id=developer_id, session_id=session_id, @@ -143,8 +135,5 @@ def prepare_chat_context( **sd_vars, **t_vars, **e_vars, - # "doc_query_embedding": doc_query_embedding, - # "k_docs": k_docs, - # "docs_radius": round(docs_radius, 2), }, ) From 41e5df83eb3f17a05f4a626b592ff74480ae3b1e Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Mon, 5 Aug 2024 14:29:16 -0400 Subject: [PATCH 006/110] feat(agents-api): Add temporal workflow lookup relation and queries (#446) * fix(agents-api): Fix execution endpoints Signed-off-by: Diwank Tomer * feat(agents-api): Add temporal workflow lookup relation and queries Signed-off-by: Diwank Tomer * feat(agents-api): Add query for getting token of paused execution Signed-off-by: Diwank Tomer * fix: Minorfix Signed-off-by: Diwank Tomer --------- Signed-off-by: Diwank Tomer Co-authored-by: Diwank Tomer --- agents-api/agents_api/autogen/Executions.py | 4 - .../models/entry/test_entry_queries.py | 2 +- .../models/execution/create_execution.py | 23 +- .../execution/get_paused_execution_token.py | 70 +++ .../execution/get_temporal_workflow_data.py | 51 ++ .../execution/test_execution_queries.py | 2 - .../models/session/test_session_queries.py | 2 - ...migrate_1722875101_add_temporal_mapping.py | 40 ++ agents-api/poetry.lock | 577 +++++++++--------- sdks/python/julep/api/client.py | 490 +++++++-------- sdks/python/julep/api/reference.md | 342 +++++------ sdks/python/poetry.lock | 205 ++++--- ...cutions_TaskTokenResumeExecutionRequest.ts | 4 - ...cutions_TaskTokenResumeExecutionRequest.ts | 5 - sdks/ts/src/api/services/DefaultService.ts | 112 ++-- typespec/executions/endpoints.tsp | 40 +- typespec/executions/models.tsp | 5 + typespec/main.tsp | 2 +- 18 files changed, 1079 insertions(+), 897 deletions(-) create mode 100644 agents-api/agents_api/models/execution/get_paused_execution_token.py create mode 100644 agents-api/agents_api/models/execution/get_temporal_workflow_data.py create mode 100644 agents-api/migrations/migrate_1722875101_add_temporal_mapping.py diff --git a/agents-api/agents_api/autogen/Executions.py b/agents-api/agents_api/autogen/Executions.py index d1cb40f0f..f1b66897d 100644 --- a/agents-api/agents_api/autogen/Executions.py +++ b/agents-api/agents_api/autogen/Executions.py @@ -68,10 +68,6 @@ class TaskTokenResumeExecutionRequest(BaseModel): populate_by_name=True, ) status: Literal["running"] = "running" - task_token: str - """ - A Task Token is a unique identifier for a specific Task Execution. - """ input: dict[str, Any] | None = None """ The input to resume the execution with diff --git a/agents-api/agents_api/models/entry/test_entry_queries.py b/agents-api/agents_api/models/entry/test_entry_queries.py index 70823a0b6..6b7a35960 100644 --- a/agents-api/agents_api/models/entry/test_entry_queries.py +++ b/agents-api/agents_api/models/entry/test_entry_queries.py @@ -111,7 +111,7 @@ def _(): user_id = uuid4() agent_id = uuid4() session_id = uuid4() - tool_id = uuid4() + uuid4() user_doc_id = uuid4() agent_doc_id = uuid4() diff --git a/agents-api/agents_api/models/execution/create_execution.py b/agents-api/agents_api/models/execution/create_execution.py index 90da93fc5..575c4de4c 100644 --- a/agents-api/agents_api/models/execution/create_execution.py +++ b/agents-api/agents_api/models/execution/create_execution.py @@ -5,6 +5,7 @@ from fastapi import HTTPException from pycozo.client import QueryException from pydantic import ValidationError +from temporalio.client import WorkflowHandle from ...autogen.openapi_model import CreateExecutionRequest, Execution from ...common.utils.cozo import cozo_process_mutate_data @@ -39,6 +40,7 @@ def create_execution( task_id: UUID, execution_id: UUID | None = None, data: Annotated[CreateExecutionRequest | dict, dict_like(CreateExecutionRequest)], + workflow_hande: WorkflowHandle, ) -> tuple[list[str], dict]: execution_id = execution_id or uuid4() @@ -53,6 +55,24 @@ def create_execution( data["metadata"] = data.get("metadata", {}) execution_data = data + temporal_columns, temporal_values = cozo_process_mutate_data( + { + "execution_id": execution_id, + "id": workflow_hande.id, + "run_id": workflow_hande.run_id, + "first_execution_run_id": workflow_hande.first_execution_run_id, + "result_run_id": workflow_hande.result_run_id, + } + ) + + temporal_executions_lookup_query = f""" + ?[{temporal_columns}] <- $temporal_values + + :insert temporal_executions_lookup {{ + {temporal_columns} + }} + """ + columns, values = cozo_process_mutate_data( { **execution_data, @@ -79,7 +99,8 @@ def create_execution( task_id=task_id, parents=[("agents", "agent_id")], ), + temporal_executions_lookup_query, insert_query, ] - return (queries, {"values": values}) + return (queries, {"values": values, "temporal_values": temporal_values}) diff --git a/agents-api/agents_api/models/execution/get_paused_execution_token.py b/agents-api/agents_api/models/execution/get_paused_execution_token.py new file mode 100644 index 000000000..00fe39873 --- /dev/null +++ b/agents-api/agents_api/models/execution/get_paused_execution_token.py @@ -0,0 +1,70 @@ +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...autogen.openapi_model import Transition +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + wrap_in_class, +) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + AssertionError: partialclass(HTTPException, status_code=500), + } +) +@wrap_in_class(dict, one=True) +@cozo_query +@beartype +def get_paused_execution_token( + *, + developer_id: UUID, + execution_id: UUID, +) -> tuple[list[str], dict]: + execution_id = str(execution_id) + + check_status_query = """ + ?[execution_id, status] := + *executions { + execution_id, + status, + }, + execution_id = to_uuid($execution_id), + status = "awaiting_input" + + :assert some + """ + + get_query = """ + ?[task_token, max(created_at)] := + execution_id = to_uuid($execution_id), + *executions { + execution_id, + }, + *transitions { + execution_id, + created_at, + task_token, + type, + }, + type = "wait" + + """ + + queries = [ + verify_developer_id_query(developer_id), + check_status_query, + get_query, + ] + + return (queries, {"execution_id": execution_id}) diff --git a/agents-api/agents_api/models/execution/get_temporal_workflow_data.py b/agents-api/agents_api/models/execution/get_temporal_workflow_data.py new file mode 100644 index 000000000..104918f22 --- /dev/null +++ b/agents-api/agents_api/models/execution/get_temporal_workflow_data.py @@ -0,0 +1,51 @@ +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + wrap_in_class, +) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class(dict, one=True) +@cozo_query +@beartype +def get_temporal_workflow_data( + *, + execution_id: UUID, +) -> tuple[str, dict]: + # Executions are allowed direct GET access if they have execution_id + + query = """ + input[execution_id] <- [[to_uuid($execution_id)]] + + ?[id, run_id, result_run_id, first_execution_run_id] := + input[execution_id], + *temporal_executions_lookup { + execution_id, + id, + run_id, + result_run_id, + first_execution_run_id, + } + """ + + return ( + query, + { + "execution_id": str(execution_id), + }, + ) diff --git a/agents-api/agents_api/models/execution/test_execution_queries.py b/agents-api/agents_api/models/execution/test_execution_queries.py index 6ca5e3a2f..a2b87c1ae 100644 --- a/agents-api/agents_api/models/execution/test_execution_queries.py +++ b/agents-api/agents_api/models/execution/test_execution_queries.py @@ -7,8 +7,6 @@ from agents_api.autogen.openapi_model import Execution, Transition -from ..agent.create_agent import create_agent -from ..task.create_task import create_task from .create_execution import create_execution from .create_execution_transition import create_execution_transition from .get_execution import get_execution diff --git a/agents-api/agents_api/models/session/test_session_queries.py b/agents-api/agents_api/models/session/test_session_queries.py index b878e8c54..aab7737ea 100644 --- a/agents-api/agents_api/models/session/test_session_queries.py +++ b/agents-api/agents_api/models/session/test_session_queries.py @@ -7,8 +7,6 @@ from agents_api.autogen.openapi_model import Session -from ..agent.create_agent import create_agent -from ..user.create_user import create_user from .create_session import create_session from .delete_session import delete_session from .get_session import get_session diff --git a/agents-api/migrations/migrate_1722875101_add_temporal_mapping.py b/agents-api/migrations/migrate_1722875101_add_temporal_mapping.py new file mode 100644 index 000000000..b38a3717c --- /dev/null +++ b/agents-api/migrations/migrate_1722875101_add_temporal_mapping.py @@ -0,0 +1,40 @@ +# /usr/bin/env python3 + +MIGRATION_ID = "add_temporal_mapping" +CREATED_AT = 1722875101.262791 + + +def run(client, queries): + joiner = "}\n\n{" + + query = joiner.join(queries) + query = f"{{\n{query}\n}}" + client.run(query) + + +create_temporal_executions_lookup = dict( + up=""" + :create temporal_executions_lookup { + execution_id: Uuid, + id: String, + => + run_id: String?, + first_execution_run_id: String?, + result_run_id: String?, + created_at: Float default now(), + } + """, + down="::remove temporal_executions_lookup", +) + +queries = [ + create_temporal_executions_lookup, +] + + +def up(client): + run(client, [q["up"] for q in queries]) + + +def down(client): + run(client, [q["down"] for q in reversed(queries)]) diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index cd7983feb..d6ccc1344 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -13,87 +13,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.0" +version = "3.10.1" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:68ab608118e212f56feef44d4785aa90b713042da301f26338f36497b481cd79"}, - {file = "aiohttp-3.10.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:64a117c16273ca9f18670f33fc7fd9604b9f46ddb453ce948262889a6be72868"}, - {file = "aiohttp-3.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:54076a25f32305e585a3abae1f0ad10646bec539e0e5ebcc62b54ee4982ec29f"}, - {file = "aiohttp-3.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71c76685773444d90ae83874433505ed800e1706c391fdf9e57cc7857611e2f4"}, - {file = "aiohttp-3.10.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdda86ab376f9b3095a1079a16fbe44acb9ddde349634f1c9909d13631ff3bcf"}, - {file = "aiohttp-3.10.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d6dcd1d21da5ae1416f69aa03e883a51e84b6c803b8618cbab341ac89a85b9e"}, - {file = "aiohttp-3.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06ef0135d7ab7fb0284342fbbf8e8ddf73b7fee8ecc55f5c3a3d0a6b765e6d8b"}, - {file = "aiohttp-3.10.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccab9381f38c669bb9254d848f3b41a3284193b3e274a34687822f98412097e9"}, - {file = "aiohttp-3.10.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:947da3aee057010bc750b7b4bb65cbd01b0bdb7c4e1cf278489a1d4a1e9596b3"}, - {file = "aiohttp-3.10.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5268b35fee7eb754fb5b3d0f16a84a2e9ed21306f5377f3818596214ad2d7714"}, - {file = "aiohttp-3.10.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:ff25d988fd6ce433b5c393094a5ca50df568bdccf90a8b340900e24e0d5fb45c"}, - {file = "aiohttp-3.10.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:594b4b4f1dfe8378b4a0342576dc87a930c960641159f5ae83843834016dbd59"}, - {file = "aiohttp-3.10.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c8820dad615cd2f296ed3fdea8402b12663ac9e5ea2aafc90ef5141eb10b50b8"}, - {file = "aiohttp-3.10.0-cp310-cp310-win32.whl", hash = "sha256:ab1d870403817c9a0486ca56ccbc0ebaf85d992277d48777faa5a95e40e5bcca"}, - {file = "aiohttp-3.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:563705a94ea3af43467167f3a21c665f3b847b2a0ae5544fa9e18df686a660da"}, - {file = "aiohttp-3.10.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:13679e11937d3f37600860de1f848e2e062e2b396d3aa79b38c89f9c8ab7e791"}, - {file = "aiohttp-3.10.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c66a1aadafbc0bd7d648cb7fcb3860ec9beb1b436ce3357036a4d9284fcef9a"}, - {file = "aiohttp-3.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7e3545b06aae925f90f06402e05cfb9c62c6409ce57041932163b09c48daad6"}, - {file = "aiohttp-3.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:effafe5144aa32f0388e8f99b1b2692cf094ea2f6b7ceca384b54338b77b1f50"}, - {file = "aiohttp-3.10.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a04f2c8d41821a2507b49b2694c40495a295b013afb0cc7355b337980b47c546"}, - {file = "aiohttp-3.10.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6dbfac556219d884d50edc6e1952a93545c2786193f00f5521ec0d9d464040ab"}, - {file = "aiohttp-3.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a65472256c5232681968deeea3cd5453aa091c44e8db09f22f1a1491d422c2d9"}, - {file = "aiohttp-3.10.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:941366a554e566efdd3f042e17a9e461a36202469e5fd2aee66fe3efe6412aef"}, - {file = "aiohttp-3.10.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:927b4aca6340301e7d8bb05278d0b6585b8633ea852b7022d604a5df920486bf"}, - {file = "aiohttp-3.10.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:34adb8412e736a5d0df6d1fccdf71599dfb07a63add241a94a189b6364e997f1"}, - {file = "aiohttp-3.10.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:43c60d9b332a01ee985f080f639f3e56abcfb95ec1320013c94083c3b6a2e143"}, - {file = "aiohttp-3.10.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:3f49edf7c5cd2987634116e1b6a0ee2438fca17f7c4ee480ff41decb76cf6158"}, - {file = "aiohttp-3.10.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9784246431eaf9d651b3cc06f9c64f9a9f57299f4971c5ea778fa0b81074ef13"}, - {file = "aiohttp-3.10.0-cp311-cp311-win32.whl", hash = "sha256:bec91402df78b897a47b66b9c071f48051cea68d853d8bc1d4404896c6de41ae"}, - {file = "aiohttp-3.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:25a9924343bf91b0c5082cae32cfc5a1f8787ac0433966319ec07b0ed4570722"}, - {file = "aiohttp-3.10.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:21dab4a704c68dc7bc2a1219a4027158e8968e2079f1444eda2ba88bc9f2895f"}, - {file = "aiohttp-3.10.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:872c0dcaccebd5733d535868fe2356aa6939f5827dcea7a8b9355bb2eff6f56e"}, - {file = "aiohttp-3.10.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f381424dbce313bb5a666a215e7a9dcebbc533e9a2c467a1f0c95279d24d1fa7"}, - {file = "aiohttp-3.10.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ca48e9f092a417c6669ee8d3a19d40b3c66dde1a2ae0d57e66c34812819b671"}, - {file = "aiohttp-3.10.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbe2f6d0466f5c59c7258e0745c20d74806a1385fbb7963e5bbe2309a11cc69b"}, - {file = "aiohttp-3.10.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:03799a95402a7ed62671c4465e1eae51d749d5439dbc49edb6eee52ea165c50b"}, - {file = "aiohttp-3.10.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5549c71c35b5f057a4eebcc538c41299826f7813f28880722b60e41c861a57ec"}, - {file = "aiohttp-3.10.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f6fa7a42b78d8698491dc4ad388169de54cca551aa9900f750547372de396277"}, - {file = "aiohttp-3.10.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:77bbf0a2f6fefac6c0db1792c234f577d80299a33ce7125467439097cf869198"}, - {file = "aiohttp-3.10.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:34eaf5cfcc979846d73571b1a4be22cad5e029d55cdbe77cdc7545caa4dcb925"}, - {file = "aiohttp-3.10.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4f1de31a585344a106db43a9c3af2e15bb82e053618ff759f1fdd31d82da38eb"}, - {file = "aiohttp-3.10.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f3a1ea61d96146e9b9e5597069466e2e4d9e01e09381c5dd51659f890d5e29e7"}, - {file = "aiohttp-3.10.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:73c01201219eb039a828bb58dcc13112eec2fed6eea718356316cd552df26e04"}, - {file = "aiohttp-3.10.0-cp312-cp312-win32.whl", hash = "sha256:33e915971eee6d2056d15470a1214e4e0f72b6aad10225548a7ab4c4f54e2db7"}, - {file = "aiohttp-3.10.0-cp312-cp312-win_amd64.whl", hash = "sha256:2dc75da06c35a7b47a88ceadbf993a53d77d66423c2a78de8c6f9fb41ec35687"}, - {file = "aiohttp-3.10.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f1bc4d68b83966012813598fe39b35b4e6019b69d29385cf7ec1cb08e1ff829b"}, - {file = "aiohttp-3.10.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d9b8b31c057a0b7bb822a159c490af05cb11b8069097f3236746a78315998afa"}, - {file = "aiohttp-3.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10f0d7894ddc6ff8f369e3fdc082ef1f940dc1f5b9003cd40945d24845477220"}, - {file = "aiohttp-3.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72de8ffba4a27e3c6e83e58a379fc4fe5548f69f9b541fde895afb9be8c31658"}, - {file = "aiohttp-3.10.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:cd36d0f0afc2bd84f007cedd2d9a449c3cf04af471853a25eb71f28bc2e1a119"}, - {file = "aiohttp-3.10.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f64d503c661864866c09806ac360b95457f872d639ca61719115a9f389b2ec90"}, - {file = "aiohttp-3.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31616121369bc823791056c632f544c6c8f8d1ceecffd8bf3f72ef621eaabf49"}, - {file = "aiohttp-3.10.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f76c12abb88b7ee64b3f9ae72f0644af49ff139067b5add142836dab405d60d4"}, - {file = "aiohttp-3.10.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:6c99eef30a7e98144bcf44d615bc0f445b3a3730495fcc16124cb61117e1f81e"}, - {file = "aiohttp-3.10.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:39e7ec718e7a1971a5d98357e3e8c0529477d45c711d32cd91999dc8d8404e1e"}, - {file = "aiohttp-3.10.0-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:f1cef548ee4e84264b78879de0c754bbe223193c6313beb242ce862f82eab184"}, - {file = "aiohttp-3.10.0-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:f98f036eab11d2f90cdd01b9d1410de9d7eb520d070debeb2edadf158b758431"}, - {file = "aiohttp-3.10.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:cc4376ff537f7d2c1e98f97f6d548e99e5d96078b0333c1d3177c11467b972de"}, - {file = "aiohttp-3.10.0-cp38-cp38-win32.whl", hash = "sha256:ebedc51ee6d39f9ea5e26e255fd56a7f4e79a56e77d960f9bae75ef4f95ed57f"}, - {file = "aiohttp-3.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:aad87626f31a85fd4af02ba7fd6cc424b39d4bff5c8677e612882649da572e47"}, - {file = "aiohttp-3.10.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1dc95c5e2a5e60095f1bb51822e3b504e6a7430c9b44bff2120c29bb876c5202"}, - {file = "aiohttp-3.10.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1c83977f7b6f4f4a96fab500f5a76d355f19f42675224a3002d375b3fb309174"}, - {file = "aiohttp-3.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8cedc48d36652dd3ac40e5c7c139d528202393e341a5e3475acedb5e8d5c4c75"}, - {file = "aiohttp-3.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b099fbb823efed3c1d736f343ac60d66531b13680ee9b2669e368280f41c2b8"}, - {file = "aiohttp-3.10.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d583755ddb9c97a2da1322f17fc7d26792f4e035f472d675e2761c766f94c2ff"}, - {file = "aiohttp-3.10.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a03a4407bdb9ae815f0d5a19df482b17df530cf7bf9c78771aa1c713c37ff1f"}, - {file = "aiohttp-3.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcb6e65f6ea7caa0188e36bebe9e72b259d3d525634758c91209afb5a6cbcba7"}, - {file = "aiohttp-3.10.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b6612c6ed3147a4a2d6463454b94b877566b38215665be4c729cd8b7bdce15b4"}, - {file = "aiohttp-3.10.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0b0c0148d2a69b82ffe650c2ce235b431d49a90bde7dd2629bcb40314957acf6"}, - {file = "aiohttp-3.10.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0d85a173b4dbbaaad1900e197181ea0fafa617ca6656663f629a8a372fdc7d06"}, - {file = "aiohttp-3.10.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:12c43dace645023583f3dd2337dfc3aa92c99fb943b64dcf2bc15c7aa0fb4a95"}, - {file = "aiohttp-3.10.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:33acb0d9bf12cdc80ceec6f5fda83ea7990ce0321c54234d629529ca2c54e33d"}, - {file = "aiohttp-3.10.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:91e0b76502205484a4d1d6f25f461fa60fe81a7987b90e57f7b941b0753c3ec8"}, - {file = "aiohttp-3.10.0-cp39-cp39-win32.whl", hash = "sha256:1ebd8ed91428ffbe8b33a5bd6f50174e11882d5b8e2fe28670406ab5ee045ede"}, - {file = "aiohttp-3.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:0433795c4a8bafc03deb3e662192250ba5db347c41231b0273380d2f53c9ea0b"}, - {file = "aiohttp-3.10.0.tar.gz", hash = "sha256:e8dd7da2609303e3574c95b0ec9f1fd49647ef29b94701a2862cceae76382e1d"}, + {file = "aiohttp-3.10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:47b4c2412960e64d97258f40616efddaebcb34ff664c8a972119ed38fac2a62c"}, + {file = "aiohttp-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7dbf637f87dd315fa1f36aaed8afa929ee2c607454fb7791e74c88a0d94da59"}, + {file = "aiohttp-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c8fb76214b5b739ce59e2236a6489d9dc3483649cfd6f563dbf5d8e40dbdd57d"}, + {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c577cdcf8f92862363b3d598d971c6a84ed8f0bf824d4cc1ce70c2fb02acb4a"}, + {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:777e23609899cb230ad2642b4bdf1008890f84968be78de29099a8a86f10b261"}, + {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b07286a1090483799599a2f72f76ac396993da31f6e08efedb59f40876c144fa"}, + {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9db600a86414a9a653e3c1c7f6a2f6a1894ab8f83d11505247bd1b90ad57157"}, + {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c3f1eb280008e51965a8d160a108c333136f4a39d46f516c64d2aa2e6a53f2"}, + {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f5dd109a925fee4c9ac3f6a094900461a2712df41745f5d04782ebcbe6479ccb"}, + {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8c81ff4afffef9b1186639506d70ea90888218f5ddfff03870e74ec80bb59970"}, + {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2a384dfbe8bfebd203b778a30a712886d147c61943675f4719b56725a8bbe803"}, + {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b9fb6508893dc31cfcbb8191ef35abd79751db1d6871b3e2caee83959b4d91eb"}, + {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:88596384c3bec644a96ae46287bb646d6a23fa6014afe3799156aef42669c6bd"}, + {file = "aiohttp-3.10.1-cp310-cp310-win32.whl", hash = "sha256:68164d43c580c2e8bf8e0eb4960142919d304052ccab92be10250a3a33b53268"}, + {file = "aiohttp-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:d6bbe2c90c10382ca96df33b56e2060404a4f0f88673e1e84b44c8952517e5f3"}, + {file = "aiohttp-3.10.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6979b4f20d3e557a867da9d9227de4c156fcdcb348a5848e3e6190fd7feb972"}, + {file = "aiohttp-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03c0c380c83f8a8d4416224aafb88d378376d6f4cadebb56b060688251055cd4"}, + {file = "aiohttp-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c2b104e81b3c3deba7e6f5bc1a9a0e9161c380530479970766a6655b8b77c7c"}, + {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b023b68c61ab0cd48bd38416b421464a62c381e32b9dc7b4bdfa2905807452a4"}, + {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a07c76a82390506ca0eabf57c0540cf5a60c993c442928fe4928472c4c6e5e6"}, + {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:41d8dab8c64ded1edf117d2a64f353efa096c52b853ef461aebd49abae979f16"}, + {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:615348fab1a9ef7d0960a905e83ad39051ae9cb0d2837da739b5d3a7671e497a"}, + {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:256ee6044214ee9d66d531bb374f065ee94e60667d6bbeaa25ca111fc3997158"}, + {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d5bb926805022508b7ddeaad957f1fce7a8d77532068d7bdb431056dc630cd"}, + {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:028faf71b338f069077af6315ad54281612705d68889f5d914318cbc2aab0d50"}, + {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5c12310d153b27aa630750be44e79313acc4e864c421eb7d2bc6fa3429c41bf8"}, + {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:de1a91d5faded9054957ed0a9e01b9d632109341942fc123947ced358c5d9009"}, + {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9c186b270979fb1dee3ababe2d12fb243ed7da08b30abc83ebac3a928a4ddb15"}, + {file = "aiohttp-3.10.1-cp311-cp311-win32.whl", hash = "sha256:4a9ce70f5e00380377aac0e568abd075266ff992be2e271765f7b35d228a990c"}, + {file = "aiohttp-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:a77c79bac8d908d839d32c212aef2354d2246eb9deb3e2cb01ffa83fb7a6ea5d"}, + {file = "aiohttp-3.10.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2212296cdb63b092e295c3e4b4b442e7b7eb41e8a30d0f53c16d5962efed395d"}, + {file = "aiohttp-3.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4dcb127ca3eb0a61205818a606393cbb60d93b7afb9accd2fd1e9081cc533144"}, + {file = "aiohttp-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb8b79a65332e1a426ccb6290ce0409e1dc16b4daac1cc5761e059127fa3d134"}, + {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68cc24f707ed9cb961f6ee04020ca01de2c89b2811f3cf3361dc7c96a14bfbcc"}, + {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cb54f5725b4b37af12edf6c9e834df59258c82c15a244daa521a065fbb11717"}, + {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51d03e948e53b3639ce4d438f3d1d8202898ec6655cadcc09ec99229d4adc2a9"}, + {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:786299d719eb5d868f161aeec56d589396b053925b7e0ce36e983d30d0a3e55c"}, + {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abda4009a30d51d3f06f36bc7411a62b3e647fa6cc935ef667e3e3d3a7dd09b1"}, + {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:67f7639424c313125213954e93a6229d3a1d386855d70c292a12628f600c7150"}, + {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e5a26d7aac4c0d8414a347da162696eea0629fdce939ada6aedf951abb1d745"}, + {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:120548d89f14b76a041088b582454d89389370632ee12bf39d919cc5c561d1ca"}, + {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f5293726943bdcea24715b121d8c4ae12581441d22623b0e6ab12d07ce85f9c4"}, + {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1f8605e573ed6c44ec689d94544b2c4bb1390aaa723a8b5a2cc0a5a485987a68"}, + {file = "aiohttp-3.10.1-cp312-cp312-win32.whl", hash = "sha256:e7168782621be4448d90169a60c8b37e9b0926b3b79b6097bc180c0a8a119e73"}, + {file = "aiohttp-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fbf8c0ded367c5c8eaf585f85ca8dd85ff4d5b73fb8fe1e6ac9e1b5e62e11f7"}, + {file = "aiohttp-3.10.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:54b7f4a20d7cc6bfa4438abbde069d417bb7a119f870975f78a2b99890226d55"}, + {file = "aiohttp-3.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2fa643ca990323db68911b92f3f7a0ca9ae300ae340d0235de87c523601e58d9"}, + {file = "aiohttp-3.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8311d0d690487359fe2247ec5d2cac9946e70d50dced8c01ce9e72341c21151"}, + {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222821c60b8f6a64c5908cb43d69c0ee978a1188f6a8433d4757d39231b42cdb"}, + {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7b55d9ede66af7feb6de87ff277e0ccf6d51c7db74cc39337fe3a0e31b5872d"}, + {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a95151a5567b3b00368e99e9c5334a919514f60888a6b6d2054fea5e66e527e"}, + {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9e9171d2fe6bfd9d3838a6fe63b1e91b55e0bf726c16edf265536e4eafed19"}, + {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a57e73f9523e980f6101dc9a83adcd7ac0006ea8bf7937ca3870391c7bb4f8ff"}, + {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0df51a3d70a2bfbb9c921619f68d6d02591f24f10e9c76de6f3388c89ed01de6"}, + {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:b0de63ff0307eac3961b4af74382d30220d4813f36b7aaaf57f063a1243b4214"}, + {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8db9b749f589b5af8e4993623dbda6716b2b7a5fcb0fa2277bf3ce4b278c7059"}, + {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6b14c19172eb53b63931d3e62a9749d6519f7c121149493e6eefca055fcdb352"}, + {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cd57ad998e3038aa87c38fe85c99ed728001bf5dde8eca121cadee06ee3f637"}, + {file = "aiohttp-3.10.1-cp38-cp38-win32.whl", hash = "sha256:df31641e3f02b77eb3c5fb63c0508bee0fc067cf153da0e002ebbb0db0b6d91a"}, + {file = "aiohttp-3.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:93094eba50bc2ad4c40ff4997ead1fdcd41536116f2e7d6cfec9596a8ecb3615"}, + {file = "aiohttp-3.10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:440954ddc6b77257e67170d57b1026aa9545275c33312357472504eef7b4cc0b"}, + {file = "aiohttp-3.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f9f8beed277488a52ee2b459b23c4135e54d6a819eaba2e120e57311015b58e9"}, + {file = "aiohttp-3.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8a8221a63602008550022aa3a4152ca357e1dde7ab3dd1da7e1925050b56863"}, + {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a702bd3663b5cbf3916e84bf332400d24cdb18399f0877ca6b313ce6c08bfb43"}, + {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1988b370536eb14f0ce7f3a4a5b422ab64c4e255b3f5d7752c5f583dc8c967fc"}, + {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ccf1f0a304352c891d124ac1a9dea59b14b2abed1704aaa7689fc90ef9c5be1"}, + {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc3ea6ef2a83edad84bbdb5d96e22f587b67c68922cd7b6f9d8f24865e655bcf"}, + {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b47c125ab07f0831803b88aeb12b04c564d5f07a1c1a225d4eb4d2f26e8b5e"}, + {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:21778552ef3d44aac3278cc6f6d13a6423504fa5f09f2df34bfe489ed9ded7f5"}, + {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bde0693073fd5e542e46ea100aa6c1a5d36282dbdbad85b1c3365d5421490a92"}, + {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bf66149bb348d8e713f3a8e0b4f5b952094c2948c408e1cfef03b49e86745d60"}, + {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:587237571a85716d6f71f60d103416c9df7d5acb55d96d3d3ced65f39bff9c0c"}, + {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bfe33cba6e127d0b5b417623c9aa621f0a69f304742acdca929a9fdab4593693"}, + {file = "aiohttp-3.10.1-cp39-cp39-win32.whl", hash = "sha256:9fbff00646cf8211b330690eb2fd64b23e1ce5b63a342436c1d1d6951d53d8dd"}, + {file = "aiohttp-3.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:5951c328f9ac42d7bce7a6ded535879bc9ae13032818d036749631fa27777905"}, + {file = "aiohttp-3.10.1.tar.gz", hash = "sha256:8b0d058e4e425d3b45e8ec70d49b402f4d6b21041e674798b1f91ba027c73f28"}, ] [package.dependencies] @@ -3234,13 +3234,13 @@ files = [ [[package]] name = "openai" -version = "1.38.0" +version = "1.39.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.38.0-py3-none-any.whl", hash = "sha256:a19ef052f1676320f52183ae6f9775da6d888fbe3aec57886117163c095d9f7c"}, - {file = "openai-1.38.0.tar.gz", hash = "sha256:30fb324bf452ecb1194ca7dbc64566a4d7aa054c6a5da857937ede7d517a220b"}, + {file = "openai-1.39.0-py3-none-any.whl", hash = "sha256:a712553a131c59a249c474d0bb6a0414f41df36dc186d3a018fa7e600e57fb7f"}, + {file = "openai-1.39.0.tar.gz", hash = "sha256:0cea446082f50985f26809d704a97749cb366a1ba230ef432c684a9745b3f2d9"}, ] [package.dependencies] @@ -4127,99 +4127,120 @@ files = [ [[package]] name = "pyzmq" -version = "26.0.3" +version = "26.1.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:44dd6fc3034f1eaa72ece33588867df9e006a7303725a12d64c3dff92330f625"}, - {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:acb704195a71ac5ea5ecf2811c9ee19ecdc62b91878528302dd0be1b9451cc90"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbb9c997932473a27afa93954bb77a9f9b786b4ccf718d903f35da3232317de"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bcb34f869d431799c3ee7d516554797f7760cb2198ecaa89c3f176f72d062be"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ece17ec5f20d7d9b442e5174ae9f020365d01ba7c112205a4d59cf19dc38ee"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ba6e5e6588e49139a0979d03a7deb9c734bde647b9a8808f26acf9c547cab1bf"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3bf8b000a4e2967e6dfdd8656cd0757d18c7e5ce3d16339e550bd462f4857e59"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2136f64fbb86451dbbf70223635a468272dd20075f988a102bf8a3f194a411dc"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e8918973fbd34e7814f59143c5f600ecd38b8038161239fd1a3d33d5817a38b8"}, - {file = "pyzmq-26.0.3-cp310-cp310-win32.whl", hash = "sha256:0aaf982e68a7ac284377d051c742610220fd06d330dcd4c4dbb4cdd77c22a537"}, - {file = "pyzmq-26.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f1a9b7d00fdf60b4039f4455afd031fe85ee8305b019334b72dcf73c567edc47"}, - {file = "pyzmq-26.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:80b12f25d805a919d53efc0a5ad7c0c0326f13b4eae981a5d7b7cc343318ebb7"}, - {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:a72a84570f84c374b4c287183debc776dc319d3e8ce6b6a0041ce2e400de3f32"}, - {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ca684ee649b55fd8f378127ac8462fb6c85f251c2fb027eb3c887e8ee347bcd"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e222562dc0f38571c8b1ffdae9d7adb866363134299264a1958d077800b193b7"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f17cde1db0754c35a91ac00b22b25c11da6eec5746431d6e5092f0cd31a3fea9"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7c0c0b3244bb2275abe255d4a30c050d541c6cb18b870975553f1fb6f37527"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac97a21de3712afe6a6c071abfad40a6224fd14fa6ff0ff8d0c6e6cd4e2f807a"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88b88282e55fa39dd556d7fc04160bcf39dea015f78e0cecec8ff4f06c1fc2b5"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:72b67f966b57dbd18dcc7efbc1c7fc9f5f983e572db1877081f075004614fcdd"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4b6cecbbf3b7380f3b61de3a7b93cb721125dc125c854c14ddc91225ba52f83"}, - {file = "pyzmq-26.0.3-cp311-cp311-win32.whl", hash = "sha256:eed56b6a39216d31ff8cd2f1d048b5bf1700e4b32a01b14379c3b6dde9ce3aa3"}, - {file = "pyzmq-26.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:3191d312c73e3cfd0f0afdf51df8405aafeb0bad71e7ed8f68b24b63c4f36500"}, - {file = "pyzmq-26.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:b6907da3017ef55139cf0e417c5123a84c7332520e73a6902ff1f79046cd3b94"}, - {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:068ca17214038ae986d68f4a7021f97e187ed278ab6dccb79f837d765a54d753"}, - {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7821d44fe07335bea256b9f1f41474a642ca55fa671dfd9f00af8d68a920c2d4"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb438a26d87c123bb318e5f2b3d86a36060b01f22fbdffd8cf247d52f7c9a2b"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69ea9d6d9baa25a4dc9cef5e2b77b8537827b122214f210dd925132e34ae9b12"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7daa3e1369355766dea11f1d8ef829905c3b9da886ea3152788dc25ee6079e02"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6ca7a9a06b52d0e38ccf6bca1aeff7be178917893f3883f37b75589d42c4ac20"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1b7d0e124948daa4d9686d421ef5087c0516bc6179fdcf8828b8444f8e461a77"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e746524418b70f38550f2190eeee834db8850088c834d4c8406fbb9bc1ae10b2"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6b3146f9ae6af82c47a5282ac8803523d381b3b21caeae0327ed2f7ecb718798"}, - {file = "pyzmq-26.0.3-cp312-cp312-win32.whl", hash = "sha256:2b291d1230845871c00c8462c50565a9cd6026fe1228e77ca934470bb7d70ea0"}, - {file = "pyzmq-26.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:926838a535c2c1ea21c903f909a9a54e675c2126728c21381a94ddf37c3cbddf"}, - {file = "pyzmq-26.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:5bf6c237f8c681dfb91b17f8435b2735951f0d1fad10cc5dfd96db110243370b"}, - {file = "pyzmq-26.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c0991f5a96a8e620f7691e61178cd8f457b49e17b7d9cfa2067e2a0a89fc1d5"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dbf012d8fcb9f2cf0643b65df3b355fdd74fc0035d70bb5c845e9e30a3a4654b"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:01fbfbeb8249a68d257f601deb50c70c929dc2dfe683b754659569e502fbd3aa"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c8eb19abe87029c18f226d42b8a2c9efdd139d08f8bf6e085dd9075446db450"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5344b896e79800af86ad643408ca9aa303a017f6ebff8cee5a3163c1e9aec987"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:204e0f176fd1d067671157d049466869b3ae1fc51e354708b0dc41cf94e23a3a"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a42db008d58530efa3b881eeee4991146de0b790e095f7ae43ba5cc612decbc5"}, - {file = "pyzmq-26.0.3-cp37-cp37m-win32.whl", hash = "sha256:8d7a498671ca87e32b54cb47c82a92b40130a26c5197d392720a1bce1b3c77cf"}, - {file = "pyzmq-26.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:3b4032a96410bdc760061b14ed6a33613ffb7f702181ba999df5d16fb96ba16a"}, - {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2cc4e280098c1b192c42a849de8de2c8e0f3a84086a76ec5b07bfee29bda7d18"}, - {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bde86a2ed3ce587fa2b207424ce15b9a83a9fa14422dcc1c5356a13aed3df9d"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34106f68e20e6ff253c9f596ea50397dbd8699828d55e8fa18bd4323d8d966e6"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ebbbd0e728af5db9b04e56389e2299a57ea8b9dd15c9759153ee2455b32be6ad"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b1d1c631e5940cac5a0b22c5379c86e8df6a4ec277c7a856b714021ab6cfad"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e891ce81edd463b3b4c3b885c5603c00141151dd9c6936d98a680c8c72fe5c67"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9b273ecfbc590a1b98f014ae41e5cf723932f3b53ba9367cfb676f838038b32c"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b32bff85fb02a75ea0b68f21e2412255b5731f3f389ed9aecc13a6752f58ac97"}, - {file = "pyzmq-26.0.3-cp38-cp38-win32.whl", hash = "sha256:f6c21c00478a7bea93caaaef9e7629145d4153b15a8653e8bb4609d4bc70dbfc"}, - {file = "pyzmq-26.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:3401613148d93ef0fd9aabdbddb212de3db7a4475367f49f590c837355343972"}, - {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:2ed8357f4c6e0daa4f3baf31832df8a33334e0fe5b020a61bc8b345a3db7a606"}, - {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1c8f2a2ca45292084c75bb6d3a25545cff0ed931ed228d3a1810ae3758f975f"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b63731993cdddcc8e087c64e9cf003f909262b359110070183d7f3025d1c56b5"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b3cd31f859b662ac5d7f4226ec7d8bd60384fa037fc02aee6ff0b53ba29a3ba8"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115f8359402fa527cf47708d6f8a0f8234f0e9ca0cab7c18c9c189c194dbf620"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:715bdf952b9533ba13dfcf1f431a8f49e63cecc31d91d007bc1deb914f47d0e4"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e1258c639e00bf5e8a522fec6c3eaa3e30cf1c23a2f21a586be7e04d50c9acab"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:15c59e780be8f30a60816a9adab900c12a58d79c1ac742b4a8df044ab2a6d920"}, - {file = "pyzmq-26.0.3-cp39-cp39-win32.whl", hash = "sha256:d0cdde3c78d8ab5b46595054e5def32a755fc028685add5ddc7403e9f6de9879"}, - {file = "pyzmq-26.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ce828058d482ef860746bf532822842e0ff484e27f540ef5c813d516dd8896d2"}, - {file = "pyzmq-26.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:788f15721c64109cf720791714dc14afd0f449d63f3a5487724f024345067381"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c18645ef6294d99b256806e34653e86236eb266278c8ec8112622b61db255de"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e6bc96ebe49604df3ec2c6389cc3876cabe475e6bfc84ced1bf4e630662cb35"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:971e8990c5cc4ddcff26e149398fc7b0f6a042306e82500f5e8db3b10ce69f84"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8416c23161abd94cc7da80c734ad7c9f5dbebdadfdaa77dad78244457448223"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:082a2988364b60bb5de809373098361cf1dbb239623e39e46cb18bc035ed9c0c"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d57dfbf9737763b3a60d26e6800e02e04284926329aee8fb01049635e957fe81"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77a85dca4c2430ac04dc2a2185c2deb3858a34fe7f403d0a946fa56970cf60a1"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c82a6d952a1d555bf4be42b6532927d2a5686dd3c3e280e5f63225ab47ac1f5"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4496b1282c70c442809fc1b151977c3d967bfb33e4e17cedbf226d97de18f709"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e4946d6bdb7ba972dfda282f9127e5756d4f299028b1566d1245fa0d438847e6"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:03c0ae165e700364b266876d712acb1ac02693acd920afa67da2ebb91a0b3c09"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3e3070e680f79887d60feeda051a58d0ac36622e1759f305a41059eff62c6da7"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6ca08b840fe95d1c2bd9ab92dac5685f949fc6f9ae820ec16193e5ddf603c3b2"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e76654e9dbfb835b3518f9938e565c7806976c07b37c33526b574cc1a1050480"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:871587bdadd1075b112e697173e946a07d722459d20716ceb3d1bd6c64bd08ce"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d0a2d1bd63a4ad79483049b26514e70fa618ce6115220da9efdff63688808b17"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0270b49b6847f0d106d64b5086e9ad5dc8a902413b5dbbb15d12b60f9c1747a4"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:703c60b9910488d3d0954ca585c34f541e506a091a41930e663a098d3b794c67"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74423631b6be371edfbf7eabb02ab995c2563fee60a80a30829176842e71722a"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4adfbb5451196842a88fda3612e2c0414134874bffb1c2ce83ab4242ec9e027d"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3516119f4f9b8671083a70b6afaa0a070f5683e431ab3dc26e9215620d7ca1ad"}, - {file = "pyzmq-26.0.3.tar.gz", hash = "sha256:dba7d9f2e047dfa2bca3b01f4f84aa5246725203d6284e3790f2ca15fba6b40a"}, + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e"}, + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88"}, + {file = "pyzmq-26.1.0-cp310-cp310-win32.whl", hash = "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b"}, + {file = "pyzmq-26.1.0-cp311-cp311-win32.whl", hash = "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83"}, + {file = "pyzmq-26.1.0-cp312-cp312-win32.whl", hash = "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4"}, + {file = "pyzmq-26.1.0-cp313-cp313-win32.whl", hash = "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win32.whl", hash = "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c"}, + {file = "pyzmq-26.1.0-cp38-cp38-win32.whl", hash = "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741"}, + {file = "pyzmq-26.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8"}, + {file = "pyzmq-26.1.0-cp39-cp39-win32.whl", hash = "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c"}, + {file = "pyzmq-26.1.0.tar.gz", hash = "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f"}, ] [package.dependencies] @@ -4547,111 +4568,121 @@ files = [ [[package]] name = "safetensors" -version = "0.4.3" +version = "0.4.4" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "safetensors-0.4.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:dcf5705cab159ce0130cd56057f5f3425023c407e170bca60b4868048bae64fd"}, - {file = "safetensors-0.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bb4f8c5d0358a31e9a08daeebb68f5e161cdd4018855426d3f0c23bb51087055"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70a5319ef409e7f88686a46607cbc3c428271069d8b770076feaf913664a07ac"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb9c65bd82f9ef3ce4970dc19ee86be5f6f93d032159acf35e663c6bea02b237"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edb5698a7bc282089f64c96c477846950358a46ede85a1c040e0230344fdde10"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efcc860be094b8d19ac61b452ec635c7acb9afa77beb218b1d7784c6d41fe8ad"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d88b33980222085dd6001ae2cad87c6068e0991d4f5ccf44975d216db3b57376"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5fc6775529fb9f0ce2266edd3e5d3f10aab068e49f765e11f6f2a63b5367021d"}, - {file = "safetensors-0.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9c6ad011c1b4e3acff058d6b090f1da8e55a332fbf84695cf3100c649cc452d1"}, - {file = "safetensors-0.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c496c5401c1b9c46d41a7688e8ff5b0310a3b9bae31ce0f0ae870e1ea2b8caf"}, - {file = "safetensors-0.4.3-cp310-none-win32.whl", hash = "sha256:38e2a8666178224a51cca61d3cb4c88704f696eac8f72a49a598a93bbd8a4af9"}, - {file = "safetensors-0.4.3-cp310-none-win_amd64.whl", hash = "sha256:393e6e391467d1b2b829c77e47d726f3b9b93630e6a045b1d1fca67dc78bf632"}, - {file = "safetensors-0.4.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:22f3b5d65e440cec0de8edaa672efa888030802e11c09b3d6203bff60ebff05a"}, - {file = "safetensors-0.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c4fa560ebd4522adddb71dcd25d09bf211b5634003f015a4b815b7647d62ebe"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9afd5358719f1b2cf425fad638fc3c887997d6782da317096877e5b15b2ce93"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8c5093206ef4b198600ae484230402af6713dab1bd5b8e231905d754022bec7"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0b2104df1579d6ba9052c0ae0e3137c9698b2d85b0645507e6fd1813b70931a"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8cf18888606dad030455d18f6c381720e57fc6a4170ee1966adb7ebc98d4d6a3"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bf4f9d6323d9f86eef5567eabd88f070691cf031d4c0df27a40d3b4aaee755b"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:585c9ae13a205807b63bef8a37994f30c917ff800ab8a1ca9c9b5d73024f97ee"}, - {file = "safetensors-0.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faefeb3b81bdfb4e5a55b9bbdf3d8d8753f65506e1d67d03f5c851a6c87150e9"}, - {file = "safetensors-0.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:befdf0167ad626f22f6aac6163477fcefa342224a22f11fdd05abb3995c1783c"}, - {file = "safetensors-0.4.3-cp311-none-win32.whl", hash = "sha256:a7cef55929dcbef24af3eb40bedec35d82c3c2fa46338bb13ecf3c5720af8a61"}, - {file = "safetensors-0.4.3-cp311-none-win_amd64.whl", hash = "sha256:840b7ac0eff5633e1d053cc9db12fdf56b566e9403b4950b2dc85393d9b88d67"}, - {file = "safetensors-0.4.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:22d21760dc6ebae42e9c058d75aa9907d9f35e38f896e3c69ba0e7b213033856"}, - {file = "safetensors-0.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d22c1a10dff3f64d0d68abb8298a3fd88ccff79f408a3e15b3e7f637ef5c980"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1648568667f820b8c48317c7006221dc40aced1869908c187f493838a1362bc"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:446e9fe52c051aeab12aac63d1017e0f68a02a92a027b901c4f8e931b24e5397"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fef5d70683643618244a4f5221053567ca3e77c2531e42ad48ae05fae909f542"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a1f4430cc0c9d6afa01214a4b3919d0a029637df8e09675ceef1ca3f0dfa0df"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d603846a8585b9432a0fd415db1d4c57c0f860eb4aea21f92559ff9902bae4d"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a844cdb5d7cbc22f5f16c7e2a0271170750763c4db08381b7f696dbd2c78a361"}, - {file = "safetensors-0.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:88887f69f7a00cf02b954cdc3034ffb383b2303bc0ab481d4716e2da51ddc10e"}, - {file = "safetensors-0.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ee463219d9ec6c2be1d331ab13a8e0cd50d2f32240a81d498266d77d07b7e71e"}, - {file = "safetensors-0.4.3-cp312-none-win32.whl", hash = "sha256:d0dd4a1db09db2dba0f94d15addc7e7cd3a7b0d393aa4c7518c39ae7374623c3"}, - {file = "safetensors-0.4.3-cp312-none-win_amd64.whl", hash = "sha256:d14d30c25897b2bf19b6fb5ff7e26cc40006ad53fd4a88244fdf26517d852dd7"}, - {file = "safetensors-0.4.3-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d1456f814655b224d4bf6e7915c51ce74e389b413be791203092b7ff78c936dd"}, - {file = "safetensors-0.4.3-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:455d538aa1aae4a8b279344a08136d3f16334247907b18a5c3c7fa88ef0d3c46"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf476bca34e1340ee3294ef13e2c625833f83d096cfdf69a5342475602004f95"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:02ef3a24face643456020536591fbd3c717c5abaa2737ec428ccbbc86dffa7a4"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7de32d0d34b6623bb56ca278f90db081f85fb9c5d327e3c18fd23ac64f465768"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a0deb16a1d3ea90c244ceb42d2c6c276059616be21a19ac7101aa97da448faf"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c59d51f182c729f47e841510b70b967b0752039f79f1de23bcdd86462a9b09ee"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f598b713cc1a4eb31d3b3203557ac308acf21c8f41104cdd74bf640c6e538e3"}, - {file = "safetensors-0.4.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5757e4688f20df083e233b47de43845d1adb7e17b6cf7da5f8444416fc53828d"}, - {file = "safetensors-0.4.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fe746d03ed8d193674a26105e4f0fe6c726f5bb602ffc695b409eaf02f04763d"}, - {file = "safetensors-0.4.3-cp37-none-win32.whl", hash = "sha256:0d5ffc6a80f715c30af253e0e288ad1cd97a3d0086c9c87995e5093ebc075e50"}, - {file = "safetensors-0.4.3-cp37-none-win_amd64.whl", hash = "sha256:a11c374eb63a9c16c5ed146457241182f310902bd2a9c18255781bb832b6748b"}, - {file = "safetensors-0.4.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1e31be7945f66be23f4ec1682bb47faa3df34cb89fc68527de6554d3c4258a4"}, - {file = "safetensors-0.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:03a4447c784917c9bf01d8f2ac5080bc15c41692202cd5f406afba16629e84d6"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d244bcafeb1bc06d47cfee71727e775bca88a8efda77a13e7306aae3813fa7e4"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53c4879b9c6bd7cd25d114ee0ef95420e2812e676314300624594940a8d6a91f"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74707624b81f1b7f2b93f5619d4a9f00934d5948005a03f2c1845ffbfff42212"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d52c958dc210265157573f81d34adf54e255bc2b59ded6218500c9b15a750eb"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f9568f380f513a60139971169c4a358b8731509cc19112369902eddb33faa4d"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0d9cd8e1560dfc514b6d7859247dc6a86ad2f83151a62c577428d5102d872721"}, - {file = "safetensors-0.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:89f9f17b0dacb913ed87d57afbc8aad85ea42c1085bd5de2f20d83d13e9fc4b2"}, - {file = "safetensors-0.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1139eb436fd201c133d03c81209d39ac57e129f5e74e34bb9ab60f8d9b726270"}, - {file = "safetensors-0.4.3-cp38-none-win32.whl", hash = "sha256:d9c289f140a9ae4853fc2236a2ffc9a9f2d5eae0cb673167e0f1b8c18c0961ac"}, - {file = "safetensors-0.4.3-cp38-none-win_amd64.whl", hash = "sha256:622afd28968ef3e9786562d352659a37de4481a4070f4ebac883f98c5836563e"}, - {file = "safetensors-0.4.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8651c7299cbd8b4161a36cd6a322fa07d39cd23535b144d02f1c1972d0c62f3c"}, - {file = "safetensors-0.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e375d975159ac534c7161269de24ddcd490df2157b55c1a6eeace6cbb56903f0"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:084fc436e317f83f7071fc6a62ca1c513b2103db325cd09952914b50f51cf78f"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:41a727a7f5e6ad9f1db6951adee21bbdadc632363d79dc434876369a17de6ad6"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7dbbde64b6c534548696808a0e01276d28ea5773bc9a2dfb97a88cd3dffe3df"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbae3b4b9d997971431c346edbfe6e41e98424a097860ee872721e176040a893"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01e4b22e3284cd866edeabe4f4d896229495da457229408d2e1e4810c5187121"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dd37306546b58d3043eb044c8103a02792cc024b51d1dd16bd3dd1f334cb3ed"}, - {file = "safetensors-0.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8815b5e1dac85fc534a97fd339e12404db557878c090f90442247e87c8aeaea"}, - {file = "safetensors-0.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e011cc162503c19f4b1fd63dfcddf73739c7a243a17dac09b78e57a00983ab35"}, - {file = "safetensors-0.4.3-cp39-none-win32.whl", hash = "sha256:01feb3089e5932d7e662eda77c3ecc389f97c0883c4a12b5cfdc32b589a811c3"}, - {file = "safetensors-0.4.3-cp39-none-win_amd64.whl", hash = "sha256:3f9cdca09052f585e62328c1c2923c70f46814715c795be65f0b93f57ec98a02"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1b89381517891a7bb7d1405d828b2bf5d75528299f8231e9346b8eba092227f9"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:cd6fff9e56df398abc5866b19a32124815b656613c1c5ec0f9350906fd798aac"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:840caf38d86aa7014fe37ade5d0d84e23dcfbc798b8078015831996ecbc206a3"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9650713b2cfa9537a2baf7dd9fee458b24a0aaaa6cafcea8bdd5fb2b8efdc34"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e4119532cd10dba04b423e0f86aecb96cfa5a602238c0aa012f70c3a40c44b50"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e066e8861eef6387b7c772344d1fe1f9a72800e04ee9a54239d460c400c72aab"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:90964917f5b0fa0fa07e9a051fbef100250c04d150b7026ccbf87a34a54012e0"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c41e1893d1206aa7054029681778d9a58b3529d4c807002c156d58426c225173"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae7613a119a71a497d012ccc83775c308b9c1dab454806291427f84397d852fd"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9bac020faba7f5dc481e881b14b6425265feabb5bfc552551d21189c0eddc3"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:420a98f593ff9930f5822560d14c395ccbc57342ddff3b463bc0b3d6b1951550"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f5e6883af9a68c0028f70a4c19d5a6ab6238a379be36ad300a22318316c00cb0"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:cdd0a3b5da66e7f377474599814dbf5cbf135ff059cc73694de129b58a5e8a2c"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9bfb92f82574d9e58401d79c70c716985dc049b635fef6eecbb024c79b2c46ad"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3615a96dd2dcc30eb66d82bc76cda2565f4f7bfa89fcb0e31ba3cea8a1a9ecbb"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:868ad1b6fc41209ab6bd12f63923e8baeb1a086814cb2e81a65ed3d497e0cf8f"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffba80aa49bd09195145a7fd233a7781173b422eeb995096f2b30591639517"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0acbe31340ab150423347e5b9cc595867d814244ac14218932a5cf1dd38eb39"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19bbdf95de2cf64f25cd614c5236c8b06eb2cfa47cbf64311f4b5d80224623a3"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b852e47eb08475c2c1bd8131207b405793bfc20d6f45aff893d3baaad449ed14"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5d07cbca5b99babb692d76d8151bec46f461f8ad8daafbfd96b2fca40cadae65"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1ab6527a20586d94291c96e00a668fa03f86189b8a9defa2cdd34a1a01acc7d5"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02318f01e332cc23ffb4f6716e05a492c5f18b1d13e343c49265149396284a44"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec4b52ce9a396260eb9731eb6aea41a7320de22ed73a1042c2230af0212758ce"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:018b691383026a2436a22b648873ed11444a364324e7088b99cd2503dd828400"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:309b10dbcab63269ecbf0e2ca10ce59223bb756ca5d431ce9c9eeabd446569da"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b277482120df46e27a58082df06a15aebda4481e30a1c21eefd0921ae7e03f65"}, - {file = "safetensors-0.4.3.tar.gz", hash = "sha256:2f85fc50c4e07a21e95c24e07460fe6f7e2859d0ce88092838352b798ce711c2"}, + {file = "safetensors-0.4.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2adb497ada13097f30e386e88c959c0fda855a5f6f98845710f5bb2c57e14f12"}, + {file = "safetensors-0.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7db7fdc2d71fd1444d85ca3f3d682ba2df7d61a637dfc6d80793f439eae264ab"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4f0eed76b430f009fbefca1a0028ddb112891b03cb556d7440d5cd68eb89a9"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57d216fab0b5c432aabf7170883d7c11671622bde8bd1436c46d633163a703f6"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7d9b76322e49c056bcc819f8bdca37a2daa5a6d42c07f30927b501088db03309"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32f0d1f6243e90ee43bc6ee3e8c30ac5b09ca63f5dd35dbc985a1fc5208c451a"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d464bdc384874601a177375028012a5f177f1505279f9456fea84bbc575c7f"}, + {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63144e36209ad8e4e65384dbf2d52dd5b1866986079c00a72335402a38aacdc5"}, + {file = "safetensors-0.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:051d5ecd490af7245258000304b812825974d5e56f14a3ff7e1b8b2ba6dc2ed4"}, + {file = "safetensors-0.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51bc8429d9376224cd3cf7e8ce4f208b4c930cd10e515b6ac6a72cbc3370f0d9"}, + {file = "safetensors-0.4.4-cp310-none-win32.whl", hash = "sha256:fb7b54830cee8cf9923d969e2df87ce20e625b1af2fd194222ab902d3adcc29c"}, + {file = "safetensors-0.4.4-cp310-none-win_amd64.whl", hash = "sha256:4b3e8aa8226d6560de8c2b9d5ff8555ea482599c670610758afdc97f3e021e9c"}, + {file = "safetensors-0.4.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bbaa31f2cb49013818bde319232ccd72da62ee40f7d2aa532083eda5664e85ff"}, + {file = "safetensors-0.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fdcb80f4e9fbb33b58e9bf95e7dbbedff505d1bcd1c05f7c7ce883632710006"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55c14c20be247b8a1aeaf3ab4476265e3ca83096bb8e09bb1a7aa806088def4f"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:949aaa1118660f992dbf0968487b3e3cfdad67f948658ab08c6b5762e90cc8b6"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c11a4ab7debc456326a2bac67f35ee0ac792bcf812c7562a4a28559a5c795e27"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0cea44bba5c5601b297bc8307e4075535b95163402e4906b2e9b82788a2a6df"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9d752c97f6bbe327352f76e5b86442d776abc789249fc5e72eacb49e6916482"}, + {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03f2bb92e61b055ef6cc22883ad1ae898010a95730fa988c60a23800eb742c2c"}, + {file = "safetensors-0.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:87bf3f91a9328a941acc44eceffd4e1f5f89b030985b2966637e582157173b98"}, + {file = "safetensors-0.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:20d218ec2b6899d29d6895419a58b6e44cc5ff8f0cc29fac8d236a8978ab702e"}, + {file = "safetensors-0.4.4-cp311-none-win32.whl", hash = "sha256:8079486118919f600c603536e2490ca37b3dbd3280e3ad6eaacfe6264605ac8a"}, + {file = "safetensors-0.4.4-cp311-none-win_amd64.whl", hash = "sha256:2f8c2eb0615e2e64ee27d478c7c13f51e5329d7972d9e15528d3e4cfc4a08f0d"}, + {file = "safetensors-0.4.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:baec5675944b4a47749c93c01c73d826ef7d42d36ba8d0dba36336fa80c76426"}, + {file = "safetensors-0.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f15117b96866401825f3e94543145028a2947d19974429246ce59403f49e77c6"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a13a9caea485df164c51be4eb0c87f97f790b7c3213d635eba2314d959fe929"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b54bc4ca5f9b9bba8cd4fb91c24b2446a86b5ae7f8975cf3b7a277353c3127c"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08332c22e03b651c8eb7bf5fc2de90044f3672f43403b3d9ac7e7e0f4f76495e"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bb62841e839ee992c37bb75e75891c7f4904e772db3691c59daaca5b4ab960e1"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5b927acc5f2f59547270b0309a46d983edc44be64e1ca27a7fcb0474d6cd67"}, + {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2a69c71b1ae98a8021a09a0b43363b0143b0ce74e7c0e83cacba691b62655fb8"}, + {file = "safetensors-0.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23654ad162c02a5636f0cd520a0310902c4421aab1d91a0b667722a4937cc445"}, + {file = "safetensors-0.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0677c109d949cf53756859160b955b2e75b0eefe952189c184d7be30ecf7e858"}, + {file = "safetensors-0.4.4-cp312-none-win32.whl", hash = "sha256:a51d0ddd4deb8871c6de15a772ef40b3dbd26a3c0451bb9e66bc76fc5a784e5b"}, + {file = "safetensors-0.4.4-cp312-none-win_amd64.whl", hash = "sha256:2d065059e75a798bc1933c293b68d04d79b586bb7f8c921e0ca1e82759d0dbb1"}, + {file = "safetensors-0.4.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9d625692578dd40a112df30c02a1adf068027566abd8e6a74893bb13d441c150"}, + {file = "safetensors-0.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7cabcf39c81e5b988d0adefdaea2eb9b4fd9bd62d5ed6559988c62f36bfa9a89"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8359bef65f49d51476e9811d59c015f0ddae618ee0e44144f5595278c9f8268c"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a32c662e7df9226fd850f054a3ead0e4213a96a70b5ce37b2d26ba27004e013"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c329a4dcc395364a1c0d2d1574d725fe81a840783dda64c31c5a60fc7d41472c"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:239ee093b1db877c9f8fe2d71331a97f3b9c7c0d3ab9f09c4851004a11f44b65"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd574145d930cf9405a64f9923600879a5ce51d9f315443a5f706374841327b6"}, + {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f6784eed29f9e036acb0b7769d9e78a0dc2c72c2d8ba7903005350d817e287a4"}, + {file = "safetensors-0.4.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:65a4a6072436bf0a4825b1c295d248cc17e5f4651e60ee62427a5bcaa8622a7a"}, + {file = "safetensors-0.4.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:df81e3407630de060ae8313da49509c3caa33b1a9415562284eaf3d0c7705f9f"}, + {file = "safetensors-0.4.4-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:e4a0f374200e8443d9746e947ebb346c40f83a3970e75a685ade0adbba5c48d9"}, + {file = "safetensors-0.4.4-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:181fb5f3dee78dae7fd7ec57d02e58f7936498d587c6b7c1c8049ef448c8d285"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb4ac1d8f6b65ec84ddfacd275079e89d9df7c92f95675ba96c4f790a64df6e"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76897944cd9239e8a70955679b531b9a0619f76e25476e57ed373322d9c2075d"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a9e9d1a27e51a0f69e761a3d581c3af46729ec1c988fa1f839e04743026ae35"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:005ef9fc0f47cb9821c40793eb029f712e97278dae84de91cb2b4809b856685d"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26987dac3752688c696c77c3576f951dbbdb8c57f0957a41fb6f933cf84c0b62"}, + {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c05270b290acd8d249739f40d272a64dd597d5a4b90f27d830e538bc2549303c"}, + {file = "safetensors-0.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:068d3a33711fc4d93659c825a04480ff5a3854e1d78632cdc8f37fee917e8a60"}, + {file = "safetensors-0.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:063421ef08ca1021feea8b46951251b90ae91f899234dd78297cbe7c1db73b99"}, + {file = "safetensors-0.4.4-cp37-none-win32.whl", hash = "sha256:d52f5d0615ea83fd853d4e1d8acf93cc2e0223ad4568ba1e1f6ca72e94ea7b9d"}, + {file = "safetensors-0.4.4-cp37-none-win_amd64.whl", hash = "sha256:88a5ac3280232d4ed8e994cbc03b46a1807ce0aa123867b40c4a41f226c61f94"}, + {file = "safetensors-0.4.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3467ab511bfe3360967d7dc53b49f272d59309e57a067dd2405b4d35e7dcf9dc"}, + {file = "safetensors-0.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2ab4c96d922e53670ce25fbb9b63d5ea972e244de4fa1dd97b590d9fd66aacef"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87df18fce4440477c3ef1fd7ae17c704a69a74a77e705a12be135ee0651a0c2d"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e5fe345b2bc7d88587149ac11def1f629d2671c4c34f5df38aed0ba59dc37f8"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f1a3e01dce3cd54060791e7e24588417c98b941baa5974700eeb0b8eb65b0a0"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6bf35e9a8998d8339fd9a05ac4ce465a4d2a2956cc0d837b67c4642ed9e947"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:166c0c52f6488b8538b2a9f3fbc6aad61a7261e170698779b371e81b45f0440d"}, + {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:87e9903b8668a16ef02c08ba4ebc91e57a49c481e9b5866e31d798632805014b"}, + {file = "safetensors-0.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9c421153aa23c323bd8483d4155b4eee82c9a50ac11cccd83539104a8279c64"}, + {file = "safetensors-0.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a4b8617499b2371c7353302c5116a7e0a3a12da66389ce53140e607d3bf7b3d3"}, + {file = "safetensors-0.4.4-cp38-none-win32.whl", hash = "sha256:c6280f5aeafa1731f0a3709463ab33d8e0624321593951aefada5472f0b313fd"}, + {file = "safetensors-0.4.4-cp38-none-win_amd64.whl", hash = "sha256:6ceed6247fc2d33b2a7b7d25d8a0fe645b68798856e0bc7a9800c5fd945eb80f"}, + {file = "safetensors-0.4.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5cf6c6f6193797372adf50c91d0171743d16299491c75acad8650107dffa9269"}, + {file = "safetensors-0.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:419010156b914a3e5da4e4adf992bee050924d0fe423c4b329e523e2c14c3547"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88f6fd5a5c1302ce79993cc5feeadcc795a70f953c762544d01fb02b2db4ea33"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d468cffb82d90789696d5b4d8b6ab8843052cba58a15296691a7a3df55143cd2"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9353c2af2dd467333d4850a16edb66855e795561cd170685178f706c80d2c71e"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83c155b4a33368d9b9c2543e78f2452090fb030c52401ca608ef16fa58c98353"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9850754c434e636ce3dc586f534bb23bcbd78940c304775bee9005bf610e98f1"}, + {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:275f500b4d26f67b6ec05629a4600645231bd75e4ed42087a7c1801bff04f4b3"}, + {file = "safetensors-0.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5c2308de665b7130cd0e40a2329278226e4cf083f7400c51ca7e19ccfb3886f3"}, + {file = "safetensors-0.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e06a9ebc8656e030ccfe44634f2a541b4b1801cd52e390a53ad8bacbd65f8518"}, + {file = "safetensors-0.4.4-cp39-none-win32.whl", hash = "sha256:ef73df487b7c14b477016947c92708c2d929e1dee2bacdd6fff5a82ed4539537"}, + {file = "safetensors-0.4.4-cp39-none-win_amd64.whl", hash = "sha256:83d054818a8d1198d8bd8bc3ea2aac112a2c19def2bf73758321976788706398"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1d1f34c71371f0e034004a0b583284b45d233dd0b5f64a9125e16b8a01d15067"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a8043a33d58bc9b30dfac90f75712134ca34733ec3d8267b1bd682afe7194f5"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8db8f0c59c84792c12661f8efa85de160f80efe16b87a9d5de91b93f9e0bce3c"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfc1fc38e37630dd12d519bdec9dcd4b345aec9930bb9ce0ed04461f49e58b52"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5c9d86d9b13b18aafa88303e2cd21e677f5da2a14c828d2c460fe513af2e9a5"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:43251d7f29a59120a26f5a0d9583b9e112999e500afabcfdcb91606d3c5c89e3"}, + {file = "safetensors-0.4.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:2c42e9b277513b81cf507e6121c7b432b3235f980cac04f39f435b7902857f91"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3daacc9a4e3f428a84dd56bf31f20b768eb0b204af891ed68e1f06db9edf546f"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218bbb9b883596715fc9997bb42470bf9f21bb832c3b34c2bf744d6fa8f2bbba"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bd5efc26b39f7fc82d4ab1d86a7f0644c8e34f3699c33f85bfa9a717a030e1b"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56ad9776b65d8743f86698a1973292c966cf3abff627efc44ed60e66cc538ddd"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:30f23e6253c5f43a809dea02dc28a9f5fa747735dc819f10c073fe1b605e97d4"}, + {file = "safetensors-0.4.4-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5512078d00263de6cb04e9d26c9ae17611098f52357fea856213e38dc462f81f"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b96c3d9266439d17f35fc2173111d93afc1162f168e95aed122c1ca517b1f8f1"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:08d464aa72a9a13826946b4fb9094bb4b16554bbea2e069e20bd903289b6ced9"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:210160816d5a36cf41f48f38473b6f70d7bcb4b0527bedf0889cc0b4c3bb07db"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb276a53717f2bcfb6df0bcf284d8a12069002508d4c1ca715799226024ccd45"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a2c28c6487f17d8db0089e8b2cdc13de859366b94cc6cdc50e1b0a4147b56551"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7915f0c60e4e6e65d90f136d85dd3b429ae9191c36b380e626064694563dbd9f"}, + {file = "safetensors-0.4.4-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:00eea99ae422fbfa0b46065acbc58b46bfafadfcec179d4b4a32d5c45006af6c"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb1ed4fcb0b3c2f3ea2c5767434622fe5d660e5752f21ac2e8d737b1e5e480bb"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:73fc9a0a4343188bdb421783e600bfaf81d0793cd4cce6bafb3c2ed567a74cd5"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c37e6b714200824c73ca6eaf007382de76f39466a46e97558b8dc4cf643cfbf"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f75698c5c5c542417ac4956acfc420f7d4a2396adca63a015fd66641ea751759"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca1a209157f242eb183e209040097118472e169f2e069bfbd40c303e24866543"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:177f2b60a058f92a3cec7a1786c9106c29eca8987ecdfb79ee88126e5f47fa31"}, + {file = "safetensors-0.4.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ee9622e84fe6e4cd4f020e5fda70d6206feff3157731df7151d457fdae18e541"}, + {file = "safetensors-0.4.4.tar.gz", hash = "sha256:5fe3e9b705250d0172ed4e100a811543108653fb2b66b9e702a088ad03772a07"}, ] [package.extras] @@ -5365,13 +5396,13 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "transformers" -version = "4.43.3" +version = "4.43.4" description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" optional = false python-versions = ">=3.8.0" files = [ - {file = "transformers-4.43.3-py3-none-any.whl", hash = "sha256:6552beada5d826c25ff9b79139d237ab9050c6ea96b73d7fd2f8a8ba23ee76a4"}, - {file = "transformers-4.43.3.tar.gz", hash = "sha256:820c5b192bb1bf47250802901a8f0bf581e06b8fded89179d4ef08a1e903ee1c"}, + {file = "transformers-4.43.4-py3-none-any.whl", hash = "sha256:d2202ed201e0c44f80de8d753a19f6164187754630bc1f915661b9511d61c773"}, + {file = "transformers-4.43.4.tar.gz", hash = "sha256:b62288990a65ed9bfb79191e04dbb76c9376834ae6e0dd911320a2ced63324fe"}, ] [package.dependencies] diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index b0a21c027..e42669227 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -1945,6 +1945,60 @@ def embed_route_embed( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def executions_route_resume_with_task_token( + self, + *, + task_token: str, + input: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceUpdatedResponse: + """ + Resume an execution with a task token + + Parameters + ---------- + task_token : str + A Task Token is a unique identifier for a specific Task Execution. + + input : typing.Optional[typing.Dict[str, typing.Any]] + The input to resume the execution with + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceUpdatedResponse + The request has succeeded. + + Examples + -------- + from julep.client import JulepApi + + client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + client.executions_route_resume_with_task_token( + task_token="task_token", + ) + """ + _response = self._client_wrapper.httpx_client.request( + "executions", + method="POST", + params={"task_token": task_token}, + json={"input": input, "status": "running"}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def executions_route_get( self, id: CommonUuid, *, request_options: typing.Optional[RequestOptions] = None ) -> ExecutionsExecution: @@ -1989,6 +2043,62 @@ def executions_route_get( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def executions_route_update( + self, + id: CommonUuid, + *, + request: ExecutionsUpdateExecutionRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceUpdatedResponse: + """ + Update an existing Execution + + Parameters + ---------- + id : CommonUuid + ID of the resource + + request : ExecutionsUpdateExecutionRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceUpdatedResponse + The request has succeeded. + + Examples + -------- + from julep import ExecutionsUpdateExecutionRequest_Cancelled + from julep.client import JulepApi + + client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + client.executions_route_update( + id="string", + request=ExecutionsUpdateExecutionRequest_Cancelled( + reason="string", + ), + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"executions/{jsonable_encoder(id)}", + method="PUT", + json=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def execution_transitions_route_list( self, id: CommonUuid, @@ -2904,125 +3014,6 @@ def task_executions_route_create( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def task_executions_route_resume_with_task_token( - self, - id: CommonUuid, - *, - task_token: str, - input: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> CommonResourceUpdatedResponse: - """ - Resume an execution with a task token - - Parameters - ---------- - id : CommonUuid - ID of parent Task - - task_token : str - A Task Token is a unique identifier for a specific Task Execution. - - input : typing.Optional[typing.Dict[str, typing.Any]] - The input to resume the execution with - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CommonResourceUpdatedResponse - The request has succeeded. - - Examples - -------- - from julep.client import JulepApi - - client = JulepApi( - auth_key="YOUR_AUTH_KEY", - api_key="YOUR_API_KEY", - ) - client.task_executions_route_resume_with_task_token( - id="id", - task_token="task_token", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"tasks/{jsonable_encoder(id)}/executions", - method="PUT", - json={"task_token": task_token, "input": input, "status": "running"}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) - - def task_executions_route_update( - self, - id: CommonUuid, - child_id: CommonUuid, - *, - request: ExecutionsUpdateExecutionRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> CommonResourceUpdatedResponse: - """ - Update an existing Execution - - Parameters - ---------- - id : CommonUuid - ID of parent resource - - child_id : CommonUuid - ID of the resource to be updated - - request : ExecutionsUpdateExecutionRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CommonResourceUpdatedResponse - The request has succeeded. - - Examples - -------- - from julep import ExecutionsUpdateExecutionRequest_Cancelled - from julep.client import JulepApi - - client = JulepApi( - auth_key="YOUR_AUTH_KEY", - api_key="YOUR_API_KEY", - ) - client.task_executions_route_update( - id="string", - child_id="string", - request=ExecutionsUpdateExecutionRequest_Cancelled( - reason="string", - ), - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"tasks/{jsonable_encoder(id)}/executions/{jsonable_encoder(child_id)}", - method="PUT", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) - def users_route_list( self, *, @@ -5661,6 +5652,68 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def executions_route_resume_with_task_token( + self, + *, + task_token: str, + input: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceUpdatedResponse: + """ + Resume an execution with a task token + + Parameters + ---------- + task_token : str + A Task Token is a unique identifier for a specific Task Execution. + + input : typing.Optional[typing.Dict[str, typing.Any]] + The input to resume the execution with + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceUpdatedResponse + The request has succeeded. + + Examples + -------- + import asyncio + + from julep.client import AsyncJulepApi + + client = AsyncJulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.executions_route_resume_with_task_token( + task_token="task_token", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + "executions", + method="POST", + params={"task_token": task_token}, + json={"input": input, "status": "running"}, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def executions_route_get( self, id: CommonUuid, *, request_options: typing.Optional[RequestOptions] = None ) -> ExecutionsExecution: @@ -5713,6 +5766,70 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def executions_route_update( + self, + id: CommonUuid, + *, + request: ExecutionsUpdateExecutionRequest, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceUpdatedResponse: + """ + Update an existing Execution + + Parameters + ---------- + id : CommonUuid + ID of the resource + + request : ExecutionsUpdateExecutionRequest + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceUpdatedResponse + The request has succeeded. + + Examples + -------- + import asyncio + + from julep import ExecutionsUpdateExecutionRequest_Cancelled + from julep.client import AsyncJulepApi + + client = AsyncJulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.executions_route_update( + id="string", + request=ExecutionsUpdateExecutionRequest_Cancelled( + reason="string", + ), + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"executions/{jsonable_encoder(id)}", + method="PUT", + json=request, + request_options=request_options, + omit=OMIT, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def execution_transitions_route_list( self, id: CommonUuid, @@ -6740,141 +6857,6 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def task_executions_route_resume_with_task_token( - self, - id: CommonUuid, - *, - task_token: str, - input: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - request_options: typing.Optional[RequestOptions] = None, - ) -> CommonResourceUpdatedResponse: - """ - Resume an execution with a task token - - Parameters - ---------- - id : CommonUuid - ID of parent Task - - task_token : str - A Task Token is a unique identifier for a specific Task Execution. - - input : typing.Optional[typing.Dict[str, typing.Any]] - The input to resume the execution with - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CommonResourceUpdatedResponse - The request has succeeded. - - Examples - -------- - import asyncio - - from julep.client import AsyncJulepApi - - client = AsyncJulepApi( - auth_key="YOUR_AUTH_KEY", - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.task_executions_route_resume_with_task_token( - id="id", - task_token="task_token", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"tasks/{jsonable_encoder(id)}/executions", - method="PUT", - json={"task_token": task_token, "input": input, "status": "running"}, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) - - async def task_executions_route_update( - self, - id: CommonUuid, - child_id: CommonUuid, - *, - request: ExecutionsUpdateExecutionRequest, - request_options: typing.Optional[RequestOptions] = None, - ) -> CommonResourceUpdatedResponse: - """ - Update an existing Execution - - Parameters - ---------- - id : CommonUuid - ID of parent resource - - child_id : CommonUuid - ID of the resource to be updated - - request : ExecutionsUpdateExecutionRequest - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CommonResourceUpdatedResponse - The request has succeeded. - - Examples - -------- - import asyncio - - from julep import ExecutionsUpdateExecutionRequest_Cancelled - from julep.client import AsyncJulepApi - - client = AsyncJulepApi( - auth_key="YOUR_AUTH_KEY", - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.task_executions_route_update( - id="string", - child_id="string", - request=ExecutionsUpdateExecutionRequest_Cancelled( - reason="string", - ), - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"tasks/{jsonable_encoder(id)}/executions/{jsonable_encoder(child_id)}", - method="PUT", - json=request, - request_options=request_options, - omit=OMIT, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) - async def users_route_list( self, *, diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index 795aeea75..0e13814f2 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -2607,6 +2607,85 @@ client.embed_route_embed( + + + + +
client.executions_route_resume_with_task_token(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Resume an execution with a task token +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from julep.client import JulepApi + +client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", +) +client.executions_route_resume_with_task_token( + task_token="task_token", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**task_token:** `str` — A Task Token is a unique identifier for a specific Task Execution. + +
+
+ +
+
+ +**input:** `typing.Optional[typing.Dict[str, typing.Any]]` — The input to resume the execution with + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -2678,6 +2757,89 @@ client.executions_route_get( + + + + +
client.executions_route_update(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Update an existing Execution +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from julep import ExecutionsUpdateExecutionRequest_Cancelled +from julep.client import JulepApi + +client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", +) +client.executions_route_update( + id="string", + request=ExecutionsUpdateExecutionRequest_Cancelled( + reason="string", + ), +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `CommonUuid` — ID of the resource + +
+
+ +
+
+ +**request:** `ExecutionsUpdateExecutionRequest` + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -4066,186 +4228,6 @@ client.task_executions_route_create( - - - - -
client.task_executions_route_resume_with_task_token(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Resume an execution with a task token -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from julep.client import JulepApi - -client = JulepApi( - auth_key="YOUR_AUTH_KEY", - api_key="YOUR_API_KEY", -) -client.task_executions_route_resume_with_task_token( - id="id", - task_token="task_token", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `CommonUuid` — ID of parent Task - -
-
- -
-
- -**task_token:** `str` — A Task Token is a unique identifier for a specific Task Execution. - -
-
- -
-
- -**input:** `typing.Optional[typing.Dict[str, typing.Any]]` — The input to resume the execution with - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- - -
-
-
- -
client.task_executions_route_update(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Update an existing Execution -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from julep import ExecutionsUpdateExecutionRequest_Cancelled -from julep.client import JulepApi - -client = JulepApi( - auth_key="YOUR_AUTH_KEY", - api_key="YOUR_API_KEY", -) -client.task_executions_route_update( - id="string", - child_id="string", - request=ExecutionsUpdateExecutionRequest_Cancelled( - reason="string", - ), -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `CommonUuid` — ID of parent resource - -
-
- -
-
- -**child_id:** `CommonUuid` — ID of the resource to be updated - -
-
- -
-
- -**request:** `ExecutionsUpdateExecutionRequest` - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- -
diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 02908773a..870dfac4c 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -1646,13 +1646,13 @@ files = [ [[package]] name = "openai" -version = "1.38.0" +version = "1.39.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.38.0-py3-none-any.whl", hash = "sha256:a19ef052f1676320f52183ae6f9775da6d888fbe3aec57886117163c095d9f7c"}, - {file = "openai-1.38.0.tar.gz", hash = "sha256:30fb324bf452ecb1194ca7dbc64566a4d7aa054c6a5da857937ede7d517a220b"}, + {file = "openai-1.39.0-py3-none-any.whl", hash = "sha256:a712553a131c59a249c474d0bb6a0414f41df36dc186d3a018fa7e600e57fb7f"}, + {file = "openai-1.39.0.tar.gz", hash = "sha256:0cea446082f50985f26809d704a97749cb366a1ba230ef432c684a9745b3f2d9"}, ] [package.dependencies] @@ -2402,99 +2402,120 @@ files = [ [[package]] name = "pyzmq" -version = "26.0.3" +version = "26.1.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:44dd6fc3034f1eaa72ece33588867df9e006a7303725a12d64c3dff92330f625"}, - {file = "pyzmq-26.0.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:acb704195a71ac5ea5ecf2811c9ee19ecdc62b91878528302dd0be1b9451cc90"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5dbb9c997932473a27afa93954bb77a9f9b786b4ccf718d903f35da3232317de"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6bcb34f869d431799c3ee7d516554797f7760cb2198ecaa89c3f176f72d062be"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38ece17ec5f20d7d9b442e5174ae9f020365d01ba7c112205a4d59cf19dc38ee"}, - {file = "pyzmq-26.0.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ba6e5e6588e49139a0979d03a7deb9c734bde647b9a8808f26acf9c547cab1bf"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3bf8b000a4e2967e6dfdd8656cd0757d18c7e5ce3d16339e550bd462f4857e59"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2136f64fbb86451dbbf70223635a468272dd20075f988a102bf8a3f194a411dc"}, - {file = "pyzmq-26.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e8918973fbd34e7814f59143c5f600ecd38b8038161239fd1a3d33d5817a38b8"}, - {file = "pyzmq-26.0.3-cp310-cp310-win32.whl", hash = "sha256:0aaf982e68a7ac284377d051c742610220fd06d330dcd4c4dbb4cdd77c22a537"}, - {file = "pyzmq-26.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:f1a9b7d00fdf60b4039f4455afd031fe85ee8305b019334b72dcf73c567edc47"}, - {file = "pyzmq-26.0.3-cp310-cp310-win_arm64.whl", hash = "sha256:80b12f25d805a919d53efc0a5ad7c0c0326f13b4eae981a5d7b7cc343318ebb7"}, - {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:a72a84570f84c374b4c287183debc776dc319d3e8ce6b6a0041ce2e400de3f32"}, - {file = "pyzmq-26.0.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7ca684ee649b55fd8f378127ac8462fb6c85f251c2fb027eb3c887e8ee347bcd"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e222562dc0f38571c8b1ffdae9d7adb866363134299264a1958d077800b193b7"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f17cde1db0754c35a91ac00b22b25c11da6eec5746431d6e5092f0cd31a3fea9"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b7c0c0b3244bb2275abe255d4a30c050d541c6cb18b870975553f1fb6f37527"}, - {file = "pyzmq-26.0.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ac97a21de3712afe6a6c071abfad40a6224fd14fa6ff0ff8d0c6e6cd4e2f807a"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88b88282e55fa39dd556d7fc04160bcf39dea015f78e0cecec8ff4f06c1fc2b5"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:72b67f966b57dbd18dcc7efbc1c7fc9f5f983e572db1877081f075004614fcdd"}, - {file = "pyzmq-26.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f4b6cecbbf3b7380f3b61de3a7b93cb721125dc125c854c14ddc91225ba52f83"}, - {file = "pyzmq-26.0.3-cp311-cp311-win32.whl", hash = "sha256:eed56b6a39216d31ff8cd2f1d048b5bf1700e4b32a01b14379c3b6dde9ce3aa3"}, - {file = "pyzmq-26.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:3191d312c73e3cfd0f0afdf51df8405aafeb0bad71e7ed8f68b24b63c4f36500"}, - {file = "pyzmq-26.0.3-cp311-cp311-win_arm64.whl", hash = "sha256:b6907da3017ef55139cf0e417c5123a84c7332520e73a6902ff1f79046cd3b94"}, - {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:068ca17214038ae986d68f4a7021f97e187ed278ab6dccb79f837d765a54d753"}, - {file = "pyzmq-26.0.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7821d44fe07335bea256b9f1f41474a642ca55fa671dfd9f00af8d68a920c2d4"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eeb438a26d87c123bb318e5f2b3d86a36060b01f22fbdffd8cf247d52f7c9a2b"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:69ea9d6d9baa25a4dc9cef5e2b77b8537827b122214f210dd925132e34ae9b12"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7daa3e1369355766dea11f1d8ef829905c3b9da886ea3152788dc25ee6079e02"}, - {file = "pyzmq-26.0.3-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:6ca7a9a06b52d0e38ccf6bca1aeff7be178917893f3883f37b75589d42c4ac20"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1b7d0e124948daa4d9686d421ef5087c0516bc6179fdcf8828b8444f8e461a77"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e746524418b70f38550f2190eeee834db8850088c834d4c8406fbb9bc1ae10b2"}, - {file = "pyzmq-26.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6b3146f9ae6af82c47a5282ac8803523d381b3b21caeae0327ed2f7ecb718798"}, - {file = "pyzmq-26.0.3-cp312-cp312-win32.whl", hash = "sha256:2b291d1230845871c00c8462c50565a9cd6026fe1228e77ca934470bb7d70ea0"}, - {file = "pyzmq-26.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:926838a535c2c1ea21c903f909a9a54e675c2126728c21381a94ddf37c3cbddf"}, - {file = "pyzmq-26.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:5bf6c237f8c681dfb91b17f8435b2735951f0d1fad10cc5dfd96db110243370b"}, - {file = "pyzmq-26.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0c0991f5a96a8e620f7691e61178cd8f457b49e17b7d9cfa2067e2a0a89fc1d5"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dbf012d8fcb9f2cf0643b65df3b355fdd74fc0035d70bb5c845e9e30a3a4654b"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:01fbfbeb8249a68d257f601deb50c70c929dc2dfe683b754659569e502fbd3aa"}, - {file = "pyzmq-26.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c8eb19abe87029c18f226d42b8a2c9efdd139d08f8bf6e085dd9075446db450"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5344b896e79800af86ad643408ca9aa303a017f6ebff8cee5a3163c1e9aec987"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:204e0f176fd1d067671157d049466869b3ae1fc51e354708b0dc41cf94e23a3a"}, - {file = "pyzmq-26.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a42db008d58530efa3b881eeee4991146de0b790e095f7ae43ba5cc612decbc5"}, - {file = "pyzmq-26.0.3-cp37-cp37m-win32.whl", hash = "sha256:8d7a498671ca87e32b54cb47c82a92b40130a26c5197d392720a1bce1b3c77cf"}, - {file = "pyzmq-26.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:3b4032a96410bdc760061b14ed6a33613ffb7f702181ba999df5d16fb96ba16a"}, - {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2cc4e280098c1b192c42a849de8de2c8e0f3a84086a76ec5b07bfee29bda7d18"}, - {file = "pyzmq-26.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5bde86a2ed3ce587fa2b207424ce15b9a83a9fa14422dcc1c5356a13aed3df9d"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:34106f68e20e6ff253c9f596ea50397dbd8699828d55e8fa18bd4323d8d966e6"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ebbbd0e728af5db9b04e56389e2299a57ea8b9dd15c9759153ee2455b32be6ad"}, - {file = "pyzmq-26.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f6b1d1c631e5940cac5a0b22c5379c86e8df6a4ec277c7a856b714021ab6cfad"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:e891ce81edd463b3b4c3b885c5603c00141151dd9c6936d98a680c8c72fe5c67"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:9b273ecfbc590a1b98f014ae41e5cf723932f3b53ba9367cfb676f838038b32c"}, - {file = "pyzmq-26.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b32bff85fb02a75ea0b68f21e2412255b5731f3f389ed9aecc13a6752f58ac97"}, - {file = "pyzmq-26.0.3-cp38-cp38-win32.whl", hash = "sha256:f6c21c00478a7bea93caaaef9e7629145d4153b15a8653e8bb4609d4bc70dbfc"}, - {file = "pyzmq-26.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:3401613148d93ef0fd9aabdbddb212de3db7a4475367f49f590c837355343972"}, - {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:2ed8357f4c6e0daa4f3baf31832df8a33334e0fe5b020a61bc8b345a3db7a606"}, - {file = "pyzmq-26.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c1c8f2a2ca45292084c75bb6d3a25545cff0ed931ed228d3a1810ae3758f975f"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:b63731993cdddcc8e087c64e9cf003f909262b359110070183d7f3025d1c56b5"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b3cd31f859b662ac5d7f4226ec7d8bd60384fa037fc02aee6ff0b53ba29a3ba8"}, - {file = "pyzmq-26.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:115f8359402fa527cf47708d6f8a0f8234f0e9ca0cab7c18c9c189c194dbf620"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:715bdf952b9533ba13dfcf1f431a8f49e63cecc31d91d007bc1deb914f47d0e4"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e1258c639e00bf5e8a522fec6c3eaa3e30cf1c23a2f21a586be7e04d50c9acab"}, - {file = "pyzmq-26.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:15c59e780be8f30a60816a9adab900c12a58d79c1ac742b4a8df044ab2a6d920"}, - {file = "pyzmq-26.0.3-cp39-cp39-win32.whl", hash = "sha256:d0cdde3c78d8ab5b46595054e5def32a755fc028685add5ddc7403e9f6de9879"}, - {file = "pyzmq-26.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:ce828058d482ef860746bf532822842e0ff484e27f540ef5c813d516dd8896d2"}, - {file = "pyzmq-26.0.3-cp39-cp39-win_arm64.whl", hash = "sha256:788f15721c64109cf720791714dc14afd0f449d63f3a5487724f024345067381"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2c18645ef6294d99b256806e34653e86236eb266278c8ec8112622b61db255de"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7e6bc96ebe49604df3ec2c6389cc3876cabe475e6bfc84ced1bf4e630662cb35"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:971e8990c5cc4ddcff26e149398fc7b0f6a042306e82500f5e8db3b10ce69f84"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8416c23161abd94cc7da80c734ad7c9f5dbebdadfdaa77dad78244457448223"}, - {file = "pyzmq-26.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:082a2988364b60bb5de809373098361cf1dbb239623e39e46cb18bc035ed9c0c"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d57dfbf9737763b3a60d26e6800e02e04284926329aee8fb01049635e957fe81"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:77a85dca4c2430ac04dc2a2185c2deb3858a34fe7f403d0a946fa56970cf60a1"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4c82a6d952a1d555bf4be42b6532927d2a5686dd3c3e280e5f63225ab47ac1f5"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4496b1282c70c442809fc1b151977c3d967bfb33e4e17cedbf226d97de18f709"}, - {file = "pyzmq-26.0.3-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:e4946d6bdb7ba972dfda282f9127e5756d4f299028b1566d1245fa0d438847e6"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:03c0ae165e700364b266876d712acb1ac02693acd920afa67da2ebb91a0b3c09"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:3e3070e680f79887d60feeda051a58d0ac36622e1759f305a41059eff62c6da7"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6ca08b840fe95d1c2bd9ab92dac5685f949fc6f9ae820ec16193e5ddf603c3b2"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e76654e9dbfb835b3518f9938e565c7806976c07b37c33526b574cc1a1050480"}, - {file = "pyzmq-26.0.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:871587bdadd1075b112e697173e946a07d722459d20716ceb3d1bd6c64bd08ce"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d0a2d1bd63a4ad79483049b26514e70fa618ce6115220da9efdff63688808b17"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0270b49b6847f0d106d64b5086e9ad5dc8a902413b5dbbb15d12b60f9c1747a4"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:703c60b9910488d3d0954ca585c34f541e506a091a41930e663a098d3b794c67"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74423631b6be371edfbf7eabb02ab995c2563fee60a80a30829176842e71722a"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:4adfbb5451196842a88fda3612e2c0414134874bffb1c2ce83ab4242ec9e027d"}, - {file = "pyzmq-26.0.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3516119f4f9b8671083a70b6afaa0a070f5683e431ab3dc26e9215620d7ca1ad"}, - {file = "pyzmq-26.0.3.tar.gz", hash = "sha256:dba7d9f2e047dfa2bca3b01f4f84aa5246725203d6284e3790f2ca15fba6b40a"}, + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e"}, + {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0"}, + {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598"}, + {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88"}, + {file = "pyzmq-26.1.0-cp310-cp310-win32.whl", hash = "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2"}, + {file = "pyzmq-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71"}, + {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2"}, + {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d"}, + {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b"}, + {file = "pyzmq-26.1.0-cp311-cp311-win32.whl", hash = "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29"}, + {file = "pyzmq-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072"}, + {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820"}, + {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb"}, + {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83"}, + {file = "pyzmq-26.1.0-cp312-cp312-win32.whl", hash = "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd"}, + {file = "pyzmq-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322"}, + {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee"}, + {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f"}, + {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4"}, + {file = "pyzmq-26.1.0-cp313-cp313-win32.whl", hash = "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250"}, + {file = "pyzmq-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d"}, + {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c"}, + {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73"}, + {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384"}, + {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf"}, + {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win32.whl", hash = "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1"}, + {file = "pyzmq-26.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0"}, + {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f"}, + {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c"}, + {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c"}, + {file = "pyzmq-26.1.0-cp38-cp38-win32.whl", hash = "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741"}, + {file = "pyzmq-26.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b"}, + {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de"}, + {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6"}, + {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8"}, + {file = "pyzmq-26.1.0-cp39-cp39-win32.whl", hash = "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402"}, + {file = "pyzmq-26.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5"}, + {file = "pyzmq-26.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099"}, + {file = "pyzmq-26.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544"}, + {file = "pyzmq-26.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d"}, + {file = "pyzmq-26.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c"}, + {file = "pyzmq-26.1.0.tar.gz", hash = "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f"}, ] [package.dependencies] diff --git a/sdks/ts/src/api/models/Executions_TaskTokenResumeExecutionRequest.ts b/sdks/ts/src/api/models/Executions_TaskTokenResumeExecutionRequest.ts index 2f988e385..d81fdfa9c 100644 --- a/sdks/ts/src/api/models/Executions_TaskTokenResumeExecutionRequest.ts +++ b/sdks/ts/src/api/models/Executions_TaskTokenResumeExecutionRequest.ts @@ -4,10 +4,6 @@ /* eslint-disable */ export type Executions_TaskTokenResumeExecutionRequest = { status: "running"; - /** - * A Task Token is a unique identifier for a specific Task Execution. - */ - task_token: string; /** * The input to resume the execution with */ diff --git a/sdks/ts/src/api/schemas/$Executions_TaskTokenResumeExecutionRequest.ts b/sdks/ts/src/api/schemas/$Executions_TaskTokenResumeExecutionRequest.ts index c6ecd34b4..fd85a8716 100644 --- a/sdks/ts/src/api/schemas/$Executions_TaskTokenResumeExecutionRequest.ts +++ b/sdks/ts/src/api/schemas/$Executions_TaskTokenResumeExecutionRequest.ts @@ -8,11 +8,6 @@ export const $Executions_TaskTokenResumeExecutionRequest = { type: "Enum", isRequired: true, }, - task_token: { - type: "string", - description: `A Task Token is a unique identifier for a specific Task Execution.`, - isRequired: true, - }, input: { type: "dictionary", contains: { diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index c642a99bd..bfae0db2c 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -804,6 +804,34 @@ export class DefaultService { mediaType: "application/json", }); } + /** + * Resume an execution with a task token + * @returns Common_ResourceUpdatedResponse The request has succeeded. + * @throws ApiError + */ + public executionsRouteResumeWithTaskToken({ + taskToken, + requestBody, + }: { + /** + * A Task Token is a unique identifier for a specific Task Execution. + */ + taskToken: string; + /** + * Request to resume an execution with a task token + */ + requestBody: Executions_TaskTokenResumeExecutionRequest; + }): CancelablePromise { + return this.httpRequest.request({ + method: "POST", + url: "/executions", + query: { + task_token: taskToken, + }, + body: requestBody, + mediaType: "application/json", + }); + } /** * Get an Execution by id * @returns Executions_Execution The request has succeeded. @@ -825,6 +853,31 @@ export class DefaultService { }, }); } + /** + * Update an existing Execution + * @returns Common_ResourceUpdatedResponse The request has succeeded. + * @throws ApiError + */ + public executionsRouteUpdate({ + id, + requestBody, + }: { + /** + * ID of the resource + */ + id: Common_uuid; + requestBody: Executions_UpdateExecutionRequest; + }): CancelablePromise { + return this.httpRequest.request({ + method: "PUT", + url: "/executions/{id}", + path: { + id: id, + }, + body: requestBody, + mediaType: "application/json", + }); + } /** * List the Transitions of an Execution by id * @returns any The request has succeeded. @@ -1453,65 +1506,6 @@ export class DefaultService { }, }); } - /** - * Resume an execution with a task token - * @returns Common_ResourceUpdatedResponse The request has succeeded. - * @throws ApiError - */ - public taskExecutionsRouteResumeWithTaskToken({ - id, - requestBody, - }: { - /** - * ID of parent Task - */ - id: Common_uuid; - /** - * Request to resume an execution with a task token - */ - requestBody: Executions_TaskTokenResumeExecutionRequest; - }): CancelablePromise { - return this.httpRequest.request({ - method: "PUT", - url: "/tasks/{id}/executions", - path: { - id: id, - }, - body: requestBody, - mediaType: "application/json", - }); - } - /** - * Update an existing Execution - * @returns Common_ResourceUpdatedResponse The request has succeeded. - * @throws ApiError - */ - public taskExecutionsRouteUpdate({ - id, - childId, - requestBody, - }: { - /** - * ID of parent resource - */ - id: Common_uuid; - /** - * ID of the resource to be updated - */ - childId: Common_uuid; - requestBody: Executions_UpdateExecutionRequest; - }): CancelablePromise { - return this.httpRequest.request({ - method: "PUT", - url: "/tasks/{id}/executions/{child_id}", - path: { - id: id, - child_id: childId, - }, - body: requestBody, - mediaType: "application/json", - }); - } /** * List users (paginated) * @returns any The request has succeeded. diff --git a/typespec/executions/endpoints.tsp b/typespec/executions/endpoints.tsp index 46ec49286..9b5820d9c 100644 --- a/typespec/executions/endpoints.tsp +++ b/typespec/executions/endpoints.tsp @@ -14,28 +14,20 @@ namespace Executions; // interface Endpoints - extends GetEndpoint {} - -interface TaskEndpoints - extends ChildUpdateEndpoint< - UpdateExecutionRequest, - "Update an existing Execution" - >, - ChildCreateEndpoint< - CreateExecutionRequest, - "Create an execution for the given task" - >, - ChildLimitOffsetPagination { - @put + extends GetEndpoint, + UpdateEndpoint {} + +interface PublicEndpoints { + @post + @useAuth([]) @doc("Resume an execution with a task token") - resumeWithTaskToken( - @header contentType: yaml | json, + op resumeWithTaskToken( + @header contentType: json, - @path - @doc("ID of parent Task") - id: uuid, + // Uses a query parameter `task_token` to get the task token + // See `TaskTokenResumeExecutionRequest` for more details - @body + @bodyRoot @doc("Request to resume an execution with a task token") body: TaskTokenResumeExecutionRequest, ): { @@ -47,6 +39,16 @@ interface TaskEndpoints }; } +interface TaskEndpoints + extends ChildCreateEndpoint< + CreateExecutionRequest, + "Create an execution for the given task" + >, + ChildLimitOffsetPagination< + Execution, + "List executions of the given task" + > {} + interface TransitionEndpoints extends ChildLimitOffsetPagination< { diff --git a/typespec/executions/models.tsp b/typespec/executions/models.tsp index bbc34028c..dd5378b30 100644 --- a/typespec/executions/models.tsp +++ b/typespec/executions/models.tsp @@ -1,6 +1,10 @@ +import "@typespec/http"; + import "../common"; import "../tasks"; +using TypeSpec.Http; + using Common; using Tasks; @@ -80,6 +84,7 @@ model TaskTokenResumeExecutionRequest { status: "running" = "running"; /** A Task Token is a unique identifier for a specific Task Execution. */ + @query task_token: string; /** The input to resume the execution with */ diff --git a/typespec/main.tsp b/typespec/main.tsp index b6213871e..4cf61f6fa 100644 --- a/typespec/main.tsp +++ b/typespec/main.tsp @@ -105,7 +105,7 @@ namespace Api { interface TaskExecutionsRoute extends Executions.TaskEndpoints {} @route("/executions") - interface ExecutionsRoute extends Executions.Endpoints {} + interface ExecutionsRoute extends Executions.Endpoints, Executions.PublicEndpoints {} @route("/executions/{id}/transitions") interface ExecutionTransitionsRoute extends Executions.TransitionEndpoints {} From 1dc4ed3090e2f16f2c885dc12c221b1a0f57bda9 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Mon, 5 Aug 2024 22:10:55 +0300 Subject: [PATCH 007/110] feat: Resume workflow execution --- .../activities/task_steps/__init__.py | 14 +++++ agents-api/agents_api/clients/temporal.py | 4 +- agents-api/agents_api/routers/jobs/routers.py | 34 +++++++---- .../agents_api/routers/tasks/routers.py | 61 ++++++++++--------- .../agents_api/workflows/task_execution.py | 20 +++--- 5 files changed, 84 insertions(+), 49 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 1029e409f..afd0de233 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -6,12 +6,14 @@ from temporalio import activity from ...autogen.openapi_model import ( + CreateTransitionRequest, EvaluateStep, # ErrorWorkflowStep, IfElseWorkflowStep, InputChatMLMessage, PromptStep, ToolCallStep, + UpdateExecutionRequest, YieldStep, ) from ...clients.worker.types import ChatML @@ -23,6 +25,9 @@ from ...models.execution.create_execution_transition import ( create_execution_transition as create_execution_transition_query, ) +from ...models.execution.update_execution import ( + update_execution as update_execution_query, +) from ...routers.sessions.protocol import Settings from ...routers.sessions.session import llm_generate @@ -142,6 +147,15 @@ async def transition_step( **transition_data, ) + update_execution_query( + developer_id=context.developer_id, + task_id=context.task.id, + execution_id=context.execution.id, + data=UpdateExecutionRequest( + status="awaiting_input", + ), + ) + # Raise if it's a waiting step if transition_info.type == "awaiting_input": activity.raise_complete_async() diff --git a/agents-api/agents_api/clients/temporal.py b/agents-api/agents_api/clients/temporal.py index ad9205056..3fb9e9e99 100644 --- a/agents-api/agents_api/clients/temporal.py +++ b/agents-api/agents_api/clients/temporal.py @@ -1,6 +1,6 @@ from uuid import UUID -from temporalio.client import Client, TLSConfig +from temporalio.client import Client, TLSConfig, WorkflowHandle from agents_api.env import ( temporal_client_cert, @@ -75,7 +75,7 @@ async def run_task_execution_workflow( ): client = await get_client() - await client.execute_workflow( + return await client.start_workflow( "TaskExecutionWorkflow", args=[execution_input, start, previous_inputs], task_queue="memory-task-queue", diff --git a/agents-api/agents_api/routers/jobs/routers.py b/agents-api/agents_api/routers/jobs/routers.py index 2d936cb28..1ec60c2b4 100644 --- a/agents-api/agents_api/routers/jobs/routers.py +++ b/agents-api/agents_api/routers/jobs/routers.py @@ -1,31 +1,43 @@ +from typing import Literal + from fastapi import APIRouter from pydantic import UUID4 from temporalio.client import WorkflowExecutionStatus -from agents_api.autogen.openapi_model import JobStatus, State +from agents_api.autogen.openapi_model import JobStatus from agents_api.clients.temporal import get_client router = APIRouter() -def map_job_status(status: WorkflowExecutionStatus) -> State: +def map_job_status( + status: WorkflowExecutionStatus, +) -> Literal[ + "pending", + "in_progress", + "retrying", + "succeeded", + "aborted", + "failed", + "unknown", +]: match status: case WorkflowExecutionStatus.RUNNING: - return State.in_progress + return "in_progress" case WorkflowExecutionStatus.COMPLETED: - return State.succeeded + return "succeeded" case WorkflowExecutionStatus.FAILED: - return State.failed + return "failed" case WorkflowExecutionStatus.CANCELED: - return State.aborted + return "aborted" case WorkflowExecutionStatus.TERMINATED: - return State.aborted + return "aborted" case WorkflowExecutionStatus.CONTINUED_AS_NEW: - return State.in_progress + return "in_progress" case WorkflowExecutionStatus.TIMED_OUT: - return State.failed + return "failed" case _: - return State.unknown + return "unknown" @router.get("/jobs/{job_id}", tags=["jobs"]) @@ -39,7 +51,7 @@ async def get_job_status(job_id: UUID4) -> JobStatus: return JobStatus( name=handle.id, - reason=f"Execution status: {state.name}", + reason=f"Execution status: {state}", created_at=job_description.start_time, updated_at=job_description.execution_time, id=job_id, diff --git a/agents-api/agents_api/routers/tasks/routers.py b/agents-api/agents_api/routers/tasks/routers.py index aa9cebb75..836531453 100644 --- a/agents-api/agents_api/routers/tasks/routers.py +++ b/agents-api/agents_api/routers/tasks/routers.py @@ -9,19 +9,22 @@ from pycozo.client import QueryException from pydantic import UUID4, BaseModel from starlette.status import HTTP_201_CREATED +from temporalio.client import WorkflowHandle from agents_api.autogen.openapi_model import ( CreateExecutionRequest, CreateTaskRequest, Execution, ResourceCreatedResponse, + ResumeExecutionRequest, + StopExecutionRequest, # ResourceUpdatedResponse, Task, Transition, UpdateExecutionRequest, ) from agents_api.clients.cozo import client as cozo_client -from agents_api.clients.temporal import run_task_execution_workflow +from agents_api.clients.temporal import get_client, run_task_execution_workflow from agents_api.common.protocol.tasks import ExecutionInput from agents_api.dependencies.developer_id import get_developer_id from agents_api.models.execution.create_execution import ( @@ -30,6 +33,12 @@ from agents_api.models.execution.get_execution import ( get_execution as get_execution_query, ) +from agents_api.models.execution.get_paused_execution_token import ( + get_paused_execution_token, +) +from agents_api.models.execution.get_temporal_workflow_data import ( + get_temporal_workflow_data, +) # from agents_api.models.execution.get_execution_transition import ( # get_execution_transition as get_execution_transition_query, @@ -43,6 +52,7 @@ from agents_api.models.execution.list_executions import ( list_executions as list_task_executions_query, ) +from agents_api.models.execution.prepare_execution_input import prepare_execution_input from agents_api.models.execution.update_execution import ( update_execution as update_execution_query, ) @@ -207,22 +217,14 @@ async def create_task_execution( raise execution_id = uuid4() - execution = create_execution_query( - developer_id=x_developer_id, - task_id=task_id, - execution_id=execution_id, - data=data, - ) - - execution_input = ExecutionInput.fetch( + execution_input = prepare_execution_input( developer_id=x_developer_id, task_id=task_id, execution_id=execution_id, - client=cozo_client, ) try: - await run_task_execution_workflow( + handle = await run_task_execution_workflow( execution_input=execution_input, job_id=uuid4(), ) @@ -241,6 +243,14 @@ async def create_task_execution( detail="Task creation failed", ) + execution = create_execution_query( + developer_id=x_developer_id, + task_id=task_id, + execution_id=execution_id, + data=data, + workflow_hande=handle, + ) + return ResourceCreatedResponse( id=execution["execution_id"][0], created_at=execution["created_at"][0] ) @@ -305,26 +315,21 @@ async def patch_execution( @router.put("/tasks/{task_id}/executions/{execution_id}", tags=["tasks"]) async def put_execution( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], - task_id: UUID4, execution_id: UUID4, - data: UpdateExecutionRequest, + data: ResumeExecutionRequest | StopExecutionRequest, ) -> Execution: - try: - res = [ - row.to_dict() - for _, row in update_execution_query( - developer_id=x_developer_id, - task_id=task_id, - execution_id=execution_id, - data=data, - ).iterrows() - ][0] - return Execution(**res) - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Execution not found", + temporal_client = await get_client() + if isinstance(data, StopExecutionRequest): + handle = temporal_client.get_workflow_handle_for( + *get_temporal_workflow_data(execution_id=execution_id) + ) + await handle.cancel() + else: + token_data = get_paused_execution_token( + developer_id=x_developer_id, execution_id=execution_id ) + handle = temporal_client.get_async_activity_handle(token_data["task_token"]) + await handle.complete("finished") @router.get("/tasks/{task_id}/executions", tags=["tasks"]) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 1b888788b..4870454d1 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -3,7 +3,7 @@ from datetime import timedelta -from temporalio import workflow +from temporalio import activity, workflow with workflow.unsafe.imports_passed_through(): from ..activities.task_steps import ( @@ -13,17 +13,20 @@ tool_call_step, transition_step, ) - from ..common.protocol.tasks import ( - EvaluateStep, - ExecutionInput, + from ..autogen.openapi_model import ( # ErrorWorkflowStep, + EvaluateStep, IfElseWorkflowStep, PromptStep, - StepContext, ToolCallStep, - TransitionInfo, + WaitForInputStep, YieldStep, ) + from ..common.protocol.tasks import ( + ExecutionInput, + StepContext, + TransitionInfo, + ) @workflow.defn @@ -36,8 +39,7 @@ async def run( previous_inputs: list[dict] = [], ) -> None: wf_name, step_idx = start - spec = execution_input.task.spec - workflow_map = {wf.name: wf.steps for wf in spec.workflows} + workflow_map = {wf.name: wf.steps for wf in execution_input.task.workflows} current_workflow = workflow_map[wf_name] previous_inputs = previous_inputs or [execution_input.arguments] step = current_workflow[step_idx] @@ -108,6 +110,8 @@ async def run( previous_inputs, ], ) + case WaitForInputStep(): + should_wait = True is_last = step_idx + 1 == len(current_workflow) # Transition type From 160c76f9e316e04acc63af448e24ba39d8c91383 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 5 Aug 2024 17:59:40 -0400 Subject: [PATCH 008/110] feat(agents-api): Add user routes Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Chat.py | 2 +- agents-api/agents_api/autogen/Entries.py | 2 +- agents-api/agents_api/clients/temporal.py | 2 +- .../models/docs/search_docs_hybrid.py | 6 +- .../execution/get_paused_execution_token.py | 1 - .../agents_api/models/user/delete_user.py | 107 ++++++++++++++++++ .../agents_api/routers/agents/list_agents.py | 12 +- .../agents_api/routers/tasks/routers.py | 3 - .../agents_api/routers/users/__init__.py | 15 ++- .../routers/users/create_or_update_user.py | 27 +++++ .../agents_api/routers/users/create_user.py | 16 +-- .../agents_api/routers/users/delete_user.py | 17 +++ .../agents_api/routers/users/exceptions.py | 7 -- .../routers/users/get_user_details.py | 28 +---- .../agents_api/routers/users/list_users.py | 25 ++-- .../agents_api/routers/users/patch_user.py | 31 ++--- .../agents_api/routers/users/update_user.py | 31 ++--- .../agents_api/workflows/task_execution.py | 2 +- 18 files changed, 215 insertions(+), 119 deletions(-) create mode 100644 agents-api/agents_api/models/user/delete_user.py create mode 100644 agents-api/agents_api/routers/users/create_or_update_user.py create mode 100644 agents-api/agents_api/routers/users/delete_user.py delete mode 100644 agents-api/agents_api/routers/users/exceptions.py diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 6669fbcec..c7c25c7ad 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field from .Docs import DocReference from .Entries import ChatMLMessage diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index d56b333d8..6c8bf9ca4 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, RootModel +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field from .Tools import ChosenToolCall, Tool, ToolResponse diff --git a/agents-api/agents_api/clients/temporal.py b/agents-api/agents_api/clients/temporal.py index 3fb9e9e99..7e45b50d7 100644 --- a/agents-api/agents_api/clients/temporal.py +++ b/agents-api/agents_api/clients/temporal.py @@ -1,6 +1,6 @@ from uuid import UUID -from temporalio.client import Client, TLSConfig, WorkflowHandle +from temporalio.client import Client, TLSConfig from agents_api.env import ( temporal_client_cert, diff --git a/agents-api/agents_api/models/docs/search_docs_hybrid.py b/agents-api/agents_api/models/docs/search_docs_hybrid.py index 1595b3ca5..b9d6a1ec0 100644 --- a/agents-api/agents_api/models/docs/search_docs_hybrid.py +++ b/agents-api/agents_api/models/docs/search_docs_hybrid.py @@ -39,7 +39,11 @@ def dbsf_fuse( """ all_docs = {doc.id: doc for doc in text_results + embedding_results} - text_scores: dict[UUID, float] = {doc.id: -doc.distance for doc in text_results} + assert all(doc.distance is not None in all_docs for doc in text_results) + + text_scores: dict[UUID, float] = { + doc.id: -(doc.distance or 0.0) for doc in text_results + } # Because these are cosine distances, we need to invert them embedding_scores: dict[UUID, float] = { diff --git a/agents-api/agents_api/models/execution/get_paused_execution_token.py b/agents-api/agents_api/models/execution/get_paused_execution_token.py index 00fe39873..1b1f6e803 100644 --- a/agents-api/agents_api/models/execution/get_paused_execution_token.py +++ b/agents-api/agents_api/models/execution/get_paused_execution_token.py @@ -5,7 +5,6 @@ from pycozo.client import QueryException from pydantic import ValidationError -from ...autogen.openapi_model import Transition from ..utils import ( cozo_query, partialclass, diff --git a/agents-api/agents_api/models/user/delete_user.py b/agents-api/agents_api/models/user/delete_user.py new file mode 100644 index 000000000..dfb720cf8 --- /dev/null +++ b/agents-api/agents_api/models/user/delete_user.py @@ -0,0 +1,107 @@ +""" +This module contains the implementation of the delete_user_query function, which is responsible for deleting an user and its related default settings from the CozoDB database. +""" + +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...autogen.openapi_model import ResourceDeletedResponse +from ...common.utils.datetime import utcnow +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + ResourceDeletedResponse, + one=True, + transform=lambda d: { + "id": UUID(d.pop("user_id")), + "deleted_at": utcnow(), + "jobs": [], + }, +) +@cozo_query +@beartype +def delete_user(*, developer_id: UUID, user_id: UUID) -> tuple[list[str], dict]: + """ + Constructs and returns a datalog query for deleting an user and its default settings from the database. + + Parameters: + - developer_id (UUID): The UUID of the developer owning the user. + - user_id (UUID): The UUID of the user to be deleted. + - client (CozoClient, optional): An instance of the CozoClient to execute the query. + + Returns: + - ResourceDeletedResponse: The response indicating the deletion of the user. + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query(developer_id, "users", user_id=user_id), + """ + # Delete docs + ?[user_id, doc_id] := + *user_docs{ + user_id, + doc_id, + }, user_id = to_uuid($user_id) + + :delete user_docs { + user_id, + doc_id + } + :returning + """, + """ + # Delete tools + ?[user_id, tool_id] := + *tools{ + user_id, + tool_id, + }, user_id = to_uuid($user_id) + + :delete tools { + user_id, + tool_id + } + :returning + """, + """ + # Delete default user settings + ?[user_id] <- [[$user_id]] + + :delete user_default_settings { + user_id + } + :returning + """, + """ + # Delete the user + ?[user_id, developer_id] <- [[$user_id, $developer_id]] + + :delete users { + developer_id, + user_id + } + :returning + """, + ] + + return (queries, {"user_id": str(user_id), "developer_id": str(developer_id)}) diff --git a/agents-api/agents_api/routers/agents/list_agents.py b/agents-api/agents_api/routers/agents/list_agents.py index f894d9c36..f64d4bdbf 100644 --- a/agents-api/agents_api/routers/agents/list_agents.py +++ b/agents-api/agents_api/routers/agents/list_agents.py @@ -1,6 +1,8 @@ +import json +from json import JSONDecodeError from typing import Annotated, Literal -from fastapi import Depends +from fastapi import Depends, HTTPException, status from pydantic import UUID4 from ...autogen.openapi_model import Agent, ListResponse @@ -18,6 +20,14 @@ async def list_agents( direction: Literal["asc", "desc"] = "desc", metadata_filter: str = "{}", ) -> ListResponse[Agent]: + try: + metadata_filter = json.loads(metadata_filter) + except JSONDecodeError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="metadata_filter is not a valid JSON", + ) + agents = list_agents_query( developer_id=x_developer_id, limit=limit, diff --git a/agents-api/agents_api/routers/tasks/routers.py b/agents-api/agents_api/routers/tasks/routers.py index 836531453..ca9a9c745 100644 --- a/agents-api/agents_api/routers/tasks/routers.py +++ b/agents-api/agents_api/routers/tasks/routers.py @@ -9,7 +9,6 @@ from pycozo.client import QueryException from pydantic import UUID4, BaseModel from starlette.status import HTTP_201_CREATED -from temporalio.client import WorkflowHandle from agents_api.autogen.openapi_model import ( CreateExecutionRequest, @@ -23,9 +22,7 @@ Transition, UpdateExecutionRequest, ) -from agents_api.clients.cozo import client as cozo_client from agents_api.clients.temporal import get_client, run_task_execution_workflow -from agents_api.common.protocol.tasks import ExecutionInput from agents_api.dependencies.developer_id import get_developer_id from agents_api.models.execution.create_execution import ( create_execution as create_execution_query, diff --git a/agents-api/agents_api/routers/users/__init__.py b/agents-api/agents_api/routers/users/__init__.py index 91888636d..0cfdf4a5e 100644 --- a/agents-api/agents_api/routers/users/__init__.py +++ b/agents-api/agents_api/routers/users/__init__.py @@ -1,6 +1,9 @@ -from .create_user import create_user # noqa: F401 -from .get_user_details import get_user_details # noqa: F401 -from .list_users import list_users # noqa: F401 -from .patch_user import patch_user # noqa: F401 -from .router import router # noqa: F401 -from .update_user import update_user # noqa: F401 +# ruff: noqa: F401 +from .create_or_update_user import create_or_update_user +from .create_user import create_user +from .delete_user import delete_user +from .get_user_details import get_user_details +from .list_users import list_users +from .patch_user import patch_user +from .router import router +from .update_user import update_user diff --git a/agents-api/agents_api/routers/users/create_or_update_user.py b/agents-api/agents_api/routers/users/create_or_update_user.py new file mode 100644 index 000000000..928bee62f --- /dev/null +++ b/agents-api/agents_api/routers/users/create_or_update_user.py @@ -0,0 +1,27 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 +from starlette.status import HTTP_201_CREATED + +from ...autogen.openapi_model import CreateOrUpdateUserRequest, ResourceCreatedResponse +from ...dependencies.developer_id import get_developer_id +from ...models.user.create_or_update_user import ( + create_or_update_user as create_or_update_user_query, +) +from .router import router + + +@router.post("/users/{user_id}", status_code=HTTP_201_CREATED, tags=["users"]) +async def create_or_update_user( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + user_id: UUID4, + data: CreateOrUpdateUserRequest, +) -> ResourceCreatedResponse: + user = create_or_update_user_query( + developer_id=x_developer_id, + user_id=user_id, + data=data, + ) + + return ResourceCreatedResponse(id=user.id, created_at=user.created_at) diff --git a/agents-api/agents_api/routers/users/create_user.py b/agents-api/agents_api/routers/users/create_user.py index 4e1986315..07e763b66 100644 --- a/agents-api/agents_api/routers/users/create_user.py +++ b/agents-api/agents_api/routers/users/create_user.py @@ -1,5 +1,4 @@ from typing import Annotated -from uuid import uuid4 from fastapi import Depends from pydantic import UUID4 @@ -13,17 +12,12 @@ @router.post("/users", status_code=HTTP_201_CREATED, tags=["users"]) async def create_user( - request: CreateUserRequest, + data: CreateUserRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> ResourceCreatedResponse: - user_id = uuid4() - created_user = create_user_query( + user = create_user_query( developer_id=x_developer_id, - user_id=user_id, - name=request.name, - about=request.about, - metadata=request.metadata, - ) - return ResourceCreatedResponse( - id=str(user_id), created_at=created_user["created_at"] + data=data, ) + + return ResourceCreatedResponse(id=user.id, created_at=user.created_at) diff --git a/agents-api/agents_api/routers/users/delete_user.py b/agents-api/agents_api/routers/users/delete_user.py new file mode 100644 index 000000000..fd1d02a94 --- /dev/null +++ b/agents-api/agents_api/routers/users/delete_user.py @@ -0,0 +1,17 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 +from starlette.status import HTTP_202_ACCEPTED + +from ...autogen.openapi_model import ResourceDeletedResponse +from ...dependencies.developer_id import get_developer_id +from ...models.user.delete_user import delete_user as delete_user_query +from .router import router + + +@router.delete("/users/{user_id}", status_code=HTTP_202_ACCEPTED, tags=["users"]) +async def delete_user( + user_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] +) -> ResourceDeletedResponse: + return delete_user_query(developer_id=x_developer_id, user_id=user_id) diff --git a/agents-api/agents_api/routers/users/exceptions.py b/agents-api/agents_api/routers/users/exceptions.py deleted file mode 100644 index 188c31bb8..000000000 --- a/agents-api/agents_api/routers/users/exceptions.py +++ /dev/null @@ -1,7 +0,0 @@ -class BaseUserException(Exception): - pass - - -class InvalidUserQueryError(BaseUserException): - def __init__(self, message: str): - super().__init__(f"Invalid user query: {message}") diff --git a/agents-api/agents_api/routers/users/get_user_details.py b/agents-api/agents_api/routers/users/get_user_details.py index bb3950cb6..0bc0460ca 100644 --- a/agents-api/agents_api/routers/users/get_user_details.py +++ b/agents-api/agents_api/routers/users/get_user_details.py @@ -1,11 +1,9 @@ from typing import Annotated -from fastapi import Depends, HTTPException, status -from pycozo.client import QueryException +from fastapi import Depends from pydantic import UUID4 from ...autogen.openapi_model import User -from ...common.exceptions.users import UserNotFoundError from ...dependencies.developer_id import get_developer_id from ...models.user.get_user import get_user as get_user_query from .router import router @@ -13,27 +11,7 @@ @router.get("/users/{user_id}", tags=["users"]) async def get_user_details( - user_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + user_id: UUID4, ) -> User: - try: - resp = [ - row.to_dict() - for _, row in get_user_query( - developer_id=x_developer_id, - user_id=user_id, - ).iterrows() - ][0] - - return User(**resp) - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="User not found", - ) - except QueryException as e: - # the code is not so informative now, but it may be a good solution in the future - if e.code == "transact::assertion_failure": - raise UserNotFoundError(x_developer_id, user_id) - - raise + return get_user_query(developer_id=x_developer_id, user_id=user_id) diff --git a/agents-api/agents_api/routers/users/list_users.py b/agents-api/agents_api/routers/users/list_users.py index c7e7b33c4..aa57883b3 100644 --- a/agents-api/agents_api/routers/users/list_users.py +++ b/agents-api/agents_api/routers/users/list_users.py @@ -1,11 +1,11 @@ import json from json import JSONDecodeError -from typing import Annotated, List +from typing import Annotated, List, Literal from fastapi import Depends, HTTPException, status from pydantic import UUID4 -from ...autogen.openapi_model import User +from ...autogen.openapi_model import ListResponse, User from ...dependencies.developer_id import get_developer_id from ...models.user.list_users import list_users as list_users_query from .router import router @@ -16,6 +16,8 @@ async def list_users( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], limit: int = 100, offset: int = 0, + sort_by: Literal["created_at", "updated_at"] = "created_at", + direction: Literal["asc", "desc"] = "desc", metadata_filter: str = "{}", ) -> List[User]: try: @@ -26,14 +28,13 @@ async def list_users( detail="metadata_filter is not a valid JSON", ) - users = [ - User(**row.to_dict()) - for _, row in list_users_query( - developer_id=x_developer_id, - limit=limit, - offset=offset, - metadata_filter=metadata_filter, - ).iterrows() - ] + users = list_users_query( + developer_id=x_developer_id, + limit=limit, + offset=offset, + sort_by=sort_by, + direction=direction, + metadata_filter=metadata_filter, + ) - return users + return ListResponse[User](items=users) diff --git a/agents-api/agents_api/routers/users/patch_user.py b/agents-api/agents_api/routers/users/patch_user.py index 7ffeb8251..fcd1e9380 100644 --- a/agents-api/agents_api/routers/users/patch_user.py +++ b/agents-api/agents_api/routers/users/patch_user.py @@ -1,11 +1,9 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 -from starlette.status import HTTP_404_NOT_FOUND from ...autogen.openapi_model import PatchUserRequest, ResourceUpdatedResponse -from ...common.exceptions.users import UserNotFoundError from ...dependencies.developer_id import get_developer_id from ...models.user.patch_user import patch_user as patch_user_query from .router import router @@ -14,26 +12,11 @@ @router.patch("/users/{user_id}", tags=["users"]) async def patch_user( user_id: UUID4, - request: PatchUserRequest, + data: PatchUserRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> ResourceUpdatedResponse: - try: - resp = patch_user_query( - developer_id=x_developer_id, - user_id=user_id, - name=request.name, - about=request.about, - metadata=request.metadata, - ) - - return ResourceUpdatedResponse( - id=resp["user_id"][0], - updated_at=resp["updated_at"][0], - ) - except (IndexError, KeyError): - raise HTTPException( - status_code=HTTP_404_NOT_FOUND, - detail="User not found", - ) - except UserNotFoundError as e: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail=str(e)) + return patch_user_query( + developer_id=x_developer_id, + user_id=user_id, + data=data, + ) diff --git a/agents-api/agents_api/routers/users/update_user.py b/agents-api/agents_api/routers/users/update_user.py index 39e8f782b..258023173 100644 --- a/agents-api/agents_api/routers/users/update_user.py +++ b/agents-api/agents_api/routers/users/update_user.py @@ -1,11 +1,9 @@ from typing import Annotated -from fastapi import Depends, HTTPException +from fastapi import Depends from pydantic import UUID4 -from starlette.status import HTTP_404_NOT_FOUND from ...autogen.openapi_model import ResourceUpdatedResponse, UpdateUserRequest -from ...common.exceptions.users import UserNotFoundError from ...dependencies.developer_id import get_developer_id from ...models.user.update_user import update_user as update_user_query from .router import router @@ -14,26 +12,11 @@ @router.put("/users/{user_id}", tags=["users"]) async def update_user( user_id: UUID4, - request: UpdateUserRequest, + data: UpdateUserRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> ResourceUpdatedResponse: - try: - resp = update_user_query( - developer_id=x_developer_id, - user_id=user_id, - name=request.name, - about=request.about, - metadata=request.metadata, - ) - - return ResourceUpdatedResponse( - id=resp["user_id"][0], - updated_at=resp["updated_at"][0], - ) - except (IndexError, KeyError): - raise HTTPException( - status_code=HTTP_404_NOT_FOUND, - detail="User not found", - ) - except UserNotFoundError as e: - raise HTTPException(status_code=HTTP_404_NOT_FOUND, detail=str(e)) + return update_user_query( + developer_id=x_developer_id, + user_id=user_id, + data=data, + ) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 4870454d1..13c1af9f9 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -3,7 +3,7 @@ from datetime import timedelta -from temporalio import activity, workflow +from temporalio import workflow with workflow.unsafe.imports_passed_through(): from ..activities.task_steps import ( From c9c9a0681bc2d2e887ff04f74608aa22b4608299 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 5 Aug 2024 18:49:11 -0400 Subject: [PATCH 009/110] feat(agents-api): Add docs routes Signed-off-by: Diwank Tomer --- .../agents_api/models/docs/delete_doc.py | 27 +--- agents-api/agents_api/models/docs/get_doc.py | 8 +- agents-api/agents_api/routers/__init__.py | 8 ++ .../agents_api/routers/docs/__init__.py | 6 + .../agents_api/routers/docs/create_doc.py | 42 ++++++ .../agents_api/routers/docs/delete_doc.py | 17 +++ agents-api/agents_api/routers/docs/get_doc.py | 17 +++ .../agents_api/routers/docs/list_docs.py | 75 +++++++++++ agents-api/agents_api/routers/docs/router.py | 3 + .../agents_api/routers/sessions/__init__.py | 1 + .../routers/sessions/get_session_history.py | 16 +++ agents-api/poetry.lock | 126 ++++++++++-------- sdks/python/julep/api/client.py | 22 +-- sdks/python/julep/api/reference.md | 9 -- sdks/python/poetry.lock | 26 +++- sdks/ts/src/api/services/DefaultService.ts | 8 -- typespec/entries/endpoints.tsp | 4 - 17 files changed, 288 insertions(+), 127 deletions(-) create mode 100644 agents-api/agents_api/routers/docs/__init__.py create mode 100644 agents-api/agents_api/routers/docs/create_doc.py create mode 100644 agents-api/agents_api/routers/docs/delete_doc.py create mode 100644 agents-api/agents_api/routers/docs/get_doc.py create mode 100644 agents-api/agents_api/routers/docs/list_docs.py create mode 100644 agents-api/agents_api/routers/docs/router.py create mode 100644 agents-api/agents_api/routers/sessions/get_session_history.py diff --git a/agents-api/agents_api/models/docs/delete_doc.py b/agents-api/agents_api/models/docs/delete_doc.py index 842fc9d6f..d754a671a 100644 --- a/agents-api/agents_api/models/docs/delete_doc.py +++ b/agents-api/agents_api/models/docs/delete_doc.py @@ -1,4 +1,3 @@ -from typing import Literal from uuid import UUID from beartype import beartype @@ -13,7 +12,6 @@ partialclass, rewrap_exceptions, verify_developer_id_query, - verify_developer_owns_resource_query, wrap_in_class, ) @@ -39,8 +37,6 @@ def delete_doc( *, developer_id: UUID, - owner_type: Literal["user", "agent"], - owner_id: UUID, doc_id: UUID, ) -> tuple[list[str], dict]: """Constructs and returns a datalog query for deleting documents and associated information snippets. @@ -48,8 +44,6 @@ def delete_doc( This function targets the 'cozodb' database, allowing for the removal of documents and their related information snippets based on the provided document ID and owner (user or agent). Parameters: - owner_type (Literal["user", "agent"]): The type of the owner, either 'user' or 'agent'. - owner_id (UUID): The UUID of the owner. doc_id (UUID): The UUID of the document to be deleted. client (CozoClient): An instance of the CozoClient to execute the query. @@ -57,12 +51,11 @@ def delete_doc( pd.DataFrame: The result of the executed datalog query. """ # Convert UUID parameters to string format for use in the datalog query - owner_id = str(owner_id) doc_id = str(doc_id) # The following query is divided into two main parts: # 1. Deleting information snippets associated with the document - # 2. Deleting the document itself from the owner's collection + # 2. Deleting the document itself delete_snippets_query = """ # This section constructs the subquery for identifying and deleting all information snippets associated with the given document ID. # Delete snippets @@ -81,29 +74,17 @@ def delete_doc( """ delete_doc_query = """ - # This section constructs the subquery for deleting the document from the specified owner's (user or agent) document collection. # Delete the docs - ?[doc_id, owner_id, owner_type] <- [[ - to_uuid($doc_id), - to_uuid($owner_id), - $owner_type, - ]] + ?[doc_id] <- [[ to_uuid($doc_id) ]] - :delete docs { - doc_id, - owner_type, - owner_id, - } + :delete docs { doc_id } :returning """ queries = [ verify_developer_id_query(developer_id), - verify_developer_owns_resource_query( - developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} - ), delete_snippets_query, delete_doc_query, ] - return (queries, {"doc_id": doc_id, "owner_id": owner_id, "owner_type": owner_type}) + return (queries, {"doc_id": doc_id}) diff --git a/agents-api/agents_api/models/docs/get_doc.py b/agents-api/agents_api/models/docs/get_doc.py index 95b9414f4..fcac5b0a4 100644 --- a/agents-api/agents_api/models/docs/get_doc.py +++ b/agents-api/agents_api/models/docs/get_doc.py @@ -1,6 +1,5 @@ """Module for retrieving document snippets from the CozoDB based on document IDs.""" -from typing import Literal from uuid import UUID from beartype import beartype @@ -38,14 +37,12 @@ def get_doc( *, developer_id: UUID, - owner_type: Literal["user", "agent"], doc_id: UUID, ) -> tuple[list[str], dict]: """ Retrieves snippets of documents by their ID from the CozoDB. Parameters: - owner_type (Literal["user", "agent"]): The type of the owner of the document. doc_id (UUID): The unique identifier of the document. client (CozoClient, optional): The CozoDB client instance. Defaults to a pre-configured client. @@ -67,16 +64,13 @@ def get_doc( snippet_data = [index, content] ?[ - owner_type, id, title, snippet_data, created_at, metadata, ] := input[id], - owner_type = $owner_type, *docs { - owner_type, doc_id: id, title, created_at, @@ -90,4 +84,4 @@ def get_doc( get_query, ] - return (queries, {"doc_id": doc_id, "owner_type": owner_type}) + return (queries, {"doc_id": doc_id}) diff --git a/agents-api/agents_api/routers/__init__.py b/agents-api/agents_api/routers/__init__.py index 8d16aa32a..04280e706 100644 --- a/agents-api/agents_api/routers/__init__.py +++ b/agents-api/agents_api/routers/__init__.py @@ -9,3 +9,11 @@ Each sub-module defines its own set of API endpoints and is responsible for handling requests and responses related to its domain, ensuring a modular and organized approach to API development. """ + +# ruff: noqa: F401 + +from .agents import router as agents_router +from .docs import router as docs_router +from .jobs import router as jobs_router +from .sessions import router as sessions_router +from .users import router as users_router diff --git a/agents-api/agents_api/routers/docs/__init__.py b/agents-api/agents_api/routers/docs/__init__.py new file mode 100644 index 000000000..062db6a67 --- /dev/null +++ b/agents-api/agents_api/routers/docs/__init__.py @@ -0,0 +1,6 @@ +# ruff: noqa: F401 +from .create_doc import create_agent_doc, create_user_doc +from .delete_doc import delete_doc +from .get_doc import get_doc +from .list_docs import list_agent_docs, list_user_docs +from .router import router diff --git a/agents-api/agents_api/routers/docs/create_doc.py b/agents-api/agents_api/routers/docs/create_doc.py new file mode 100644 index 000000000..d99468733 --- /dev/null +++ b/agents-api/agents_api/routers/docs/create_doc.py @@ -0,0 +1,42 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 +from starlette.status import HTTP_201_CREATED + +from ...autogen.openapi_model import CreateDocRequest, ResourceCreatedResponse +from ...dependencies.developer_id import get_developer_id +from ...models.docs.create_doc import create_doc as create_doc_query +from .router import router + + +@router.post("/users/{user_id}/docs", status_code=HTTP_201_CREATED, tags=["docs"]) +async def create_user_doc( + user_id: UUID4, + data: CreateDocRequest, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], +) -> ResourceCreatedResponse: + doc = create_doc_query( + developer_id=x_developer_id, + owner_type="user", + owner_id=user_id, + data=data, + ) + + return ResourceCreatedResponse(id=doc.id, created_at=doc.created_at) + + +@router.post("/agents/{agent_id}/docs", status_code=HTTP_201_CREATED, tags=["docs"]) +async def create_agent_doc( + agent_id: UUID4, + data: CreateDocRequest, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], +) -> ResourceCreatedResponse: + doc = create_doc_query( + developer_id=x_developer_id, + owner_type="agent", + owner_id=agent_id, + data=data, + ) + + return ResourceCreatedResponse(id=doc.id, created_at=doc.created_at) diff --git a/agents-api/agents_api/routers/docs/delete_doc.py b/agents-api/agents_api/routers/docs/delete_doc.py new file mode 100644 index 000000000..d1ae4416f --- /dev/null +++ b/agents-api/agents_api/routers/docs/delete_doc.py @@ -0,0 +1,17 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 +from starlette.status import HTTP_202_ACCEPTED + +from ...autogen.openapi_model import ResourceDeletedResponse +from ...dependencies.developer_id import get_developer_id +from ...models.docs.delete_doc import delete_doc as delete_doc_query +from .router import router + + +@router.delete("/docs/{doc_id}", status_code=HTTP_202_ACCEPTED, tags=["docs"]) +async def delete_doc( + doc_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] +) -> ResourceDeletedResponse: + return delete_doc_query(developer_id=x_developer_id, doc_id=doc_id) diff --git a/agents-api/agents_api/routers/docs/get_doc.py b/agents-api/agents_api/routers/docs/get_doc.py new file mode 100644 index 000000000..febebf1bd --- /dev/null +++ b/agents-api/agents_api/routers/docs/get_doc.py @@ -0,0 +1,17 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 + +from ...autogen.openapi_model import Doc +from ...dependencies.developer_id import get_developer_id +from ...models.docs.get_doc import get_doc as get_doc_query +from .router import router + + +@router.get("/docs/{doc_id}", tags=["docs"]) +async def get_doc( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + doc_id: UUID4, +) -> Doc: + return get_doc_query(developer_id=x_developer_id, doc_id=doc_id) diff --git a/agents-api/agents_api/routers/docs/list_docs.py b/agents-api/agents_api/routers/docs/list_docs.py new file mode 100644 index 000000000..80a6ba6ae --- /dev/null +++ b/agents-api/agents_api/routers/docs/list_docs.py @@ -0,0 +1,75 @@ +import json +from json import JSONDecodeError +from typing import Annotated, Literal + +from fastapi import Depends, HTTPException, status +from pydantic import UUID4 + +from ...autogen.openapi_model import Doc, ListResponse +from ...dependencies.developer_id import get_developer_id +from ...models.docs.list_docs import list_docs as list_docs_query +from .router import router + + +@router.get("/users/{user_id}/docs", tags=["docs"]) +async def list_user_docs( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + user_id: UUID4, + limit: int = 100, + offset: int = 0, + sort_by: Literal["created_at", "updated_at"] = "created_at", + direction: Literal["asc", "desc"] = "desc", + metadata_filter: str = "{}", +) -> ListResponse[Doc]: + try: + metadata_filter = json.loads(metadata_filter) + except JSONDecodeError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="metadata_filter is not a valid JSON", + ) + + docs = list_docs_query( + developer_id=x_developer_id, + owner_type="user", + owner_id=user_id, + limit=limit, + offset=offset, + sort_by=sort_by, + direction=direction, + metadata_filter=metadata_filter, + ) + + return ListResponse[Doc](items=docs) + + +@router.get("/agents/{agent_id}/docs", tags=["docs"]) +async def list_agent_docs( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + agent_id: UUID4, + limit: int = 100, + offset: int = 0, + sort_by: Literal["created_at", "updated_at"] = "created_at", + direction: Literal["asc", "desc"] = "desc", + metadata_filter: str = "{}", +) -> ListResponse[Doc]: + try: + metadata_filter = json.loads(metadata_filter) + except JSONDecodeError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="metadata_filter is not a valid JSON", + ) + + docs = list_docs_query( + developer_id=x_developer_id, + owner_type="agent", + owner_id=agent_id, + limit=limit, + offset=offset, + sort_by=sort_by, + direction=direction, + metadata_filter=metadata_filter, + ) + + return ListResponse[Doc](items=docs) diff --git a/agents-api/agents_api/routers/docs/router.py b/agents-api/agents_api/routers/docs/router.py new file mode 100644 index 000000000..af9233c56 --- /dev/null +++ b/agents-api/agents_api/routers/docs/router.py @@ -0,0 +1,3 @@ +from fastapi import APIRouter + +router = APIRouter() diff --git a/agents-api/agents_api/routers/sessions/__init__.py b/agents-api/agents_api/routers/sessions/__init__.py index 24ae2d9e8..8ed3d953d 100644 --- a/agents-api/agents_api/routers/sessions/__init__.py +++ b/agents-api/agents_api/routers/sessions/__init__.py @@ -4,6 +4,7 @@ from .create_session import create_session from .delete_session import delete_session from .get_session import get_session +from .get_session_history import get_session_history from .list_sessions import list_sessions from .patch_session import patch_session from .router import router diff --git a/agents-api/agents_api/routers/sessions/get_session_history.py b/agents-api/agents_api/routers/sessions/get_session_history.py new file mode 100644 index 000000000..c960f001e --- /dev/null +++ b/agents-api/agents_api/routers/sessions/get_session_history.py @@ -0,0 +1,16 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 + +from ...autogen.openapi_model import Session +from ...dependencies.developer_id import get_developer_id +from ...models.entry.get_history import get_history as get_history_query +from .router import router + + +@router.get("/sessions/{session_id}/history", tags=["sessions"]) +async def get_session_history( + session_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] +) -> Session: + return get_history_query(developer_id=x_developer_id, session_id=session_id) diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index d6ccc1344..ddfee9708 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -848,13 +848,33 @@ validation = ["openapi-spec-validator (>=0.2.8,<0.7.0)", "prance (>=0.18.2)"] [[package]] name = "debugpy" -version = "1.8.3" +version = "1.8.2" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0df2c400853150af14996b8d1a4f54d45ffa98e76c0f3de30665e89e273ea293"}, - {file = "debugpy-1.8.3.zip", hash = "sha256:0f5a6326d9fc375b864ed368d06cddf2dabe5135511e71cde3758be699847d36"}, + {file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"}, + {file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"}, + {file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"}, + {file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"}, + {file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"}, + {file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"}, + {file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"}, + {file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"}, + {file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"}, + {file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"}, + {file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"}, + {file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"}, + {file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"}, + {file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"}, + {file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"}, + {file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"}, + {file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"}, + {file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"}, + {file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"}, + {file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"}, + {file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"}, + {file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"}, ] [[package]] @@ -4898,60 +4918,60 @@ files = [ [[package]] name = "sqlalchemy" -version = "2.0.31" +version = "2.0.32" description = "Database Abstraction Library" optional = false python-versions = ">=3.7" files = [ - {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2a213c1b699d3f5768a7272de720387ae0122f1becf0901ed6eaa1abd1baf6c"}, - {file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9fea3d0884e82d1e33226935dac990b967bef21315cbcc894605db3441347443"}, - {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ad7f221d8a69d32d197e5968d798217a4feebe30144986af71ada8c548e9fa"}, - {file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2bee229715b6366f86a95d497c347c22ddffa2c7c96143b59a2aa5cc9eebbc"}, - {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cd5b94d4819c0c89280b7c6109c7b788a576084bf0a480ae17c227b0bc41e109"}, - {file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:750900a471d39a7eeba57580b11983030517a1f512c2cb287d5ad0fcf3aebd58"}, - {file = "SQLAlchemy-2.0.31-cp310-cp310-win32.whl", hash = "sha256:7bd112be780928c7f493c1a192cd8c5fc2a2a7b52b790bc5a84203fb4381c6be"}, - {file = "SQLAlchemy-2.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:5a48ac4d359f058474fadc2115f78a5cdac9988d4f99eae44917f36aa1476327"}, - {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f68470edd70c3ac3b6cd5c2a22a8daf18415203ca1b036aaeb9b0fb6f54e8298"}, - {file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e2c38c2a4c5c634fe6c3c58a789712719fa1bf9b9d6ff5ebfce9a9e5b89c1ca"}, - {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd15026f77420eb2b324dcb93551ad9c5f22fab2c150c286ef1dc1160f110203"}, - {file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2196208432deebdfe3b22185d46b08f00ac9d7b01284e168c212919891289396"}, - {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:352b2770097f41bff6029b280c0e03b217c2dcaddc40726f8f53ed58d8a85da4"}, - {file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56d51ae825d20d604583f82c9527d285e9e6d14f9a5516463d9705dab20c3740"}, - {file = "SQLAlchemy-2.0.31-cp311-cp311-win32.whl", hash = "sha256:6e2622844551945db81c26a02f27d94145b561f9d4b0c39ce7bfd2fda5776dac"}, - {file = "SQLAlchemy-2.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:ccaf1b0c90435b6e430f5dd30a5aede4764942a695552eb3a4ab74ed63c5b8d3"}, - {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42"}, - {file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c"}, - {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449"}, - {file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e"}, - {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67"}, - {file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e"}, - {file = "SQLAlchemy-2.0.31-cp312-cp312-win32.whl", hash = "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96"}, - {file = "SQLAlchemy-2.0.31-cp312-cp312-win_amd64.whl", hash = "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a"}, - {file = "SQLAlchemy-2.0.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f43e93057cf52a227eda401251c72b6fbe4756f35fa6bfebb5d73b86881e59b0"}, - {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d337bf94052856d1b330d5fcad44582a30c532a2463776e1651bd3294ee7e58b"}, - {file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06fb43a51ccdff3b4006aafee9fcf15f63f23c580675f7734245ceb6b6a9e05"}, - {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:b6e22630e89f0e8c12332b2b4c282cb01cf4da0d26795b7eae16702a608e7ca1"}, - {file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:79a40771363c5e9f3a77f0e28b3302801db08040928146e6808b5b7a40749c88"}, - {file = "SQLAlchemy-2.0.31-cp37-cp37m-win32.whl", hash = "sha256:501ff052229cb79dd4c49c402f6cb03b5a40ae4771efc8bb2bfac9f6c3d3508f"}, - {file = "SQLAlchemy-2.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:597fec37c382a5442ffd471f66ce12d07d91b281fd474289356b1a0041bdf31d"}, - {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dc6d69f8829712a4fd799d2ac8d79bdeff651c2301b081fd5d3fe697bd5b4ab9"}, - {file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23b9fbb2f5dd9e630db70fbe47d963c7779e9c81830869bd7d137c2dc1ad05fb"}, - {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21c97efcbb9f255d5c12a96ae14da873233597dfd00a3a0c4ce5b3e5e79704"}, - {file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a6a9837589c42b16693cf7bf836f5d42218f44d198f9343dd71d3164ceeeac"}, - {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc251477eae03c20fae8db9c1c23ea2ebc47331bcd73927cdcaecd02af98d3c3"}, - {file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2fd17e3bb8058359fa61248c52c7b09a97cf3c820e54207a50af529876451808"}, - {file = "SQLAlchemy-2.0.31-cp38-cp38-win32.whl", hash = "sha256:c76c81c52e1e08f12f4b6a07af2b96b9b15ea67ccdd40ae17019f1c373faa227"}, - {file = "SQLAlchemy-2.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:4b600e9a212ed59355813becbcf282cfda5c93678e15c25a0ef896b354423238"}, - {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b6cf796d9fcc9b37011d3f9936189b3c8074a02a4ed0c0fbbc126772c31a6d4"}, - {file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78fe11dbe37d92667c2c6e74379f75746dc947ee505555a0197cfba9a6d4f1a4"}, - {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc47dc6185a83c8100b37acda27658fe4dbd33b7d5e7324111f6521008ab4fe"}, - {file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a41514c1a779e2aa9a19f67aaadeb5cbddf0b2b508843fcd7bafdf4c6864005"}, - {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:afb6dde6c11ea4525318e279cd93c8734b795ac8bb5dda0eedd9ebaca7fa23f1"}, - {file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3f9faef422cfbb8fd53716cd14ba95e2ef655400235c3dfad1b5f467ba179c8c"}, - {file = "SQLAlchemy-2.0.31-cp39-cp39-win32.whl", hash = "sha256:fc6b14e8602f59c6ba893980bea96571dd0ed83d8ebb9c4479d9ed5425d562e9"}, - {file = "SQLAlchemy-2.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:3cb8a66b167b033ec72c3812ffc8441d4e9f5f78f5e31e54dcd4c90a4ca5bebc"}, - {file = "SQLAlchemy-2.0.31-py3-none-any.whl", hash = "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911"}, - {file = "SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484"}, + {file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0c9045ecc2e4db59bfc97b20516dfdf8e41d910ac6fb667ebd3a79ea54084619"}, + {file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1467940318e4a860afd546ef61fefb98a14d935cd6817ed07a228c7f7c62f389"}, + {file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5954463675cb15db8d4b521f3566a017c8789222b8316b1e6934c811018ee08b"}, + {file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167e7497035c303ae50651b351c28dc22a40bb98fbdb8468cdc971821b1ae533"}, + {file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b27dfb676ac02529fb6e343b3a482303f16e6bc3a4d868b73935b8792edb52d0"}, + {file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bf2360a5e0f7bd75fa80431bf8ebcfb920c9f885e7956c7efde89031695cafb8"}, + {file = "SQLAlchemy-2.0.32-cp310-cp310-win32.whl", hash = "sha256:306fe44e754a91cd9d600a6b070c1f2fadbb4a1a257b8781ccf33c7067fd3e4d"}, + {file = "SQLAlchemy-2.0.32-cp310-cp310-win_amd64.whl", hash = "sha256:99db65e6f3ab42e06c318f15c98f59a436f1c78179e6a6f40f529c8cc7100b22"}, + {file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21b053be28a8a414f2ddd401f1be8361e41032d2ef5884b2f31d31cb723e559f"}, + {file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b178e875a7a25b5938b53b006598ee7645172fccafe1c291a706e93f48499ff5"}, + {file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723a40ee2cc7ea653645bd4cf024326dea2076673fc9d3d33f20f6c81db83e1d"}, + {file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:295ff8689544f7ee7e819529633d058bd458c1fd7f7e3eebd0f9268ebc56c2a0"}, + {file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49496b68cd190a147118af585173ee624114dfb2e0297558c460ad7495f9dfe2"}, + {file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:acd9b73c5c15f0ec5ce18128b1fe9157ddd0044abc373e6ecd5ba376a7e5d961"}, + {file = "SQLAlchemy-2.0.32-cp311-cp311-win32.whl", hash = "sha256:9365a3da32dabd3e69e06b972b1ffb0c89668994c7e8e75ce21d3e5e69ddef28"}, + {file = "SQLAlchemy-2.0.32-cp311-cp311-win_amd64.whl", hash = "sha256:8bd63d051f4f313b102a2af1cbc8b80f061bf78f3d5bd0843ff70b5859e27924"}, + {file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bab3db192a0c35e3c9d1560eb8332463e29e5507dbd822e29a0a3c48c0a8d92"}, + {file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:19d98f4f58b13900d8dec4ed09dd09ef292208ee44cc9c2fe01c1f0a2fe440e9"}, + {file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd33c61513cb1b7371fd40cf221256456d26a56284e7d19d1f0b9f1eb7dd7e8"}, + {file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6ba0497c1d066dd004e0f02a92426ca2df20fac08728d03f67f6960271feec"}, + {file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2b6be53e4fde0065524f1a0a7929b10e9280987b320716c1509478b712a7688c"}, + {file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:916a798f62f410c0b80b63683c8061f5ebe237b0f4ad778739304253353bc1cb"}, + {file = "SQLAlchemy-2.0.32-cp312-cp312-win32.whl", hash = "sha256:31983018b74908ebc6c996a16ad3690301a23befb643093fcfe85efd292e384d"}, + {file = "SQLAlchemy-2.0.32-cp312-cp312-win_amd64.whl", hash = "sha256:4363ed245a6231f2e2957cccdda3c776265a75851f4753c60f3004b90e69bfeb"}, + {file = "SQLAlchemy-2.0.32-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8afd5b26570bf41c35c0121801479958b4446751a3971fb9a480c1afd85558e"}, + {file = "SQLAlchemy-2.0.32-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c750987fc876813f27b60d619b987b057eb4896b81117f73bb8d9918c14f1cad"}, + {file = "SQLAlchemy-2.0.32-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0102afff4890f651ed91120c1120065663506b760da4e7823913ebd3258be"}, + {file = "SQLAlchemy-2.0.32-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:78c03d0f8a5ab4f3034c0e8482cfcc415a3ec6193491cfa1c643ed707d476f16"}, + {file = "SQLAlchemy-2.0.32-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:3bd1cae7519283ff525e64645ebd7a3e0283f3c038f461ecc1c7b040a0c932a1"}, + {file = "SQLAlchemy-2.0.32-cp37-cp37m-win32.whl", hash = "sha256:01438ebcdc566d58c93af0171c74ec28efe6a29184b773e378a385e6215389da"}, + {file = "SQLAlchemy-2.0.32-cp37-cp37m-win_amd64.whl", hash = "sha256:4979dc80fbbc9d2ef569e71e0896990bc94df2b9fdbd878290bd129b65ab579c"}, + {file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c742be912f57586ac43af38b3848f7688863a403dfb220193a882ea60e1ec3a"}, + {file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:62e23d0ac103bcf1c5555b6c88c114089587bc64d048fef5bbdb58dfd26f96da"}, + {file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:251f0d1108aab8ea7b9aadbd07fb47fb8e3a5838dde34aa95a3349876b5a1f1d"}, + {file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef18a84e5116340e38eca3e7f9eeaaef62738891422e7c2a0b80feab165905f"}, + {file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3eb6a97a1d39976f360b10ff208c73afb6a4de86dd2a6212ddf65c4a6a2347d5"}, + {file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0c1c9b673d21477cec17ab10bc4decb1322843ba35b481585facd88203754fc5"}, + {file = "SQLAlchemy-2.0.32-cp38-cp38-win32.whl", hash = "sha256:c41a2b9ca80ee555decc605bd3c4520cc6fef9abde8fd66b1cf65126a6922d65"}, + {file = "SQLAlchemy-2.0.32-cp38-cp38-win_amd64.whl", hash = "sha256:8a37e4d265033c897892279e8adf505c8b6b4075f2b40d77afb31f7185cd6ecd"}, + {file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fec964fba2ef46476312a03ec8c425956b05c20220a1a03703537824b5e8e1"}, + {file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:328429aecaba2aee3d71e11f2477c14eec5990fb6d0e884107935f7fb6001632"}, + {file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85a01b5599e790e76ac3fe3aa2f26e1feba56270023d6afd5550ed63c68552b3"}, + {file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf04784797dcdf4c0aa952c8d234fa01974c4729db55c45732520ce12dd95b4"}, + {file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4488120becf9b71b3ac718f4138269a6be99a42fe023ec457896ba4f80749525"}, + {file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14e09e083a5796d513918a66f3d6aedbc131e39e80875afe81d98a03312889e6"}, + {file = "SQLAlchemy-2.0.32-cp39-cp39-win32.whl", hash = "sha256:0d322cc9c9b2154ba7e82f7bf25ecc7c36fbe2d82e2933b3642fc095a52cfc78"}, + {file = "SQLAlchemy-2.0.32-cp39-cp39-win_amd64.whl", hash = "sha256:7dd8583df2f98dea28b5cd53a1beac963f4f9d087888d75f22fcc93a07cf8d84"}, + {file = "SQLAlchemy-2.0.32-py3-none-any.whl", hash = "sha256:e567a8793a692451f706b363ccf3c45e056b67d90ead58c3bc9471af5d212202"}, + {file = "SQLAlchemy-2.0.32.tar.gz", hash = "sha256:c1b88cc8b02b6a5f0efb0345a03672d4c897dc7d92585176f88c67346f565ea8"}, ] [package.dependencies] diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index e42669227..f8eb4cae5 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -2782,11 +2782,7 @@ def chat_route_generate( raise ApiError(status_code=_response.status_code, body=_response_json) def history_route_history( - self, - id: CommonUuid, - *, - limit: CommonLimit, - request_options: typing.Optional[RequestOptions] = None, + self, id: CommonUuid, *, request_options: typing.Optional[RequestOptions] = None ) -> EntriesHistory: """ Get history of a Session @@ -2796,9 +2792,6 @@ def history_route_history( id : CommonUuid ID of parent - limit : CommonLimit - Limit the number of items returned - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -2817,13 +2810,11 @@ def history_route_history( ) client.history_route_history( id="id", - limit=1, ) """ _response = self._client_wrapper.httpx_client.request( f"sessions/{jsonable_encoder(id)}/history", method="GET", - params={"limit": limit}, request_options=request_options, ) try: @@ -6593,11 +6584,7 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response_json) async def history_route_history( - self, - id: CommonUuid, - *, - limit: CommonLimit, - request_options: typing.Optional[RequestOptions] = None, + self, id: CommonUuid, *, request_options: typing.Optional[RequestOptions] = None ) -> EntriesHistory: """ Get history of a Session @@ -6607,9 +6594,6 @@ async def history_route_history( id : CommonUuid ID of parent - limit : CommonLimit - Limit the number of items returned - request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -6633,7 +6617,6 @@ async def history_route_history( async def main() -> None: await client.history_route_history( id="id", - limit=1, ) @@ -6642,7 +6625,6 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( f"sessions/{jsonable_encoder(id)}/history", method="GET", - params={"limit": limit}, request_options=request_options, ) try: diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index 0e13814f2..77af661e3 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -3912,7 +3912,6 @@ client = JulepApi( ) client.history_route_history( id="id", - limit=1, ) ``` @@ -3937,14 +3936,6 @@ client.history_route_history(
-**limit:** `CommonLimit` — Limit the number of items returned - -
-
- -
-
- **request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration.
diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 870dfac4c..545eebbe1 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -577,13 +577,33 @@ develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest- [[package]] name = "debugpy" -version = "1.8.3" +version = "1.8.2" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.3-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:0df2c400853150af14996b8d1a4f54d45ffa98e76c0f3de30665e89e273ea293"}, - {file = "debugpy-1.8.3.zip", hash = "sha256:0f5a6326d9fc375b864ed368d06cddf2dabe5135511e71cde3758be699847d36"}, + {file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"}, + {file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"}, + {file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"}, + {file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"}, + {file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"}, + {file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"}, + {file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"}, + {file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"}, + {file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"}, + {file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"}, + {file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"}, + {file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"}, + {file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"}, + {file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"}, + {file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"}, + {file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"}, + {file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"}, + {file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"}, + {file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"}, + {file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"}, + {file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"}, + {file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"}, ] [[package]] diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index 81f17c7f2..c25ff685a 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -1404,16 +1404,11 @@ export class DefaultService { */ public historyRouteHistory({ id, - limit = 100, }: { /** * ID of parent */ id: Common_uuid; - /** - * Limit the number of items returned - */ - limit?: Common_limit; }): CancelablePromise { return this.httpRequest.request({ method: "GET", @@ -1421,9 +1416,6 @@ export class DefaultService { path: { id: id, }, - query: { - limit: limit, - }, }); } /** diff --git a/typespec/entries/endpoints.tsp b/typespec/entries/endpoints.tsp index bcb9459b6..3a4a0335b 100644 --- a/typespec/entries/endpoints.tsp +++ b/typespec/entries/endpoints.tsp @@ -23,9 +23,5 @@ interface Endpoints @path @doc("ID of parent") id: uuid, - - @query - @doc("Limit the number of items returned") - limit: limit = 100, ): History; } From 3b6e81deeae9ed0c7e30d63a971ab1d036b891a7 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 5 Aug 2024 19:25:24 -0400 Subject: [PATCH 010/110] feat(agents-api): Add doc search routes Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Docs.py | 39 ++-- .../models/docs/search_docs_hybrid.py | 3 +- .../agents_api/routers/docs/__init__.py | 1 + .../agents_api/routers/docs/search_docs.py | 111 +++++++++++ sdks/python/julep/api/__init__.py | 14 +- sdks/python/julep/api/client.py | 183 ++---------------- sdks/python/julep/api/reference.md | 96 +-------- sdks/python/julep/api/types/__init__.py | 24 +-- ...s_search_route_search_request_direction.py | 7 - ...ocs_search_route_search_request_sort_by.py | 7 - ...gents_docs_search_route_search_response.py | 43 ---- .../api/types/docs_base_doc_search_request.py | 16 +- ...esponse.py => docs_doc_search_response.py} | 12 +- .../types/docs_hybrid_doc_search_request.py | 10 + .../types/docs_vector_doc_search_request.py | 5 + ...s_search_route_search_request_direction.py | 7 - ...ocs_search_route_search_request_sort_by.py | 7 - sdks/ts/src/api/index.ts | 2 + .../api/models/Docs_BaseDocSearchRequest.ts | 13 +- .../src/api/models/Docs_DocSearchResponse.ts | 15 ++ .../api/models/Docs_HybridDocSearchRequest.ts | 8 + .../api/models/Docs_VectorDocSearchRequest.ts | 4 + .../api/schemas/$Docs_BaseDocSearchRequest.ts | 18 +- .../api/schemas/$Docs_DocSearchResponse.ts | 21 ++ .../schemas/$Docs_HybridDocSearchRequest.ts | 12 ++ .../schemas/$Docs_VectorDocSearchRequest.ts | 6 + sdks/ts/src/api/services/DefaultService.ts | 78 +------- typespec/docs/endpoints.tsp | 5 +- typespec/docs/models.tsp | 30 +-- 29 files changed, 287 insertions(+), 510 deletions(-) create mode 100644 agents-api/agents_api/routers/docs/search_docs.py delete mode 100644 sdks/python/julep/api/types/agents_docs_search_route_search_request_direction.py delete mode 100644 sdks/python/julep/api/types/agents_docs_search_route_search_request_sort_by.py delete mode 100644 sdks/python/julep/api/types/agents_docs_search_route_search_response.py rename sdks/python/julep/api/types/{user_docs_search_route_search_response.py => docs_doc_search_response.py} (82%) delete mode 100644 sdks/python/julep/api/types/user_docs_search_route_search_request_direction.py delete mode 100644 sdks/python/julep/api/types/user_docs_search_route_search_request_sort_by.py create mode 100644 sdks/ts/src/api/models/Docs_DocSearchResponse.ts create mode 100644 sdks/ts/src/api/schemas/$Docs_DocSearchResponse.ts diff --git a/agents-api/agents_api/autogen/Docs.py b/agents-api/agents_api/autogen/Docs.py index b9da3646d..36d0c5a95 100644 --- a/agents-api/agents_api/autogen/Docs.py +++ b/agents-api/agents_api/autogen/Docs.py @@ -13,18 +13,7 @@ class BaseDocSearchRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - confidence: Annotated[float, Field(0.5, ge=0.0, le=1.0)] - """ - The confidence cutoff level - """ - alpha: Annotated[float, Field(0.75, ge=0.0, le=1.0)] - """ - The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; - """ - mmr: bool = False - """ - Whether to include the MMR algorithm in the search. Optimizes for diversity in search results. - """ + limit: Annotated[int, Field(10, ge=1, le=100)] lang: Literal["en-US"] = "en-US" """ The language to be used for text-only search. Support for other languages coming soon. @@ -105,6 +94,20 @@ class DocReference(BaseModel): distance: float | None = None +class DocSearchResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + docs: list[DocReference] + """ + The documents that were found + """ + time: Annotated[float, Field(gt=0.0)] + """ + The time taken to search in seconds + """ + + class EmbedQueryRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -129,6 +132,14 @@ class HybridDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( populate_by_name=True, ) + confidence: Annotated[float, Field(0.5, ge=0.0, le=1.0)] + """ + The confidence cutoff level + """ + alpha: Annotated[float, Field(0.75, ge=0.0, le=1.0)] + """ + The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; + """ text: str """ Text to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. @@ -161,6 +172,10 @@ class VectorDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( populate_by_name=True, ) + confidence: Annotated[float, Field(0.5, ge=0.0, le=1.0)] + """ + The confidence cutoff level + """ vector: list[float] """ Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. diff --git a/agents-api/agents_api/models/docs/search_docs_hybrid.py b/agents-api/agents_api/models/docs/search_docs_hybrid.py index b9d6a1ec0..82f550ac9 100644 --- a/agents-api/agents_api/models/docs/search_docs_hybrid.py +++ b/agents-api/agents_api/models/docs/search_docs_hybrid.py @@ -97,6 +97,7 @@ def search_docs_hybrid( query: str, query_embedding: list[float], k: int = 3, + alpha: float = 0.7, # Weight of the embedding search results (this is a good default) embed_search_options: dict = {}, text_search_options: dict = {}, **kwargs, @@ -122,4 +123,4 @@ def search_docs_hybrid( **kwargs, ) - return dbsf_fuse(text_results, embedding_results)[:k] + return dbsf_fuse(text_results, embedding_results, alpha)[:k] diff --git a/agents-api/agents_api/routers/docs/__init__.py b/agents-api/agents_api/routers/docs/__init__.py index 062db6a67..2db2d042a 100644 --- a/agents-api/agents_api/routers/docs/__init__.py +++ b/agents-api/agents_api/routers/docs/__init__.py @@ -4,3 +4,4 @@ from .get_doc import get_doc from .list_docs import list_agent_docs, list_user_docs from .router import router +from .search_docs import search_agent_docs, search_user_docs diff --git a/agents-api/agents_api/routers/docs/search_docs.py b/agents-api/agents_api/routers/docs/search_docs.py new file mode 100644 index 000000000..8de06dd17 --- /dev/null +++ b/agents-api/agents_api/routers/docs/search_docs.py @@ -0,0 +1,111 @@ +import time +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 + +from ...autogen.openapi_model import ( + DocSearchResponse, + HybridDocSearchRequest, + TextOnlyDocSearchRequest, + VectorDocSearchRequest, +) +from ...dependencies.developer_id import get_developer_id +from ...models.docs.search_docs_by_embedding import search_docs_by_embedding +from ...models.docs.search_docs_by_text import search_docs_by_text +from ...models.docs.search_docs_hybrid import search_docs_hybrid +from .router import router + + +def get_search_fn_and_params(search_params): + search_fn, params = None, None + + match search_params: + case TextOnlyDocSearchRequest(text=query, limit=k): + search_fn = search_docs_by_text + params = dict( + query=query, + k=k, + ) + + case VectorDocSearchRequest( + vector=query_embedding, limit=k, confidence=confidence + ): + search_fn = search_docs_by_embedding + params = dict( + query_embedding=query_embedding, + k=k, + confidence=confidence, + ) + + case HybridDocSearchRequest( + text=query, + vector=query_embedding, + limit=k, + confidence=confidence, + alpha=alpha, + ): + search_fn = search_docs_hybrid + params = dict( + query=query, + query_embedding=query_embedding, + k=k, + embed_search_options=dict(confidence=confidence), + alpha=alpha, + ) + + return search_fn, params + + +@router.post("/users/{user_id}/search", tags=["docs"]) +async def search_user_docs( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + search_params: ( + TextOnlyDocSearchRequest | VectorDocSearchRequest | HybridDocSearchRequest + ), + user_id: UUID4, +) -> DocSearchResponse: + search_fn, params = get_search_fn_and_params(search_params) + + start = time.time() + docs = search_fn( + developer_id=x_developer_id, + owner_type="user", + owner_id=user_id, + **params, + ) + end = time.time() + + time_taken = end - start + + return DocSearchResponse( + docs=docs, + time=time_taken, + ) + + +@router.post("/agents/{agent_id}/search", tags=["docs"]) +async def search_agent_docs( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + search_params: ( + TextOnlyDocSearchRequest | VectorDocSearchRequest | HybridDocSearchRequest + ), + agent_id: UUID4, +) -> DocSearchResponse: + search_fn, params = get_search_fn_and_params(search_params) + + start = time.time() + docs = search_fn( + developer_id=x_developer_id, + owner_type="agent", + owner_id=agent_id, + **params, + ) + end = time.time() + + time_taken = end - start + + return DocSearchResponse( + docs=docs, + time=time_taken, + ) diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index fbcacb597..30f156d29 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -14,9 +14,6 @@ AgentsCreateAgentRequestDefaultSettings, AgentsCreateAgentRequestInstructions, AgentsDocsSearchRouteSearchRequestBody, - AgentsDocsSearchRouteSearchRequestDirection, - AgentsDocsSearchRouteSearchRequestSortBy, - AgentsDocsSearchRouteSearchResponse, AgentsPatchAgentRequestDefaultSettings, AgentsPatchAgentRequestInstructions, AgentsRouteListRequestDirection, @@ -70,6 +67,7 @@ DocsDocOwner, DocsDocOwnerRole, DocsDocReference, + DocsDocSearchResponse, DocsEmbedQueryRequest, DocsEmbedQueryRequestText, DocsEmbedQueryResponse, @@ -210,9 +208,6 @@ UserDocsRouteListRequestSortBy, UserDocsRouteListResponse, UserDocsSearchRouteSearchRequestBody, - UserDocsSearchRouteSearchRequestDirection, - UserDocsSearchRouteSearchRequestSortBy, - UserDocsSearchRouteSearchResponse, UsersRouteListRequestDirection, UsersRouteListRequestSortBy, UsersRouteListResponse, @@ -235,9 +230,6 @@ "AgentsCreateAgentRequestDefaultSettings", "AgentsCreateAgentRequestInstructions", "AgentsDocsSearchRouteSearchRequestBody", - "AgentsDocsSearchRouteSearchRequestDirection", - "AgentsDocsSearchRouteSearchRequestSortBy", - "AgentsDocsSearchRouteSearchResponse", "AgentsPatchAgentRequestDefaultSettings", "AgentsPatchAgentRequestInstructions", "AgentsRouteListRequestDirection", @@ -291,6 +283,7 @@ "DocsDocOwner", "DocsDocOwnerRole", "DocsDocReference", + "DocsDocSearchResponse", "DocsEmbedQueryRequest", "DocsEmbedQueryRequestText", "DocsEmbedQueryResponse", @@ -432,9 +425,6 @@ "UserDocsRouteListRequestSortBy", "UserDocsRouteListResponse", "UserDocsSearchRouteSearchRequestBody", - "UserDocsSearchRouteSearchRequestDirection", - "UserDocsSearchRouteSearchRequestSortBy", - "UserDocsSearchRouteSearchResponse", "UsersRouteListRequestDirection", "UsersRouteListRequestSortBy", "UsersRouteListResponse", diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index f8eb4cae5..207aa7c32 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -33,15 +33,6 @@ from .types.agents_docs_search_route_search_request_body import ( AgentsDocsSearchRouteSearchRequestBody, ) -from .types.agents_docs_search_route_search_request_direction import ( - AgentsDocsSearchRouteSearchRequestDirection, -) -from .types.agents_docs_search_route_search_request_sort_by import ( - AgentsDocsSearchRouteSearchRequestSortBy, -) -from .types.agents_docs_search_route_search_response import ( - AgentsDocsSearchRouteSearchResponse, -) from .types.agents_patch_agent_request_default_settings import ( AgentsPatchAgentRequestDefaultSettings, ) @@ -69,6 +60,7 @@ from .types.common_valid_python_identifier import CommonValidPythonIdentifier from .types.docs_create_doc_request_content import DocsCreateDocRequestContent from .types.docs_doc import DocsDoc +from .types.docs_doc_search_response import DocsDocSearchResponse from .types.docs_embed_query_request import DocsEmbedQueryRequest from .types.docs_embed_query_response import DocsEmbedQueryResponse from .types.entries_history import EntriesHistory @@ -116,15 +108,6 @@ from .types.user_docs_search_route_search_request_body import ( UserDocsSearchRouteSearchRequestBody, ) -from .types.user_docs_search_route_search_request_direction import ( - UserDocsSearchRouteSearchRequestDirection, -) -from .types.user_docs_search_route_search_request_sort_by import ( - UserDocsSearchRouteSearchRequestSortBy, -) -from .types.user_docs_search_route_search_response import ( - UserDocsSearchRouteSearchResponse, -) from .types.users_route_list_request_direction import UsersRouteListRequestDirection from .types.users_route_list_request_sort_by import UsersRouteListRequestSortBy from .types.users_route_list_response import UsersRouteListResponse @@ -842,14 +825,9 @@ def agents_docs_search_route_search( self, id: CommonUuid, *, - limit: CommonLimit, - offset: CommonOffset, - sort_by: AgentsDocsSearchRouteSearchRequestSortBy, - direction: AgentsDocsSearchRouteSearchRequestDirection, - metadata_filter: str, body: AgentsDocsSearchRouteSearchRequestBody, request_options: typing.Optional[RequestOptions] = None, - ) -> AgentsDocsSearchRouteSearchResponse: + ) -> DocsDocSearchResponse: """ Search Docs owned by an Agent @@ -858,21 +836,6 @@ def agents_docs_search_route_search( id : CommonUuid ID of the parent - limit : CommonLimit - Limit the number of items returned - - offset : CommonOffset - Offset the items returned - - sort_by : AgentsDocsSearchRouteSearchRequestSortBy - Sort by a field - - direction : AgentsDocsSearchRouteSearchRequestDirection - Sort direction - - metadata_filter : str - JSON string of object that should be used to filter objects by metadata - body : AgentsDocsSearchRouteSearchRequestBody request_options : typing.Optional[RequestOptions] @@ -880,7 +843,7 @@ def agents_docs_search_route_search( Returns ------- - AgentsDocsSearchRouteSearchResponse + DocsDocSearchResponse The request has succeeded. Examples @@ -894,15 +857,9 @@ def agents_docs_search_route_search( ) client.agents_docs_search_route_search( id="id", - limit=1, - offset=1, - sort_by="created_at", - direction="asc", - metadata_filter="metadata_filter", body=DocsVectorDocSearchRequest( + limit=1, confidence=1.1, - alpha=1.1, - mmr=True, vector=[1.1], ), ) @@ -910,20 +867,13 @@ def agents_docs_search_route_search( _response = self._client_wrapper.httpx_client.request( f"agents/{jsonable_encoder(id)}/search", method="POST", - params={ - "limit": limit, - "offset": offset, - "sort_by": sort_by, - "direction": direction, - "metadata_filter": metadata_filter, - }, json={"body": body}, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AgentsDocsSearchRouteSearchResponse, _response.json()) # type: ignore + return pydantic_v1.parse_obj_as(DocsDocSearchResponse, _response.json()) # type: ignore _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) @@ -3552,14 +3502,9 @@ def user_docs_search_route_search( self, id: CommonUuid, *, - limit: CommonLimit, - offset: CommonOffset, - sort_by: UserDocsSearchRouteSearchRequestSortBy, - direction: UserDocsSearchRouteSearchRequestDirection, - metadata_filter: str, body: UserDocsSearchRouteSearchRequestBody, request_options: typing.Optional[RequestOptions] = None, - ) -> UserDocsSearchRouteSearchResponse: + ) -> DocsDocSearchResponse: """ Search Docs owned by a User @@ -3568,21 +3513,6 @@ def user_docs_search_route_search( id : CommonUuid ID of the parent - limit : CommonLimit - Limit the number of items returned - - offset : CommonOffset - Offset the items returned - - sort_by : UserDocsSearchRouteSearchRequestSortBy - Sort by a field - - direction : UserDocsSearchRouteSearchRequestDirection - Sort direction - - metadata_filter : str - JSON string of object that should be used to filter objects by metadata - body : UserDocsSearchRouteSearchRequestBody request_options : typing.Optional[RequestOptions] @@ -3590,7 +3520,7 @@ def user_docs_search_route_search( Returns ------- - UserDocsSearchRouteSearchResponse + DocsDocSearchResponse The request has succeeded. Examples @@ -3604,15 +3534,9 @@ def user_docs_search_route_search( ) client.user_docs_search_route_search( id="id", - limit=1, - offset=1, - sort_by="created_at", - direction="asc", - metadata_filter="metadata_filter", body=DocsVectorDocSearchRequest( + limit=1, confidence=1.1, - alpha=1.1, - mmr=True, vector=[1.1], ), ) @@ -3620,20 +3544,13 @@ def user_docs_search_route_search( _response = self._client_wrapper.httpx_client.request( f"users/{jsonable_encoder(id)}/search", method="POST", - params={ - "limit": limit, - "offset": offset, - "sort_by": sort_by, - "direction": direction, - "metadata_filter": metadata_filter, - }, json={"body": body}, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(UserDocsSearchRouteSearchResponse, _response.json()) # type: ignore + return pydantic_v1.parse_obj_as(DocsDocSearchResponse, _response.json()) # type: ignore _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) @@ -4420,14 +4337,9 @@ async def agents_docs_search_route_search( self, id: CommonUuid, *, - limit: CommonLimit, - offset: CommonOffset, - sort_by: AgentsDocsSearchRouteSearchRequestSortBy, - direction: AgentsDocsSearchRouteSearchRequestDirection, - metadata_filter: str, body: AgentsDocsSearchRouteSearchRequestBody, request_options: typing.Optional[RequestOptions] = None, - ) -> AgentsDocsSearchRouteSearchResponse: + ) -> DocsDocSearchResponse: """ Search Docs owned by an Agent @@ -4436,21 +4348,6 @@ async def agents_docs_search_route_search( id : CommonUuid ID of the parent - limit : CommonLimit - Limit the number of items returned - - offset : CommonOffset - Offset the items returned - - sort_by : AgentsDocsSearchRouteSearchRequestSortBy - Sort by a field - - direction : AgentsDocsSearchRouteSearchRequestDirection - Sort direction - - metadata_filter : str - JSON string of object that should be used to filter objects by metadata - body : AgentsDocsSearchRouteSearchRequestBody request_options : typing.Optional[RequestOptions] @@ -4458,7 +4355,7 @@ async def agents_docs_search_route_search( Returns ------- - AgentsDocsSearchRouteSearchResponse + DocsDocSearchResponse The request has succeeded. Examples @@ -4477,15 +4374,9 @@ async def agents_docs_search_route_search( async def main() -> None: await client.agents_docs_search_route_search( id="id", - limit=1, - offset=1, - sort_by="created_at", - direction="asc", - metadata_filter="metadata_filter", body=DocsVectorDocSearchRequest( + limit=1, confidence=1.1, - alpha=1.1, - mmr=True, vector=[1.1], ), ) @@ -4496,20 +4387,13 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( f"agents/{jsonable_encoder(id)}/search", method="POST", - params={ - "limit": limit, - "offset": offset, - "sort_by": sort_by, - "direction": direction, - "metadata_filter": metadata_filter, - }, json={"body": body}, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(AgentsDocsSearchRouteSearchResponse, _response.json()) # type: ignore + return pydantic_v1.parse_obj_as(DocsDocSearchResponse, _response.json()) # type: ignore _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) @@ -7458,14 +7342,9 @@ async def user_docs_search_route_search( self, id: CommonUuid, *, - limit: CommonLimit, - offset: CommonOffset, - sort_by: UserDocsSearchRouteSearchRequestSortBy, - direction: UserDocsSearchRouteSearchRequestDirection, - metadata_filter: str, body: UserDocsSearchRouteSearchRequestBody, request_options: typing.Optional[RequestOptions] = None, - ) -> UserDocsSearchRouteSearchResponse: + ) -> DocsDocSearchResponse: """ Search Docs owned by a User @@ -7474,21 +7353,6 @@ async def user_docs_search_route_search( id : CommonUuid ID of the parent - limit : CommonLimit - Limit the number of items returned - - offset : CommonOffset - Offset the items returned - - sort_by : UserDocsSearchRouteSearchRequestSortBy - Sort by a field - - direction : UserDocsSearchRouteSearchRequestDirection - Sort direction - - metadata_filter : str - JSON string of object that should be used to filter objects by metadata - body : UserDocsSearchRouteSearchRequestBody request_options : typing.Optional[RequestOptions] @@ -7496,7 +7360,7 @@ async def user_docs_search_route_search( Returns ------- - UserDocsSearchRouteSearchResponse + DocsDocSearchResponse The request has succeeded. Examples @@ -7515,15 +7379,9 @@ async def user_docs_search_route_search( async def main() -> None: await client.user_docs_search_route_search( id="id", - limit=1, - offset=1, - sort_by="created_at", - direction="asc", - metadata_filter="metadata_filter", body=DocsVectorDocSearchRequest( + limit=1, confidence=1.1, - alpha=1.1, - mmr=True, vector=[1.1], ), ) @@ -7534,20 +7392,13 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( f"users/{jsonable_encoder(id)}/search", method="POST", - params={ - "limit": limit, - "offset": offset, - "sort_by": sort_by, - "direction": direction, - "metadata_filter": metadata_filter, - }, json={"body": body}, request_options=request_options, omit=OMIT, ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(UserDocsSearchRouteSearchResponse, _response.json()) # type: ignore + return pydantic_v1.parse_obj_as(DocsDocSearchResponse, _response.json()) # type: ignore _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index 77af661e3..c7b27f1a6 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -976,15 +976,9 @@ client = JulepApi( ) client.agents_docs_search_route_search( id="id", - limit=1, - offset=1, - sort_by="created_at", - direction="asc", - metadata_filter="metadata_filter", body=DocsVectorDocSearchRequest( + limit=1, confidence=1.1, - alpha=1.1, - mmr=True, vector=[1.1], ), ) @@ -1011,46 +1005,6 @@ client.agents_docs_search_route_search(
-**limit:** `CommonLimit` — Limit the number of items returned - -
-
- -
-
- -**offset:** `CommonOffset` — Offset the items returned - -
-
- -
-
- -**sort_by:** `AgentsDocsSearchRouteSearchRequestSortBy` — Sort by a field - -
-
- -
-
- -**direction:** `AgentsDocsSearchRouteSearchRequestDirection` — Sort direction - -
-
- -
-
- -**metadata_filter:** `str` — JSON string of object that should be used to filter objects by metadata - -
-
- -
-
- **body:** `AgentsDocsSearchRouteSearchRequestBody`
@@ -5098,15 +5052,9 @@ client = JulepApi( ) client.user_docs_search_route_search( id="id", - limit=1, - offset=1, - sort_by="created_at", - direction="asc", - metadata_filter="metadata_filter", body=DocsVectorDocSearchRequest( + limit=1, confidence=1.1, - alpha=1.1, - mmr=True, vector=[1.1], ), ) @@ -5133,46 +5081,6 @@ client.user_docs_search_route_search(
-**limit:** `CommonLimit` — Limit the number of items returned - -
-
- -
-
- -**offset:** `CommonOffset` — Offset the items returned - -
-
- -
-
- -**sort_by:** `UserDocsSearchRouteSearchRequestSortBy` — Sort by a field - -
-
- -
-
- -**direction:** `UserDocsSearchRouteSearchRequestDirection` — Sort direction - -
-
- -
-
- -**metadata_filter:** `str` — JSON string of object that should be used to filter objects by metadata - -
-
- -
-
- **body:** `UserDocsSearchRouteSearchRequestBody`
diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index 6da79d6ab..a2ab207d0 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -21,15 +21,6 @@ from .agents_docs_search_route_search_request_body import ( AgentsDocsSearchRouteSearchRequestBody, ) -from .agents_docs_search_route_search_request_direction import ( - AgentsDocsSearchRouteSearchRequestDirection, -) -from .agents_docs_search_route_search_request_sort_by import ( - AgentsDocsSearchRouteSearchRequestSortBy, -) -from .agents_docs_search_route_search_response import ( - AgentsDocsSearchRouteSearchResponse, -) from .agents_patch_agent_request_default_settings import ( AgentsPatchAgentRequestDefaultSettings, ) @@ -97,6 +88,7 @@ from .docs_doc_owner import DocsDocOwner from .docs_doc_owner_role import DocsDocOwnerRole from .docs_doc_reference import DocsDocReference +from .docs_doc_search_response import DocsDocSearchResponse from .docs_embed_query_request import DocsEmbedQueryRequest from .docs_embed_query_request_text import DocsEmbedQueryRequestText from .docs_embed_query_response import DocsEmbedQueryResponse @@ -274,13 +266,6 @@ from .user_docs_search_route_search_request_body import ( UserDocsSearchRouteSearchRequestBody, ) -from .user_docs_search_route_search_request_direction import ( - UserDocsSearchRouteSearchRequestDirection, -) -from .user_docs_search_route_search_request_sort_by import ( - UserDocsSearchRouteSearchRequestSortBy, -) -from .user_docs_search_route_search_response import UserDocsSearchRouteSearchResponse from .users_route_list_request_direction import UsersRouteListRequestDirection from .users_route_list_request_sort_by import UsersRouteListRequestSortBy from .users_route_list_response import UsersRouteListResponse @@ -301,9 +286,6 @@ "AgentsCreateAgentRequestDefaultSettings", "AgentsCreateAgentRequestInstructions", "AgentsDocsSearchRouteSearchRequestBody", - "AgentsDocsSearchRouteSearchRequestDirection", - "AgentsDocsSearchRouteSearchRequestSortBy", - "AgentsDocsSearchRouteSearchResponse", "AgentsPatchAgentRequestDefaultSettings", "AgentsPatchAgentRequestInstructions", "AgentsRouteListRequestDirection", @@ -357,6 +339,7 @@ "DocsDocOwner", "DocsDocOwnerRole", "DocsDocReference", + "DocsDocSearchResponse", "DocsEmbedQueryRequest", "DocsEmbedQueryRequestText", "DocsEmbedQueryResponse", @@ -497,9 +480,6 @@ "UserDocsRouteListRequestSortBy", "UserDocsRouteListResponse", "UserDocsSearchRouteSearchRequestBody", - "UserDocsSearchRouteSearchRequestDirection", - "UserDocsSearchRouteSearchRequestSortBy", - "UserDocsSearchRouteSearchResponse", "UsersRouteListRequestDirection", "UsersRouteListRequestSortBy", "UsersRouteListResponse", diff --git a/sdks/python/julep/api/types/agents_docs_search_route_search_request_direction.py b/sdks/python/julep/api/types/agents_docs_search_route_search_request_direction.py deleted file mode 100644 index 07c53fe78..000000000 --- a/sdks/python/julep/api/types/agents_docs_search_route_search_request_direction.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -AgentsDocsSearchRouteSearchRequestDirection = typing.Union[ - typing.Literal["asc", "desc"], typing.Any -] diff --git a/sdks/python/julep/api/types/agents_docs_search_route_search_request_sort_by.py b/sdks/python/julep/api/types/agents_docs_search_route_search_request_sort_by.py deleted file mode 100644 index a85bdee6c..000000000 --- a/sdks/python/julep/api/types/agents_docs_search_route_search_request_sort_by.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -AgentsDocsSearchRouteSearchRequestSortBy = typing.Union[ - typing.Literal["created_at", "updated_at"], typing.Any -] diff --git a/sdks/python/julep/api/types/agents_docs_search_route_search_response.py b/sdks/python/julep/api/types/agents_docs_search_route_search_response.py deleted file mode 100644 index 400d1a51d..000000000 --- a/sdks/python/julep/api/types/agents_docs_search_route_search_response.py +++ /dev/null @@ -1,43 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .docs_doc_reference import DocsDocReference - - -class AgentsDocsSearchRouteSearchResponse(pydantic_v1.BaseModel): - results: typing.List[DocsDocReference] - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/docs_base_doc_search_request.py b/sdks/python/julep/api/types/docs_base_doc_search_request.py index f7c1ecfa2..1b9646593 100644 --- a/sdks/python/julep/api/types/docs_base_doc_search_request.py +++ b/sdks/python/julep/api/types/docs_base_doc_search_request.py @@ -8,21 +8,7 @@ class DocsBaseDocSearchRequest(pydantic_v1.BaseModel): - confidence: float = pydantic_v1.Field() - """ - The confidence cutoff level - """ - - alpha: float = pydantic_v1.Field() - """ - The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; - """ - - mmr: bool = pydantic_v1.Field() - """ - Whether to include the MMR algorithm in the search. Optimizes for diversity in search results. - """ - + limit: int lang: typing.Literal["en-US"] = pydantic_v1.Field(default="en-US") """ The language to be used for text-only search. Support for other languages coming soon. diff --git a/sdks/python/julep/api/types/user_docs_search_route_search_response.py b/sdks/python/julep/api/types/docs_doc_search_response.py similarity index 82% rename from sdks/python/julep/api/types/user_docs_search_route_search_response.py rename to sdks/python/julep/api/types/docs_doc_search_response.py index 9206fc909..59d26bdb9 100644 --- a/sdks/python/julep/api/types/user_docs_search_route_search_response.py +++ b/sdks/python/julep/api/types/docs_doc_search_response.py @@ -8,8 +8,16 @@ from .docs_doc_reference import DocsDocReference -class UserDocsSearchRouteSearchResponse(pydantic_v1.BaseModel): - results: typing.List[DocsDocReference] +class DocsDocSearchResponse(pydantic_v1.BaseModel): + docs: typing.List[DocsDocReference] = pydantic_v1.Field() + """ + The documents that were found + """ + + time: float = pydantic_v1.Field() + """ + The time taken to search in seconds + """ def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { diff --git a/sdks/python/julep/api/types/docs_hybrid_doc_search_request.py b/sdks/python/julep/api/types/docs_hybrid_doc_search_request.py index 51de991e1..8e460c40f 100644 --- a/sdks/python/julep/api/types/docs_hybrid_doc_search_request.py +++ b/sdks/python/julep/api/types/docs_hybrid_doc_search_request.py @@ -9,6 +9,16 @@ class DocsHybridDocSearchRequest(DocsBaseDocSearchRequest): + confidence: float = pydantic_v1.Field() + """ + The confidence cutoff level + """ + + alpha: float = pydantic_v1.Field() + """ + The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; + """ + text: str = pydantic_v1.Field() """ Text to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. diff --git a/sdks/python/julep/api/types/docs_vector_doc_search_request.py b/sdks/python/julep/api/types/docs_vector_doc_search_request.py index 4ea4e9632..f9c103ec6 100644 --- a/sdks/python/julep/api/types/docs_vector_doc_search_request.py +++ b/sdks/python/julep/api/types/docs_vector_doc_search_request.py @@ -9,6 +9,11 @@ class DocsVectorDocSearchRequest(DocsBaseDocSearchRequest): + confidence: float = pydantic_v1.Field() + """ + The confidence cutoff level + """ + vector: typing.List[float] = pydantic_v1.Field() """ Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. diff --git a/sdks/python/julep/api/types/user_docs_search_route_search_request_direction.py b/sdks/python/julep/api/types/user_docs_search_route_search_request_direction.py deleted file mode 100644 index 3a2ef70a8..000000000 --- a/sdks/python/julep/api/types/user_docs_search_route_search_request_direction.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -UserDocsSearchRouteSearchRequestDirection = typing.Union[ - typing.Literal["asc", "desc"], typing.Any -] diff --git a/sdks/python/julep/api/types/user_docs_search_route_search_request_sort_by.py b/sdks/python/julep/api/types/user_docs_search_route_search_request_sort_by.py deleted file mode 100644 index 8cf9538a6..000000000 --- a/sdks/python/julep/api/types/user_docs_search_route_search_request_sort_by.py +++ /dev/null @@ -1,7 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -UserDocsSearchRouteSearchRequestSortBy = typing.Union[ - typing.Literal["created_at", "updated_at"], typing.Any -] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index f279f2bb9..b38e497f5 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -53,6 +53,7 @@ export type { Docs_CreateDocRequest } from "./models/Docs_CreateDocRequest"; export type { Docs_Doc } from "./models/Docs_Doc"; export type { Docs_DocOwner } from "./models/Docs_DocOwner"; export type { Docs_DocReference } from "./models/Docs_DocReference"; +export type { Docs_DocSearchResponse } from "./models/Docs_DocSearchResponse"; export type { Docs_EmbedQueryRequest } from "./models/Docs_EmbedQueryRequest"; export type { Docs_EmbedQueryResponse } from "./models/Docs_EmbedQueryResponse"; export type { Docs_HybridDocSearchRequest } from "./models/Docs_HybridDocSearchRequest"; @@ -167,6 +168,7 @@ export { $Docs_CreateDocRequest } from "./schemas/$Docs_CreateDocRequest"; export { $Docs_Doc } from "./schemas/$Docs_Doc"; export { $Docs_DocOwner } from "./schemas/$Docs_DocOwner"; export { $Docs_DocReference } from "./schemas/$Docs_DocReference"; +export { $Docs_DocSearchResponse } from "./schemas/$Docs_DocSearchResponse"; export { $Docs_EmbedQueryRequest } from "./schemas/$Docs_EmbedQueryRequest"; export { $Docs_EmbedQueryResponse } from "./schemas/$Docs_EmbedQueryResponse"; export { $Docs_HybridDocSearchRequest } from "./schemas/$Docs_HybridDocSearchRequest"; diff --git a/sdks/ts/src/api/models/Docs_BaseDocSearchRequest.ts b/sdks/ts/src/api/models/Docs_BaseDocSearchRequest.ts index d99eefaf9..b6dd20f99 100644 --- a/sdks/ts/src/api/models/Docs_BaseDocSearchRequest.ts +++ b/sdks/ts/src/api/models/Docs_BaseDocSearchRequest.ts @@ -3,18 +3,7 @@ /* tslint:disable */ /* eslint-disable */ export type Docs_BaseDocSearchRequest = { - /** - * The confidence cutoff level - */ - confidence: number; - /** - * The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; - */ - alpha: number; - /** - * Whether to include the MMR algorithm in the search. Optimizes for diversity in search results. - */ - mmr: boolean; + limit: number; /** * The language to be used for text-only search. Support for other languages coming soon. */ diff --git a/sdks/ts/src/api/models/Docs_DocSearchResponse.ts b/sdks/ts/src/api/models/Docs_DocSearchResponse.ts new file mode 100644 index 000000000..cfb8ad225 --- /dev/null +++ b/sdks/ts/src/api/models/Docs_DocSearchResponse.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Docs_DocReference } from "./Docs_DocReference"; +export type Docs_DocSearchResponse = { + /** + * The documents that were found + */ + docs: Array; + /** + * The time taken to search in seconds + */ + time: number; +}; diff --git a/sdks/ts/src/api/models/Docs_HybridDocSearchRequest.ts b/sdks/ts/src/api/models/Docs_HybridDocSearchRequest.ts index 93b099294..a1ba32811 100644 --- a/sdks/ts/src/api/models/Docs_HybridDocSearchRequest.ts +++ b/sdks/ts/src/api/models/Docs_HybridDocSearchRequest.ts @@ -4,6 +4,14 @@ /* eslint-disable */ import type { Docs_BaseDocSearchRequest } from "./Docs_BaseDocSearchRequest"; export type Docs_HybridDocSearchRequest = Docs_BaseDocSearchRequest & { + /** + * The confidence cutoff level + */ + confidence: number; + /** + * The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; + */ + alpha: number; /** * Text to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. */ diff --git a/sdks/ts/src/api/models/Docs_VectorDocSearchRequest.ts b/sdks/ts/src/api/models/Docs_VectorDocSearchRequest.ts index 8839067b0..7a720c46a 100644 --- a/sdks/ts/src/api/models/Docs_VectorDocSearchRequest.ts +++ b/sdks/ts/src/api/models/Docs_VectorDocSearchRequest.ts @@ -4,6 +4,10 @@ /* eslint-disable */ import type { Docs_BaseDocSearchRequest } from "./Docs_BaseDocSearchRequest"; export type Docs_VectorDocSearchRequest = Docs_BaseDocSearchRequest & { + /** + * The confidence cutoff level + */ + confidence: number; /** * Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. */ diff --git a/sdks/ts/src/api/schemas/$Docs_BaseDocSearchRequest.ts b/sdks/ts/src/api/schemas/$Docs_BaseDocSearchRequest.ts index 00b992770..99188755e 100644 --- a/sdks/ts/src/api/schemas/$Docs_BaseDocSearchRequest.ts +++ b/sdks/ts/src/api/schemas/$Docs_BaseDocSearchRequest.ts @@ -4,22 +4,12 @@ /* eslint-disable */ export const $Docs_BaseDocSearchRequest = { properties: { - confidence: { + limit: { type: "number", - description: `The confidence cutoff level`, - isRequired: true, - maximum: 1, - }, - alpha: { - type: "number", - description: `The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector;`, - isRequired: true, - maximum: 1, - }, - mmr: { - type: "boolean", - description: `Whether to include the MMR algorithm in the search. Optimizes for diversity in search results.`, isRequired: true, + format: "uint16", + maximum: 100, + minimum: 1, }, lang: { type: "Enum", diff --git a/sdks/ts/src/api/schemas/$Docs_DocSearchResponse.ts b/sdks/ts/src/api/schemas/$Docs_DocSearchResponse.ts new file mode 100644 index 000000000..df2b37b48 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Docs_DocSearchResponse.ts @@ -0,0 +1,21 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Docs_DocSearchResponse = { + properties: { + docs: { + type: "array", + contains: { + type: "Docs_DocReference", + }, + isRequired: true, + }, + time: { + type: "number", + description: `The time taken to search in seconds`, + isRequired: true, + exclusiveMinimum: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Docs_HybridDocSearchRequest.ts b/sdks/ts/src/api/schemas/$Docs_HybridDocSearchRequest.ts index 14948c59a..2bc5005fb 100644 --- a/sdks/ts/src/api/schemas/$Docs_HybridDocSearchRequest.ts +++ b/sdks/ts/src/api/schemas/$Docs_HybridDocSearchRequest.ts @@ -10,6 +10,18 @@ export const $Docs_HybridDocSearchRequest = { }, { properties: { + confidence: { + type: "number", + description: `The confidence cutoff level`, + isRequired: true, + maximum: 1, + }, + alpha: { + type: "number", + description: `The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector;`, + isRequired: true, + maximum: 1, + }, text: { type: "string", description: `Text to use in the search. In \`hybrid\` search mode, either \`text\` or both \`text\` and \`vector\` fields are required.`, diff --git a/sdks/ts/src/api/schemas/$Docs_VectorDocSearchRequest.ts b/sdks/ts/src/api/schemas/$Docs_VectorDocSearchRequest.ts index 54ba49bf5..af6de0b12 100644 --- a/sdks/ts/src/api/schemas/$Docs_VectorDocSearchRequest.ts +++ b/sdks/ts/src/api/schemas/$Docs_VectorDocSearchRequest.ts @@ -10,6 +10,12 @@ export const $Docs_VectorDocSearchRequest = { }, { properties: { + confidence: { + type: "number", + description: `The confidence cutoff level`, + isRequired: true, + maximum: 1, + }, vector: { type: "array", contains: { diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index c25ff685a..c71a6d7e9 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -20,7 +20,7 @@ import type { Common_ResourceUpdatedResponse } from "../models/Common_ResourceUp import type { Common_uuid } from "../models/Common_uuid"; import type { Docs_CreateDocRequest } from "../models/Docs_CreateDocRequest"; import type { Docs_Doc } from "../models/Docs_Doc"; -import type { Docs_DocReference } from "../models/Docs_DocReference"; +import type { Docs_DocSearchResponse } from "../models/Docs_DocSearchResponse"; import type { Docs_EmbedQueryRequest } from "../models/Docs_EmbedQueryRequest"; import type { Docs_EmbedQueryResponse } from "../models/Docs_EmbedQueryResponse"; import type { Docs_HybridDocSearchRequest } from "../models/Docs_HybridDocSearchRequest"; @@ -315,17 +315,12 @@ export class DefaultService { } /** * Search Docs owned by an Agent - * @returns any The request has succeeded. + * @returns Docs_DocSearchResponse The request has succeeded. * @throws ApiError */ public agentsDocsSearchRouteSearch({ id, requestBody, - limit = 100, - offset, - sortBy = "created_at", - direction = "asc", - metadataFilter = "{}", }: { /** * ID of the parent @@ -337,42 +332,13 @@ export class DefaultService { | Docs_TextOnlyDocSearchRequest | Docs_HybridDocSearchRequest; }; - /** - * Limit the number of items returned - */ - limit?: Common_limit; - /** - * Offset the items returned - */ - offset: Common_offset; - /** - * Sort by a field - */ - sortBy?: "created_at" | "updated_at"; - /** - * Sort direction - */ - direction?: "asc" | "desc"; - /** - * JSON string of object that should be used to filter objects by metadata - */ - metadataFilter?: string; - }): CancelablePromise<{ - results: Array; - }> { + }): CancelablePromise { return this.httpRequest.request({ method: "POST", url: "/agents/{id}/search", path: { id: id, }, - query: { - limit: limit, - offset: offset, - sort_by: sortBy, - direction: direction, - metadata_filter: metadataFilter, - }, body: requestBody, mediaType: "application/json", }); @@ -1758,17 +1724,12 @@ export class DefaultService { } /** * Search Docs owned by a User - * @returns any The request has succeeded. + * @returns Docs_DocSearchResponse The request has succeeded. * @throws ApiError */ public userDocsSearchRouteSearch({ id, requestBody, - limit = 100, - offset, - sortBy = "created_at", - direction = "asc", - metadataFilter = "{}", }: { /** * ID of the parent @@ -1780,42 +1741,13 @@ export class DefaultService { | Docs_TextOnlyDocSearchRequest | Docs_HybridDocSearchRequest; }; - /** - * Limit the number of items returned - */ - limit?: Common_limit; - /** - * Offset the items returned - */ - offset: Common_offset; - /** - * Sort by a field - */ - sortBy?: "created_at" | "updated_at"; - /** - * Sort direction - */ - direction?: "asc" | "desc"; - /** - * JSON string of object that should be used to filter objects by metadata - */ - metadataFilter?: string; - }): CancelablePromise<{ - results: Array; - }> { + }): CancelablePromise { return this.httpRequest.request({ method: "POST", url: "/users/{id}/search", path: { id: id, }, - query: { - limit: limit, - offset: offset, - sort_by: sortBy, - direction: direction, - metadata_filter: metadataFilter, - }, body: requestBody, mediaType: "application/json", }); diff --git a/typespec/docs/endpoints.tsp b/typespec/docs/endpoints.tsp index a493f9b86..143d18f33 100644 --- a/typespec/docs/endpoints.tsp +++ b/typespec/docs/endpoints.tsp @@ -37,11 +37,8 @@ interface SearchEndpoints pure BM25; 1 => pure vector; */ - @minValue(0) - @maxValue(1) - alpha: float = 0.75; - - /** Whether to include the MMR algorithm in the search. Optimizes for diversity in search results. */ - mmr: boolean = false; + @minValue(1) + @maxValue(100) + limit: uint16 = 10; /** The language to be used for text-only search. Support for other languages coming soon. */ lang: "en-US" = "en-US"; } model VectorDocSearchRequest extends BaseDocSearchRequest { + /** The confidence cutoff level */ + @minValue(0) + @maxValue(1) + confidence: float = 0.5; + /** Vector to use in the search. Must be the same dimensions as the embedding model or else an error will be thrown. */ vector: float[]; @@ -98,6 +94,16 @@ model TextOnlyDocSearchRequest extends BaseDocSearchRequest { } model HybridDocSearchRequest extends BaseDocSearchRequest { + /** The confidence cutoff level */ + @minValue(0) + @maxValue(1) + confidence: float = 0.5; + + /** The weight to apply to BM25 vs Vector search results. 0 => pure BM25; 1 => pure vector; */ + @minValue(0) + @maxValue(1) + alpha: float = 0.75; + /** Text to use in the search. In `hybrid` search mode, either `text` or both `text` and `vector` fields are required. */ text: string; From 6483fbe20e6f03f3173cc6d6c6a871f97f5636a5 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Tue, 6 Aug 2024 21:03:15 +0300 Subject: [PATCH 011/110] fix: Fix update execution route --- agents-api/agents_api/routers/tasks/routers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agents-api/agents_api/routers/tasks/routers.py b/agents-api/agents_api/routers/tasks/routers.py index ca9a9c745..857a139de 100644 --- a/agents-api/agents_api/routers/tasks/routers.py +++ b/agents-api/agents_api/routers/tasks/routers.py @@ -309,12 +309,12 @@ async def patch_execution( ) -@router.put("/tasks/{task_id}/executions/{execution_id}", tags=["tasks"]) +@router.put("/executions/{execution_id}", tags=["executions"]) async def put_execution( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], execution_id: UUID4, data: ResumeExecutionRequest | StopExecutionRequest, -) -> Execution: +): temporal_client = await get_client() if isinstance(data, StopExecutionRequest): handle = temporal_client.get_workflow_handle_for( @@ -326,7 +326,7 @@ async def put_execution( developer_id=x_developer_id, execution_id=execution_id ) handle = temporal_client.get_async_activity_handle(token_data["task_token"]) - await handle.complete("finished") + await handle.complete(data.input) @router.get("/tasks/{task_id}/executions", tags=["tasks"]) From 781c7abfa8ce6b12009a353bad959ddefe96c95a Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Thu, 8 Aug 2024 19:52:25 -0400 Subject: [PATCH 012/110] feat(agents-api): Add litellm proxy to docker compose Signed-off-by: Diwank Tomer --- agents-api/docker-compose.yml | 4 +- agents-api/poetry.lock | 1917 ++++++++++----------------------- agents-api/pyproject.toml | 8 +- docker-compose.yml | 1 + llm-proxy/.dockerignore | 1 + llm-proxy/.gitignore | 1 + llm-proxy/docker-compose.yml | 54 + llm-proxy/litellm-config.yaml | 127 +++ 8 files changed, 761 insertions(+), 1352 deletions(-) create mode 100644 llm-proxy/.dockerignore create mode 100644 llm-proxy/.gitignore create mode 100644 llm-proxy/docker-compose.yml create mode 100644 llm-proxy/litellm-config.yaml diff --git a/agents-api/docker-compose.yml b/agents-api/docker-compose.yml index 150473c3c..96334e1e8 100644 --- a/agents-api/docker-compose.yml +++ b/agents-api/docker-compose.yml @@ -59,9 +59,9 @@ services: container_name: text-embeddings-inference environment: - DTYPE=float16 - - MODEL_ID=BAAI/bge-m3 + - MODEL_ID=Alibaba-NLP/gte-large-en-v1.5 - image: ghcr.io/huggingface/text-embeddings-inference:1.3 + image: ghcr.io/huggingface/text-embeddings-inference:1.5 ports: - "8082:80" volumes: diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index ddfee9708..ecfed6feb 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "aiohappyeyeballs" -version = "2.3.4" +version = "2.3.5" description = "Happy Eyeballs for asyncio" optional = false -python-versions = "<4.0,>=3.8" +python-versions = ">=3.8" files = [ - {file = "aiohappyeyeballs-2.3.4-py3-none-any.whl", hash = "sha256:40a16ceffcf1fc9e142fd488123b2e218abc4188cf12ac20c67200e1579baa42"}, - {file = "aiohappyeyeballs-2.3.4.tar.gz", hash = "sha256:7e1ae8399c320a8adec76f6c919ed5ceae6edd4c3672f4d9eae2b27e37c80ff6"}, + {file = "aiohappyeyeballs-2.3.5-py3-none-any.whl", hash = "sha256:4d6dea59215537dbc746e93e779caea8178c866856a721c9c660d7a5a7b8be03"}, + {file = "aiohappyeyeballs-2.3.5.tar.gz", hash = "sha256:6fa48b9f1317254f122a07a131a86b71ca6946ca989ce6326fff54a99a920105"}, ] [[package]] @@ -186,13 +186,13 @@ files = [ [[package]] name = "argcomplete" -version = "3.4.0" +version = "3.5.0" description = "Bash tab completion for argparse" optional = false python-versions = ">=3.8" files = [ - {file = "argcomplete-3.4.0-py3-none-any.whl", hash = "sha256:69a79e083a716173e5532e0fa3bef45f793f4e61096cf52b5a42c0211c8b8aa5"}, - {file = "argcomplete-3.4.0.tar.gz", hash = "sha256:c2abcdfe1be8ace47ba777d4fce319eb13bf8ad9dace8d085dcad6eded88057f"}, + {file = "argcomplete-3.5.0-py3-none-any.whl", hash = "sha256:d4bcf3ff544f51e16e54228a7ac7f486ed70ebf2ecfe49a63a91171c76bf029b"}, + {file = "argcomplete-3.5.0.tar.gz", hash = "sha256:4349400469dccfb7950bb60334a680c58d88699bff6159df61251878dc6bf74b"}, ] [package.extras] @@ -319,13 +319,13 @@ files = [ [[package]] name = "attrs" -version = "24.1.0" +version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-24.1.0-py3-none-any.whl", hash = "sha256:377b47448cb61fea38533f671fba0d0f8a96fd58facd4dc518e3dac9dbea0905"}, - {file = "attrs-24.1.0.tar.gz", hash = "sha256:adbdec84af72d38be7628e353a09b6a6790d15cd71819f6e9d7b0faa8a125745"}, + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] @@ -338,13 +338,13 @@ tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "babel" -version = "2.15.0" +version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" files = [ - {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, - {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, ] [package.extras] @@ -453,17 +453,6 @@ webencodings = "*" [package.extras] css = ["tinycss2 (>=1.1.0,<1.3)"] -[[package]] -name = "cachetools" -version = "5.4.0" -description = "Extensible memoizing collections and decorators" -optional = false -python-versions = ">=3.7" -files = [ - {file = "cachetools-5.4.0-py3-none-any.whl", hash = "sha256:3ae3b49a3d5e28a77a0be2b37dbcb89005058959cb2323858c2657c4a8cab474"}, - {file = "cachetools-5.4.0.tar.gz", hash = "sha256:b8adc2e7c07f105ced7bc56dbb6dfbe7c4a00acce20e2227b3f355be89bc6827"}, -] - [[package]] name = "certifi" version = "2024.7.4" @@ -477,63 +466,78 @@ files = [ [[package]] name = "cffi" -version = "1.16.0" +version = "1.17.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, + {file = "cffi-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb"}, + {file = "cffi-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b"}, + {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206"}, + {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa"}, + {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f"}, + {file = "cffi-1.17.0-cp310-cp310-win32.whl", hash = "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc"}, + {file = "cffi-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2"}, + {file = "cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720"}, + {file = "cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6"}, + {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91"}, + {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8"}, + {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb"}, + {file = "cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9"}, + {file = "cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0"}, + {file = "cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc"}, + {file = "cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150"}, + {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a"}, + {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885"}, + {file = "cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492"}, + {file = "cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2"}, + {file = "cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118"}, + {file = "cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f"}, + {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0"}, + {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4"}, + {file = "cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a"}, + {file = "cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7"}, + {file = "cffi-1.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c"}, + {file = "cffi-1.17.0-cp38-cp38-win32.whl", hash = "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499"}, + {file = "cffi-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c"}, + {file = "cffi-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2"}, + {file = "cffi-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932"}, + {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693"}, + {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3"}, + {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4"}, + {file = "cffi-1.17.0-cp39-cp39-win32.whl", hash = "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb"}, + {file = "cffi-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29"}, + {file = "cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76"}, ] [package.dependencies] @@ -774,19 +778,19 @@ develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest- [[package]] name = "dask" -version = "2024.7.1" +version = "2024.8.0" description = "Parallel PyData with Task Scheduling" optional = false python-versions = ">=3.9" files = [ - {file = "dask-2024.7.1-py3-none-any.whl", hash = "sha256:dd046840050376c317de90629db5c6197adda820176cf3e2df10c3219d11951f"}, - {file = "dask-2024.7.1.tar.gz", hash = "sha256:dbaef2d50efee841a9d981a218cfeb50392fc9a95e0403b6d680450e4f50d531"}, + {file = "dask-2024.8.0-py3-none-any.whl", hash = "sha256:250ea3df30d4a25958290eec4f252850091c6cfaed82d098179c3b25bba18309"}, + {file = "dask-2024.8.0.tar.gz", hash = "sha256:f1fec39373d2f101bc045529ad4e9b30e34e6eb33b7aa0fa7073aec7b1bf9eee"}, ] [package.dependencies] click = ">=8.1" cloudpickle = ">=1.5.0" -distributed = {version = "2024.7.1", optional = true, markers = "extra == \"distributed\""} +distributed = {version = "2024.8.0", optional = true, markers = "extra == \"distributed\""} fsspec = ">=2021.09.0" importlib-metadata = {version = ">=4.13.0", markers = "python_version < \"3.12\""} packaging = ">=20.0" @@ -799,7 +803,7 @@ array = ["numpy (>=1.21)"] complete = ["dask[array,dataframe,diagnostics,distributed]", "lz4 (>=4.3.2)", "pyarrow (>=7.0)", "pyarrow-hotfix"] dataframe = ["dask-expr (>=1.1,<1.2)", "dask[array]", "pandas (>=2.0)"] diagnostics = ["bokeh (>=2.4.2)", "jinja2 (>=2.10.3)"] -distributed = ["distributed (==2024.7.1)"] +distributed = ["distributed (==2024.8.0)"] test = ["pandas[test]", "pre-commit", "pytest", "pytest-cov", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist"] [[package]] @@ -819,13 +823,13 @@ typing-inspect = ">=0.4.0,<1" [[package]] name = "datamodel-code-generator" -version = "0.25.8" +version = "0.25.9" description = "Datamodel Code Generator" optional = false python-versions = "<4.0,>=3.7" files = [ - {file = "datamodel_code_generator-0.25.8-py3-none-any.whl", hash = "sha256:f9b216efad84d8dcb517273d2728875b6052b7e8dc4e5c13a597441cef236f6e"}, - {file = "datamodel_code_generator-0.25.8.tar.gz", hash = "sha256:b7838122b8133dae6e46f36a1cf25c0ccc66745da057988f490d00ab71121de7"}, + {file = "datamodel_code_generator-0.25.9-py3-none-any.whl", hash = "sha256:9e0324233123d6e39a35bc0004771956935889a974aacfd7a0651de11d2219a9"}, + {file = "datamodel_code_generator-0.25.9.tar.gz", hash = "sha256:65ca9807d8edbd88a7f7931c10f4bc1c08bd9bbc5bb0508418a2b6a16590eb65"}, ] [package.dependencies] @@ -848,33 +852,33 @@ validation = ["openapi-spec-validator (>=0.2.8,<0.7.0)", "prance (>=0.18.2)"] [[package]] name = "debugpy" -version = "1.8.2" +version = "1.8.5" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"}, - {file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"}, - {file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"}, - {file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"}, - {file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"}, - {file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"}, - {file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"}, - {file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"}, - {file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"}, - {file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"}, - {file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"}, - {file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"}, - {file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"}, - {file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"}, - {file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"}, - {file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"}, - {file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"}, - {file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"}, - {file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"}, - {file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"}, - {file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"}, - {file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"}, + {file = "debugpy-1.8.5-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7e4d594367d6407a120b76bdaa03886e9eb652c05ba7f87e37418426ad2079f7"}, + {file = "debugpy-1.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4413b7a3ede757dc33a273a17d685ea2b0c09dbd312cc03f5534a0fd4d40750a"}, + {file = "debugpy-1.8.5-cp310-cp310-win32.whl", hash = "sha256:dd3811bd63632bb25eda6bd73bea8e0521794cda02be41fa3160eb26fc29e7ed"}, + {file = "debugpy-1.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:b78c1250441ce893cb5035dd6f5fc12db968cc07f91cc06996b2087f7cefdd8e"}, + {file = "debugpy-1.8.5-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:606bccba19f7188b6ea9579c8a4f5a5364ecd0bf5a0659c8a5d0e10dcee3032a"}, + {file = "debugpy-1.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db9fb642938a7a609a6c865c32ecd0d795d56c1aaa7a7a5722d77855d5e77f2b"}, + {file = "debugpy-1.8.5-cp311-cp311-win32.whl", hash = "sha256:4fbb3b39ae1aa3e5ad578f37a48a7a303dad9a3d018d369bc9ec629c1cfa7408"}, + {file = "debugpy-1.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:345d6a0206e81eb68b1493ce2fbffd57c3088e2ce4b46592077a943d2b968ca3"}, + {file = "debugpy-1.8.5-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:5b5c770977c8ec6c40c60d6f58cacc7f7fe5a45960363d6974ddb9b62dbee156"}, + {file = "debugpy-1.8.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a65b00b7cdd2ee0c2cf4c7335fef31e15f1b7056c7fdbce9e90193e1a8c8cb"}, + {file = "debugpy-1.8.5-cp312-cp312-win32.whl", hash = "sha256:c9f7c15ea1da18d2fcc2709e9f3d6de98b69a5b0fff1807fb80bc55f906691f7"}, + {file = "debugpy-1.8.5-cp312-cp312-win_amd64.whl", hash = "sha256:28ced650c974aaf179231668a293ecd5c63c0a671ae6d56b8795ecc5d2f48d3c"}, + {file = "debugpy-1.8.5-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:3df6692351172a42af7558daa5019651f898fc67450bf091335aa8a18fbf6f3a"}, + {file = "debugpy-1.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd04a73eb2769eb0bfe43f5bfde1215c5923d6924b9b90f94d15f207a402226"}, + {file = "debugpy-1.8.5-cp38-cp38-win32.whl", hash = "sha256:8f913ee8e9fcf9d38a751f56e6de12a297ae7832749d35de26d960f14280750a"}, + {file = "debugpy-1.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:a697beca97dad3780b89a7fb525d5e79f33821a8bc0c06faf1f1289e549743cf"}, + {file = "debugpy-1.8.5-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:0a1029a2869d01cb777216af8c53cda0476875ef02a2b6ff8b2f2c9a4b04176c"}, + {file = "debugpy-1.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84c276489e141ed0b93b0af648eef891546143d6a48f610945416453a8ad406"}, + {file = "debugpy-1.8.5-cp39-cp39-win32.whl", hash = "sha256:ad84b7cde7fd96cf6eea34ff6c4a1b7887e0fe2ea46e099e53234856f9d99a34"}, + {file = "debugpy-1.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:7b0fe36ed9d26cb6836b0a51453653f8f2e347ba7348f2bbfe76bfeb670bfb1c"}, + {file = "debugpy-1.8.5-py2.py3-none-any.whl", hash = "sha256:55919dce65b471eff25901acf82d328bbd5b833526b6c1364bd5133754777a44"}, + {file = "debugpy-1.8.5.zip", hash = "sha256:b2112cfeb34b4507399d298fe7023a16656fc553ed5246536060ca7bd0e668d0"}, ] [[package]] @@ -912,19 +916,19 @@ files = [ [[package]] name = "distributed" -version = "2024.7.1" +version = "2024.8.0" description = "Distributed scheduler for Dask" optional = false python-versions = ">=3.9" files = [ - {file = "distributed-2024.7.1-py3-none-any.whl", hash = "sha256:d5ac38d9682c191e6582c86ebf37c10d7adb60bf4a95048a05ae4fb0866119bc"}, - {file = "distributed-2024.7.1.tar.gz", hash = "sha256:7bce7fa745163b55bdd67fd632b3edf57b31827640390b92d0ee3f73436429d3"}, + {file = "distributed-2024.8.0-py3-none-any.whl", hash = "sha256:11af55d22dd6e04eb868b87f166b8f59ef1b300f659f87c016643b7f98280ec6"}, + {file = "distributed-2024.8.0.tar.gz", hash = "sha256:b99caf0a7f257f59477a70a334e081c1241f7cd9860211cc669742e6450e1310"}, ] [package.dependencies] click = ">=8.0" cloudpickle = ">=1.5.0" -dask = "2024.7.1" +dask = "2024.8.0" jinja2 = ">=2.10.3" locket = ">=1.0.0" msgpack = ">=1.0.0" @@ -969,17 +973,6 @@ idna = ["idna (>=3.6)"] trio = ["trio (>=0.23)"] wmi = ["wmi (>=1.5.1)"] -[[package]] -name = "docstring-parser" -version = "0.16" -description = "Parse Python docstrings in reST, Google and Numpydoc format" -optional = false -python-versions = ">=3.6,<4.0" -files = [ - {file = "docstring_parser-0.16-py3-none-any.whl", hash = "sha256:bf0a1387354d3691d102edef7ec124f219ef639982d096e26e3b60aeffa90637"}, - {file = "docstring_parser-0.16.tar.gz", hash = "sha256:538beabd0af1e2db0146b6bd3caa526c35a34d61af9fd2887f3a8a27a739aa6e"}, -] - [[package]] name = "email-validator" version = "2.2.0" @@ -1292,308 +1285,6 @@ files = [ {file = "genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37"}, ] -[[package]] -name = "google-api-core" -version = "2.19.1" -description = "Google API client core library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-api-core-2.19.1.tar.gz", hash = "sha256:f4695f1e3650b316a795108a76a1c416e6afb036199d1c1f1f110916df479ffd"}, - {file = "google_api_core-2.19.1-py3-none-any.whl", hash = "sha256:f12a9b8309b5e21d92483bbd47ce2c445861ec7d269ef6784ecc0ea8c1fa6125"}, -] - -[package.dependencies] -google-auth = ">=2.14.1,<3.0.dev0" -googleapis-common-protos = ">=1.56.2,<2.0.dev0" -grpcio = {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""} -grpcio-status = {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "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,<6.0.0.dev0" -requests = ">=2.18.0,<3.0.0.dev0" - -[package.extras] -grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio-status (>=1.49.1,<2.0.dev0)"] -grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] -grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] - -[[package]] -name = "google-auth" -version = "2.32.0" -description = "Google Authentication Library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google_auth-2.32.0-py2.py3-none-any.whl", hash = "sha256:53326ea2ebec768070a94bee4e1b9194c9646ea0c2bd72422785bd0f9abfad7b"}, - {file = "google_auth-2.32.0.tar.gz", hash = "sha256:49315be72c55a6a37d62819e3573f6b416aca00721f7e3e31a008d928bf64022"}, -] - -[package.dependencies] -cachetools = ">=2.0.0,<6.0" -pyasn1-modules = ">=0.2.1" -rsa = ">=3.1.4,<5" - -[package.extras] -aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] -enterprise-cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] -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-cloud-aiplatform" -version = "1.60.0" -description = "Vertex AI API client library" -optional = false -python-versions = ">=3.8" -files = [ - {file = "google-cloud-aiplatform-1.60.0.tar.gz", hash = "sha256:782c7f1ec0e77a7c7daabef3b65bfd506ed2b4b1dc2186753c43cd6faf8dd04e"}, - {file = "google_cloud_aiplatform-1.60.0-py2.py3-none-any.whl", hash = "sha256:5f14159c9575f4b46335027e3ceb8fa57bd5eaa76a07f858105b8c6c034ec0d6"}, -] - -[package.dependencies] -docstring-parser = "<1" -google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.8.dev0,<3.0.0dev", extras = ["grpc"]} -google-auth = ">=2.14.1,<3.0.0dev" -google-cloud-bigquery = ">=1.15.0,<3.20.0 || >3.20.0,<4.0.0dev" -google-cloud-resource-manager = ">=1.3.3,<3.0.0dev" -google-cloud-storage = ">=1.32.0,<3.0.0dev" -packaging = ">=14.3" -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" -pydantic = "<3" -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 (>=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.109.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 (>=14.0.0)", "pyarrow (>=3.0.0,<8.0dev)", "pyarrow (>=6.0.1)", "pydantic (<2)", "pyyaml (>=5.3.1,<7)", "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)", "setuptools (<70.0.0)", "starlette (>=0.17.1)", "tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)"] -langchain = ["langchain (>=0.1.16,<0.3)", "langchain-core (<0.3)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "tenacity (<=8.3)"] -langchain-testing = ["absl-py", "cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "langchain (>=0.1.16,<0.3)", "langchain-core (<0.3)", "langchain-google-vertexai (<2)", "openinference-instrumentation-langchain (>=0.1.19,<0.2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)", "pytest-xdist", "tenacity (<=8.3)"] -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,<7)"] -prediction = ["docker (>=5.0.3)", "fastapi (>=0.71.0,<=0.109.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 = ["pandas (>=1.0.0,<2.2.0)", "tqdm (>=4.23.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)", "setuptools (<70.0.0)"] -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.9.3)", "scikit-learn", "setuptools (<70.0.0)", "tensorflow", "torch (>=2.0.0,<2.1.0)", "xgboost", "xgboost-ray"] -reasoningengine = ["cloudpickle (>=3.0,<4.0)", "google-cloud-trace (<2)", "opentelemetry-exporter-gcp-trace (<2)", "opentelemetry-sdk (<2)", "pydantic (>=2.6.3,<3)"] -tensorboard = ["tensorboard-plugin-profile (>=2.4.0,<3.0.0dev)", "tensorflow (>=2.3.0,<3.0.0dev)", "tensorflow (>=2.4.0,<3.0.0dev)", "werkzeug (>=2.0.0,<2.1.0dev)"] -testing = ["bigframes", "cloudpickle (<3.0)", "docker (>=5.0.3)", "explainable-ai-sdk (>=1.0.0)", "fastapi (>=0.71.0,<=0.109.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)", "nltk", "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)", "pyfakefs", "pytest-asyncio", "pytest-xdist", "pyyaml (>=5.3.1,<7)", "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", "sentencepiece (>=0.2.0)", "setuptools (<70.0.0)", "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,<3.0.0dev)", "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)", "tqdm (>=4.23.0)", "urllib3 (>=1.21.1,<1.27)", "uvicorn[standard] (>=0.16.0)", "werkzeug (>=2.0.0,<2.1.0dev)", "xgboost"] -tokenization = ["sentencepiece (>=0.2.0)"] -vizier = ["google-vizier (>=0.1.6)"] -xai = ["tensorflow (>=2.3.0,<3.0.0dev)"] - -[[package]] -name = "google-cloud-bigquery" -version = "3.25.0" -description = "Google BigQuery API client library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-bigquery-3.25.0.tar.gz", hash = "sha256:5b2aff3205a854481117436836ae1403f11f2594e6810a98886afd57eda28509"}, - {file = "google_cloud_bigquery-3.25.0-py2.py3-none-any.whl", hash = "sha256:7f0c371bc74d2a7fb74dacbc00ac0f90c8c2bec2289b51dd6685a275873b1ce9"}, -] - -[package.dependencies] -google-api-core = {version = ">=1.34.1,<2.0.dev0 || >=2.11.dev0,<3.0.0dev", extras = ["grpc"]} -google-auth = ">=2.14.1,<3.0.0dev" -google-cloud-core = ">=1.6.0,<3.0.0dev" -google-resumable-media = ">=0.6.0,<3.0dev" -packaging = ">=20.0.0" -python-dateutil = ">=2.7.2,<3.0dev" -requests = ">=2.21.0,<3.0.0dev" - -[package.extras] -all = ["Shapely (>=1.8.4,<3.0.0dev)", "db-dtypes (>=0.3.0,<2.0.0dev)", "geopandas (>=0.9.0,<1.0dev)", "google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "importlib-metadata (>=1.0.0)", "ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)", "ipywidgets (>=7.7.0)", "opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)", "pandas (>=1.1.0)", "proto-plus (>=1.15.0,<2.0.0dev)", "protobuf (>=3.19.5,!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev)", "pyarrow (>=3.0.0)", "tqdm (>=4.7.4,<5.0.0dev)"] -bigquery-v2 = ["proto-plus (>=1.15.0,<2.0.0dev)", "protobuf (>=3.19.5,!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev)"] -bqstorage = ["google-cloud-bigquery-storage (>=2.6.0,<3.0.0dev)", "grpcio (>=1.47.0,<2.0dev)", "grpcio (>=1.49.1,<2.0dev)", "pyarrow (>=3.0.0)"] -geopandas = ["Shapely (>=1.8.4,<3.0.0dev)", "geopandas (>=0.9.0,<1.0dev)"] -ipython = ["ipykernel (>=6.0.0)", "ipython (>=7.23.1,!=8.1.0)"] -ipywidgets = ["ipykernel (>=6.0.0)", "ipywidgets (>=7.7.0)"] -opentelemetry = ["opentelemetry-api (>=1.1.0)", "opentelemetry-instrumentation (>=0.20b0)", "opentelemetry-sdk (>=1.1.0)"] -pandas = ["db-dtypes (>=0.3.0,<2.0.0dev)", "importlib-metadata (>=1.0.0)", "pandas (>=1.1.0)", "pyarrow (>=3.0.0)"] -tqdm = ["tqdm (>=4.7.4,<5.0.0dev)"] - -[[package]] -name = "google-cloud-core" -version = "2.4.1" -description = "Google Cloud API client core library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-cloud-core-2.4.1.tar.gz", hash = "sha256:9b7749272a812bde58fff28868d0c5e2f585b82f37e09a1f6ed2d4d10f134073"}, - {file = "google_cloud_core-2.4.1-py2.py3-none-any.whl", hash = "sha256:a9e6a4422b9ac5c29f79a0ede9485473338e2ce78d91f2370c01e730eab22e61"}, -] - -[package.dependencies] -google-api-core = ">=1.31.6,<2.0.dev0 || >2.3.0,<3.0.0dev" -google-auth = ">=1.25.0,<3.0dev" - -[package.extras] -grpc = ["grpcio (>=1.38.0,<2.0dev)", "grpcio-status (>=1.38.0,<2.0.dev0)"] - -[[package]] -name = "google-cloud-resource-manager" -version = "1.12.5" -description = "Google Cloud Resource Manager API client library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google_cloud_resource_manager-1.12.5-py2.py3-none-any.whl", hash = "sha256:2708a718b45c79464b7b21559c701b5c92e6b0b1ab2146d0a256277a623dc175"}, - {file = "google_cloud_resource_manager-1.12.5.tar.gz", hash = "sha256:b7af4254401ed4efa3aba3a929cb3ddb803fa6baf91a78485e45583597de5891"}, -] - -[package.dependencies] -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" -grpc-google-iam-v1 = ">=0.12.4,<1.0.0dev" -proto-plus = ">=1.22.3,<2.0.0dev" -protobuf = ">=3.20.2,<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,<6.0.0dev" - -[[package]] -name = "google-cloud-storage" -version = "2.18.0" -description = "Google Cloud Storage API client library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google_cloud_storage-2.18.0-py2.py3-none-any.whl", hash = "sha256:e8e1a9577952143c3fca8163005ecfadd2d70ec080fa158a8b305000e2c22fbb"}, - {file = "google_cloud_storage-2.18.0.tar.gz", hash = "sha256:0aa3f7c57f3632f81b455d91558d2b27ada96eee2de3aaa17f689db1470d9578"}, -] - -[package.dependencies] -google-api-core = ">=2.15.0,<3.0.0dev" -google-auth = ">=2.26.1,<3.0dev" -google-cloud-core = ">=2.3.0,<3.0dev" -google-crc32c = ">=1.0,<2.0dev" -google-resumable-media = ">=2.6.0" -requests = ">=2.18.0,<3.0.0dev" - -[package.extras] -protobuf = ["protobuf (<6.0.0dev)"] -tracing = ["opentelemetry-api (>=1.1.0)"] - -[[package]] -name = "google-crc32c" -version = "1.5.0" -description = "A python wrapper of the C library 'Google CRC32C'" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-crc32c-1.5.0.tar.gz", hash = "sha256:89284716bc6a5a415d4eaa11b1726d2d60a0cd12aadf5439828353662ede9dd7"}, - {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:596d1f98fc70232fcb6590c439f43b350cb762fb5d61ce7b0e9db4539654cc13"}, - {file = "google_crc32c-1.5.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:be82c3c8cfb15b30f36768797a640e800513793d6ae1724aaaafe5bf86f8f346"}, - {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:461665ff58895f508e2866824a47bdee72497b091c730071f2b7575d5762ab65"}, - {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e2096eddb4e7c7bdae4bd69ad364e55e07b8316653234a56552d9c988bd2d61b"}, - {file = "google_crc32c-1.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:116a7c3c616dd14a3de8c64a965828b197e5f2d121fedd2f8c5585c547e87b02"}, - {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5829b792bf5822fd0a6f6eb34c5f81dd074f01d570ed7f36aa101d6fc7a0a6e4"}, - {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:64e52e2b3970bd891309c113b54cf0e4384762c934d5ae56e283f9a0afcd953e"}, - {file = "google_crc32c-1.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:02ebb8bf46c13e36998aeaad1de9b48f4caf545e91d14041270d9dca767b780c"}, - {file = "google_crc32c-1.5.0-cp310-cp310-win32.whl", hash = "sha256:2e920d506ec85eb4ba50cd4228c2bec05642894d4c73c59b3a2fe20346bd00ee"}, - {file = "google_crc32c-1.5.0-cp310-cp310-win_amd64.whl", hash = "sha256:07eb3c611ce363c51a933bf6bd7f8e3878a51d124acfc89452a75120bc436289"}, - {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:cae0274952c079886567f3f4f685bcaf5708f0a23a5f5216fdab71f81a6c0273"}, - {file = "google_crc32c-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1034d91442ead5a95b5aaef90dbfaca8633b0247d1e41621d1e9f9db88c36298"}, - {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c42c70cd1d362284289c6273adda4c6af8039a8ae12dc451dcd61cdabb8ab57"}, - {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8485b340a6a9e76c62a7dce3c98e5f102c9219f4cfbf896a00cf48caf078d438"}, - {file = "google_crc32c-1.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:77e2fd3057c9d78e225fa0a2160f96b64a824de17840351b26825b0848022906"}, - {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f583edb943cf2e09c60441b910d6a20b4d9d626c75a36c8fcac01a6c96c01183"}, - {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:a1fd716e7a01f8e717490fbe2e431d2905ab8aa598b9b12f8d10abebb36b04dd"}, - {file = "google_crc32c-1.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:72218785ce41b9cfd2fc1d6a017dc1ff7acfc4c17d01053265c41a2c0cc39b8c"}, - {file = "google_crc32c-1.5.0-cp311-cp311-win32.whl", hash = "sha256:66741ef4ee08ea0b2cc3c86916ab66b6aef03768525627fd6a1b34968b4e3709"}, - {file = "google_crc32c-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba1eb1843304b1e5537e1fca632fa894d6f6deca8d6389636ee5b4797affb968"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:98cb4d057f285bd80d8778ebc4fde6b4d509ac3f331758fb1528b733215443ae"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd8536e902db7e365f49e7d9029283403974ccf29b13fc7028b97e2295b33556"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19e0a019d2c4dcc5e598cd4a4bc7b008546b0358bd322537c74ad47a5386884f"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02c65b9817512edc6a4ae7c7e987fea799d2e0ee40c53ec573a692bee24de876"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6ac08d24c1f16bd2bf5eca8eaf8304812f44af5cfe5062006ec676e7e1d50afc"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:3359fc442a743e870f4588fcf5dcbc1bf929df1fad8fb9905cd94e5edb02e84c"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:1e986b206dae4476f41bcec1faa057851f3889503a70e1bdb2378d406223994a"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:de06adc872bcd8c2a4e0dc51250e9e65ef2ca91be023b9d13ebd67c2ba552e1e"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-win32.whl", hash = "sha256:d3515f198eaa2f0ed49f8819d5732d70698c3fa37384146079b3799b97667a94"}, - {file = "google_crc32c-1.5.0-cp37-cp37m-win_amd64.whl", hash = "sha256:67b741654b851abafb7bc625b6d1cdd520a379074e64b6a128e3b688c3c04740"}, - {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c02ec1c5856179f171e032a31d6f8bf84e5a75c45c33b2e20a3de353b266ebd8"}, - {file = "google_crc32c-1.5.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edfedb64740750e1a3b16152620220f51d58ff1b4abceb339ca92e934775c27a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84e6e8cd997930fc66d5bb4fde61e2b62ba19d62b7abd7a69920406f9ecca946"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:024894d9d3cfbc5943f8f230e23950cd4906b2fe004c72e29b209420a1e6b05a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:998679bf62b7fb599d2878aa3ed06b9ce688b8974893e7223c60db155f26bd8d"}, - {file = "google_crc32c-1.5.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:83c681c526a3439b5cf94f7420471705bbf96262f49a6fe546a6db5f687a3d4a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:4c6fdd4fccbec90cc8a01fc00773fcd5fa28db683c116ee3cb35cd5da9ef6c37"}, - {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5ae44e10a8e3407dbe138984f21e536583f2bba1be9491239f942c2464ac0894"}, - {file = "google_crc32c-1.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37933ec6e693e51a5b07505bd05de57eee12f3e8c32b07da7e73669398e6630a"}, - {file = "google_crc32c-1.5.0-cp38-cp38-win32.whl", hash = "sha256:fe70e325aa68fa4b5edf7d1a4b6f691eb04bbccac0ace68e34820d283b5f80d4"}, - {file = "google_crc32c-1.5.0-cp38-cp38-win_amd64.whl", hash = "sha256:74dea7751d98034887dbd821b7aae3e1d36eda111d6ca36c206c44478035709c"}, - {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c6c777a480337ac14f38564ac88ae82d4cd238bf293f0a22295b66eb89ffced7"}, - {file = "google_crc32c-1.5.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:759ce4851a4bb15ecabae28f4d2e18983c244eddd767f560165563bf9aefbc8d"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f13cae8cc389a440def0c8c52057f37359014ccbc9dc1f0827936bcd367c6100"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e560628513ed34759456a416bf86b54b2476c59144a9138165c9a1575801d0d9"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1674e4307fa3024fc897ca774e9c7562c957af85df55efe2988ed9056dc4e57"}, - {file = "google_crc32c-1.5.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:278d2ed7c16cfc075c91378c4f47924c0625f5fc84b2d50d921b18b7975bd210"}, - {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d5280312b9af0976231f9e317c20e4a61cd2f9629b7bfea6a693d1878a264ebd"}, - {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8b87e1a59c38f275c0e3676fc2ab6d59eccecfd460be267ac360cc31f7bcde96"}, - {file = "google_crc32c-1.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c074fece789b5034b9b1404a1f8208fc2d4c6ce9decdd16e8220c5a793e6f61"}, - {file = "google_crc32c-1.5.0-cp39-cp39-win32.whl", hash = "sha256:7f57f14606cd1dd0f0de396e1e53824c371e9544a822648cd76c034d209b559c"}, - {file = "google_crc32c-1.5.0-cp39-cp39-win_amd64.whl", hash = "sha256:a2355cba1f4ad8b6988a4ca3feed5bff33f6af2d7f134852cf279c2aebfde541"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f314013e7dcd5cf45ab1945d92e713eec788166262ae8deb2cfacd53def27325"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b747a674c20a67343cb61d43fdd9207ce5da6a99f629c6e2541aa0e89215bcd"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f24ed114432de109aa9fd317278518a5af2d31ac2ea6b952b2f7782b43da091"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8667b48e7a7ef66afba2c81e1094ef526388d35b873966d8a9a447974ed9178"}, - {file = "google_crc32c-1.5.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:1c7abdac90433b09bad6c43a43af253e688c9cfc1c86d332aed13f9a7c7f65e2"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6f998db4e71b645350b9ac28a2167e6632c239963ca9da411523bb439c5c514d"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c99616c853bb585301df6de07ca2cadad344fd1ada6d62bb30aec05219c45d2"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ad40e31093a4af319dadf503b2467ccdc8f67c72e4bcba97f8c10cb078207b5"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd67cf24a553339d5062eff51013780a00d6f97a39ca062781d06b3a73b15462"}, - {file = "google_crc32c-1.5.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:398af5e3ba9cf768787eef45c803ff9614cc3e22a5b2f7d7ae116df8b11e3314"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b1f8133c9a275df5613a451e73f36c2aea4fe13c5c8997e22cf355ebd7bd0728"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ba053c5f50430a3fcfd36f75aff9caeba0440b2d076afdb79a318d6ca245f88"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:272d3892a1e1a2dbc39cc5cde96834c236d5327e2122d3aaa19f6614531bb6eb"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:635f5d4dd18758a1fbd1049a8e8d2fee4ffed124462d837d1a02a0e009c3ab31"}, - {file = "google_crc32c-1.5.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c672d99a345849301784604bfeaeba4db0c7aae50b95be04dd651fd2a7310b93"}, -] - -[package.extras] -testing = ["pytest"] - -[[package]] -name = "google-resumable-media" -version = "2.7.1" -description = "Utilities for Google Media Downloads and Resumable Uploads" -optional = false -python-versions = ">=3.7" -files = [ - {file = "google-resumable-media-2.7.1.tar.gz", hash = "sha256:eae451a7b2e2cdbaaa0fd2eb00cc8a1ee5e95e16b55597359cbc3d27d7d90e33"}, - {file = "google_resumable_media-2.7.1-py2.py3-none-any.whl", hash = "sha256:103ebc4ba331ab1bfdac0250f8033627a2cd7cde09e7ccff9181e31ba4315b2c"}, -] - -[package.dependencies] -google-crc32c = ">=1.0,<2.0dev" - -[package.extras] -aiohttp = ["aiohttp (>=3.6.2,<4.0.0dev)", "google-auth (>=1.22.0,<2.0dev)"] -requests = ["requests (>=2.18.0,<3.0.0dev)"] - -[[package]] -name = "googleapis-common-protos" -version = "1.63.2" -description = "Common protobufs used in Google APIs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "googleapis-common-protos-1.63.2.tar.gz", hash = "sha256:27c5abdffc4911f28101e635de1533fb4cfd2c37fbaa9174587c799fac90aa87"}, - {file = "googleapis_common_protos-1.63.2-py2.py3-none-any.whl", hash = "sha256:27a2499c7e8aff199665b22741997e485eccc8645aa9176c7c988e6fae507945"}, -] - -[package.dependencies] -grpcio = {version = ">=1.44.0,<2.0.0.dev0", optional = true, markers = "extra == \"grpc\""} -protobuf = ">=3.20.2,<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,<6.0.0.dev0" - -[package.extras] -grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] - [[package]] name = "greenlet" version = "3.0.3" @@ -1665,96 +1356,6 @@ files = [ docs = ["Sphinx", "furo"] test = ["objgraph", "psutil"] -[[package]] -name = "grpc-google-iam-v1" -version = "0.13.1" -description = "IAM API client library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "grpc-google-iam-v1-0.13.1.tar.gz", hash = "sha256:3ff4b2fd9d990965e410965253c0da6f66205d5a8291c4c31c6ebecca18a9001"}, - {file = "grpc_google_iam_v1-0.13.1-py2.py3-none-any.whl", hash = "sha256:c3e86151a981811f30d5e7330f271cee53e73bb87755e88cc3b6f0c7b5fe374e"}, -] - -[package.dependencies] -googleapis-common-protos = {version = ">=1.56.0,<2.0.0dev", extras = ["grpc"]} -grpcio = ">=1.44.0,<2.0.0dev" -protobuf = ">=3.20.2,<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,<6.0.0dev" - -[[package]] -name = "grpcio" -version = "1.65.4" -description = "HTTP/2-based RPC framework" -optional = false -python-versions = ">=3.8" -files = [ - {file = "grpcio-1.65.4-cp310-cp310-linux_armv7l.whl", hash = "sha256:0e85c8766cf7f004ab01aff6a0393935a30d84388fa3c58d77849fcf27f3e98c"}, - {file = "grpcio-1.65.4-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:e4a795c02405c7dfa8affd98c14d980f4acea16ea3b539e7404c645329460e5a"}, - {file = "grpcio-1.65.4-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:d7b984a8dd975d949c2042b9b5ebcf297d6d5af57dcd47f946849ee15d3c2fb8"}, - {file = "grpcio-1.65.4-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:644a783ce604a7d7c91412bd51cf9418b942cf71896344b6dc8d55713c71ce82"}, - {file = "grpcio-1.65.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5764237d751d3031a36fafd57eb7d36fd2c10c658d2b4057c516ccf114849a3e"}, - {file = "grpcio-1.65.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ee40d058cf20e1dd4cacec9c39e9bce13fedd38ce32f9ba00f639464fcb757de"}, - {file = "grpcio-1.65.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4482a44ce7cf577a1f8082e807a5b909236bce35b3e3897f839f2fbd9ae6982d"}, - {file = "grpcio-1.65.4-cp310-cp310-win32.whl", hash = "sha256:66bb051881c84aa82e4f22d8ebc9d1704b2e35d7867757f0740c6ef7b902f9b1"}, - {file = "grpcio-1.65.4-cp310-cp310-win_amd64.whl", hash = "sha256:870370524eff3144304da4d1bbe901d39bdd24f858ce849b7197e530c8c8f2ec"}, - {file = "grpcio-1.65.4-cp311-cp311-linux_armv7l.whl", hash = "sha256:85e9c69378af02e483bc626fc19a218451b24a402bdf44c7531e4c9253fb49ef"}, - {file = "grpcio-1.65.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2bd672e005afab8bf0d6aad5ad659e72a06dd713020554182a66d7c0c8f47e18"}, - {file = "grpcio-1.65.4-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:abccc5d73f5988e8f512eb29341ed9ced923b586bb72e785f265131c160231d8"}, - {file = "grpcio-1.65.4-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:886b45b29f3793b0c2576201947258782d7e54a218fe15d4a0468d9a6e00ce17"}, - {file = "grpcio-1.65.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be952436571dacc93ccc7796db06b7daf37b3b56bb97e3420e6503dccfe2f1b4"}, - {file = "grpcio-1.65.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:8dc9ddc4603ec43f6238a5c95400c9a901b6d079feb824e890623da7194ff11e"}, - {file = "grpcio-1.65.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ade1256c98cba5a333ef54636095f2c09e6882c35f76acb04412f3b1aa3c29a5"}, - {file = "grpcio-1.65.4-cp311-cp311-win32.whl", hash = "sha256:280e93356fba6058cbbfc6f91a18e958062ef1bdaf5b1caf46c615ba1ae71b5b"}, - {file = "grpcio-1.65.4-cp311-cp311-win_amd64.whl", hash = "sha256:d2b819f9ee27ed4e3e737a4f3920e337e00bc53f9e254377dd26fc7027c4d558"}, - {file = "grpcio-1.65.4-cp312-cp312-linux_armv7l.whl", hash = "sha256:926a0750a5e6fb002542e80f7fa6cab8b1a2ce5513a1c24641da33e088ca4c56"}, - {file = "grpcio-1.65.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2a1d4c84d9e657f72bfbab8bedf31bdfc6bfc4a1efb10b8f2d28241efabfaaf2"}, - {file = "grpcio-1.65.4-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:17de4fda50967679677712eec0a5c13e8904b76ec90ac845d83386b65da0ae1e"}, - {file = "grpcio-1.65.4-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3dee50c1b69754a4228e933696408ea87f7e896e8d9797a3ed2aeed8dbd04b74"}, - {file = "grpcio-1.65.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74c34fc7562bdd169b77966068434a93040bfca990e235f7a67cdf26e1bd5c63"}, - {file = "grpcio-1.65.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:24a2246e80a059b9eb981e4c2a6d8111b1b5e03a44421adbf2736cc1d4988a8a"}, - {file = "grpcio-1.65.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:18c10f0d054d2dce34dd15855fcca7cc44ec3b811139437543226776730c0f28"}, - {file = "grpcio-1.65.4-cp312-cp312-win32.whl", hash = "sha256:d72962788b6c22ddbcdb70b10c11fbb37d60ae598c51eb47ec019db66ccfdff0"}, - {file = "grpcio-1.65.4-cp312-cp312-win_amd64.whl", hash = "sha256:7656376821fed8c89e68206a522522317787a3d9ed66fb5110b1dff736a5e416"}, - {file = "grpcio-1.65.4-cp38-cp38-linux_armv7l.whl", hash = "sha256:4934077b33aa6fe0b451de8b71dabde96bf2d9b4cb2b3187be86e5adebcba021"}, - {file = "grpcio-1.65.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0cef8c919a3359847c357cb4314e50ed1f0cca070f828ee8f878d362fd744d52"}, - {file = "grpcio-1.65.4-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:a925446e6aa12ca37114840d8550f308e29026cdc423a73da3043fd1603a6385"}, - {file = "grpcio-1.65.4-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf53e6247f1e2af93657e62e240e4f12e11ee0b9cef4ddcb37eab03d501ca864"}, - {file = "grpcio-1.65.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cdb34278e4ceb224c89704cd23db0d902e5e3c1c9687ec9d7c5bb4c150f86816"}, - {file = "grpcio-1.65.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e6cbdd107e56bde55c565da5fd16f08e1b4e9b0674851d7749e7f32d8645f524"}, - {file = "grpcio-1.65.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:626319a156b1f19513156a3b0dbfe977f5f93db63ca673a0703238ebd40670d7"}, - {file = "grpcio-1.65.4-cp38-cp38-win32.whl", hash = "sha256:3d1bbf7e1dd1096378bd83c83f554d3b93819b91161deaf63e03b7022a85224a"}, - {file = "grpcio-1.65.4-cp38-cp38-win_amd64.whl", hash = "sha256:a99e6dffefd3027b438116f33ed1261c8d360f0dd4f943cb44541a2782eba72f"}, - {file = "grpcio-1.65.4-cp39-cp39-linux_armv7l.whl", hash = "sha256:874acd010e60a2ec1e30d5e505b0651ab12eb968157cd244f852b27c6dbed733"}, - {file = "grpcio-1.65.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b07f36faf01fca5427d4aa23645e2d492157d56c91fab7e06fe5697d7e171ad4"}, - {file = "grpcio-1.65.4-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:b81711bf4ec08a3710b534e8054c7dcf90f2edc22bebe11c1775a23f145595fe"}, - {file = "grpcio-1.65.4-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88fcabc332a4aef8bcefadc34a02e9ab9407ab975d2c7d981a8e12c1aed92aa1"}, - {file = "grpcio-1.65.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9ba3e63108a8749994f02c7c0e156afb39ba5bdf755337de8e75eb685be244b"}, - {file = "grpcio-1.65.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:8eb485801957a486bf5de15f2c792d9f9c897a86f2f18db8f3f6795a094b4bb2"}, - {file = "grpcio-1.65.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:075f3903bc1749ace93f2b0664f72964ee5f2da5c15d4b47e0ab68e4f442c257"}, - {file = "grpcio-1.65.4-cp39-cp39-win32.whl", hash = "sha256:0a0720299bdb2cc7306737295d56e41ce8827d5669d4a3cd870af832e3b17c4d"}, - {file = "grpcio-1.65.4-cp39-cp39-win_amd64.whl", hash = "sha256:a146bc40fa78769f22e1e9ff4f110ef36ad271b79707577bf2a31e3e931141b9"}, - {file = "grpcio-1.65.4.tar.gz", hash = "sha256:2a4f476209acffec056360d3e647ae0e14ae13dcf3dfb130c227ae1c594cbe39"}, -] - -[package.extras] -protobuf = ["grpcio-tools (>=1.65.4)"] - -[[package]] -name = "grpcio-status" -version = "1.62.2" -description = "Status proto mapping for gRPC" -optional = false -python-versions = ">=3.6" -files = [ - {file = "grpcio-status-1.62.2.tar.gz", hash = "sha256:62e1bfcb02025a1cd73732a2d33672d3e9d0df4d21c12c51e0bbcaf09bab742a"}, - {file = "grpcio_status-1.62.2-py3-none-any.whl", hash = "sha256:206ddf0eb36bc99b033f03b2c8e95d319f0044defae9b41ae21408e7e0cda48f"}, -] - -[package.dependencies] -googleapis-common-protos = ">=1.5.5" -grpcio = ">=1.62.2" -protobuf = ">=4.21.6" - [[package]] name = "h11" version = "0.14.0" @@ -2084,6 +1685,76 @@ files = [ [package.dependencies] Jinja2 = ">=2.2" +[[package]] +name = "jiter" +version = "0.5.0" +description = "Fast iterable JSON parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jiter-0.5.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b599f4e89b3def9a94091e6ee52e1d7ad7bc33e238ebb9c4c63f211d74822c3f"}, + {file = "jiter-0.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a063f71c4b06225543dddadbe09d203dc0c95ba352d8b85f1221173480a71d5"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc0d5b8b3dd12e91dd184b87273f864b363dfabc90ef29a1092d269f18c7e28"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c22541f0b672f4d741382a97c65609332a783501551445ab2df137ada01e019e"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63314832e302cc10d8dfbda0333a384bf4bcfce80d65fe99b0f3c0da8945a91a"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a25fbd8a5a58061e433d6fae6d5298777c0814a8bcefa1e5ecfff20c594bd749"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:503b2c27d87dfff5ab717a8200fbbcf4714516c9d85558048b1fc14d2de7d8dc"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d1f3d27cce923713933a844872d213d244e09b53ec99b7a7fdf73d543529d6d"}, + {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c95980207b3998f2c3b3098f357994d3fd7661121f30669ca7cb945f09510a87"}, + {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afa66939d834b0ce063f57d9895e8036ffc41c4bd90e4a99631e5f261d9b518e"}, + {file = "jiter-0.5.0-cp310-none-win32.whl", hash = "sha256:f16ca8f10e62f25fd81d5310e852df6649af17824146ca74647a018424ddeccf"}, + {file = "jiter-0.5.0-cp310-none-win_amd64.whl", hash = "sha256:b2950e4798e82dd9176935ef6a55cf6a448b5c71515a556da3f6b811a7844f1e"}, + {file = "jiter-0.5.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4c8e1ed0ef31ad29cae5ea16b9e41529eb50a7fba70600008e9f8de6376d553"}, + {file = "jiter-0.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6f16e21276074a12d8421692515b3fd6d2ea9c94fd0734c39a12960a20e85f3"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5280e68e7740c8c128d3ae5ab63335ce6d1fb6603d3b809637b11713487af9e6"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:583c57fc30cc1fec360e66323aadd7fc3edeec01289bfafc35d3b9dcb29495e4"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26351cc14507bdf466b5f99aba3df3143a59da75799bf64a53a3ad3155ecded9"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829df14d656b3fb87e50ae8b48253a8851c707da9f30d45aacab2aa2ba2d614"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42a4bdcf7307b86cb863b2fb9bb55029b422d8f86276a50487982d99eed7c6e"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04d461ad0aebf696f8da13c99bc1b3e06f66ecf6cfd56254cc402f6385231c06"}, + {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6375923c5f19888c9226582a124b77b622f8fd0018b843c45eeb19d9701c403"}, + {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cec323a853c24fd0472517113768c92ae0be8f8c384ef4441d3632da8baa646"}, + {file = "jiter-0.5.0-cp311-none-win32.whl", hash = "sha256:aa1db0967130b5cab63dfe4d6ff547c88b2a394c3410db64744d491df7f069bb"}, + {file = "jiter-0.5.0-cp311-none-win_amd64.whl", hash = "sha256:aa9d2b85b2ed7dc7697597dcfaac66e63c1b3028652f751c81c65a9f220899ae"}, + {file = "jiter-0.5.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9f664e7351604f91dcdd557603c57fc0d551bc65cc0a732fdacbf73ad335049a"}, + {file = "jiter-0.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:044f2f1148b5248ad2c8c3afb43430dccf676c5a5834d2f5089a4e6c5bbd64df"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:702e3520384c88b6e270c55c772d4bd6d7b150608dcc94dea87ceba1b6391248"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:528d742dcde73fad9d63e8242c036ab4a84389a56e04efd854062b660f559544"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf80e5fe6ab582c82f0c3331df27a7e1565e2dcf06265afd5173d809cdbf9ba"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:44dfc9ddfb9b51a5626568ef4e55ada462b7328996294fe4d36de02fce42721f"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c451f7922992751a936b96c5f5b9bb9312243d9b754c34b33d0cb72c84669f4e"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:308fce789a2f093dca1ff91ac391f11a9f99c35369117ad5a5c6c4903e1b3e3a"}, + {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7f5ad4a7c6b0d90776fdefa294f662e8a86871e601309643de30bf94bb93a64e"}, + {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ea189db75f8eca08807d02ae27929e890c7d47599ce3d0a6a5d41f2419ecf338"}, + {file = "jiter-0.5.0-cp312-none-win32.whl", hash = "sha256:e3bbe3910c724b877846186c25fe3c802e105a2c1fc2b57d6688b9f8772026e4"}, + {file = "jiter-0.5.0-cp312-none-win_amd64.whl", hash = "sha256:a586832f70c3f1481732919215f36d41c59ca080fa27a65cf23d9490e75b2ef5"}, + {file = "jiter-0.5.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f04bc2fc50dc77be9d10f73fcc4e39346402ffe21726ff41028f36e179b587e6"}, + {file = "jiter-0.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6f433a4169ad22fcb550b11179bb2b4fd405de9b982601914ef448390b2954f3"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad4a6398c85d3a20067e6c69890ca01f68659da94d74c800298581724e426c7e"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6baa88334e7af3f4d7a5c66c3a63808e5efbc3698a1c57626541ddd22f8e4fbf"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ece0a115c05efca597c6d938f88c9357c843f8c245dbbb53361a1c01afd7148"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:335942557162ad372cc367ffaf93217117401bf930483b4b3ebdb1223dbddfa7"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649b0ee97a6e6da174bffcb3c8c051a5935d7d4f2f52ea1583b5b3e7822fbf14"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4be354c5de82157886ca7f5925dbda369b77344b4b4adf2723079715f823989"}, + {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5206144578831a6de278a38896864ded4ed96af66e1e63ec5dd7f4a1fce38a3a"}, + {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8120c60f8121ac3d6f072b97ef0e71770cc72b3c23084c72c4189428b1b1d3b6"}, + {file = "jiter-0.5.0-cp38-none-win32.whl", hash = "sha256:6f1223f88b6d76b519cb033a4d3687ca157c272ec5d6015c322fc5b3074d8a5e"}, + {file = "jiter-0.5.0-cp38-none-win_amd64.whl", hash = "sha256:c59614b225d9f434ea8fc0d0bec51ef5fa8c83679afedc0433905994fb36d631"}, + {file = "jiter-0.5.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0af3838cfb7e6afee3f00dc66fa24695199e20ba87df26e942820345b0afc566"}, + {file = "jiter-0.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:550b11d669600dbc342364fd4adbe987f14d0bbedaf06feb1b983383dcc4b961"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:489875bf1a0ffb3cb38a727b01e6673f0f2e395b2aad3c9387f94187cb214bbf"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b250ca2594f5599ca82ba7e68785a669b352156260c5362ea1b4e04a0f3e2389"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ea18e01f785c6667ca15407cd6dabbe029d77474d53595a189bdc813347218e"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462a52be85b53cd9bffd94e2d788a09984274fe6cebb893d6287e1c296d50653"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92cc68b48d50fa472c79c93965e19bd48f40f207cb557a8346daa020d6ba973b"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c834133e59a8521bc87ebcad773608c6fa6ab5c7a022df24a45030826cf10bc"}, + {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab3a71ff31cf2d45cb216dc37af522d335211f3a972d2fe14ea99073de6cb104"}, + {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cccd3af9c48ac500c95e1bcbc498020c87e1781ff0345dd371462d67b76643eb"}, + {file = "jiter-0.5.0-cp39-none-win32.whl", hash = "sha256:368084d8d5c4fc40ff7c3cc513c4f73e02c85f6009217922d0823a48ee7adf61"}, + {file = "jiter-0.5.0-cp39-none-win_amd64.whl", hash = "sha256:ce03f7b4129eb72f1687fa11300fbf677b02990618428934662406d2a76742a1"}, + {file = "jiter-0.5.0.tar.gz", hash = "sha256:1d916ba875bcab5c5f7d927df998c4cb694d27dceddf3392e58beaf10563368a"}, +] + [[package]] name = "json5" version = "0.9.25" @@ -2226,13 +1897,13 @@ test = ["coverage", "jupyter-server[test] (>=1.6,<3)", "pytest", "pytest-asyncio [[package]] name = "jupyter-ai-magics" -version = "2.18.1" +version = "2.20.0" description = "Jupyter AI magics Python package. Not published on NPM." optional = false python-versions = ">=3.8" files = [ - {file = "jupyter_ai_magics-2.18.1-py3-none-any.whl", hash = "sha256:54af7a833eae553c3bccf00eff845071826b0fb84b77c8238845f058541fd70d"}, - {file = "jupyter_ai_magics-2.18.1.tar.gz", hash = "sha256:df028a0566cd04555bd59dbdb5c08cb4eaf8150157a5ec176376390c73d4ac5b"}, + {file = "jupyter_ai_magics-2.20.0-py3-none-any.whl", hash = "sha256:a25b759d40da59a8f1432cece66ba7cc73ef92b3416026b22a446061ab2bc606"}, + {file = "jupyter_ai_magics-2.20.0.tar.gz", hash = "sha256:b5cdee73b6f0bea56dce5b6b92be89960471c2d877ccfab4795d889d8b834fbf"}, ] [package.dependencies] @@ -2240,11 +1911,12 @@ click = ">=8.0,<9.0" importlib-metadata = ">=5.2.0" ipython = "*" jsonpath-ng = ">=1.5.3,<2" -langchain = ">=0.1.0,<0.2.0" +langchain = ">=0.1.0,<0.3.0" +langchain-community = ">=0.1.0,<0.3.0" typing-extensions = ">=4.5.0" [package.extras] -all = ["ai21", "boto3", "gpt4all", "huggingface-hub", "ipywidgets", "langchain-anthropic", "langchain-cohere", "langchain-google-genai", "langchain-mistralai", "langchain-nvidia-ai-endpoints", "langchain-openai", "pillow", "qianfan", "together"] +all = ["ai21", "boto3", "gpt4all", "huggingface-hub", "ipywidgets", "langchain-anthropic", "langchain-aws", "langchain-cohere", "langchain-google-genai", "langchain-mistralai", "langchain-nvidia-ai-endpoints", "langchain-openai", "pillow", "qianfan", "together"] dev = ["pre-commit (>=3.3.3,<4)"] test = ["coverage", "pytest", "pytest-asyncio", "pytest-cov"] @@ -2467,134 +2139,110 @@ files = [ [[package]] name = "langchain" -version = "0.1.20" +version = "0.2.12" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.1.20-py3-none-any.whl", hash = "sha256:09991999fbd6c3421a12db3c7d1f52d55601fc41d9b2a3ef51aab2e0e9c38da9"}, - {file = "langchain-0.1.20.tar.gz", hash = "sha256:f35c95eed8c8375e02dce95a34f2fd4856a4c98269d6dc34547a23dba5beab7e"}, + {file = "langchain-0.2.12-py3-none-any.whl", hash = "sha256:565d2f5df1c06815d1c684400218ec4ae5e1027887aad343226fad846c54e726"}, + {file = "langchain-0.2.12.tar.gz", hash = "sha256:fe7bd409c133017446fec54c38a5e7cb14f74e020090d7b5065374badf71e6d1"}, ] [package.dependencies] 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" -langchain-community = ">=0.0.38,<0.1" -langchain-core = ">=0.1.52,<0.2.0" -langchain-text-splitters = ">=0.0.1,<0.1" +langchain-core = ">=0.2.27,<0.3.0" +langchain-text-splitters = ">=0.2.0,<0.3.0" langsmith = ">=0.1.17,<0.2.0" -numpy = ">=1,<2" +numpy = {version = ">=1,<2", markers = "python_version < \"3.12\""} pydantic = ">=1,<3" PyYAML = ">=5.3" requests = ">=2,<3" SQLAlchemy = ">=1.4,<3" -tenacity = ">=8.1.0,<9.0.0" - -[package.extras] -azure = ["azure-ai-formrecognizer (>=3.2.1,<4.0.0)", "azure-ai-textanalytics (>=5.3.0,<6.0.0)", "azure-cognitiveservices-speech (>=1.28.0,<2.0.0)", "azure-core (>=1.26.4,<2.0.0)", "azure-cosmos (>=4.4.0b1,<5.0.0)", "azure-identity (>=1.12.0,<2.0.0)", "azure-search-documents (==11.4.0b8)", "openai (<2)"] -clarifai = ["clarifai (>=9.1.0)"] -cli = ["typer (>=0.9.0,<0.10.0)"] -cohere = ["cohere (>=4,<6)"] -docarray = ["docarray[hnswlib] (>=0.32.0,<0.33.0)"] -embeddings = ["sentence-transformers (>=2,<3)"] -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)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.0,<0.2.0)", "chardet (>=5.1.0,<6.0.0)", "cohere (>=4,<6)", "couchbase (>=4.1.9,<5.0.0)", "dashvector (>=1.0.1,<2.0.0)", "databricks-vectorsearch (>=0.21,<0.22)", "datasets (>=2.15.0,<3.0.0)", "dgml-utils (>=0.3.0,<0.4.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)", "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)", "hologres-vector (>=0.0.6,<0.0.7)", "html2text (>=2020.1.16,<2021.0.0)", "javelin-sdk (>=0.1.8,<0.2.0)", "jinja2 (>=3,<4)", "jq (>=1.4.1,<2.0.0)", "jsonschema (>1)", "langchain-openai (>=0.0.2,<0.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)", "openai (<2)", "openapi-pydantic (>=0.3.2,<0.4.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)", "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)", "timescale-vector (>=0.0.1,<0.0.2)", "tqdm (>=4.48.0)", "upstash-redis (>=0.15.0,<0.16.0)", "xata (>=1.0.0a7,<2.0.0)", "xmltodict (>=0.13.0,<0.14.0)"] -javascript = ["esprima (>=4.0.1,<5.0.0)"] -llms = ["clarifai (>=9.1.0)", "cohere (>=4,<6)", "huggingface_hub (>=0,<1)", "manifest-ml (>=0.0.1,<0.0.2)", "nlpcloud (>=1,<2)", "openai (<2)", "openlm (>=0.0.5,<0.0.6)", "torch (>=1,<3)", "transformers (>=4,<5)"] -openai = ["openai (<2)", "tiktoken (>=0.3.2,<0.6.0)"] -qdrant = ["qdrant-client (>=1.3.1,<2.0.0)"] -text-helpers = ["chardet (>=5.1.0,<6.0.0)"] +tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" [[package]] name = "langchain-community" -version = "0.0.38" +version = "0.2.11" description = "Community contributed LangChain integrations." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_community-0.0.38-py3-none-any.whl", hash = "sha256:ecb48660a70a08c90229be46b0cc5f6bc9f38f2833ee44c57dfab9bf3a2c121a"}, - {file = "langchain_community-0.0.38.tar.gz", hash = "sha256:127fc4b75bc67b62fe827c66c02e715a730fef8fe69bd2023d466bab06b5810d"}, + {file = "langchain_community-0.2.11-py3-none-any.whl", hash = "sha256:465c03ba1603975d141533424185e09546ecf09e379c93aee2671bdc9b325cda"}, + {file = "langchain_community-0.2.11.tar.gz", hash = "sha256:ede261ff8202f1433f004ee90baf89f371cee37cb1abfc16dd0f8392db10b23e"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" dataclasses-json = ">=0.5.7,<0.7" -langchain-core = ">=0.1.52,<0.2.0" +langchain = ">=0.2.12,<0.3.0" +langchain-core = ">=0.2.27,<0.3.0" langsmith = ">=0.1.0,<0.2.0" -numpy = ">=1,<2" +numpy = {version = ">=1,<2", markers = "python_version < \"3.12\""} PyYAML = ">=5.3" requests = ">=2,<3" SQLAlchemy = ">=1.4,<3" -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)", "azure-identity (>=1.15.0,<2.0.0)", "azure-search-documents (==11.4.0)", "beautifulsoup4 (>=4,<5)", "bibtexparser (>=1.4.0,<2.0.0)", "cassio (>=0.1.6,<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)", "oracledb (>=2.2.0,<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)"] +tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.1.52" +version = "0.2.29" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.1.52-py3-none-any.whl", hash = "sha256:62566749c92e8a1181c255c788548dc16dbc319d896cd6b9c95dc17af9b2a6db"}, - {file = "langchain_core-0.1.52.tar.gz", hash = "sha256:084c3fc452f5a6966c28ab3ec5dbc8b8d26fc3f63378073928f4e29d90b6393f"}, + {file = "langchain_core-0.2.29-py3-none-any.whl", hash = "sha256:846c04a3bb72e409a9b928e0eb3ea1762e1473f2c4fb6df2596fbd7b3ab75973"}, + {file = "langchain_core-0.2.29.tar.gz", hash = "sha256:491324745a7afee5a7b285c3904edd9dd0c6efa7daf26b92fec6e84a2d2f5d10"}, ] [package.dependencies] jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.0,<0.2.0" -packaging = ">=23.2,<24.0" -pydantic = ">=1,<3" +langsmith = ">=0.1.75,<0.2.0" +packaging = ">=23.2,<25" +pydantic = {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""} PyYAML = ">=5.3" -tenacity = ">=8.1.0,<9.0.0" - -[package.extras] -extended-testing = ["jinja2 (>=3,<4)"] +tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" +typing-extensions = ">=4.7" [[package]] name = "langchain-openai" -version = "0.1.6" +version = "0.1.20" description = "An integration package connecting OpenAI and LangChain" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_openai-0.1.6-py3-none-any.whl", hash = "sha256:7f62ecb12d3cdd0d96679abea00e4e3ceb1f829f6d1f127a5f7b97c1315d157f"}, - {file = "langchain_openai-0.1.6.tar.gz", hash = "sha256:7d2e838e57ef231cb7689fd58ac5fa8a6e9e504174f8c5698c837739786e2030"}, + {file = "langchain_openai-0.1.20-py3-none-any.whl", hash = "sha256:232ebfe90b1898ef7cf181e364d45191edcf04bfc31b292ecaa1d2121942c28e"}, + {file = "langchain_openai-0.1.20.tar.gz", hash = "sha256:2c91e9f771541076b138e65dd4c5427b26957a2272406a7f4ee747d7896f9b35"}, ] [package.dependencies] -langchain-core = ">=0.1.46,<0.2.0" -openai = ">=1.24.0,<2.0.0" -tiktoken = ">=0.5.2,<1" +langchain-core = ">=0.2.26,<0.3.0" +openai = ">=1.32.0,<2.0.0" +tiktoken = ">=0.7,<1" [[package]] name = "langchain-text-splitters" -version = "0.0.2" +version = "0.2.2" description = "LangChain text splitting utilities" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_text_splitters-0.0.2-py3-none-any.whl", hash = "sha256:13887f32705862c1e1454213cb7834a63aae57c26fcd80346703a1d09c46168d"}, - {file = "langchain_text_splitters-0.0.2.tar.gz", hash = "sha256:ac8927dc0ba08eba702f6961c9ed7df7cead8de19a9f7101ab2b5ea34201b3c1"}, + {file = "langchain_text_splitters-0.2.2-py3-none-any.whl", hash = "sha256:1c80d4b11b55e2995f02d2a326c0323ee1eeff24507329bb22924e420c782dff"}, + {file = "langchain_text_splitters-0.2.2.tar.gz", hash = "sha256:a1e45de10919fa6fb080ef0525deab56557e9552083600455cb9fa4238076140"}, ] [package.dependencies] -langchain-core = ">=0.1.28,<0.3" - -[package.extras] -extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"] +langchain-core = ">=0.2.10,<0.3.0" [[package]] name = "langsmith" -version = "0.1.96" +version = "0.1.98" 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.96-py3-none-any.whl", hash = "sha256:1e8285c3f84cffebc761ff5624647de20686dbbf659f5d1135918261f85bad13"}, - {file = "langsmith-0.1.96.tar.gz", hash = "sha256:01b7fa7d538b6409ee74bff458cc3dcdc1799fc70d329f79eb26ba54c32991ae"}, + {file = "langsmith-0.1.98-py3-none-any.whl", hash = "sha256:f79e8a128652bbcee4606d10acb6236973b5cd7dde76e3741186d3b97b5698e9"}, + {file = "langsmith-0.1.98.tar.gz", hash = "sha256:e07678219a0502e8f26d35294e72127a39d25e32fafd091af5a7bb661e9a6bd1"}, ] [package.dependencies] @@ -2644,13 +2292,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.40.8" +version = "1.43.3" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.40.8-py3-none-any.whl", hash = "sha256:cd0c313423dad49224696c45ac02c574abcaed6666c597543c2318b3521f4320"}, - {file = "litellm-1.40.8.tar.gz", hash = "sha256:8878d2437ac50bcc6f39ded1729e2113eb5fee645fcebcd32fc241c529a21c00"}, + {file = "litellm-1.43.3-py3-none-any.whl", hash = "sha256:b626df5da48b794be4d1888eaecfe941d68a081bfeab50531e5395e32d0e7894"}, + {file = "litellm-1.43.3.tar.gz", hash = "sha256:b139fd3965cc1fdf98746dac67ce1f57f72c247083174ee8f1f46ed86fafdffc"}, ] [package.dependencies] @@ -2658,14 +2306,16 @@ aiohttp = "*" click = "*" importlib-metadata = ">=6.8.0" jinja2 = ">=3.1.2,<4.0.0" -openai = ">=1.27.0" +jsonschema = ">=4.22.0,<5.0.0" +openai = ">=1.40.0" +pydantic = ">=2.0.0,<3.0.0" python-dotenv = ">=0.2.0" requests = ">=2.31.0,<3.0.0" -tiktoken = ">=0.4.0" +tiktoken = ">=0.7.0" tokenizers = "*" [package.extras] -extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "resend (>=0.8.0,<0.9.0)"] +extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "pynacl (>=1.5.0,<2.0.0)", "resend (>=0.8.0,<0.9.0)"] proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.111.0,<0.112.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] [[package]] @@ -3254,23 +2904,24 @@ files = [ [[package]] name = "openai" -version = "1.39.0" +version = "1.40.1" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.39.0-py3-none-any.whl", hash = "sha256:a712553a131c59a249c474d0bb6a0414f41df36dc186d3a018fa7e600e57fb7f"}, - {file = "openai-1.39.0.tar.gz", hash = "sha256:0cea446082f50985f26809d704a97749cb366a1ba230ef432c684a9745b3f2d9"}, + {file = "openai-1.40.1-py3-none-any.whl", hash = "sha256:cf5929076c6ca31c26f1ed207e9fd19eb05404cc9104f64c9d29bb0ac0c5bcd4"}, + {file = "openai-1.40.1.tar.gz", hash = "sha256:cb1294ac1f8c6a1acbb07e090698eb5ad74a7a88484e77126612a4f22579673d"}, ] [package.dependencies] anyio = ">=3.5.0,<5" distro = ">=1.7.0,<2" httpx = ">=0.23.0,<1" +jiter = ">=0.4.0,<1" pydantic = ">=1.9.0,<3" sniffio = "*" tqdm = ">4" -typing-extensions = ">=4.7,<5" +typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] @@ -3348,13 +2999,13 @@ files = [ [[package]] name = "packaging" -version = "23.2" +version = "24.1" description = "Core utilities for Python packages" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, - {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, + {file = "packaging-24.1-py3-none-any.whl", hash = "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"}, + {file = "packaging-24.1.tar.gz", hash = "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002"}, ] [[package]] @@ -3605,41 +3256,24 @@ files = [ [package.dependencies] wcwidth = "*" -[[package]] -name = "proto-plus" -version = "1.24.0" -description = "Beautiful, Pythonic protocol buffers." -optional = false -python-versions = ">=3.7" -files = [ - {file = "proto-plus-1.24.0.tar.gz", hash = "sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445"}, - {file = "proto_plus-1.24.0-py3-none-any.whl", hash = "sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12"}, -] - -[package.dependencies] -protobuf = ">=3.19.0,<6.0.0dev" - -[package.extras] -testing = ["google-api-core (>=1.31.5)"] - [[package]] name = "protobuf" -version = "4.25.4" +version = "5.27.3" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-4.25.4-cp310-abi3-win32.whl", hash = "sha256:db9fd45183e1a67722cafa5c1da3e85c6492a5383f127c86c4c4aa4845867dc4"}, - {file = "protobuf-4.25.4-cp310-abi3-win_amd64.whl", hash = "sha256:ba3d8504116a921af46499471c63a85260c1a5fc23333154a427a310e015d26d"}, - {file = "protobuf-4.25.4-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:eecd41bfc0e4b1bd3fa7909ed93dd14dd5567b98c941d6c1ad08fdcab3d6884b"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:4c8a70fdcb995dcf6c8966cfa3a29101916f7225e9afe3ced4395359955d3835"}, - {file = "protobuf-4.25.4-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:3319e073562e2515c6ddc643eb92ce20809f5d8f10fead3332f71c63be6a7040"}, - {file = "protobuf-4.25.4-cp38-cp38-win32.whl", hash = "sha256:7e372cbbda66a63ebca18f8ffaa6948455dfecc4e9c1029312f6c2edcd86c4e1"}, - {file = "protobuf-4.25.4-cp38-cp38-win_amd64.whl", hash = "sha256:051e97ce9fa6067a4546e75cb14f90cf0232dcb3e3d508c448b8d0e4265b61c1"}, - {file = "protobuf-4.25.4-cp39-cp39-win32.whl", hash = "sha256:90bf6fd378494eb698805bbbe7afe6c5d12c8e17fca817a646cd6a1818c696ca"}, - {file = "protobuf-4.25.4-cp39-cp39-win_amd64.whl", hash = "sha256:ac79a48d6b99dfed2729ccccee547b34a1d3d63289c71cef056653a846a2240f"}, - {file = "protobuf-4.25.4-py3-none-any.whl", hash = "sha256:bfbebc1c8e4793cfd58589acfb8a1026be0003e852b9da7db5a4285bde996978"}, - {file = "protobuf-4.25.4.tar.gz", hash = "sha256:0dc4a62cc4052a036ee2204d26fe4d835c62827c855c8a03f29fe6da146b380d"}, + {file = "protobuf-5.27.3-cp310-abi3-win32.whl", hash = "sha256:dcb307cd4ef8fec0cf52cb9105a03d06fbb5275ce6d84a6ae33bc6cf84e0a07b"}, + {file = "protobuf-5.27.3-cp310-abi3-win_amd64.whl", hash = "sha256:16ddf3f8c6c41e1e803da7abea17b1793a97ef079a912e42351eabb19b2cffe7"}, + {file = "protobuf-5.27.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:68248c60d53f6168f565a8c76dc58ba4fa2ade31c2d1ebdae6d80f969cdc2d4f"}, + {file = "protobuf-5.27.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:b8a994fb3d1c11156e7d1e427186662b64694a62b55936b2b9348f0a7c6625ce"}, + {file = "protobuf-5.27.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:a55c48f2a2092d8e213bd143474df33a6ae751b781dd1d1f4d953c128a415b25"}, + {file = "protobuf-5.27.3-cp38-cp38-win32.whl", hash = "sha256:043853dcb55cc262bf2e116215ad43fa0859caab79bb0b2d31b708f128ece035"}, + {file = "protobuf-5.27.3-cp38-cp38-win_amd64.whl", hash = "sha256:c2a105c24f08b1e53d6c7ffe69cb09d0031512f0b72f812dd4005b8112dbe91e"}, + {file = "protobuf-5.27.3-cp39-cp39-win32.whl", hash = "sha256:c84eee2c71ed83704f1afbf1a85c3171eab0fd1ade3b399b3fad0884cbcca8bf"}, + {file = "protobuf-5.27.3-cp39-cp39-win_amd64.whl", hash = "sha256:af7c0b7cfbbb649ad26132e53faa348580f844d9ca46fd3ec7ca48a1ea5db8a1"}, + {file = "protobuf-5.27.3-py3-none-any.whl", hash = "sha256:8572c6533e544ebf6899c360e91d6bcbbee2549251643d32c52cf8a5de295ba5"}, + {file = "protobuf-5.27.3.tar.gz", hash = "sha256:82460903e640f2b7e34ee81a947fdaad89de796d324bcbc38ff5430bcdead82c"}, ] [[package]] @@ -3696,31 +3330,6 @@ files = [ [package.extras] tests = ["pytest"] -[[package]] -name = "pyasn1" -version = "0.6.0" -description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -optional = false -python-versions = ">=3.8" -files = [ - {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.4.0" -description = "A collection of ASN.1-based protocols modules" -optional = false -python-versions = ">=3.8" -files = [ - {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.7.0" - [[package]] name = "pycnite" version = "2024.7.31" @@ -4088,61 +3697,64 @@ files = [ [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {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_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"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] @@ -4435,289 +4047,143 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rpds-py" -version = "0.19.1" +version = "0.20.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:aaf71f95b21f9dc708123335df22e5a2fef6307e3e6f9ed773b2e0938cc4d491"}, - {file = "rpds_py-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca0dda0c5715efe2ab35bb83f813f681ebcd2840d8b1b92bfc6fe3ab382fae4a"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81db2e7282cc0487f500d4db203edc57da81acde9e35f061d69ed983228ffe3b"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a8dfa125b60ec00c7c9baef945bb04abf8ac772d8ebefd79dae2a5f316d7850"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271accf41b02687cef26367c775ab220372ee0f4925591c6796e7c148c50cab5"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9bc4161bd3b970cd6a6fcda70583ad4afd10f2750609fb1f3ca9505050d4ef3"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0cf2a0dbb5987da4bd92a7ca727eadb225581dd9681365beba9accbe5308f7d"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b5e28e56143750808c1c79c70a16519e9bc0a68b623197b96292b21b62d6055c"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7af6f7b80f687b33a4cdb0a785a5d4de1fb027a44c9a049d8eb67d5bfe8a687"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e429fc517a1c5e2a70d576077231538a98d59a45dfc552d1ac45a132844e6dfb"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d2dbd8f4990d4788cb122f63bf000357533f34860d269c1a8e90ae362090ff3a"}, - {file = "rpds_py-0.19.1-cp310-none-win32.whl", hash = "sha256:e0f9d268b19e8f61bf42a1da48276bcd05f7ab5560311f541d22557f8227b866"}, - {file = "rpds_py-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:df7c841813f6265e636fe548a49664c77af31ddfa0085515326342a751a6ba51"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:902cf4739458852fe917104365ec0efbea7d29a15e4276c96a8d33e6ed8ec137"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3d73022990ab0c8b172cce57c69fd9a89c24fd473a5e79cbce92df87e3d9c48"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3837c63dd6918a24de6c526277910e3766d8c2b1627c500b155f3eecad8fad65"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cdb7eb3cf3deb3dd9e7b8749323b5d970052711f9e1e9f36364163627f96da58"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26ab43b6d65d25b1a333c8d1b1c2f8399385ff683a35ab5e274ba7b8bb7dc61c"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75130df05aae7a7ac171b3b5b24714cffeabd054ad2ebc18870b3aa4526eba23"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34f751bf67cab69638564eee34023909380ba3e0d8ee7f6fe473079bf93f09b"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2671cb47e50a97f419a02cd1e0c339b31de017b033186358db92f4d8e2e17d8"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c73254c256081704dba0a333457e2fb815364018788f9b501efe7c5e0ada401"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4383beb4a29935b8fa28aca8fa84c956bf545cb0c46307b091b8d312a9150e6a"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dbceedcf4a9329cc665452db1aaf0845b85c666e4885b92ee0cddb1dbf7e052a"}, - {file = "rpds_py-0.19.1-cp311-none-win32.whl", hash = "sha256:f0a6d4a93d2a05daec7cb885157c97bbb0be4da739d6f9dfb02e101eb40921cd"}, - {file = "rpds_py-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:c149a652aeac4902ecff2dd93c3b2681c608bd5208c793c4a99404b3e1afc87c"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:56313be667a837ff1ea3508cebb1ef6681d418fa2913a0635386cf29cff35165"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d1d7539043b2b31307f2c6c72957a97c839a88b2629a348ebabe5aa8b626d6b"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1dc59a5e7bc7f44bd0c048681f5e05356e479c50be4f2c1a7089103f1621d5"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8f78398e67a7227aefa95f876481485403eb974b29e9dc38b307bb6eb2315ea"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef07a0a1d254eeb16455d839cef6e8c2ed127f47f014bbda64a58b5482b6c836"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8124101e92c56827bebef084ff106e8ea11c743256149a95b9fd860d3a4f331f"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08ce9c95a0b093b7aec75676b356a27879901488abc27e9d029273d280438505"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b02dd77a2de6e49078c8937aadabe933ceac04b41c5dde5eca13a69f3cf144e"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4dd02e29c8cbed21a1875330b07246b71121a1c08e29f0ee3db5b4cfe16980c4"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9c7042488165f7251dc7894cd533a875d2875af6d3b0e09eda9c4b334627ad1c"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f809a17cc78bd331e137caa25262b507225854073fd319e987bd216bed911b7c"}, - {file = "rpds_py-0.19.1-cp312-none-win32.whl", hash = "sha256:3ddab996807c6b4227967fe1587febade4e48ac47bb0e2d3e7858bc621b1cace"}, - {file = "rpds_py-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:32e0db3d6e4f45601b58e4ac75c6f24afbf99818c647cc2066f3e4b192dabb1f"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:747251e428406b05fc86fee3904ee19550c4d2d19258cef274e2151f31ae9d38"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dc733d35f861f8d78abfaf54035461e10423422999b360966bf1c443cbc42705"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbda75f245caecff8faa7e32ee94dfaa8312a3367397975527f29654cd17a6ed"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd04d8cab16cab5b0a9ffc7d10f0779cf1120ab16c3925404428f74a0a43205a"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2d66eb41ffca6cc3c91d8387509d27ba73ad28371ef90255c50cb51f8953301"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdf4890cda3b59170009d012fca3294c00140e7f2abe1910e6a730809d0f3f9b"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1fa67ef839bad3815124f5f57e48cd50ff392f4911a9f3cf449d66fa3df62a5"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b82c9514c6d74b89a370c4060bdb80d2299bc6857e462e4a215b4ef7aa7b090e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c7b07959866a6afb019abb9564d8a55046feb7a84506c74a6f197cbcdf8a208e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4f580ae79d0b861dfd912494ab9d477bea535bfb4756a2269130b6607a21802e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c6d20c8896c00775e6f62d8373aba32956aa0b850d02b5ec493f486c88e12859"}, - {file = "rpds_py-0.19.1-cp313-none-win32.whl", hash = "sha256:afedc35fe4b9e30ab240b208bb9dc8938cb4afe9187589e8d8d085e1aacb8309"}, - {file = "rpds_py-0.19.1-cp313-none-win_amd64.whl", hash = "sha256:1d4af2eb520d759f48f1073ad3caef997d1bfd910dc34e41261a595d3f038a94"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:34bca66e2e3eabc8a19e9afe0d3e77789733c702c7c43cd008e953d5d1463fde"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:24f8ae92c7fae7c28d0fae9b52829235df83f34847aa8160a47eb229d9666c7b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71157f9db7f6bc6599a852852f3389343bea34315b4e6f109e5cbc97c1fb2963"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d494887d40dc4dd0d5a71e9d07324e5c09c4383d93942d391727e7a40ff810b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3661e6d4ba63a094138032c1356d557de5b3ea6fd3cca62a195f623e381c76"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97fbb77eaeb97591efdc654b8b5f3ccc066406ccfb3175b41382f221ecc216e8"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cc4bc73e53af8e7a42c8fd7923bbe35babacfa7394ae9240b3430b5dcf16b2a"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:35af5e4d5448fa179fd7fff0bba0fba51f876cd55212f96c8bbcecc5c684ae5c"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3511f6baf8438326e351097cecd137eb45c5f019944fe0fd0ae2fea2fd26be39"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:57863d16187995c10fe9cf911b897ed443ac68189179541734502353af33e693"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9e318e6786b1e750a62f90c6f7fa8b542102bdcf97c7c4de2a48b50b61bd36ec"}, - {file = "rpds_py-0.19.1-cp38-none-win32.whl", hash = "sha256:53dbc35808c6faa2ce3e48571f8f74ef70802218554884787b86a30947842a14"}, - {file = "rpds_py-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:8df1c283e57c9cb4d271fdc1875f4a58a143a2d1698eb0d6b7c0d7d5f49c53a1"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e76c902d229a3aa9d5ceb813e1cbcc69bf5bda44c80d574ff1ac1fa3136dea71"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de1f7cd5b6b351e1afd7568bdab94934d656abe273d66cda0ceea43bbc02a0c2"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fc5a84777cb61692d17988989690d6f34f7f95968ac81398d67c0d0994a897"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74129d5ffc4cde992d89d345f7f7d6758320e5d44a369d74d83493429dad2de5"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e360188b72f8080fefa3adfdcf3618604cc8173651c9754f189fece068d2a45"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13e6d4840897d4e4e6b2aa1443e3a8eca92b0402182aafc5f4ca1f5e24f9270a"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f09529d2332264a902688031a83c19de8fda5eb5881e44233286b9c9ec91856d"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0d4b52811dcbc1aba08fd88d475f75b4f6db0984ba12275d9bed1a04b2cae9b5"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dd635c2c4043222d80d80ca1ac4530a633102a9f2ad12252183bcf338c1b9474"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f35b34a5184d5e0cc360b61664c1c06e866aab077b5a7c538a3e20c8fcdbf90b"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d4ec0046facab83012d821b33cead742a35b54575c4edfb7ed7445f63441835f"}, - {file = "rpds_py-0.19.1-cp39-none-win32.whl", hash = "sha256:f5b8353ea1a4d7dfb59a7f45c04df66ecfd363bb5b35f33b11ea579111d4655f"}, - {file = "rpds_py-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:1fb93d3486f793d54a094e2bfd9cd97031f63fcb5bc18faeb3dd4b49a1c06523"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d5c7e32f3ee42f77d8ff1a10384b5cdcc2d37035e2e3320ded909aa192d32c3"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:89cc8921a4a5028d6dd388c399fcd2eef232e7040345af3d5b16c04b91cf3c7e"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca34e913d27401bda2a6f390d0614049f5a95b3b11cd8eff80fe4ec340a1208"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5953391af1405f968eb5701ebbb577ebc5ced8d0041406f9052638bafe52209d"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:840e18c38098221ea6201f091fc5d4de6128961d2930fbbc96806fb43f69aec1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d8b735c4d162dc7d86a9cf3d717f14b6c73637a1f9cd57fe7e61002d9cb1972"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce757c7c90d35719b38fa3d4ca55654a76a40716ee299b0865f2de21c146801c"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9421b23c85f361a133aa7c5e8ec757668f70343f4ed8fdb5a4a14abd5437244"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3b823be829407393d84ee56dc849dbe3b31b6a326f388e171555b262e8456cc1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:5e58b61dcbb483a442c6239c3836696b79f2cd8e7eec11e12155d3f6f2d886d1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39d67896f7235b2c886fb1ee77b1491b77049dcef6fbf0f401e7b4cbed86bbd4"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8b32cd4ab6db50c875001ba4f5a6b30c0f42151aa1fbf9c2e7e3674893fb1dc4"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c32e41de995f39b6b315d66c27dea3ef7f7c937c06caab4c6a79a5e09e2c415"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a129c02b42d46758c87faeea21a9f574e1c858b9f358b6dd0bbd71d17713175"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:346557f5b1d8fd9966059b7a748fd79ac59f5752cd0e9498d6a40e3ac1c1875f"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31e450840f2f27699d014cfc8865cc747184286b26d945bcea6042bb6aa4d26e"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69084fd29bfeff14816666c93a466e85414fe6b7d236cfc108a9c11afa6f7301"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d2b88efe65544a7d5121b0c3b003ebba92bfede2ea3577ce548b69c5235185"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ea961a674172ed2235d990d7edf85d15d8dfa23ab8575e48306371c070cda67"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:5beffdbe766cfe4fb04f30644d822a1080b5359df7db3a63d30fa928375b2720"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:720f3108fb1bfa32e51db58b832898372eb5891e8472a8093008010911e324c5"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c2087dbb76a87ec2c619253e021e4fb20d1a72580feeaa6892b0b3d955175a71"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ddd50f18ebc05ec29a0d9271e9dbe93997536da3546677f8ca00b76d477680c"}, - {file = "rpds_py-0.19.1.tar.gz", hash = "sha256:31dd5794837f00b46f4096aa8ccaa5972f73a938982e32ed817bb520c465e520"}, -] - -[[package]] -name = "rsa" -version = "4.9" -description = "Pure-Python RSA implementation" -optional = false -python-versions = ">=3.6,<4" -files = [ - {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, - {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, ] -[package.dependencies] -pyasn1 = ">=0.1.3" - [[package]] name = "ruff" -version = "0.5.6" +version = "0.5.7" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.5.6-py3-none-linux_armv6l.whl", hash = "sha256:a0ef5930799a05522985b9cec8290b185952f3fcd86c1772c3bdbd732667fdcd"}, - {file = "ruff-0.5.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b652dc14f6ef5d1552821e006f747802cc32d98d5509349e168f6bf0ee9f8f42"}, - {file = "ruff-0.5.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:80521b88d26a45e871f31e4b88938fd87db7011bb961d8afd2664982dfc3641a"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9bc8f328a9f1309ae80e4d392836e7dbc77303b38ed4a7112699e63d3b066ab"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4d394940f61f7720ad371ddedf14722ee1d6250fd8d020f5ea5a86e7be217daf"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:111a99cdb02f69ddb2571e2756e017a1496c2c3a2aeefe7b988ddab38b416d36"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:e395daba77a79f6dc0d07311f94cc0560375ca20c06f354c7c99af3bf4560c5d"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c476acb43c3c51e3c614a2e878ee1589655fa02dab19fe2db0423a06d6a5b1b6"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e2ff8003f5252fd68425fd53d27c1f08b201d7ed714bb31a55c9ac1d4c13e2eb"}, - {file = "ruff-0.5.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c94e084ba3eaa80c2172918c2ca2eb2230c3f15925f4ed8b6297260c6ef179ad"}, - {file = "ruff-0.5.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1f77c1c3aa0669fb230b06fb24ffa3e879391a3ba3f15e3d633a752da5a3e670"}, - {file = "ruff-0.5.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f908148c93c02873210a52cad75a6eda856b2cbb72250370ce3afef6fb99b1ed"}, - {file = "ruff-0.5.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:563a7ae61ad284187d3071d9041c08019975693ff655438d8d4be26e492760bd"}, - {file = "ruff-0.5.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:94fe60869bfbf0521e04fd62b74cbca21cbc5beb67cbb75ab33fe8c174f54414"}, - {file = "ruff-0.5.6-py3-none-win32.whl", hash = "sha256:e6a584c1de6f8591c2570e171cc7ce482bb983d49c70ddf014393cd39e9dfaed"}, - {file = "ruff-0.5.6-py3-none-win_amd64.whl", hash = "sha256:d7fe7dccb1a89dc66785d7aa0ac283b2269712d8ed19c63af908fdccca5ccc1a"}, - {file = "ruff-0.5.6-py3-none-win_arm64.whl", hash = "sha256:57c6c0dd997b31b536bff49b9eee5ed3194d60605a4427f735eeb1f9c1b8d264"}, - {file = "ruff-0.5.6.tar.gz", hash = "sha256:07c9e3c2a8e1fe377dd460371c3462671a728c981c3205a5217291422209f642"}, -] - -[[package]] -name = "safetensors" -version = "0.4.4" -description = "" -optional = false -python-versions = ">=3.7" -files = [ - {file = "safetensors-0.4.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2adb497ada13097f30e386e88c959c0fda855a5f6f98845710f5bb2c57e14f12"}, - {file = "safetensors-0.4.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7db7fdc2d71fd1444d85ca3f3d682ba2df7d61a637dfc6d80793f439eae264ab"}, - {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d4f0eed76b430f009fbefca1a0028ddb112891b03cb556d7440d5cd68eb89a9"}, - {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:57d216fab0b5c432aabf7170883d7c11671622bde8bd1436c46d633163a703f6"}, - {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7d9b76322e49c056bcc819f8bdca37a2daa5a6d42c07f30927b501088db03309"}, - {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32f0d1f6243e90ee43bc6ee3e8c30ac5b09ca63f5dd35dbc985a1fc5208c451a"}, - {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d464bdc384874601a177375028012a5f177f1505279f9456fea84bbc575c7f"}, - {file = "safetensors-0.4.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63144e36209ad8e4e65384dbf2d52dd5b1866986079c00a72335402a38aacdc5"}, - {file = "safetensors-0.4.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:051d5ecd490af7245258000304b812825974d5e56f14a3ff7e1b8b2ba6dc2ed4"}, - {file = "safetensors-0.4.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:51bc8429d9376224cd3cf7e8ce4f208b4c930cd10e515b6ac6a72cbc3370f0d9"}, - {file = "safetensors-0.4.4-cp310-none-win32.whl", hash = "sha256:fb7b54830cee8cf9923d969e2df87ce20e625b1af2fd194222ab902d3adcc29c"}, - {file = "safetensors-0.4.4-cp310-none-win_amd64.whl", hash = "sha256:4b3e8aa8226d6560de8c2b9d5ff8555ea482599c670610758afdc97f3e021e9c"}, - {file = "safetensors-0.4.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:bbaa31f2cb49013818bde319232ccd72da62ee40f7d2aa532083eda5664e85ff"}, - {file = "safetensors-0.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9fdcb80f4e9fbb33b58e9bf95e7dbbedff505d1bcd1c05f7c7ce883632710006"}, - {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55c14c20be247b8a1aeaf3ab4476265e3ca83096bb8e09bb1a7aa806088def4f"}, - {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:949aaa1118660f992dbf0968487b3e3cfdad67f948658ab08c6b5762e90cc8b6"}, - {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c11a4ab7debc456326a2bac67f35ee0ac792bcf812c7562a4a28559a5c795e27"}, - {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0cea44bba5c5601b297bc8307e4075535b95163402e4906b2e9b82788a2a6df"}, - {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9d752c97f6bbe327352f76e5b86442d776abc789249fc5e72eacb49e6916482"}, - {file = "safetensors-0.4.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:03f2bb92e61b055ef6cc22883ad1ae898010a95730fa988c60a23800eb742c2c"}, - {file = "safetensors-0.4.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:87bf3f91a9328a941acc44eceffd4e1f5f89b030985b2966637e582157173b98"}, - {file = "safetensors-0.4.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:20d218ec2b6899d29d6895419a58b6e44cc5ff8f0cc29fac8d236a8978ab702e"}, - {file = "safetensors-0.4.4-cp311-none-win32.whl", hash = "sha256:8079486118919f600c603536e2490ca37b3dbd3280e3ad6eaacfe6264605ac8a"}, - {file = "safetensors-0.4.4-cp311-none-win_amd64.whl", hash = "sha256:2f8c2eb0615e2e64ee27d478c7c13f51e5329d7972d9e15528d3e4cfc4a08f0d"}, - {file = "safetensors-0.4.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:baec5675944b4a47749c93c01c73d826ef7d42d36ba8d0dba36336fa80c76426"}, - {file = "safetensors-0.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f15117b96866401825f3e94543145028a2947d19974429246ce59403f49e77c6"}, - {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6a13a9caea485df164c51be4eb0c87f97f790b7c3213d635eba2314d959fe929"}, - {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6b54bc4ca5f9b9bba8cd4fb91c24b2446a86b5ae7f8975cf3b7a277353c3127c"}, - {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:08332c22e03b651c8eb7bf5fc2de90044f3672f43403b3d9ac7e7e0f4f76495e"}, - {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bb62841e839ee992c37bb75e75891c7f4904e772db3691c59daaca5b4ab960e1"}, - {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e5b927acc5f2f59547270b0309a46d983edc44be64e1ca27a7fcb0474d6cd67"}, - {file = "safetensors-0.4.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2a69c71b1ae98a8021a09a0b43363b0143b0ce74e7c0e83cacba691b62655fb8"}, - {file = "safetensors-0.4.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23654ad162c02a5636f0cd520a0310902c4421aab1d91a0b667722a4937cc445"}, - {file = "safetensors-0.4.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:0677c109d949cf53756859160b955b2e75b0eefe952189c184d7be30ecf7e858"}, - {file = "safetensors-0.4.4-cp312-none-win32.whl", hash = "sha256:a51d0ddd4deb8871c6de15a772ef40b3dbd26a3c0451bb9e66bc76fc5a784e5b"}, - {file = "safetensors-0.4.4-cp312-none-win_amd64.whl", hash = "sha256:2d065059e75a798bc1933c293b68d04d79b586bb7f8c921e0ca1e82759d0dbb1"}, - {file = "safetensors-0.4.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:9d625692578dd40a112df30c02a1adf068027566abd8e6a74893bb13d441c150"}, - {file = "safetensors-0.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7cabcf39c81e5b988d0adefdaea2eb9b4fd9bd62d5ed6559988c62f36bfa9a89"}, - {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8359bef65f49d51476e9811d59c015f0ddae618ee0e44144f5595278c9f8268c"}, - {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a32c662e7df9226fd850f054a3ead0e4213a96a70b5ce37b2d26ba27004e013"}, - {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c329a4dcc395364a1c0d2d1574d725fe81a840783dda64c31c5a60fc7d41472c"}, - {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:239ee093b1db877c9f8fe2d71331a97f3b9c7c0d3ab9f09c4851004a11f44b65"}, - {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bd574145d930cf9405a64f9923600879a5ce51d9f315443a5f706374841327b6"}, - {file = "safetensors-0.4.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f6784eed29f9e036acb0b7769d9e78a0dc2c72c2d8ba7903005350d817e287a4"}, - {file = "safetensors-0.4.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:65a4a6072436bf0a4825b1c295d248cc17e5f4651e60ee62427a5bcaa8622a7a"}, - {file = "safetensors-0.4.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:df81e3407630de060ae8313da49509c3caa33b1a9415562284eaf3d0c7705f9f"}, - {file = "safetensors-0.4.4-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:e4a0f374200e8443d9746e947ebb346c40f83a3970e75a685ade0adbba5c48d9"}, - {file = "safetensors-0.4.4-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:181fb5f3dee78dae7fd7ec57d02e58f7936498d587c6b7c1c8049ef448c8d285"}, - {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb4ac1d8f6b65ec84ddfacd275079e89d9df7c92f95675ba96c4f790a64df6e"}, - {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76897944cd9239e8a70955679b531b9a0619f76e25476e57ed373322d9c2075d"}, - {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a9e9d1a27e51a0f69e761a3d581c3af46729ec1c988fa1f839e04743026ae35"}, - {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:005ef9fc0f47cb9821c40793eb029f712e97278dae84de91cb2b4809b856685d"}, - {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26987dac3752688c696c77c3576f951dbbdb8c57f0957a41fb6f933cf84c0b62"}, - {file = "safetensors-0.4.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c05270b290acd8d249739f40d272a64dd597d5a4b90f27d830e538bc2549303c"}, - {file = "safetensors-0.4.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:068d3a33711fc4d93659c825a04480ff5a3854e1d78632cdc8f37fee917e8a60"}, - {file = "safetensors-0.4.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:063421ef08ca1021feea8b46951251b90ae91f899234dd78297cbe7c1db73b99"}, - {file = "safetensors-0.4.4-cp37-none-win32.whl", hash = "sha256:d52f5d0615ea83fd853d4e1d8acf93cc2e0223ad4568ba1e1f6ca72e94ea7b9d"}, - {file = "safetensors-0.4.4-cp37-none-win_amd64.whl", hash = "sha256:88a5ac3280232d4ed8e994cbc03b46a1807ce0aa123867b40c4a41f226c61f94"}, - {file = "safetensors-0.4.4-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3467ab511bfe3360967d7dc53b49f272d59309e57a067dd2405b4d35e7dcf9dc"}, - {file = "safetensors-0.4.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2ab4c96d922e53670ce25fbb9b63d5ea972e244de4fa1dd97b590d9fd66aacef"}, - {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87df18fce4440477c3ef1fd7ae17c704a69a74a77e705a12be135ee0651a0c2d"}, - {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0e5fe345b2bc7d88587149ac11def1f629d2671c4c34f5df38aed0ba59dc37f8"}, - {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9f1a3e01dce3cd54060791e7e24588417c98b941baa5974700eeb0b8eb65b0a0"}, - {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c6bf35e9a8998d8339fd9a05ac4ce465a4d2a2956cc0d837b67c4642ed9e947"}, - {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:166c0c52f6488b8538b2a9f3fbc6aad61a7261e170698779b371e81b45f0440d"}, - {file = "safetensors-0.4.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:87e9903b8668a16ef02c08ba4ebc91e57a49c481e9b5866e31d798632805014b"}, - {file = "safetensors-0.4.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a9c421153aa23c323bd8483d4155b4eee82c9a50ac11cccd83539104a8279c64"}, - {file = "safetensors-0.4.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a4b8617499b2371c7353302c5116a7e0a3a12da66389ce53140e607d3bf7b3d3"}, - {file = "safetensors-0.4.4-cp38-none-win32.whl", hash = "sha256:c6280f5aeafa1731f0a3709463ab33d8e0624321593951aefada5472f0b313fd"}, - {file = "safetensors-0.4.4-cp38-none-win_amd64.whl", hash = "sha256:6ceed6247fc2d33b2a7b7d25d8a0fe645b68798856e0bc7a9800c5fd945eb80f"}, - {file = "safetensors-0.4.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5cf6c6f6193797372adf50c91d0171743d16299491c75acad8650107dffa9269"}, - {file = "safetensors-0.4.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:419010156b914a3e5da4e4adf992bee050924d0fe423c4b329e523e2c14c3547"}, - {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88f6fd5a5c1302ce79993cc5feeadcc795a70f953c762544d01fb02b2db4ea33"}, - {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d468cffb82d90789696d5b4d8b6ab8843052cba58a15296691a7a3df55143cd2"}, - {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9353c2af2dd467333d4850a16edb66855e795561cd170685178f706c80d2c71e"}, - {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83c155b4a33368d9b9c2543e78f2452090fb030c52401ca608ef16fa58c98353"}, - {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9850754c434e636ce3dc586f534bb23bcbd78940c304775bee9005bf610e98f1"}, - {file = "safetensors-0.4.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:275f500b4d26f67b6ec05629a4600645231bd75e4ed42087a7c1801bff04f4b3"}, - {file = "safetensors-0.4.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5c2308de665b7130cd0e40a2329278226e4cf083f7400c51ca7e19ccfb3886f3"}, - {file = "safetensors-0.4.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e06a9ebc8656e030ccfe44634f2a541b4b1801cd52e390a53ad8bacbd65f8518"}, - {file = "safetensors-0.4.4-cp39-none-win32.whl", hash = "sha256:ef73df487b7c14b477016947c92708c2d929e1dee2bacdd6fff5a82ed4539537"}, - {file = "safetensors-0.4.4-cp39-none-win_amd64.whl", hash = "sha256:83d054818a8d1198d8bd8bc3ea2aac112a2c19def2bf73758321976788706398"}, - {file = "safetensors-0.4.4-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1d1f34c71371f0e034004a0b583284b45d233dd0b5f64a9125e16b8a01d15067"}, - {file = "safetensors-0.4.4-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a8043a33d58bc9b30dfac90f75712134ca34733ec3d8267b1bd682afe7194f5"}, - {file = "safetensors-0.4.4-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8db8f0c59c84792c12661f8efa85de160f80efe16b87a9d5de91b93f9e0bce3c"}, - {file = "safetensors-0.4.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cfc1fc38e37630dd12d519bdec9dcd4b345aec9930bb9ce0ed04461f49e58b52"}, - {file = "safetensors-0.4.4-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e5c9d86d9b13b18aafa88303e2cd21e677f5da2a14c828d2c460fe513af2e9a5"}, - {file = "safetensors-0.4.4-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:43251d7f29a59120a26f5a0d9583b9e112999e500afabcfdcb91606d3c5c89e3"}, - {file = "safetensors-0.4.4-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:2c42e9b277513b81cf507e6121c7b432b3235f980cac04f39f435b7902857f91"}, - {file = "safetensors-0.4.4-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3daacc9a4e3f428a84dd56bf31f20b768eb0b204af891ed68e1f06db9edf546f"}, - {file = "safetensors-0.4.4-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:218bbb9b883596715fc9997bb42470bf9f21bb832c3b34c2bf744d6fa8f2bbba"}, - {file = "safetensors-0.4.4-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bd5efc26b39f7fc82d4ab1d86a7f0644c8e34f3699c33f85bfa9a717a030e1b"}, - {file = "safetensors-0.4.4-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:56ad9776b65d8743f86698a1973292c966cf3abff627efc44ed60e66cc538ddd"}, - {file = "safetensors-0.4.4-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:30f23e6253c5f43a809dea02dc28a9f5fa747735dc819f10c073fe1b605e97d4"}, - {file = "safetensors-0.4.4-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5512078d00263de6cb04e9d26c9ae17611098f52357fea856213e38dc462f81f"}, - {file = "safetensors-0.4.4-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b96c3d9266439d17f35fc2173111d93afc1162f168e95aed122c1ca517b1f8f1"}, - {file = "safetensors-0.4.4-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:08d464aa72a9a13826946b4fb9094bb4b16554bbea2e069e20bd903289b6ced9"}, - {file = "safetensors-0.4.4-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:210160816d5a36cf41f48f38473b6f70d7bcb4b0527bedf0889cc0b4c3bb07db"}, - {file = "safetensors-0.4.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb276a53717f2bcfb6df0bcf284d8a12069002508d4c1ca715799226024ccd45"}, - {file = "safetensors-0.4.4-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a2c28c6487f17d8db0089e8b2cdc13de859366b94cc6cdc50e1b0a4147b56551"}, - {file = "safetensors-0.4.4-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:7915f0c60e4e6e65d90f136d85dd3b429ae9191c36b380e626064694563dbd9f"}, - {file = "safetensors-0.4.4-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:00eea99ae422fbfa0b46065acbc58b46bfafadfcec179d4b4a32d5c45006af6c"}, - {file = "safetensors-0.4.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:bb1ed4fcb0b3c2f3ea2c5767434622fe5d660e5752f21ac2e8d737b1e5e480bb"}, - {file = "safetensors-0.4.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:73fc9a0a4343188bdb421783e600bfaf81d0793cd4cce6bafb3c2ed567a74cd5"}, - {file = "safetensors-0.4.4-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c37e6b714200824c73ca6eaf007382de76f39466a46e97558b8dc4cf643cfbf"}, - {file = "safetensors-0.4.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f75698c5c5c542417ac4956acfc420f7d4a2396adca63a015fd66641ea751759"}, - {file = "safetensors-0.4.4-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ca1a209157f242eb183e209040097118472e169f2e069bfbd40c303e24866543"}, - {file = "safetensors-0.4.4-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:177f2b60a058f92a3cec7a1786c9106c29eca8987ecdfb79ee88126e5f47fa31"}, - {file = "safetensors-0.4.4-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ee9622e84fe6e4cd4f020e5fda70d6206feff3157731df7151d457fdae18e541"}, - {file = "safetensors-0.4.4.tar.gz", hash = "sha256:5fe3e9b705250d0172ed4e100a811543108653fb2b66b9e702a088ad03772a07"}, + {file = "ruff-0.5.7-py3-none-linux_armv6l.whl", hash = "sha256:548992d342fc404ee2e15a242cdbea4f8e39a52f2e7752d0e4cbe88d2d2f416a"}, + {file = "ruff-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00cc8872331055ee017c4f1071a8a31ca0809ccc0657da1d154a1d2abac5c0be"}, + {file = "ruff-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eaf3d86a1fdac1aec8a3417a63587d93f906c678bb9ed0b796da7b59c1114a1e"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a01c34400097b06cf8a6e61b35d6d456d5bd1ae6961542de18ec81eaf33b4cb8"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc8054f1a717e2213500edaddcf1dbb0abad40d98e1bd9d0ad364f75c763eea"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7f70284e73f36558ef51602254451e50dd6cc479f8b6f8413a95fcb5db4a55fc"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:a78ad870ae3c460394fc95437d43deb5c04b5c29297815a2a1de028903f19692"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ccd078c66a8e419475174bfe60a69adb36ce04f8d4e91b006f1329d5cd44bcf"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7e31c9bad4ebf8fdb77b59cae75814440731060a09a0e0077d559a556453acbb"}, + {file = "ruff-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d796327eed8e168164346b769dd9a27a70e0298d667b4ecee6877ce8095ec8e"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4a09ea2c3f7778cc635e7f6edf57d566a8ee8f485f3c4454db7771efb692c499"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a36d8dcf55b3a3bc353270d544fb170d75d2dff41eba5df57b4e0b67a95bb64e"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9369c218f789eefbd1b8d82a8cf25017b523ac47d96b2f531eba73770971c9e5"}, + {file = "ruff-0.5.7-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b88ca3db7eb377eb24fb7c82840546fb7acef75af4a74bd36e9ceb37a890257e"}, + {file = "ruff-0.5.7-py3-none-win32.whl", hash = "sha256:33d61fc0e902198a3e55719f4be6b375b28f860b09c281e4bdbf783c0566576a"}, + {file = "ruff-0.5.7-py3-none-win_amd64.whl", hash = "sha256:083bbcbe6fadb93cd86709037acc510f86eed5a314203079df174c40bbbca6b3"}, + {file = "ruff-0.5.7-py3-none-win_arm64.whl", hash = "sha256:2dca26154ff9571995107221d0aeaad0e75a77b5a682d6236cf89a58c70b76f4"}, + {file = "ruff-0.5.7.tar.gz", hash = "sha256:8dfc0a458797f5d9fb622dd0efc52d796f23f0a1493a9527f4e49a550ae9a7e5"}, ] -[package.extras] -all = ["safetensors[jax]", "safetensors[numpy]", "safetensors[paddlepaddle]", "safetensors[pinned-tf]", "safetensors[quality]", "safetensors[testing]", "safetensors[torch]"] -dev = ["safetensors[all]"] -jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "safetensors[numpy]"] -mlx = ["mlx (>=0.0.9)"] -numpy = ["numpy (>=1.21.6)"] -paddlepaddle = ["paddlepaddle (>=2.4.1)", "safetensors[numpy]"] -pinned-tf = ["safetensors[numpy]", "tensorflow (==2.11.0)"] -quality = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] -tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"] -testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"] -torch = ["safetensors[numpy]", "torch (>=1.10)"] - [[package]] name = "send2trash" version = "1.8.3" @@ -4798,58 +4264,6 @@ core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.te doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -[[package]] -name = "shapely" -version = "2.0.5" -description = "Manipulation and analysis of geometric objects" -optional = false -python-versions = ">=3.7" -files = [ - {file = "shapely-2.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89d34787c44f77a7d37d55ae821f3a784fa33592b9d217a45053a93ade899375"}, - {file = "shapely-2.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:798090b426142df2c5258779c1d8d5734ec6942f778dab6c6c30cfe7f3bf64ff"}, - {file = "shapely-2.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45211276900c4790d6bfc6105cbf1030742da67594ea4161a9ce6812a6721e68"}, - {file = "shapely-2.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e119444bc27ca33e786772b81760f2028d930ac55dafe9bc50ef538b794a8e1"}, - {file = "shapely-2.0.5-cp310-cp310-win32.whl", hash = "sha256:9a4492a2b2ccbeaebf181e7310d2dfff4fdd505aef59d6cb0f217607cb042fb3"}, - {file = "shapely-2.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:1e5cb5ee72f1bc7ace737c9ecd30dc174a5295fae412972d3879bac2e82c8fae"}, - {file = "shapely-2.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bbfb048a74cf273db9091ff3155d373020852805a37dfc846ab71dde4be93ec"}, - {file = "shapely-2.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93be600cbe2fbaa86c8eb70656369f2f7104cd231f0d6585c7d0aa555d6878b8"}, - {file = "shapely-2.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8e71bb9a46814019f6644c4e2560a09d44b80100e46e371578f35eaaa9da1c"}, - {file = "shapely-2.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5251c28a29012e92de01d2e84f11637eb1d48184ee8f22e2df6c8c578d26760"}, - {file = "shapely-2.0.5-cp311-cp311-win32.whl", hash = "sha256:35110e80070d664781ec7955c7de557456b25727a0257b354830abb759bf8311"}, - {file = "shapely-2.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c6b78c0007a34ce7144f98b7418800e0a6a5d9a762f2244b00ea560525290c9"}, - {file = "shapely-2.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:03bd7b5fa5deb44795cc0a503999d10ae9d8a22df54ae8d4a4cd2e8a93466195"}, - {file = "shapely-2.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ff9521991ed9e201c2e923da014e766c1aa04771bc93e6fe97c27dcf0d40ace"}, - {file = "shapely-2.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b65365cfbf657604e50d15161ffcc68de5cdb22a601bbf7823540ab4918a98d"}, - {file = "shapely-2.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21f64e647a025b61b19585d2247137b3a38a35314ea68c66aaf507a1c03ef6fe"}, - {file = "shapely-2.0.5-cp312-cp312-win32.whl", hash = "sha256:3ac7dc1350700c139c956b03d9c3df49a5b34aaf91d024d1510a09717ea39199"}, - {file = "shapely-2.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:30e8737983c9d954cd17feb49eb169f02f1da49e24e5171122cf2c2b62d65c95"}, - {file = "shapely-2.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ff7731fea5face9ec08a861ed351734a79475631b7540ceb0b66fb9732a5f529"}, - {file = "shapely-2.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff9e520af0c5a578e174bca3c18713cd47a6c6a15b6cf1f50ac17dc8bb8db6a2"}, - {file = "shapely-2.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b299b91557b04acb75e9732645428470825061f871a2edc36b9417d66c1fc5"}, - {file = "shapely-2.0.5-cp37-cp37m-win32.whl", hash = "sha256:b5870633f8e684bf6d1ae4df527ddcb6f3895f7b12bced5c13266ac04f47d231"}, - {file = "shapely-2.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:401cb794c5067598f50518e5a997e270cd7642c4992645479b915c503866abed"}, - {file = "shapely-2.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e91ee179af539100eb520281ba5394919067c6b51824e6ab132ad4b3b3e76dd0"}, - {file = "shapely-2.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8af6f7260f809c0862741ad08b1b89cb60c130ae30efab62320bbf4ee9cc71fa"}, - {file = "shapely-2.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5456dd522800306ba3faef77c5ba847ec30a0bd73ab087a25e0acdd4db2514f"}, - {file = "shapely-2.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b714a840402cde66fd7b663bb08cacb7211fa4412ea2a209688f671e0d0631fd"}, - {file = "shapely-2.0.5-cp38-cp38-win32.whl", hash = "sha256:7e8cf5c252fac1ea51b3162be2ec3faddedc82c256a1160fc0e8ddbec81b06d2"}, - {file = "shapely-2.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4461509afdb15051e73ab178fae79974387f39c47ab635a7330d7fee02c68a3f"}, - {file = "shapely-2.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7545a39c55cad1562be302d74c74586f79e07b592df8ada56b79a209731c0219"}, - {file = "shapely-2.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c83a36f12ec8dee2066946d98d4d841ab6512a6ed7eb742e026a64854019b5f"}, - {file = "shapely-2.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89e640c2cd37378480caf2eeda9a51be64201f01f786d127e78eaeff091ec897"}, - {file = "shapely-2.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06efe39beafde3a18a21dde169d32f315c57da962826a6d7d22630025200c5e6"}, - {file = "shapely-2.0.5-cp39-cp39-win32.whl", hash = "sha256:8203a8b2d44dcb366becbc8c3d553670320e4acf0616c39e218c9561dd738d92"}, - {file = "shapely-2.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:7fed9dbfbcfec2682d9a047b9699db8dcc890dfca857ecba872c42185fc9e64e"}, - {file = "shapely-2.0.5.tar.gz", hash = "sha256:bff2366bc786bfa6cb353d6b47d0443c570c32776612e527ee47b6df63fcfe32"}, -] - -[package.dependencies] -numpy = ">=1.14,<3" - -[package.extras] -docs = ["matplotlib", "numpydoc (==1.1.*)", "sphinx", "sphinx-book-theme", "sphinx-remove-toctrees"] -test = ["pytest", "pytest-cov"] - [[package]] name = "shellingham" version = "1.5.4" @@ -5141,47 +4555,47 @@ typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"] [[package]] name = "tiktoken" -version = "0.6.0" +version = "0.7.0" description = "tiktoken is a fast BPE tokeniser for use with OpenAI's models" optional = false python-versions = ">=3.8" files = [ - {file = "tiktoken-0.6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:277de84ccd8fa12730a6b4067456e5cf72fef6300bea61d506c09e45658d41ac"}, - {file = "tiktoken-0.6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9c44433f658064463650d61387623735641dcc4b6c999ca30bc0f8ba3fccaf5c"}, - {file = "tiktoken-0.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afb9a2a866ae6eef1995ab656744287a5ac95acc7e0491c33fad54d053288ad3"}, - {file = "tiktoken-0.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c62c05b3109fefca26fedb2820452a050074ad8e5ad9803f4652977778177d9f"}, - {file = "tiktoken-0.6.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ef917fad0bccda07bfbad835525bbed5f3ab97a8a3e66526e48cdc3e7beacf7"}, - {file = "tiktoken-0.6.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e095131ab6092d0769a2fda85aa260c7c383072daec599ba9d8b149d2a3f4d8b"}, - {file = "tiktoken-0.6.0-cp310-cp310-win_amd64.whl", hash = "sha256:05b344c61779f815038292a19a0c6eb7098b63c8f865ff205abb9ea1b656030e"}, - {file = "tiktoken-0.6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cefb9870fb55dca9e450e54dbf61f904aab9180ff6fe568b61f4db9564e78871"}, - {file = "tiktoken-0.6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:702950d33d8cabc039845674107d2e6dcabbbb0990ef350f640661368df481bb"}, - {file = "tiktoken-0.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8d49d076058f23254f2aff9af603863c5c5f9ab095bc896bceed04f8f0b013a"}, - {file = "tiktoken-0.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:430bc4e650a2d23a789dc2cdca3b9e5e7eb3cd3935168d97d43518cbb1f9a911"}, - {file = "tiktoken-0.6.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:293cb8669757301a3019a12d6770bd55bec38a4d3ee9978ddbe599d68976aca7"}, - {file = "tiktoken-0.6.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:7bd1a288b7903aadc054b0e16ea78e3171f70b670e7372432298c686ebf9dd47"}, - {file = "tiktoken-0.6.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac76e000183e3b749634968a45c7169b351e99936ef46f0d2353cd0d46c3118d"}, - {file = "tiktoken-0.6.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17cc8a4a3245ab7d935c83a2db6bb71619099d7284b884f4b2aea4c74f2f83e3"}, - {file = "tiktoken-0.6.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:284aebcccffe1bba0d6571651317df6a5b376ff6cfed5aeb800c55df44c78177"}, - {file = "tiktoken-0.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c1a3a5d33846f8cd9dd3b7897c1d45722f48625a587f8e6f3d3e85080559be8"}, - {file = "tiktoken-0.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6318b2bb2337f38ee954fd5efa82632c6e5ced1d52a671370fa4b2eff1355e91"}, - {file = "tiktoken-0.6.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f5f0f2ed67ba16373f9a6013b68da298096b27cd4e1cf276d2d3868b5c7efd1"}, - {file = "tiktoken-0.6.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:75af4c0b16609c2ad02581f3cdcd1fb698c7565091370bf6c0cf8624ffaba6dc"}, - {file = "tiktoken-0.6.0-cp312-cp312-win_amd64.whl", hash = "sha256:45577faf9a9d383b8fd683e313cf6df88b6076c034f0a16da243bb1c139340c3"}, - {file = "tiktoken-0.6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7c1492ab90c21ca4d11cef3a236ee31a3e279bb21b3fc5b0e2210588c4209e68"}, - {file = "tiktoken-0.6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e2b380c5b7751272015400b26144a2bab4066ebb8daae9c3cd2a92c3b508fe5a"}, - {file = "tiktoken-0.6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9f497598b9f58c99cbc0eb764b4a92272c14d5203fc713dd650b896a03a50ad"}, - {file = "tiktoken-0.6.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e65e8bd6f3f279d80f1e1fbd5f588f036b9a5fa27690b7f0cc07021f1dfa0839"}, - {file = "tiktoken-0.6.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5f1495450a54e564d236769d25bfefbf77727e232d7a8a378f97acddee08c1ae"}, - {file = "tiktoken-0.6.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6c4e4857d99f6fb4670e928250835b21b68c59250520a1941618b5b4194e20c3"}, - {file = "tiktoken-0.6.0-cp38-cp38-win_amd64.whl", hash = "sha256:168d718f07a39b013032741867e789971346df8e89983fe3c0ef3fbd5a0b1cb9"}, - {file = "tiktoken-0.6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:47fdcfe11bd55376785a6aea8ad1db967db7f66ea81aed5c43fad497521819a4"}, - {file = "tiktoken-0.6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fb7d2ccbf1a7784810aff6b80b4012fb42c6fc37eaa68cb3b553801a5cc2d1fc"}, - {file = "tiktoken-0.6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ccb7a111ee76af5d876a729a347f8747d5ad548e1487eeea90eaf58894b3138"}, - {file = "tiktoken-0.6.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2048e1086b48e3c8c6e2ceeac866561374cd57a84622fa49a6b245ffecb7744"}, - {file = "tiktoken-0.6.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:07f229a5eb250b6403a61200199cecf0aac4aa23c3ecc1c11c1ca002cbb8f159"}, - {file = "tiktoken-0.6.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:432aa3be8436177b0db5a2b3e7cc28fd6c693f783b2f8722539ba16a867d0c6a"}, - {file = "tiktoken-0.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:8bfe8a19c8b5c40d121ee7938cd9c6a278e5b97dc035fd61714b4f0399d2f7a1"}, - {file = "tiktoken-0.6.0.tar.gz", hash = "sha256:ace62a4ede83c75b0374a2ddfa4b76903cf483e9cb06247f566be3bf14e6beed"}, + {file = "tiktoken-0.7.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:485f3cc6aba7c6b6ce388ba634fbba656d9ee27f766216f45146beb4ac18b25f"}, + {file = "tiktoken-0.7.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e54be9a2cd2f6d6ffa3517b064983fb695c9a9d8aa7d574d1ef3c3f931a99225"}, + {file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79383a6e2c654c6040e5f8506f3750db9ddd71b550c724e673203b4f6b4b4590"}, + {file = "tiktoken-0.7.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d4511c52caacf3c4981d1ae2df85908bd31853f33d30b345c8b6830763f769c"}, + {file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13c94efacdd3de9aff824a788353aa5749c0faee1fbe3816df365ea450b82311"}, + {file = "tiktoken-0.7.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8e58c7eb29d2ab35a7a8929cbeea60216a4ccdf42efa8974d8e176d50c9a3df5"}, + {file = "tiktoken-0.7.0-cp310-cp310-win_amd64.whl", hash = "sha256:21a20c3bd1dd3e55b91c1331bf25f4af522c525e771691adbc9a69336fa7f702"}, + {file = "tiktoken-0.7.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:10c7674f81e6e350fcbed7c09a65bca9356eaab27fb2dac65a1e440f2bcfe30f"}, + {file = "tiktoken-0.7.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:084cec29713bc9d4189a937f8a35dbdfa785bd1235a34c1124fe2323821ee93f"}, + {file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:811229fde1652fedcca7c6dfe76724d0908775b353556d8a71ed74d866f73f7b"}, + {file = "tiktoken-0.7.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:86b6e7dc2e7ad1b3757e8a24597415bafcfb454cebf9a33a01f2e6ba2e663992"}, + {file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1063c5748be36344c7e18c7913c53e2cca116764c2080177e57d62c7ad4576d1"}, + {file = "tiktoken-0.7.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:20295d21419bfcca092644f7e2f2138ff947a6eb8cfc732c09cc7d76988d4a89"}, + {file = "tiktoken-0.7.0-cp311-cp311-win_amd64.whl", hash = "sha256:959d993749b083acc57a317cbc643fb85c014d055b2119b739487288f4e5d1cb"}, + {file = "tiktoken-0.7.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:71c55d066388c55a9c00f61d2c456a6086673ab7dec22dd739c23f77195b1908"}, + {file = "tiktoken-0.7.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:09ed925bccaa8043e34c519fbb2f99110bd07c6fd67714793c21ac298e449410"}, + {file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03c6c40ff1db0f48a7b4d2dafeae73a5607aacb472fa11f125e7baf9dce73704"}, + {file = "tiktoken-0.7.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d20b5c6af30e621b4aca094ee61777a44118f52d886dbe4f02b70dfe05c15350"}, + {file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d427614c3e074004efa2f2411e16c826f9df427d3c70a54725cae860f09e4bf4"}, + {file = "tiktoken-0.7.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8c46d7af7b8c6987fac9b9f61041b452afe92eb087d29c9ce54951280f899a97"}, + {file = "tiktoken-0.7.0-cp312-cp312-win_amd64.whl", hash = "sha256:0bc603c30b9e371e7c4c7935aba02af5994a909fc3c0fe66e7004070858d3f8f"}, + {file = "tiktoken-0.7.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2398fecd38c921bcd68418675a6d155fad5f5e14c2e92fcf5fe566fa5485a858"}, + {file = "tiktoken-0.7.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f5f6afb52fb8a7ea1c811e435e4188f2bef81b5e0f7a8635cc79b0eef0193d6"}, + {file = "tiktoken-0.7.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:861f9ee616766d736be4147abac500732b505bf7013cfaf019b85892637f235e"}, + {file = "tiktoken-0.7.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:54031f95c6939f6b78122c0aa03a93273a96365103793a22e1793ee86da31685"}, + {file = "tiktoken-0.7.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:fffdcb319b614cf14f04d02a52e26b1d1ae14a570f90e9b55461a72672f7b13d"}, + {file = "tiktoken-0.7.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c72baaeaefa03ff9ba9688624143c858d1f6b755bb85d456d59e529e17234769"}, + {file = "tiktoken-0.7.0-cp38-cp38-win_amd64.whl", hash = "sha256:131b8aeb043a8f112aad9f46011dced25d62629091e51d9dc1adbf4a1cc6aa98"}, + {file = "tiktoken-0.7.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:cabc6dc77460df44ec5b879e68692c63551ae4fae7460dd4ff17181df75f1db7"}, + {file = "tiktoken-0.7.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8d57f29171255f74c0aeacd0651e29aa47dff6f070cb9f35ebc14c82278f3b25"}, + {file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ee92776fdbb3efa02a83f968c19d4997a55c8e9ce7be821ceee04a1d1ee149c"}, + {file = "tiktoken-0.7.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e215292e99cb41fbc96988ef62ea63bb0ce1e15f2c147a61acc319f8b4cbe5bf"}, + {file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8a81bac94769cab437dd3ab0b8a4bc4e0f9cf6835bcaa88de71f39af1791727a"}, + {file = "tiktoken-0.7.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d6d73ea93e91d5ca771256dfc9d1d29f5a554b83821a1dc0891987636e0ae226"}, + {file = "tiktoken-0.7.0-cp39-cp39-win_amd64.whl", hash = "sha256:2bcb28ddf79ffa424f171dfeef9a4daff61a94c631ca6813f43967cb263b83b9"}, + {file = "tiktoken-0.7.0.tar.gz", hash = "sha256:1077266e949c24e0291f6c350433c6f0971365ece2b173a23bc3b9f9defef6b6"}, ] [package.dependencies] @@ -5211,111 +4625,111 @@ test = ["pytest", "ruff"] [[package]] name = "tokenizers" -version = "0.19.1" +version = "0.20.0" description = "" optional = false python-versions = ">=3.7" files = [ - {file = "tokenizers-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:952078130b3d101e05ecfc7fc3640282d74ed26bcf691400f872563fca15ac97"}, - {file = "tokenizers-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:82c8b8063de6c0468f08e82c4e198763e7b97aabfe573fd4cf7b33930ca4df77"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f03727225feaf340ceeb7e00604825addef622d551cbd46b7b775ac834c1e1c4"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:453e4422efdfc9c6b6bf2eae00d5e323f263fff62b29a8c9cd526c5003f3f642"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:02e81bf089ebf0e7f4df34fa0207519f07e66d8491d963618252f2e0729e0b46"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b07c538ba956843833fee1190cf769c60dc62e1cf934ed50d77d5502194d63b1"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28cab1582e0eec38b1f38c1c1fb2e56bce5dc180acb1724574fc5f47da2a4fe"}, - {file = "tokenizers-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b01afb7193d47439f091cd8f070a1ced347ad0f9144952a30a41836902fe09e"}, - {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7fb297edec6c6841ab2e4e8f357209519188e4a59b557ea4fafcf4691d1b4c98"}, - {file = "tokenizers-0.19.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2e8a3dd055e515df7054378dc9d6fa8c8c34e1f32777fb9a01fea81496b3f9d3"}, - {file = "tokenizers-0.19.1-cp310-none-win32.whl", hash = "sha256:7ff898780a155ea053f5d934925f3902be2ed1f4d916461e1a93019cc7250837"}, - {file = "tokenizers-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:bea6f9947e9419c2fda21ae6c32871e3d398cba549b93f4a65a2d369662d9403"}, - {file = "tokenizers-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:5c88d1481f1882c2e53e6bb06491e474e420d9ac7bdff172610c4f9ad3898059"}, - {file = "tokenizers-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ddf672ed719b4ed82b51499100f5417d7d9f6fb05a65e232249268f35de5ed14"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dadc509cc8a9fe460bd274c0e16ac4184d0958117cf026e0ea8b32b438171594"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfedf31824ca4915b511b03441784ff640378191918264268e6923da48104acc"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac11016d0a04aa6487b1513a3a36e7bee7eec0e5d30057c9c0408067345c48d2"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:76951121890fea8330d3a0df9a954b3f2a37e3ec20e5b0530e9a0044ca2e11fe"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b342d2ce8fc8d00f376af068e3274e2e8649562e3bc6ae4a67784ded6b99428d"}, - {file = "tokenizers-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d16ff18907f4909dca9b076b9c2d899114dd6abceeb074eca0c93e2353f943aa"}, - {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:706a37cc5332f85f26efbe2bdc9ef8a9b372b77e4645331a405073e4b3a8c1c6"}, - {file = "tokenizers-0.19.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16baac68651701364b0289979ecec728546133e8e8fe38f66fe48ad07996b88b"}, - {file = "tokenizers-0.19.1-cp311-none-win32.whl", hash = "sha256:9ed240c56b4403e22b9584ee37d87b8bfa14865134e3e1c3fb4b2c42fafd3256"}, - {file = "tokenizers-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:ad57d59341710b94a7d9dbea13f5c1e7d76fd8d9bcd944a7a6ab0b0da6e0cc66"}, - {file = "tokenizers-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:621d670e1b1c281a1c9698ed89451395d318802ff88d1fc1accff0867a06f153"}, - {file = "tokenizers-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d924204a3dbe50b75630bd16f821ebda6a5f729928df30f582fb5aade90c818a"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4f3fefdc0446b1a1e6d81cd4c07088ac015665d2e812f6dbba4a06267d1a2c95"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9620b78e0b2d52ef07b0d428323fb34e8ea1219c5eac98c2596311f20f1f9266"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:04ce49e82d100594715ac1b2ce87d1a36e61891a91de774755f743babcd0dd52"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c5c2ff13d157afe413bf7e25789879dd463e5a4abfb529a2d8f8473d8042e28f"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3174c76efd9d08f836bfccaca7cfec3f4d1c0a4cf3acbc7236ad577cc423c840"}, - {file = "tokenizers-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c9d5b6c0e7a1e979bec10ff960fae925e947aab95619a6fdb4c1d8ff3708ce3"}, - {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a179856d1caee06577220ebcfa332af046d576fb73454b8f4d4b0ba8324423ea"}, - {file = "tokenizers-0.19.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:952b80dac1a6492170f8c2429bd11fcaa14377e097d12a1dbe0ef2fb2241e16c"}, - {file = "tokenizers-0.19.1-cp312-none-win32.whl", hash = "sha256:01d62812454c188306755c94755465505836fd616f75067abcae529c35edeb57"}, - {file = "tokenizers-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:b70bfbe3a82d3e3fb2a5e9b22a39f8d1740c96c68b6ace0086b39074f08ab89a"}, - {file = "tokenizers-0.19.1-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:bb9dfe7dae85bc6119d705a76dc068c062b8b575abe3595e3c6276480e67e3f1"}, - {file = "tokenizers-0.19.1-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:1f0360cbea28ea99944ac089c00de7b2e3e1c58f479fb8613b6d8d511ce98267"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:71e3ec71f0e78780851fef28c2a9babe20270404c921b756d7c532d280349214"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b82931fa619dbad979c0ee8e54dd5278acc418209cc897e42fac041f5366d626"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e8ff5b90eabdcdaa19af697885f70fe0b714ce16709cf43d4952f1f85299e73a"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e742d76ad84acbdb1a8e4694f915fe59ff6edc381c97d6dfdd054954e3478ad4"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d8c5d59d7b59885eab559d5bc082b2985555a54cda04dda4c65528d90ad252ad"}, - {file = "tokenizers-0.19.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b2da5c32ed869bebd990c9420df49813709e953674c0722ff471a116d97b22d"}, - {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:638e43936cc8b2cbb9f9d8dde0fe5e7e30766a3318d2342999ae27f68fdc9bd6"}, - {file = "tokenizers-0.19.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:78e769eb3b2c79687d9cb0f89ef77223e8e279b75c0a968e637ca7043a84463f"}, - {file = "tokenizers-0.19.1-cp37-none-win32.whl", hash = "sha256:72791f9bb1ca78e3ae525d4782e85272c63faaef9940d92142aa3eb79f3407a3"}, - {file = "tokenizers-0.19.1-cp37-none-win_amd64.whl", hash = "sha256:f3bbb7a0c5fcb692950b041ae11067ac54826204318922da754f908d95619fbc"}, - {file = "tokenizers-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:07f9295349bbbcedae8cefdbcfa7f686aa420be8aca5d4f7d1ae6016c128c0c5"}, - {file = "tokenizers-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:10a707cc6c4b6b183ec5dbfc5c34f3064e18cf62b4a938cb41699e33a99e03c1"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6309271f57b397aa0aff0cbbe632ca9d70430839ca3178bf0f06f825924eca22"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ad23d37d68cf00d54af184586d79b84075ada495e7c5c0f601f051b162112dc"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:427c4f0f3df9109314d4f75b8d1f65d9477033e67ffaec4bca53293d3aca286d"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e83a31c9cf181a0a3ef0abad2b5f6b43399faf5da7e696196ddd110d332519ee"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c27b99889bd58b7e301468c0838c5ed75e60c66df0d4db80c08f43462f82e0d3"}, - {file = "tokenizers-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bac0b0eb952412b0b196ca7a40e7dce4ed6f6926489313414010f2e6b9ec2adf"}, - {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:8a6298bde623725ca31c9035a04bf2ef63208d266acd2bed8c2cb7d2b7d53ce6"}, - {file = "tokenizers-0.19.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:08a44864e42fa6d7d76d7be4bec62c9982f6f6248b4aa42f7302aa01e0abfd26"}, - {file = "tokenizers-0.19.1-cp38-none-win32.whl", hash = "sha256:1de5bc8652252d9357a666e609cb1453d4f8e160eb1fb2830ee369dd658e8975"}, - {file = "tokenizers-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:0bcce02bf1ad9882345b34d5bd25ed4949a480cf0e656bbd468f4d8986f7a3f1"}, - {file = "tokenizers-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0b9394bd204842a2a1fd37fe29935353742be4a3460b6ccbaefa93f58a8df43d"}, - {file = "tokenizers-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4692ab92f91b87769d950ca14dbb61f8a9ef36a62f94bad6c82cc84a51f76f6a"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6258c2ef6f06259f70a682491c78561d492e885adeaf9f64f5389f78aa49a051"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c85cf76561fbd01e0d9ea2d1cbe711a65400092bc52b5242b16cfd22e51f0c58"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:670b802d4d82bbbb832ddb0d41df7015b3e549714c0e77f9bed3e74d42400fbe"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:85aa3ab4b03d5e99fdd31660872249df5e855334b6c333e0bc13032ff4469c4a"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cbf001afbbed111a79ca47d75941e9e5361297a87d186cbfc11ed45e30b5daba"}, - {file = "tokenizers-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c89aa46c269e4e70c4d4f9d6bc644fcc39bb409cb2a81227923404dd6f5227"}, - {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:39c1ec76ea1027438fafe16ecb0fb84795e62e9d643444c1090179e63808c69d"}, - {file = "tokenizers-0.19.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c2a0d47a89b48d7daa241e004e71fb5a50533718897a4cd6235cb846d511a478"}, - {file = "tokenizers-0.19.1-cp39-none-win32.whl", hash = "sha256:61b7fe8886f2e104d4caf9218b157b106207e0f2a4905c9c7ac98890688aabeb"}, - {file = "tokenizers-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:f97660f6c43efd3e0bfd3f2e3e5615bf215680bad6ee3d469df6454b8c6e8256"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:3b11853f17b54c2fe47742c56d8a33bf49ce31caf531e87ac0d7d13d327c9334"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d26194ef6c13302f446d39972aaa36a1dda6450bc8949f5eb4c27f51191375bd"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e8d1ed93beda54bbd6131a2cb363a576eac746d5c26ba5b7556bc6f964425594"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca407133536f19bdec44b3da117ef0d12e43f6d4b56ac4c765f37eca501c7bda"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce05fde79d2bc2e46ac08aacbc142bead21614d937aac950be88dc79f9db9022"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:35583cd46d16f07c054efd18b5d46af4a2f070a2dd0a47914e66f3ff5efb2b1e"}, - {file = "tokenizers-0.19.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:43350270bfc16b06ad3f6f07eab21f089adb835544417afda0f83256a8bf8b75"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b4399b59d1af5645bcee2072a463318114c39b8547437a7c2d6a186a1b5a0e2d"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6852c5b2a853b8b0ddc5993cd4f33bfffdca4fcc5d52f89dd4b8eada99379285"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bcd266ae85c3d39df2f7e7d0e07f6c41a55e9a3123bb11f854412952deacd828"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecb2651956eea2aa0a2d099434134b1b68f1c31f9a5084d6d53f08ed43d45ff2"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:b279ab506ec4445166ac476fb4d3cc383accde1ea152998509a94d82547c8e2a"}, - {file = "tokenizers-0.19.1-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:89183e55fb86e61d848ff83753f64cded119f5d6e1f553d14ffee3700d0a4a49"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2edbc75744235eea94d595a8b70fe279dd42f3296f76d5a86dde1d46e35f574"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:0e64bfde9a723274e9a71630c3e9494ed7b4c0f76a1faacf7fe294cd26f7ae7c"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0b5ca92bfa717759c052e345770792d02d1f43b06f9e790ca0a1db62838816f3"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f8a20266e695ec9d7a946a019c1d5ca4eddb6613d4f466888eee04f16eedb85"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63c38f45d8f2a2ec0f3a20073cccb335b9f99f73b3c69483cd52ebc75369d8a1"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dd26e3afe8a7b61422df3176e06664503d3f5973b94f45d5c45987e1cb711876"}, - {file = "tokenizers-0.19.1-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:eddd5783a4a6309ce23432353cdb36220e25cbb779bfa9122320666508b44b88"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:56ae39d4036b753994476a1b935584071093b55c7a72e3b8288e68c313ca26e7"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f9939ca7e58c2758c01b40324a59c034ce0cebad18e0d4563a9b1beab3018243"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c330c0eb815d212893c67a032e9dc1b38a803eccb32f3e8172c19cc69fbb439"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec11802450a2487cdf0e634b750a04cbdc1c4d066b97d94ce7dd2cb51ebb325b"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2b718f316b596f36e1dae097a7d5b91fc5b85e90bf08b01ff139bd8953b25af"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ed69af290c2b65169f0ba9034d1dc39a5db9459b32f1dd8b5f3f32a3fcf06eab"}, - {file = "tokenizers-0.19.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f8a9c828277133af13f3859d1b6bf1c3cb6e9e1637df0e45312e6b7c2e622b1f"}, - {file = "tokenizers-0.19.1.tar.gz", hash = "sha256:ee59e6680ed0fdbe6b724cf38bd70400a0c1dd623b07ac729087270caeac88e3"}, + {file = "tokenizers-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6cff5c5e37c41bc5faa519d6f3df0679e4b37da54ea1f42121719c5e2b4905c0"}, + {file = "tokenizers-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:62a56bf75c27443432456f4ca5ca055befa95e25be8a28141cc495cac8ae4d6d"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68cc7de6a63f09c4a86909c2597b995aa66e19df852a23aea894929c74369929"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:053c37ecee482cc958fdee53af3c6534286a86f5d35aac476f7c246830e53ae5"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d7074aaabc151a6363fa03db5493fc95b423b2a1874456783989e96d541c7b6"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a11435780f2acd89e8fefe5e81cecf01776f6edb9b3ac95bcb76baee76b30b90"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9a81cd2712973b007d84268d45fc3f6f90a79c31dfe7f1925e6732f8d2959987"}, + {file = "tokenizers-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7dfd796ab9d909f76fb93080e1c7c8309f196ecb316eb130718cd5e34231c69"}, + {file = "tokenizers-0.20.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8029ad2aa8cb00605c9374566034c1cc1b15130713e0eb5afcef6cface8255c9"}, + {file = "tokenizers-0.20.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ca4d54260ebe97d59dfa9a30baa20d0c4dd9137d99a8801700055c561145c24e"}, + {file = "tokenizers-0.20.0-cp310-none-win32.whl", hash = "sha256:95ee16b57cec11b86a7940174ec5197d506439b0f415ab3859f254b1dffe9df0"}, + {file = "tokenizers-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:0a61a11e93eeadbf02aea082ffc75241c4198e0608bbbac4f65a9026851dcf37"}, + {file = "tokenizers-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6636b798b3c4d6c9b1af1a918bd07c867808e5a21c64324e95318a237e6366c3"}, + {file = "tokenizers-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ec603e42eaf499ffd58b9258162add948717cf21372458132f14e13a6bc7172"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cce124264903a8ea6f8f48e1cc7669e5ef638c18bd4ab0a88769d5f92debdf7f"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:07bbeba0231cf8de07aa6b9e33e9779ff103d47042eeeb859a8c432e3292fb98"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:06c0ca8397b35d38b83a44a9c6929790c1692957d88541df061cb34d82ebbf08"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ca6557ac3b83d912dfbb1f70ab56bd4b0594043916688e906ede09f42e192401"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a5ad94c9e80ac6098328bee2e3264dbced4c6faa34429994d473f795ec58ef4"}, + {file = "tokenizers-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b5c7f906ee6bec30a9dc20268a8b80f3b9584de1c9f051671cb057dc6ce28f6"}, + {file = "tokenizers-0.20.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:31e087e9ee1b8f075b002bfee257e858dc695f955b43903e1bb4aa9f170e37fe"}, + {file = "tokenizers-0.20.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c3124fb6f3346cb3d8d775375d3b429bf4dcfc24f739822702009d20a4297990"}, + {file = "tokenizers-0.20.0-cp311-none-win32.whl", hash = "sha256:a4bb8b40ba9eefa621fdcabf04a74aa6038ae3be0c614c6458bd91a4697a452f"}, + {file = "tokenizers-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:2b709d371f1fe60a28ef0c5c67815952d455ca7f34dbe7197eaaed3cc54b658e"}, + {file = "tokenizers-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:15c81a17d0d66f4987c6ca16f4bea7ec253b8c7ed1bb00fdc5d038b1bb56e714"}, + {file = "tokenizers-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6a531cdf1fb6dc41c984c785a3b299cb0586de0b35683842a3afbb1e5207f910"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06caabeb4587f8404e0cd9d40f458e9cba3e815c8155a38e579a74ff3e2a4301"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8768f964f23f5b9f50546c0369c75ab3262de926983888bbe8b98be05392a79c"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:626403860152c816f97b649fd279bd622c3d417678c93b4b1a8909b6380b69a8"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c1b88fa9e5ff062326f4bf82681da5a96fca7104d921a6bd7b1e6fcf224af26"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d7e559436a07dc547f22ce1101f26d8b2fad387e28ec8e7e1e3b11695d681d8"}, + {file = "tokenizers-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e48afb75e50449848964e4a67b0da01261dd3aa8df8daecf10db8fd7f5b076eb"}, + {file = "tokenizers-0.20.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:baf5d0e1ff44710a95eefc196dd87666ffc609fd447c5e5b68272a7c3d342a1d"}, + {file = "tokenizers-0.20.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e5e56df0e8ed23ba60ae3848c3f069a0710c4b197218fe4f89e27eba38510768"}, + {file = "tokenizers-0.20.0-cp312-none-win32.whl", hash = "sha256:ec53e5ecc142a82432f9c6c677dbbe5a2bfee92b8abf409a9ecb0d425ee0ce75"}, + {file = "tokenizers-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:f18661ece72e39c0dfaa174d6223248a15b457dbd4b0fc07809b8e6d3ca1a234"}, + {file = "tokenizers-0.20.0-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:f7065b1084d8d1a03dc89d9aad69bcbc8415d4bc123c367063eb32958cd85054"}, + {file = "tokenizers-0.20.0-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:e5d4069e4714e3f7ba0a4d3d44f9d84a432cd4e4aa85c3d7dd1f51440f12e4a1"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:799b808529e54b7e1a36350bda2aeb470e8390e484d3e98c10395cee61d4e3c6"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7f9baa027cc8a281ad5f7725a93c204d7a46986f88edbe8ef7357f40a23fb9c7"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:010ec7f3f7a96adc4c2a34a3ada41fa14b4b936b5628b4ff7b33791258646c6b"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98d88f06155335b14fd78e32ee28ca5b2eb30fced4614e06eb14ae5f7fba24ed"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e13eb000ef540c2280758d1b9cfa5fe424b0424ae4458f440e6340a4f18b2638"}, + {file = "tokenizers-0.20.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fab3cf066ff426f7e6d70435dc28a9ff01b2747be83810e397cba106f39430b0"}, + {file = "tokenizers-0.20.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:39fa3761b30a89368f322e5daf4130dce8495b79ad831f370449cdacfb0c0d37"}, + {file = "tokenizers-0.20.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c8da0fba4d179ddf2607821575998df3c294aa59aa8df5a6646dc64bc7352bce"}, + {file = "tokenizers-0.20.0-cp37-none-win32.whl", hash = "sha256:fada996d6da8cf213f6e3c91c12297ad4f6cdf7a85c2fadcd05ec32fa6846fcd"}, + {file = "tokenizers-0.20.0-cp37-none-win_amd64.whl", hash = "sha256:7d29aad702279e0760c265fcae832e89349078e3418dd329732d4503259fd6bd"}, + {file = "tokenizers-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:099c68207f3ef0227ecb6f80ab98ea74de559f7b124adc7b17778af0250ee90a"}, + {file = "tokenizers-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:68012d8a8cddb2eab3880870d7e2086cb359c7f7a2b03f5795044f5abff4e850"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9253bdd209c6aee168deca7d0e780581bf303e0058f268f9bb06859379de19b6"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8f868600ddbcb0545905ed075eb7218a0756bf6c09dae7528ea2f8436ebd2c93"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9a9643d9c8c5f99b6aba43fd10034f77cc6c22c31f496d2f0ee183047d948fa0"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c375c6a889aeab44734028bc65cc070acf93ccb0f9368be42b67a98e1063d3f6"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e359f852328e254f070bbd09a19a568421d23388f04aad9f2fb7da7704c7228d"}, + {file = "tokenizers-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d98b01a309d4387f3b1c1dd68a8b8136af50376cf146c1b7e8d8ead217a5be4b"}, + {file = "tokenizers-0.20.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:459f7537119554c2899067dec1ac74a00d02beef6558f4ee2e99513bf6d568af"}, + {file = "tokenizers-0.20.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:392b87ec89452628c045c9f2a88bc2a827f4c79e7d84bc3b72752b74c2581f70"}, + {file = "tokenizers-0.20.0-cp38-none-win32.whl", hash = "sha256:55a393f893d2ed4dd95a1553c2e42d4d4086878266f437b03590d3f81984c4fe"}, + {file = "tokenizers-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:30ffe33c5c2f2aab8e9a3340d0110dd9f7ace7eec7362e20a697802306bd8068"}, + {file = "tokenizers-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:aa2d4a6fed2a7e3f860c7fc9d48764bb30f2649d83915d66150d6340e06742b8"}, + {file = "tokenizers-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b5ef0f814084a897e9071fc4a868595f018c5c92889197bdc4bf19018769b148"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc1e1b791e8c3bf4c4f265f180dadaff1c957bf27129e16fdd5e5d43c2d3762c"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b69e55e481459c07885263743a0d3c18d52db19bae8226a19bcca4aaa213fff"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4806b4d82e27a2512bc23057b2986bc8b85824914286975b84d8105ff40d03d9"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9859e9ef13adf5a473ccab39d31bff9c550606ae3c784bf772b40f615742a24f"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef703efedf4c20488a8eb17637b55973745b27997ff87bad88ed499b397d1144"}, + {file = "tokenizers-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6eec0061bab94b1841ab87d10831fdf1b48ebaed60e6d66d66dbe1d873f92bf5"}, + {file = "tokenizers-0.20.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:980f3d0d7e73f845b69087f29a63c11c7eb924c4ad6b358da60f3db4cf24bdb4"}, + {file = "tokenizers-0.20.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7c157550a2f3851b29d7fdc9dc059fcf81ff0c0fc49a1e5173a89d533ed043fa"}, + {file = "tokenizers-0.20.0-cp39-none-win32.whl", hash = "sha256:8a3d2f4d08608ec4f9895ec25b4b36a97f05812543190a5f2c3cd19e8f041e5a"}, + {file = "tokenizers-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:d90188d12afd0c75e537f9a1d92f9c7375650188ee4f48fdc76f9e38afbd2251"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:d68e15f1815357b059ec266062340c343ea7f98f7f330602df81ffa3474b6122"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:23f9ecec637b9bc80da5f703808d29ed5329e56b5aa8d791d1088014f48afadc"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f830b318ee599e3d0665b3e325f85bc75ee2d2ca6285f52e439dc22b64691580"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3dc750def789cb1de1b5a37657919545e1d9ffa667658b3fa9cb7862407a1b8"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e26e6c755ae884c2ea6135cd215bdd0fccafe4ee62405014b8c3cd19954e3ab9"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:a1158c7174f427182e08baa2a8ded2940f2b4a3e94969a85cc9cfd16004cbcea"}, + {file = "tokenizers-0.20.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:6324826287a3fc198898d3dcf758fe4a8479e42d6039f4c59e2cedd3cf92f64e"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d8653149405bb0c16feaf9cfee327fdb6aaef9dc2998349fec686f35e81c4e2"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8a2dc1e402a155e97309287ca085c80eb1b7fab8ae91527d3b729181639fa51"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07bef67b20aa6e5f7868c42c7c5eae4d24f856274a464ae62e47a0f2cccec3da"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da06e397182ff53789c506c7833220c192952c57e1581a53f503d8d953e2d67e"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:302f7e11a14814028b7fc88c45a41f1bbe9b5b35fd76d6869558d1d1809baa43"}, + {file = "tokenizers-0.20.0-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:055ec46e807b875589dfbe3d9259f9a6ee43394fb553b03b3d1e9541662dbf25"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e3144b8acebfa6ae062e8f45f7ed52e4b50fb6c62f93afc8871b525ab9fdcab3"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b52aa3fd14b2a07588c00a19f66511cff5cca8f7266ca3edcdd17f3512ad159f"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b8cf52779ffc5d4d63a0170fbeb512372bad0dd014ce92bbb9149756c831124"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:983a45dd11a876124378dae71d6d9761822199b68a4c73f32873d8cdaf326a5b"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df6b819c9a19831ebec581e71a7686a54ab45d90faf3842269a10c11d746de0c"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e738cfd80795fcafcef89c5731c84b05638a4ab3f412f97d5ed7765466576eb1"}, + {file = "tokenizers-0.20.0-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:c8842c7be2fadb9c9edcee233b1b7fe7ade406c99b0973f07439985c1c1d0683"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e47a82355511c373a4a430c4909dc1e518e00031207b1fec536c49127388886b"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:9afbf359004551179a5db19424180c81276682773cff2c5d002f6eaaffe17230"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a07eaa8799a92e6af6f472c21a75bf71575de2af3c0284120b7a09297c0de2f3"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0994b2e5fc53a301071806bc4303e4bc3bdc3f490e92a21338146a36746b0872"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b6466e0355b603d10e3cc3d282d350b646341b601e50969464a54939f9848d0"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:1e86594c2a433cb1ea09cfbe596454448c566e57ee8905bd557e489d93e89986"}, + {file = "tokenizers-0.20.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3e14cdef1efa96ecead6ea64a891828432c3ebba128bdc0596e3059fea104ef3"}, + {file = "tokenizers-0.20.0.tar.gz", hash = "sha256:39d7acc43f564c274085cafcd1dae9d36f332456de1a31970296a6b8da4eac8d"}, ] [package.dependencies] @@ -5414,74 +4828,6 @@ files = [ docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.2)", "pytest-mock", "pytest-mypy-testing"] -[[package]] -name = "transformers" -version = "4.43.4" -description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "transformers-4.43.4-py3-none-any.whl", hash = "sha256:d2202ed201e0c44f80de8d753a19f6164187754630bc1f915661b9511d61c773"}, - {file = "transformers-4.43.4.tar.gz", hash = "sha256:b62288990a65ed9bfb79191e04dbb76c9376834ae6e0dd911320a2ced63324fe"}, -] - -[package.dependencies] -filelock = "*" -huggingface-hub = ">=0.23.2,<1.0" -numpy = ">=1.17" -packaging = ">=20.0" -pyyaml = ">=5.1" -regex = "!=2019.12.17" -requests = "*" -safetensors = ">=0.4.1" -tokenizers = ">=0.19,<0.20" -tqdm = ">=4.27" - -[package.extras] -accelerate = ["accelerate (>=0.21.0)"] -agents = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch"] -all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm (<=0.9.16)", "tokenizers (>=0.19,<0.20)", "torch", "torchaudio", "torchvision"] -audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] -benchmark = ["optimum-benchmark (>=0.2.0)"] -codecarbon = ["codecarbon (==1.2.0)"] -deepspeed = ["accelerate (>=0.21.0)", "deepspeed (>=0.9.3)"] -deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.21.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk", "optuna", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.4.4)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] -dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "av (==9.2.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "decord (==0.6.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "nltk", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.4.4)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "scipy (<1.13.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm (<=0.9.16)", "tokenizers (>=0.19,<0.20)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] -dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1,<0.14.0)", "librosa", "nltk", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.4.4)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.19,<0.20)", "urllib3 (<2.0.0)"] -dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "librosa", "nltk", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.4.4)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm (<=0.9.16)", "tokenizers (>=0.19,<0.20)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] -flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)", "scipy (<1.13.0)"] -flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] -ftfy = ["ftfy"] -integrations = ["optuna", "ray[tune] (>=2.7.0)", "sigopt"] -ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] -modelcreation = ["cookiecutter (==1.7.3)"] -natten = ["natten (>=0.14.6,<0.15.0)"] -onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] -onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] -optuna = ["optuna"] -quality = ["GitPython (<3.1.19)", "datasets (!=2.5.0)", "isort (>=5.5.4)", "ruff (==0.4.4)", "urllib3 (<2.0.0)"] -ray = ["ray[tune] (>=2.7.0)"] -retrieval = ["datasets (!=2.5.0)", "faiss-cpu"] -ruff = ["ruff (==0.4.4)"] -sagemaker = ["sagemaker (>=2.31.0)"] -sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"] -serving = ["fastapi", "pydantic", "starlette", "uvicorn"] -sigopt = ["sigopt"] -sklearn = ["scikit-learn"] -speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "nltk", "parameterized", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-rich", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.4.4)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] -tf = ["keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow (>2.9,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"] -tf-cpu = ["keras (>2.9,<2.16)", "keras-nlp (>=0.3.1,<0.14.0)", "onnxconverter-common", "tensorflow-cpu (>2.9,<2.16)", "tensorflow-probability (<0.24)", "tensorflow-text (<2.16)", "tf2onnx"] -tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] -timm = ["timm (<=0.9.16)"] -tokenizers = ["tokenizers (>=0.19,<0.20)"] -torch = ["accelerate (>=0.21.0)", "torch"] -torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"] -torchhub = ["filelock", "huggingface-hub (>=0.23.2,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.19,<0.20)", "torch", "tqdm (>=4.27)"] -video = ["av (==9.2.0)", "decord (==0.6.0)"] -vision = ["Pillow (>=10.0.1,<=15.0)"] - [[package]] name = "typer" version = "0.12.3" @@ -5704,123 +5050,6 @@ files = [ {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, ] -[[package]] -name = "xxhash" -version = "3.4.1" -description = "Python binding for xxHash" -optional = false -python-versions = ">=3.7" -files = [ - {file = "xxhash-3.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91dbfa55346ad3e18e738742236554531a621042e419b70ad8f3c1d9c7a16e7f"}, - {file = "xxhash-3.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:665a65c2a48a72068fcc4d21721510df5f51f1142541c890491afc80451636d2"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb11628470a6004dc71a09fe90c2f459ff03d611376c1debeec2d648f44cb693"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bef2a7dc7b4f4beb45a1edbba9b9194c60a43a89598a87f1a0226d183764189"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c0f7b2d547d72c7eda7aa817acf8791f0146b12b9eba1d4432c531fb0352228"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00f2fdef6b41c9db3d2fc0e7f94cb3db86693e5c45d6de09625caad9a469635b"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23cfd9ca09acaf07a43e5a695143d9a21bf00f5b49b15c07d5388cadf1f9ce11"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6a9ff50a3cf88355ca4731682c168049af1ca222d1d2925ef7119c1a78e95b3b"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f1d7c69a1e9ca5faa75546fdd267f214f63f52f12692f9b3a2f6467c9e67d5e7"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:672b273040d5d5a6864a36287f3514efcd1d4b1b6a7480f294c4b1d1ee1b8de0"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4178f78d70e88f1c4a89ff1ffe9f43147185930bb962ee3979dba15f2b1cc799"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9804b9eb254d4b8cc83ab5a2002128f7d631dd427aa873c8727dba7f1f0d1c2b"}, - {file = "xxhash-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c09c49473212d9c87261d22c74370457cfff5db2ddfc7fd1e35c80c31a8c14ce"}, - {file = "xxhash-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ebbb1616435b4a194ce3466d7247df23499475c7ed4eb2681a1fa42ff766aff6"}, - {file = "xxhash-3.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:25dc66be3db54f8a2d136f695b00cfe88018e59ccff0f3b8f545869f376a8a46"}, - {file = "xxhash-3.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58c49083801885273e262c0f5bbeac23e520564b8357fbb18fb94ff09d3d3ea5"}, - {file = "xxhash-3.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b526015a973bfbe81e804a586b703f163861da36d186627e27524f5427b0d520"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36ad4457644c91a966f6fe137d7467636bdc51a6ce10a1d04f365c70d6a16d7e"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:248d3e83d119770f96003271fe41e049dd4ae52da2feb8f832b7a20e791d2920"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2070b6d5bbef5ee031666cf21d4953c16e92c2f8a24a94b5c240f8995ba3b1d0"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2746035f518f0410915e247877f7df43ef3372bf36cfa52cc4bc33e85242641"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ba6181514681c2591840d5632fcf7356ab287d4aff1c8dea20f3c78097088"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aac5010869240e95f740de43cd6a05eae180c59edd182ad93bf12ee289484fa"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4cb11d8debab1626181633d184b2372aaa09825bde709bf927704ed72765bed1"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b29728cff2c12f3d9f1d940528ee83918d803c0567866e062683f300d1d2eff3"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:a15cbf3a9c40672523bdb6ea97ff74b443406ba0ab9bca10ceccd9546414bd84"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e66df260fed01ed8ea790c2913271641c58481e807790d9fca8bfd5a3c13844"}, - {file = "xxhash-3.4.1-cp311-cp311-win32.whl", hash = "sha256:e867f68a8f381ea12858e6d67378c05359d3a53a888913b5f7d35fbf68939d5f"}, - {file = "xxhash-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:200a5a3ad9c7c0c02ed1484a1d838b63edcf92ff538770ea07456a3732c577f4"}, - {file = "xxhash-3.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:1d03f1c0d16d24ea032e99f61c552cb2b77d502e545187338bea461fde253583"}, - {file = "xxhash-3.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c4bbba9b182697a52bc0c9f8ec0ba1acb914b4937cd4a877ad78a3b3eeabefb3"}, - {file = "xxhash-3.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9fd28a9da300e64e434cfc96567a8387d9a96e824a9be1452a1e7248b7763b78"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6066d88c9329ab230e18998daec53d819daeee99d003955c8db6fc4971b45ca3"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93805bc3233ad89abf51772f2ed3355097a5dc74e6080de19706fc447da99cd3"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64da57d5ed586ebb2ecdde1e997fa37c27fe32fe61a656b77fabbc58e6fbff6e"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97322e9a7440bf3c9805cbaac090358b43f650516486746f7fa482672593df"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe750d512982ee7d831838a5dee9e9848f3fb440e4734cca3f298228cc957a6"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fd79d4087727daf4d5b8afe594b37d611ab95dc8e29fe1a7517320794837eb7d"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:743612da4071ff9aa4d055f3f111ae5247342931dedb955268954ef7201a71ff"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b41edaf05734092f24f48c0958b3c6cbaaa5b7e024880692078c6b1f8247e2fc"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a90356ead70d715fe64c30cd0969072de1860e56b78adf7c69d954b43e29d9fa"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac56eebb364e44c85e1d9e9cc5f6031d78a34f0092fea7fc80478139369a8b4a"}, - {file = "xxhash-3.4.1-cp312-cp312-win32.whl", hash = "sha256:911035345932a153c427107397c1518f8ce456f93c618dd1c5b54ebb22e73747"}, - {file = "xxhash-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:f31ce76489f8601cc7b8713201ce94b4bd7b7ce90ba3353dccce7e9e1fee71fa"}, - {file = "xxhash-3.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:b5beb1c6a72fdc7584102f42c4d9df232ee018ddf806e8c90906547dfb43b2da"}, - {file = "xxhash-3.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6d42b24d1496deb05dee5a24ed510b16de1d6c866c626c2beb11aebf3be278b9"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b685fab18876b14a8f94813fa2ca80cfb5ab6a85d31d5539b7cd749ce9e3624"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:419ffe34c17ae2df019a4685e8d3934d46b2e0bbe46221ab40b7e04ed9f11137"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e041ce5714f95251a88670c114b748bca3bf80cc72400e9f23e6d0d59cf2681"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc860d887c5cb2f524899fb8338e1bb3d5789f75fac179101920d9afddef284b"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:312eba88ffe0a05e332e3a6f9788b73883752be63f8588a6dc1261a3eaaaf2b2"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e01226b6b6a1ffe4e6bd6d08cfcb3ca708b16f02eb06dd44f3c6e53285f03e4f"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9f3025a0d5d8cf406a9313cd0d5789c77433ba2004b1c75439b67678e5136537"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:6d3472fd4afef2a567d5f14411d94060099901cd8ce9788b22b8c6f13c606a93"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:43984c0a92f06cac434ad181f329a1445017c33807b7ae4f033878d860a4b0f2"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a55e0506fdb09640a82ec4f44171273eeabf6f371a4ec605633adb2837b5d9d5"}, - {file = "xxhash-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:faec30437919555b039a8bdbaba49c013043e8f76c999670aef146d33e05b3a0"}, - {file = "xxhash-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c9e1b646af61f1fc7083bb7b40536be944f1ac67ef5e360bca2d73430186971a"}, - {file = "xxhash-3.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:961d948b7b1c1b6c08484bbce3d489cdf153e4122c3dfb07c2039621243d8795"}, - {file = "xxhash-3.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:719a378930504ab159f7b8e20fa2aa1896cde050011af838af7e7e3518dd82de"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74fb5cb9406ccd7c4dd917f16630d2e5e8cbbb02fc2fca4e559b2a47a64f4940"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5dab508ac39e0ab988039bc7f962c6ad021acd81fd29145962b068df4148c476"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c59f3e46e7daf4c589e8e853d700ef6607afa037bfad32c390175da28127e8c"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc07256eff0795e0f642df74ad096f8c5d23fe66bc138b83970b50fc7f7f6c5"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9f749999ed80f3955a4af0eb18bb43993f04939350b07b8dd2f44edc98ffee9"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7688d7c02149a90a3d46d55b341ab7ad1b4a3f767be2357e211b4e893efbaaf6"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a8b4977963926f60b0d4f830941c864bed16aa151206c01ad5c531636da5708e"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8106d88da330f6535a58a8195aa463ef5281a9aa23b04af1848ff715c4398fb4"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4c76a77dbd169450b61c06fd2d5d436189fc8ab7c1571d39265d4822da16df22"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:11f11357c86d83e53719c592021fd524efa9cf024dc7cb1dfb57bbbd0d8713f2"}, - {file = "xxhash-3.4.1-cp38-cp38-win32.whl", hash = "sha256:0c786a6cd74e8765c6809892a0d45886e7c3dc54de4985b4a5eb8b630f3b8e3b"}, - {file = "xxhash-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:aabf37fb8fa27430d50507deeab2ee7b1bcce89910dd10657c38e71fee835594"}, - {file = "xxhash-3.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6127813abc1477f3a83529b6bbcfeddc23162cece76fa69aee8f6a8a97720562"}, - {file = "xxhash-3.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef2e194262f5db16075caea7b3f7f49392242c688412f386d3c7b07c7733a70a"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71be94265b6c6590f0018bbf73759d21a41c6bda20409782d8117e76cd0dfa8b"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10e0a619cdd1c0980e25eb04e30fe96cf8f4324758fa497080af9c21a6de573f"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa122124d2e3bd36581dd78c0efa5f429f5220313479fb1072858188bc2d5ff1"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17032f5a4fea0a074717fe33477cb5ee723a5f428de7563e75af64bfc1b1e10"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca7783b20e3e4f3f52f093538895863f21d18598f9a48211ad757680c3bd006f"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d77d09a1113899fad5f354a1eb4f0a9afcf58cefff51082c8ad643ff890e30cf"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:21287bcdd299fdc3328cc0fbbdeaa46838a1c05391264e51ddb38a3f5b09611f"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:dfd7a6cc483e20b4ad90224aeb589e64ec0f31e5610ab9957ff4314270b2bf31"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:543c7fcbc02bbb4840ea9915134e14dc3dc15cbd5a30873a7a5bf66039db97ec"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fe0a98d990e433013f41827b62be9ab43e3cf18e08b1483fcc343bda0d691182"}, - {file = "xxhash-3.4.1-cp39-cp39-win32.whl", hash = "sha256:b9097af00ebf429cc7c0e7d2fdf28384e4e2e91008130ccda8d5ae653db71e54"}, - {file = "xxhash-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:d699b921af0dcde50ab18be76c0d832f803034d80470703700cb7df0fbec2832"}, - {file = "xxhash-3.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:2be491723405e15cc099ade1280133ccfbf6322d2ef568494fb7d07d280e7eee"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:431625fad7ab5649368c4849d2b49a83dc711b1f20e1f7f04955aab86cd307bc"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc6dbd5fc3c9886a9e041848508b7fb65fd82f94cc793253990f81617b61fe49"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ff8dbd0ec97aec842476cb8ccc3e17dd288cd6ce3c8ef38bff83d6eb927817"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef73a53fe90558a4096e3256752268a8bdc0322f4692ed928b6cd7ce06ad4fe3"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:450401f42bbd274b519d3d8dcf3c57166913381a3d2664d6609004685039f9d3"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a162840cf4de8a7cd8720ff3b4417fbc10001eefdd2d21541a8226bb5556e3bb"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b736a2a2728ba45017cb67785e03125a79d246462dfa892d023b827007412c52"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0ae4c2e7698adef58710d6e7a32ff518b66b98854b1c68e70eee504ad061d8"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6322c4291c3ff174dcd104fae41500e75dad12be6f3085d119c2c8a80956c51"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:dd59ed668801c3fae282f8f4edadf6dc7784db6d18139b584b6d9677ddde1b6b"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:92693c487e39523a80474b0394645b393f0ae781d8db3474ccdcead0559ccf45"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4603a0f642a1e8d7f3ba5c4c25509aca6a9c1cc16f85091004a7028607ead663"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa45e8cbfbadb40a920fe9ca40c34b393e0b067082d94006f7f64e70c7490a6"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:595b252943b3552de491ff51e5bb79660f84f033977f88f6ca1605846637b7c6"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:562d8b8f783c6af969806aaacf95b6c7b776929ae26c0cd941d54644ea7ef51e"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:41ddeae47cf2828335d8d991f2d2b03b0bdc89289dc64349d712ff8ce59d0647"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c44d584afdf3c4dbb3277e32321d1a7b01d6071c1992524b6543025fb8f4206f"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7bddb3a5b86213cc3f2c61500c16945a1b80ecd572f3078ddbbe68f9dabdfb"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ecb6c987b62437c2f99c01e97caf8d25660bf541fe79a481d05732e5236719c"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:696b4e18b7023527d5c50ed0626ac0520edac45a50ec7cf3fc265cd08b1f4c03"}, - {file = "xxhash-3.4.1.tar.gz", hash = "sha256:0379d6cf1ff987cd421609a264ce025e74f346e3e145dd106c0cc2e3ec3f99a9"}, -] - [[package]] name = "yarl" version = "1.9.4" @@ -5953,4 +5182,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.10,<3.11" -content-hash = "0d21d885d24128fea87c662550775ee8df4f6af419cd7c844351b8b202b3efd1" +content-hash = "47fb59596e6067d2218064a57ca421d39a2420505082cef0d17945b120611115" diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index 701279b2f..a401aaf48 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -13,11 +13,9 @@ pycozo = {extras = ["embedded"], version = "^0.7.6"} uvicorn = "^0.23.2" fire = "^0.5.0" environs = "^10.3.0" -google-cloud-aiplatform = "^1.33.0" pandas = "^2.1.0" openai = "^1.12.0" httpx = "^0.26.0" -async-lru = "^2.0.4" sentry-sdk = {extras = ["fastapi"], version = "^1.38.0"} temporalio = "^1.4.0" pydantic = "^2.5.3" @@ -25,11 +23,9 @@ arrow = "^1.3.0" jinja2 = "^3.1.3" jinja2schema = "^0.1.4" jsonschema = "^4.21.1" -litellm = "^1.35.32" +litellm = "^1.43.3" numpy = "^1.26.4" -transformers = "^4.40.1" -tiktoken = "^0.6.0" -xxhash = "^3.4.1" +tiktoken = "^0.7.0" tenacity = "^8.3.0" beartype = "^0.18.5" pydantic-partial = "^0.5.5" diff --git a/docker-compose.yml b/docker-compose.yml index f141b9252..4af7c559e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,6 +6,7 @@ include: - ./model-serving/docker-compose.yml - ./gateway/docker-compose.yml - ./agents-api/docker-compose.yml + - ./llm-proxy/docker-compose.yml # TODO: Enable after testing # - ./monitoring/docker-compose.yml diff --git a/llm-proxy/.dockerignore b/llm-proxy/.dockerignore new file mode 100644 index 000000000..e7a1d3a41 --- /dev/null +++ b/llm-proxy/.dockerignore @@ -0,0 +1 @@ +!.keys diff --git a/llm-proxy/.gitignore b/llm-proxy/.gitignore new file mode 100644 index 000000000..aaab9d948 --- /dev/null +++ b/llm-proxy/.gitignore @@ -0,0 +1 @@ +.keys diff --git a/llm-proxy/docker-compose.yml b/llm-proxy/docker-compose.yml new file mode 100644 index 000000000..f4cab6ebe --- /dev/null +++ b/llm-proxy/docker-compose.yml @@ -0,0 +1,54 @@ +services: + litellm: + image: ghcr.io/berriai/litellm:main-stable + volumes: + - ./litellm-config.yaml:/app/config.yaml + - .keys:/app/.keys + ports: + - "4000:4000" + env_file: + - ../.env + command: + [ + "--config", + "/app/config.yaml", + "--port", + "4000", + "--num_workers", + "8", + "--telemetry", + "False" + ] + + depends_on: + - litellm-db + - litellm-redis + + litellm-db: + image: postgres + restart: always + volumes: + - litellm-db-data:/var/lib/postgresql/data + ports: + - "5432:5432" + env_file: + - ../.env + healthcheck: + test: [ "CMD-SHELL", "pg_isready -d litellm -U llmproxy" ] + interval: 1s + timeout: 5s + retries: 10 + + litellm-redis: + image: redis/redis-stack-server + restart: always + volumes: + - litellm-redis-data:/data + ports: + - "6379:6379" + env_file: + - ../.env + +volumes: + litellm-db-data: + litellm-redis-data: diff --git a/llm-proxy/litellm-config.yaml b/llm-proxy/litellm-config.yaml new file mode 100644 index 000000000..fc276ac38 --- /dev/null +++ b/llm-proxy/litellm-config.yaml @@ -0,0 +1,127 @@ +environment_variables: + NO_DOCS: "true" + +model_list: +# -*= Paid models =*- +# ------------------- + +# Gemini models +- model_name: gemini-1.5-pro + litellm_params: + model: vertex_ai_beta/gemini-1.5-pro + tags: ["paid"] + vertex_credentials: os.environ/GOOGLE_APPLICATION_CREDENTIALS + +- model_name: claude-3.5-sonnet + litellm_params: + model: vertex_ai/claude-3-5-sonnet@20240620 + tags: ["paid"] + vertex_credentials: os.environ/GOOGLE_APPLICATION_CREDENTIALS + +# OpenAI models +- model_name: "gpt-4-turbo" + litellm_params: + model: "openai/gpt-4-turbo" + tags: ["paid"] + api_key: os.environ/OPENAI_API_KEY + +- model_name: "gpt-4o" + litellm_params: + model: "openai/gpt-4o" + tags: ["paid"] + api_key: os.environ/OPENAI_API_KEY + +# Anthropic models +- model_name: "claude-3.5-sonnet" + litellm_params: + model: "claude-3-5-sonnet-20240620" + tags: ["paid"] + api_key: os.environ/ANTHROPIC_API_KEY + +# Groq models +- model_name: "llama-3.1-70b" + litellm_params: + model: "groq/llama-3.1-70b-versatile" + tags: ["paid"] + api_key: os.environ/GROQ_API_KEY + +- model_name: "llama-3.1-8b" + litellm_params: + model: "groq/llama-3.1-8b-instant" + tags: ["paid"] + api_key: os.environ/GROQ_API_KEY + + +# -*= Embedding models =*- +# ------------------------ + +- model_name: text-embedding-3-large + litellm_params: + model: "openai/text-embedding-3-large" + api_key: os.environ/OPENAI_API_KEY + tags: ["paid"] + +- model_name: voyage-multilingual-2 + litellm_params: + model: "voyage/voyage-multilingual-2" + api_key: os.environ/VOYAGE_API_KEY + tags: ["paid"] + +- model_name: voyage-large-2 + litellm_params: + model: "voyage/voyage-large-2" + api_key: os.environ/VOYAGE_API_KEY + tags: ["paid"] + +- model_name: gte-large-en-v1.5 + litellm_params: + model: openai/Alibaba-NLP/gte-large-en-v1.5 + api_base: os.environ/EMBEDDING_SERVICE_BASE + tags: ["free"] + +- model_name: bge-m3 + litellm_params: + model: openai/BAAI/bge-m3 + api_base: os.environ/EMBEDDING_SERVICE_BASE + tags: ["free"] + + +# -*= Free models =*- +# ------------------- + +- model_name: gpt-4o-mini + litellm_params: + model: openai/gpt-4o-mini + api_key: os.environ/OPENAI_API_KEY + tags: ["free"] + + +# https://github.com/BerriAI/litellm/blob/main/litellm/__init__.py +litellm_settings: + num_retries: 3 + request_timeout: 180 + allowed_fails: 3 + cooldown_time: 30 + drop_params: true + modify_params: true + telemetry: false + retry: true + add_function_to_prompt: true + + set_verbose: false + cache: true + cache_params: # set cache params for redis + type: redis + namespace: "litellm_caching" + host: os.environ/LITELLM_REDIS_HOST + port: os.environ/LITELLM_REDIS_PORT + password: os.environ/LITELLM_REDIS_PASSWORD + +router_settings: + routing_strategy: simple-shuffle + num_retries: 3 + +general_settings: + master_key: os.environ/LITELLM_MASTER_KEY + database_url: os.environ/LITELLM_DATABASE_URL + enforce_user_param: true \ No newline at end of file From 10fee9d17ff100907f709624428735e7ab2d59d7 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 9 Aug 2024 10:44:47 -0400 Subject: [PATCH 013/110] refactor(agents-api): Move tests to root dir Signed-off-by: Diwank Tomer --- .../{agents_api/models/agent => tests}/test_agent_queries.py | 0 agents-api/{agents_api/models/docs => tests}/test_docs_queries.py | 0 .../{agents_api/models/entry => tests}/test_entry_queries.py | 0 .../models/execution => tests}/test_execution_queries.py | 0 .../{agents_api/models/session => tests}/test_session_queries.py | 0 agents-api/{agents_api/models/task => tests}/test_task_queries.py | 0 .../{agents_api/models/tools => tests}/test_tool_queries.py | 0 agents-api/{agents_api/models/user => tests}/test_user_queries.py | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename agents-api/{agents_api/models/agent => tests}/test_agent_queries.py (100%) rename agents-api/{agents_api/models/docs => tests}/test_docs_queries.py (100%) rename agents-api/{agents_api/models/entry => tests}/test_entry_queries.py (100%) rename agents-api/{agents_api/models/execution => tests}/test_execution_queries.py (100%) rename agents-api/{agents_api/models/session => tests}/test_session_queries.py (100%) rename agents-api/{agents_api/models/task => tests}/test_task_queries.py (100%) rename agents-api/{agents_api/models/tools => tests}/test_tool_queries.py (100%) rename agents-api/{agents_api/models/user => tests}/test_user_queries.py (100%) diff --git a/agents-api/agents_api/models/agent/test_agent_queries.py b/agents-api/tests/test_agent_queries.py similarity index 100% rename from agents-api/agents_api/models/agent/test_agent_queries.py rename to agents-api/tests/test_agent_queries.py diff --git a/agents-api/agents_api/models/docs/test_docs_queries.py b/agents-api/tests/test_docs_queries.py similarity index 100% rename from agents-api/agents_api/models/docs/test_docs_queries.py rename to agents-api/tests/test_docs_queries.py diff --git a/agents-api/agents_api/models/entry/test_entry_queries.py b/agents-api/tests/test_entry_queries.py similarity index 100% rename from agents-api/agents_api/models/entry/test_entry_queries.py rename to agents-api/tests/test_entry_queries.py diff --git a/agents-api/agents_api/models/execution/test_execution_queries.py b/agents-api/tests/test_execution_queries.py similarity index 100% rename from agents-api/agents_api/models/execution/test_execution_queries.py rename to agents-api/tests/test_execution_queries.py diff --git a/agents-api/agents_api/models/session/test_session_queries.py b/agents-api/tests/test_session_queries.py similarity index 100% rename from agents-api/agents_api/models/session/test_session_queries.py rename to agents-api/tests/test_session_queries.py diff --git a/agents-api/agents_api/models/task/test_task_queries.py b/agents-api/tests/test_task_queries.py similarity index 100% rename from agents-api/agents_api/models/task/test_task_queries.py rename to agents-api/tests/test_task_queries.py diff --git a/agents-api/agents_api/models/tools/test_tool_queries.py b/agents-api/tests/test_tool_queries.py similarity index 100% rename from agents-api/agents_api/models/tools/test_tool_queries.py rename to agents-api/tests/test_tool_queries.py diff --git a/agents-api/agents_api/models/user/test_user_queries.py b/agents-api/tests/test_user_queries.py similarity index 100% rename from agents-api/agents_api/models/user/test_user_queries.py rename to agents-api/tests/test_user_queries.py From d37965f9c3b145c772f30870a25faa446d61ef91 Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Fri, 9 Aug 2024 10:56:16 -0400 Subject: [PATCH 014/110] Update test files to reflect new location of model functions * **agents-api/tests/test_agent_queries.py** - Update import statements to reflect the new location of the model functions - Update function calls to the new location of the model functions - Add `from agents_api.autogen.openapi_model import Agent` back * **agents-api/tests/test_docs_queries.py** - Update import statements to reflect the new location of the model functions * **agents-api/tests/test_entry_queries.py** - Update import statements to reflect the new location of the model functions * **agents-api/tests/test_execution_queries.py** - Update import statements to reflect the new location of the model functions * **agents-api/tests/test_session_queries.py** - Update import statements to reflect the new location of the model functions * **agents-api/tests/test_task_queries.py** - Update import statements to reflect the new location of the model functions * **agents-api/tests/test_tool_queries.py** - Update import statements to reflect the new location of the model functions * **agents-api/tests/test_user_queries.py** - Update import statements to reflect the new location of the model functions --- agents-api/tests/test_agent_queries.py | 29 +++++++++------------- agents-api/tests/test_docs_queries.py | 12 ++++----- agents-api/tests/test_entry_queries.py | 20 +++++++-------- agents-api/tests/test_execution_queries.py | 13 +++++----- agents-api/tests/test_session_queries.py | 10 +++----- agents-api/tests/test_task_queries.py | 11 ++++---- agents-api/tests/test_tool_queries.py | 9 +++---- agents-api/tests/test_user_queries.py | 8 +++--- 8 files changed, 51 insertions(+), 61 deletions(-) diff --git a/agents-api/tests/test_agent_queries.py b/agents-api/tests/test_agent_queries.py index f293c0700..b17d62b7a 100644 --- a/agents-api/tests/test_agent_queries.py +++ b/agents-api/tests/test_agent_queries.py @@ -5,14 +5,9 @@ from pycozo import Client from ward import test +from agents_api.models import agent from agents_api.autogen.openapi_model import Agent -from .create_agent import create_agent -from .delete_agent import delete_agent -from .get_agent import get_agent -from .list_agents import list_agents -from .update_agent import update_agent - MODEL = "julep-ai/samantha-1-turbo" @@ -33,7 +28,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - create_agent( + agent.create_agent( agent_id=agent_id, developer_id=developer_id, data={ @@ -51,7 +46,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - create_agent( + agent.create_agent( agent_id=agent_id, developer_id=developer_id, data={ @@ -70,7 +65,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - result = get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + result = agent.get_agent(agent_id=agent_id, developer_id=developer_id, client=client) assert result is None @@ -81,7 +76,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - create_agent( + agent.create_agent( agent_id=agent_id, developer_id=developer_id, data={ @@ -93,7 +88,7 @@ def _(): client=client, ) - result = get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + result = agent.get_agent(agent_id=agent_id, developer_id=developer_id, client=client) assert result is not None assert isinstance(result, Agent) @@ -107,7 +102,7 @@ def _(): developer_id = uuid4() # Create the agent - create_agent( + agent.create_agent( agent_id=agent_id, developer_id=developer_id, data={ @@ -119,10 +114,10 @@ def _(): ) # Delete the agent - delete_agent(agent_id=agent_id, developer_id=developer_id, client=client) + agent.delete_agent(agent_id=agent_id, developer_id=developer_id, client=client) # Check that the agent is deleted - result = get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + result = agent.get_agent(agent_id=agent_id, developer_id=developer_id, client=client) assert result is None @@ -133,7 +128,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - create_agent( + agent.create_agent( agent_id=agent_id, developer_id=developer_id, data={ @@ -144,7 +139,7 @@ def _(): client=client, ) - result = update_agent( + result = agent.update_agent( agent_id=agent_id, developer_id=developer_id, data={ @@ -166,7 +161,7 @@ def _(): client = cozo_client() developer_id = uuid4() - result = list_agents(developer_id=developer_id, client=client) + result = agent.list_agents(developer_id=developer_id, client=client) assert isinstance(result, list) assert all(isinstance(agent, Agent) for agent in result) diff --git a/agents-api/tests/test_docs_queries.py b/agents-api/tests/test_docs_queries.py index 3a0198601..a6eb24817 100644 --- a/agents-api/tests/test_docs_queries.py +++ b/agents-api/tests/test_docs_queries.py @@ -5,12 +5,12 @@ from pycozo import Client from ward import test -from .create_doc import create_doc -from .delete_doc import delete_doc -from .embed_snippets import embed_snippets -from .get_doc import get_doc -from .list_docs import list_docs -from .search_docs import search_docs_by_embedding +from agents_api.models.docs.create_doc import create_doc +from agents_api.models.docs.delete_doc import delete_doc +from agents_api.models.docs.embed_snippets import embed_snippets +from agents_api.models.docs.get_doc import get_doc +from agents_api.models.docs.list_docs import list_docs +from agents_api.models.docs.search_docs_by_embedding import search_docs_by_embedding EMBEDDING_SIZE: int = 1024 diff --git a/agents-api/tests/test_entry_queries.py b/agents-api/tests/test_entry_queries.py index 6b7a35960..6235254b0 100644 --- a/agents-api/tests/test_entry_queries.py +++ b/agents-api/tests/test_entry_queries.py @@ -10,16 +10,16 @@ from pycozo import Client from ward import test -from ...autogen.openapi_model import Entry, FunctionDef -from ..agent.create_agent import create_agent -from ..docs.create_doc import create_doc -from ..docs.embed_snippets import embed_snippets -from ..session.create_session import create_session -from ..tools.create_tools import create_tools -from ..user.create_user import create_user -from .create_entries import create_entries -from .get_history import get_history -from .list_entries import list_entries +from agents_api.autogen.openapi_model import Entry, FunctionDef +from agents_api.models.agent.create_agent import create_agent +from agents_api.models.docs.create_doc import create_doc +from agents_api.models.docs.embed_snippets import embed_snippets +from agents_api.models.session.create_session import create_session +from agents_api.models.tools.create_tools import create_tools +from agents_api.models.user.create_user import create_user +from agents_api.models.entry.create_entries import create_entries +from agents_api.models.entry.get_history import get_history +from agents_api.models.entry.list_entries import list_entries MODEL = "julep-ai/samantha-1-turbo" diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index a2b87c1ae..c1b15c93c 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -5,15 +5,14 @@ from pycozo import Client from ward import test +from agents_api.models.execution.create_execution import create_execution +from agents_api.models.execution.create_execution_transition import create_execution_transition +from agents_api.models.execution.get_execution import get_execution +from agents_api.models.execution.get_execution_transition import get_execution_transition +from agents_api.models.execution.list_execution_transitions import list_execution_transitions +from agents_api.models.execution.list_executions import list_executions from agents_api.autogen.openapi_model import Execution, Transition -from .create_execution import create_execution -from .create_execution_transition import create_execution_transition -from .get_execution import get_execution -from .get_execution_transition import get_execution_transition -from .list_execution_transitions import list_execution_transitions -from .list_executions import list_executions - MODEL = "julep-ai/samantha-1-turbo" diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index aab7737ea..3282f0aed 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -5,12 +5,10 @@ from pycozo import Client from ward import test -from agents_api.autogen.openapi_model import Session - -from .create_session import create_session -from .delete_session import delete_session -from .get_session import get_session -from .list_sessions import list_sessions +from agents_api.models.session.create_session import create_session +from agents_api.models.session.delete_session import delete_session +from agents_api.models.session.get_session import get_session +from agents_api.models.session.list_sessions import list_sessions MODEL = "julep-ai/samantha-1-turbo" diff --git a/agents-api/tests/test_task_queries.py b/agents-api/tests/test_task_queries.py index 4ec5ae875..5d6afe9d6 100644 --- a/agents-api/tests/test_task_queries.py +++ b/agents-api/tests/test_task_queries.py @@ -5,14 +5,13 @@ from pycozo import Client from ward import test +from agents_api.models.task.create_task import create_task +from agents_api.models.task.delete_task import delete_task +from agents_api.models.task.get_task import get_task +from agents_api.models.task.list_tasks import list_tasks +from agents_api.models.task.update_task import update_task from agents_api.autogen.openapi_model import Task -from .create_task import create_task -from .delete_task import delete_task -from .get_task import get_task -from .list_tasks import list_tasks -from .update_task import update_task - def cozo_client(migrations_dir: str = "./migrations"): # Create a new client for each test diff --git a/agents-api/tests/test_tool_queries.py b/agents-api/tests/test_tool_queries.py index 6032abd7c..c02ce562c 100644 --- a/agents-api/tests/test_tool_queries.py +++ b/agents-api/tests/test_tool_queries.py @@ -6,11 +6,10 @@ from ward import test from agents_api.autogen.openapi_model import FunctionDef, Tool - -from .create_tools import create_tools -from .delete_tool import delete_tool -from .get_tool import get_tool -from .list_tools import list_tools +from agents_api.models.tools.create_tools import create_tools +from agents_api.models.tools.delete_tool import delete_tool +from agents_api.models.tools.get_tool import get_tool +from agents_api.models.tools.list_tools import list_tools def cozo_client(migrations_dir: str = "./migrations"): diff --git a/agents-api/tests/test_user_queries.py b/agents-api/tests/test_user_queries.py index 385ba9ab0..27c745bf7 100644 --- a/agents-api/tests/test_user_queries.py +++ b/agents-api/tests/test_user_queries.py @@ -9,10 +9,10 @@ from agents_api.autogen.openapi_model import User -from .create_user import create_user -from .get_user import get_user -from .list_users import list_users -from .update_user import update_user +from agents_api.models.user.create_user import create_user +from agents_api.models.user.get_user import get_user +from agents_api.models.user.list_users import list_users +from agents_api.models.user.update_user import update_user def cozo_client(migrations_dir: str = "./migrations"): From 7bfab9aa5dfed91d2930196b35881347cdef7ff4 Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Fri, 9 Aug 2024 11:09:23 -0400 Subject: [PATCH 015/110] --- agents-api/tests/test_agent_queries.py | 82 ++++++++++++---------- agents-api/tests/test_docs_queries.py | 25 +++++-- agents-api/tests/test_entry_queries.py | 19 +++-- agents-api/tests/test_execution_queries.py | 71 ++++++++++++------- agents-api/tests/test_session_queries.py | 49 ++++++++----- agents-api/tests/test_task_queries.py | 64 ++++++++++------- agents-api/tests/test_tool_queries.py | 20 ++++-- agents-api/tests/test_user_queries.py | 68 ++++++++++-------- 8 files changed, 243 insertions(+), 155 deletions(-) diff --git a/agents-api/tests/test_agent_queries.py b/agents-api/tests/test_agent_queries.py index b17d62b7a..ec9fba84a 100644 --- a/agents-api/tests/test_agent_queries.py +++ b/agents-api/tests/test_agent_queries.py @@ -5,8 +5,12 @@ from pycozo import Client from ward import test +from agents_api.autogen.openapi_model import ( + Agent, + CreateAgentRequest, + UpdateAgentRequest, +) from agents_api.models import agent -from agents_api.autogen.openapi_model import Agent MODEL = "julep-ai/samantha-1-turbo" @@ -31,11 +35,11 @@ def _(): agent.create_agent( agent_id=agent_id, developer_id=developer_id, - data={ - "model": MODEL, - "name": "test agent", - "about": "test agent about", - }, + data=CreateAgentRequest( + model=MODEL, + name="test agent", + about="test agent about", + ), client=client, ) @@ -49,12 +53,12 @@ def _(): agent.create_agent( agent_id=agent_id, developer_id=developer_id, - data={ - "model": MODEL, - "name": "test agent", - "about": "test agent about", - "instructions": ["test instruction"], - }, + data=CreateAgentRequest( + model=MODEL, + name="test agent", + about="test agent about", + instructions=["test instruction"], + ), client=client, ) @@ -65,7 +69,9 @@ def _(): agent_id = uuid4() developer_id = uuid4() - result = agent.get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + result = agent.get_agent( + agent_id=agent_id, developer_id=developer_id, client=client + ) assert result is None @@ -79,16 +85,18 @@ def _(): agent.create_agent( agent_id=agent_id, developer_id=developer_id, - data={ - "model": MODEL, - "name": "test agent", - "about": "test agent about", - "default_settings": {"temperature": 1.5}, - }, + data=CreateAgentRequest( + model=MODEL, + name="test agent", + about="test agent about", + default_settings={"temperature": 1.5}, + ), client=client, ) - result = agent.get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + result = agent.get_agent( + agent_id=agent_id, developer_id=developer_id, client=client + ) assert result is not None assert isinstance(result, Agent) @@ -105,11 +113,11 @@ def _(): agent.create_agent( agent_id=agent_id, developer_id=developer_id, - data={ - "model": MODEL, - "name": "test agent", - "about": "test agent about", - }, + data=CreateAgentRequest( + model=MODEL, + name="test agent", + about="test agent about", + ), client=client, ) @@ -117,7 +125,9 @@ def _(): agent.delete_agent(agent_id=agent_id, developer_id=developer_id, client=client) # Check that the agent is deleted - result = agent.get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + result = agent.get_agent( + agent_id=agent_id, developer_id=developer_id, client=client + ) assert result is None @@ -131,22 +141,22 @@ def _(): agent.create_agent( agent_id=agent_id, developer_id=developer_id, - data={ - "model": MODEL, - "name": "test agent", - "about": "test agent about", - }, + data=CreateAgentRequest( + model=MODEL, + name="test agent", + about="test agent about", + ), client=client, ) result = agent.update_agent( agent_id=agent_id, developer_id=developer_id, - data={ - "name": "updated agent", - "about": "updated agent about", - "default_settings": {"temperature": 1.5}, - }, + data=UpdateAgentRequest( + name="updated agent", + about="updated agent about", + default_settings={"temperature": 1.5}, + ), client=client, ) diff --git a/agents-api/tests/test_docs_queries.py b/agents-api/tests/test_docs_queries.py index a6eb24817..9ada53033 100644 --- a/agents-api/tests/test_docs_queries.py +++ b/agents-api/tests/test_docs_queries.py @@ -5,6 +5,13 @@ from pycozo import Client from ward import test +from agents_api.autogen.Docs import ( + CreateDocRequest, + DeleteDocRequest, + GetDocRequest, + ListDocsRequest, + SearchDocsByEmbeddingRequest, +) from agents_api.models.docs.create_doc import create_doc from agents_api.models.docs.delete_doc import delete_doc from agents_api.models.docs.embed_snippets import embed_snippets @@ -40,7 +47,7 @@ def _(): owner_type=owner_type, owner_id=owner_id, doc_id=doc_id, - data={"title": "Hello", "content": ["World"]}, + data=CreateDocRequest(title="Hello", content=["World"]), client=client, ) @@ -59,7 +66,7 @@ def _(): owner_type=owner_type, owner_id=owner_id, doc_id=doc_id, - data={"title": "Hello", "content": ["World"]}, + data=CreateDocRequest(title="Hello", content=["World"]), client=client, ) @@ -67,6 +74,7 @@ def _(): developer_id=developer_id, owner_type=owner_type, doc_id=doc_id, + data=GetDocRequest(), client=client, ) @@ -87,7 +95,7 @@ def _(): owner_type=owner_type, owner_id=owner_id, doc_id=doc_id, - data={"title": "Hello", "content": ["World"]}, + data=CreateDocRequest(title="Hello", content=["World"]), client=client, ) @@ -96,6 +104,7 @@ def _(): owner_type=owner_type, owner_id=owner_id, doc_id=doc_id, + data=DeleteDocRequest(), client=client, ) @@ -103,6 +112,7 @@ def _(): developer_id=developer_id, owner_type=owner_type, doc_id=doc_id, + data=GetDocRequest(), client=client, ) @@ -123,7 +133,7 @@ def _(): owner_type=owner_type, owner_id=owner_id, doc_id=doc_id, - data={"title": "Hello", "content": ["World"]}, + data=CreateDocRequest(title="Hello", content=["World"]), client=client, ) @@ -131,6 +141,7 @@ def _(): developer_id=developer_id, owner_type=owner_type, owner_id=owner_id, + data=ListDocsRequest(), client=client, ) @@ -151,7 +162,7 @@ def _(): owner_type=owner_type, owner_id=owner_id, doc_id=doc_id, - data={"title": "Hello", "content": ["World"]}, + data=CreateDocRequest(title="Hello", content=["World"]), client=client, ) @@ -171,7 +182,7 @@ def _(): developer_id=developer_id, owner_type=owner_type, owner_id=owner_id, - query_embedding=query_embedding, + data=SearchDocsByEmbeddingRequest(query_embedding=query_embedding), client=client, ) @@ -198,7 +209,7 @@ def _(): owner_type=owner_type, owner_id=owner_id, doc_id=doc_id, - data={"title": "Hi", "content": snippets}, + data=CreateDocRequest(title="Hi", content=snippets), client=client, ) diff --git a/agents-api/tests/test_entry_queries.py b/agents-api/tests/test_entry_queries.py index 6235254b0..3b122651b 100644 --- a/agents-api/tests/test_entry_queries.py +++ b/agents-api/tests/test_entry_queries.py @@ -10,16 +10,21 @@ from pycozo import Client from ward import test +from agents_api.autogen.Entries import ( + CreateEntriesRequest, + GetHistoryRequest, + ListEntriesRequest, +) from agents_api.autogen.openapi_model import Entry, FunctionDef from agents_api.models.agent.create_agent import create_agent from agents_api.models.docs.create_doc import create_doc from agents_api.models.docs.embed_snippets import embed_snippets -from agents_api.models.session.create_session import create_session -from agents_api.models.tools.create_tools import create_tools -from agents_api.models.user.create_user import create_user from agents_api.models.entry.create_entries import create_entries from agents_api.models.entry.get_history import get_history from agents_api.models.entry.list_entries import list_entries +from agents_api.models.session.create_session import create_session +from agents_api.models.tools.create_tools import create_tools +from agents_api.models.user.create_user import create_user MODEL = "julep-ai/samantha-1-turbo" @@ -55,7 +60,7 @@ def _(): create_entries( developer_id=developer_id, session_id=session_id, - data=[test_entry], + data=CreateEntriesRequest(entries=[test_entry]), client=client, ) @@ -86,13 +91,14 @@ def _(): create_entries( developer_id=developer_id, session_id=session_id, - data=[test_entry, internal_entry], + data=CreateEntriesRequest(entries=[test_entry, internal_entry]), client=client, ) result = list_entries( developer_id=developer_id, session_id=session_id, + data=ListEntriesRequest(), client=client, ) @@ -139,7 +145,7 @@ def _(): create_entries( developer_id=developer_id, session_id=session_id, - data=[test_entry], + data=CreateEntriesRequest(entries=[test_entry]), client=client, ), create_user( @@ -227,6 +233,7 @@ def _(): result = get_history( developer_id=developer_id, session_id=session_id, + data=GetHistoryRequest(), client=client, ) diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index c1b15c93c..755c0b07f 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -5,13 +5,27 @@ from pycozo import Client from ward import test +from agents_api.autogen.Executions import ( + CreateExecutionRequest, + CreateExecutionTransitionRequest, + GetExecutionRequest, + GetExecutionTransitionRequest, + ListExecutionsRequest, + ListExecutionTransitionsRequest, +) +from agents_api.autogen.openapi_model import Execution, Transition from agents_api.models.execution.create_execution import create_execution -from agents_api.models.execution.create_execution_transition import create_execution_transition +from agents_api.models.execution.create_execution_transition import ( + create_execution_transition, +) from agents_api.models.execution.get_execution import get_execution -from agents_api.models.execution.get_execution_transition import get_execution_transition -from agents_api.models.execution.list_execution_transitions import list_execution_transitions +from agents_api.models.execution.get_execution_transition import ( + get_execution_transition, +) +from agents_api.models.execution.list_execution_transitions import ( + list_execution_transitions, +) from agents_api.models.execution.list_executions import list_executions -from agents_api.autogen.openapi_model import Execution, Transition MODEL = "julep-ai/samantha-1-turbo" @@ -38,7 +52,7 @@ def _(): developer_id=developer_id, task_id=task_id, execution_id=execution_id, - data={"input": "test"}, + data=CreateExecutionRequest(input="test"), client=client, ) @@ -55,7 +69,7 @@ def _(): developer_id=developer_id, task_id=task_id, execution_id=execution_id, - data={"input": "test", "session_id": session_id}, + data=CreateExecutionRequest(input="test", session_id=session_id), client=client, ) @@ -71,12 +85,13 @@ def _(): developer_id=developer_id, task_id=task_id, execution_id=execution_id, - data={"input": "test"}, + data=CreateExecutionRequest(input="test"), client=client, ) result = get_execution( execution_id=execution_id, + data=GetExecutionRequest(), client=client, ) @@ -94,6 +109,7 @@ def _(): result = list_executions( developer_id=developer_id, task_id=task_id, + data=ListExecutionsRequest(), client=client, ) @@ -112,13 +128,14 @@ def _(): developer_id=developer_id, task_id=task_id, execution_id=execution_id, - data={"input": "test"}, + data=CreateExecutionRequest(input="test"), client=client, ) result = list_executions( developer_id=developer_id, task_id=task_id, + data=ListExecutionsRequest(), client=client, ) @@ -138,12 +155,12 @@ def _(): developer_id=developer_id, execution_id=execution_id, transition_id=transition_id, - data={ - "type": "step", - "from": "test", - "to": "test", - "outputs": {"input": "test"}, - }, + data=CreateExecutionTransitionRequest( + type="step", + from_="test", + to="test", + outputs={"input": "test"}, + ), client=client, ) @@ -159,18 +176,19 @@ def _(): developer_id=developer_id, execution_id=execution_id, transition_id=transition_id, - data={ - "type": "step", - "from": "test", - "to": "test", - "outputs": {"input": "test"}, - }, + data=CreateExecutionTransitionRequest( + type="step", + from_="test", + to="test", + outputs={"input": "test"}, + ), client=client, ) result = get_execution_transition( developer_id=developer_id, transition_id=transition_id, + data=GetExecutionTransitionRequest(), client=client, ) @@ -190,18 +208,19 @@ def _(): developer_id=developer_id, execution_id=execution_id, transition_id=transition_id, - data={ - "type": "step", - "from": "test", - "to": "test", - "outputs": {"input": "test"}, - }, + data=CreateExecutionTransitionRequest( + type="step", + from_="test", + to="test", + outputs={"input": "test"}, + ), client=client, ) result = list_execution_transitions( developer_id=developer_id, execution_id=execution_id, + data=ListExecutionTransitionsRequest(), client=client, ) diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index 3282f0aed..20b02373b 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -5,6 +5,12 @@ from pycozo import Client from ward import test +from agents_api.autogen.Sessions import ( + CreateSessionRequest, + DeleteSessionRequest, + GetSessionRequest, + ListSessionsRequest, +) from agents_api.models.session.create_session import create_session from agents_api.models.session.delete_session import delete_session from agents_api.models.session.get_session import get_session @@ -35,11 +41,11 @@ def _(): create_session( session_id=session_id, developer_id=developer_id, - data={ - "users": [user_id], - "agents": [agent_id], - "situation": "test session about", - }, + data=CreateSessionRequest( + users=[user_id], + agents=[agent_id], + situation="test session about", + ), client=client, ) @@ -54,10 +60,10 @@ def _(): create_session( session_id=session_id, developer_id=developer_id, - data={ - "agents": [agent_id], - "situation": "test session about", - }, + data=CreateSessionRequest( + agents=[agent_id], + situation="test session about", + ), client=client, ) @@ -72,6 +78,7 @@ def _(): get_session( session_id=session_id, developer_id=developer_id, + data=GetSessionRequest(), client=client, ) except Exception as e: @@ -89,17 +96,18 @@ def _(): create_session( session_id=session_id, developer_id=developer_id, - data={ - "users": [user_id], - "agents": [agent_id], - "situation": "test session about", - }, + data=CreateSessionRequest( + users=[user_id], + agents=[agent_id], + situation="test session about", + ), client=client, ) result = get_session( session_id=session_id, developer_id=developer_id, + data=GetSessionRequest(), client=client, ) @@ -118,17 +126,18 @@ def _(): create_session( session_id=session_id, developer_id=developer_id, - data={ - "users": [user_id], - "agents": [agent_id], - "situation": "test session about", - }, + data=CreateSessionRequest( + users=[user_id], + agents=[agent_id], + situation="test session about", + ), client=client, ) delete_session( session_id=session_id, developer_id=developer_id, + data=DeleteSessionRequest(), client=client, ) @@ -136,6 +145,7 @@ def _(): get_session( session_id=session_id, developer_id=developer_id, + data=GetSessionRequest(), client=client, ) except Exception as e: @@ -149,6 +159,7 @@ def _(): result = list_sessions( developer_id=developer_id, + data=ListSessionsRequest(), client=client, ) diff --git a/agents-api/tests/test_task_queries.py b/agents-api/tests/test_task_queries.py index 5d6afe9d6..bb7d2c95f 100644 --- a/agents-api/tests/test_task_queries.py +++ b/agents-api/tests/test_task_queries.py @@ -5,12 +5,19 @@ from pycozo import Client from ward import test +from agents_api.autogen.openapi_model import Task +from agents_api.autogen.Tasks import ( + CreateTaskRequest, + DeleteTaskRequest, + GetTaskRequest, + ListTasksRequest, + UpdateTaskRequest, +) from agents_api.models.task.create_task import create_task from agents_api.models.task.delete_task import delete_task from agents_api.models.task.get_task import get_task from agents_api.models.task.list_tasks import list_tasks from agents_api.models.task.update_task import update_task -from agents_api.autogen.openapi_model import Task def cozo_client(migrations_dir: str = "./migrations"): @@ -35,11 +42,11 @@ def _(): developer_id=developer_id, agent_id=agent_id, task_id=task_id, - data={ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, + data=CreateTaskRequest( + name="test task", + description="test task about", + input_schema={"type": "object", "additionalProperties": True}, + ), client=client, ) @@ -54,6 +61,7 @@ def _(): get_task( developer_id=developer_id, task_id=task_id, + data=GetTaskRequest(), client=client, ) except Exception as e: @@ -71,17 +79,18 @@ def _(): developer_id=developer_id, agent_id=agent_id, task_id=task_id, - data={ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, + data=CreateTaskRequest( + name="test task", + description="test task about", + input_schema={"type": "object", "additionalProperties": True}, + ), client=client, ) result = get_task( developer_id=developer_id, task_id=task_id, + data=GetTaskRequest(), client=client, ) @@ -100,17 +109,18 @@ def _(): developer_id=developer_id, agent_id=agent_id, task_id=task_id, - data={ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, + data=CreateTaskRequest( + name="test task", + description="test task about", + input_schema={"type": "object", "additionalProperties": True}, + ), client=client, ) delete_task( developer_id=developer_id, task_id=task_id, + data=DeleteTaskRequest(), client=client, ) @@ -118,6 +128,7 @@ def _(): get_task( developer_id=developer_id, task_id=task_id, + data=GetTaskRequest(), client=client, ) except Exception as e: @@ -135,22 +146,22 @@ def _(): developer_id=developer_id, agent_id=agent_id, task_id=task_id, - data={ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, + data=CreateTaskRequest( + name="test task", + description="test task about", + input_schema={"type": "object", "additionalProperties": True}, + ), client=client, ) result = update_task( developer_id=developer_id, task_id=task_id, - data={ - "name": "updated task", - "description": "updated task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, + data=UpdateTaskRequest( + name="updated task", + description="updated task about", + input_schema={"type": "object", "additionalProperties": True}, + ), client=client, ) @@ -168,6 +179,7 @@ def _(): result = list_tasks( developer_id=developer_id, agent_id=agent_id, + data=ListTasksRequest(), client=client, ) diff --git a/agents-api/tests/test_tool_queries.py b/agents-api/tests/test_tool_queries.py index c02ce562c..474bdeae5 100644 --- a/agents-api/tests/test_tool_queries.py +++ b/agents-api/tests/test_tool_queries.py @@ -5,7 +5,14 @@ from pycozo import Client from ward import test -from agents_api.autogen.openapi_model import FunctionDef, Tool +from agents_api.autogen.openapi_model import ( + CreateToolsRequest, + DeleteToolRequest, + FunctionDef, + GetToolRequest, + ListToolsRequest, + Tool, +) from agents_api.models.tools.create_tools import create_tools from agents_api.models.tools.delete_tool import delete_tool from agents_api.models.tools.get_tool import get_tool @@ -38,7 +45,7 @@ def _(): result = create_tools( developer_id=developer_id, agent_id=agent_id, - data=[tool], + data=CreateToolsRequest(tools=[tool]), client=client, ) @@ -62,7 +69,7 @@ def _(): create_tools( developer_id=developer_id, agent_id=agent_id, - data=[tool], + data=CreateToolsRequest(tools=[tool]), client=client, ) @@ -70,6 +77,7 @@ def _(): developer_id=developer_id, agent_id=agent_id, tool_id=tool_id, + data=DeleteToolRequest(), client=client, ) @@ -93,7 +101,7 @@ def _(): create_tools( developer_id=developer_id, agent_id=agent_id, - data=[tool], + data=CreateToolsRequest(tools=[tool]), client=client, ) @@ -101,6 +109,7 @@ def _(): developer_id=developer_id, agent_id=agent_id, tool_id=tool_id, + data=GetToolRequest(), client=client, ) @@ -123,13 +132,14 @@ def _(): create_tools( developer_id=developer_id, agent_id=agent_id, - data=[tool], + data=CreateToolsRequest(tools=[tool]), client=client, ) result = list_tools( developer_id=developer_id, agent_id=agent_id, + data=ListToolsRequest(), client=client, ) diff --git a/agents-api/tests/test_user_queries.py b/agents-api/tests/test_user_queries.py index 27c745bf7..6f80116b9 100644 --- a/agents-api/tests/test_user_queries.py +++ b/agents-api/tests/test_user_queries.py @@ -7,8 +7,13 @@ from pycozo import Client from ward import raises, test -from agents_api.autogen.openapi_model import User - +from agents_api.autogen.openapi_model import ( + CreateUserRequest, + GetUserRequest, + ListUsersRequest, + UpdateUserRequest, + User, +) from agents_api.models.user.create_user import create_user from agents_api.models.user.get_user import get_user from agents_api.models.user.list_users import list_users @@ -37,10 +42,10 @@ def _(): create_user( user_id=user_id, developer_id=developer_id, - data={ - "name": "test user", - "about": "test user about", - }, + data=CreateUserRequest( + name="test user", + about="test user about", + ), client=client, ) @@ -58,20 +63,20 @@ def _(): create_user( user_id=user_id, developer_id=developer_id, - data={ - "name": "test user", - "about": "test user about", - }, + data=CreateUserRequest( + name="test user", + about="test user about", + ), client=client, ) create_user( user_id=user_id, developer_id=developer_id, - data={ - "name": "test user", - "about": "test user about", - }, + data=CreateUserRequest( + name="test user", + about="test user about", + ), client=client, ) @@ -88,10 +93,10 @@ def _(): update_user( user_id=user_id, developer_id=developer_id, - data={ - "name": "test user", - "about": "test user about", - }, + data=UpdateUserRequest( + name="test user", + about="test user about", + ), client=client, ) @@ -106,10 +111,10 @@ def _(): create_user( user_id=user_id, developer_id=developer_id, - data={ - "name": "test user", - "about": "test user about", - }, + data=CreateUserRequest( + name="test user", + about="test user about", + ), client=client, ) @@ -117,10 +122,10 @@ def _(): update_result = update_user( user_id=user_id, developer_id=developer_id, - data={ - "name": "updated user", - "about": "updated user about", - }, + data=UpdateUserRequest( + name="updated user", + about="updated user about", + ), client=client, ) @@ -141,6 +146,7 @@ def _(): get_user( user_id=user_id, developer_id=developer_id, + data=GetUserRequest(), client=client, ) except Exception as e: @@ -157,16 +163,17 @@ def _(): create_user( user_id=user_id, developer_id=developer_id, - data={ - "name": "test user", - "about": "test user about", - }, + data=CreateUserRequest( + name="test user", + about="test user about", + ), client=client, ) result = get_user( user_id=user_id, developer_id=developer_id, + data=GetUserRequest(), client=client, ) @@ -182,6 +189,7 @@ def _(): result = list_users( developer_id=developer_id, + data=ListUsersRequest(), client=client, ) From d5b08b944dc92a71ebf9cada9663074d91f2df7e Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Fri, 9 Aug 2024 11:18:42 -0400 Subject: [PATCH 016/110] Remove `DeleteDocRequest` import and related tests from `test_docs_queries.py` Update tests in `test_agent_queries.py` to use existing models and queries * Replace `agent.create_agent` with `create_agent` * Replace `agent.delete_agent` with `delete_agent` * Replace `agent.get_agent` with `get_agent` * Replace `agent.list_agents` with `list_agents` * Replace `agent.update_agent` with `update_agent` Update tests in `test_session_queries.py` to use existing models and queries * Import `Session` from `agents_api.autogen.openapi_model` Update tests in `test_task_queries.py` to use existing models and queries * Replace `CreateTaskRequest`, `DeleteTaskRequest`, `GetTaskRequest`, `ListTasksRequest`, `UpdateTaskRequest` with dictionaries Update tests in `test_tool_queries.py` to use existing models and queries * Replace `FunctionDef` with dictionaries Update tests in `test_user_queries.py` to use existing models and queries * Replace `agents_api.autogen.openapi_model` with `agents_api.autogen.Users` --- agents-api/tests/test_agent_queries.py | 31 ++++++------ agents-api/tests/test_docs_queries.py | 39 --------------- agents-api/tests/test_session_queries.py | 1 + agents-api/tests/test_task_queries.py | 62 ++++++++++-------------- agents-api/tests/test_tool_queries.py | 52 ++++++++++---------- agents-api/tests/test_user_queries.py | 2 +- 6 files changed, 69 insertions(+), 118 deletions(-) diff --git a/agents-api/tests/test_agent_queries.py b/agents-api/tests/test_agent_queries.py index ec9fba84a..43b94e073 100644 --- a/agents-api/tests/test_agent_queries.py +++ b/agents-api/tests/test_agent_queries.py @@ -5,12 +5,15 @@ from pycozo import Client from ward import test -from agents_api.autogen.openapi_model import ( - Agent, +from agents_api.autogen.Agents import ( CreateAgentRequest, UpdateAgentRequest, ) -from agents_api.models import agent +from agents_api.models.agent.create_agent import create_agent +from agents_api.models.agent.delete_agent import delete_agent +from agents_api.models.agent.get_agent import get_agent +from agents_api.models.agent.list_agents import list_agents +from agents_api.models.agent.update_agent import update_agent MODEL = "julep-ai/samantha-1-turbo" @@ -32,7 +35,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - agent.create_agent( + create_agent( agent_id=agent_id, developer_id=developer_id, data=CreateAgentRequest( @@ -50,7 +53,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - agent.create_agent( + create_agent( agent_id=agent_id, developer_id=developer_id, data=CreateAgentRequest( @@ -69,7 +72,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - result = agent.get_agent( + result = get_agent( agent_id=agent_id, developer_id=developer_id, client=client ) @@ -82,7 +85,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - agent.create_agent( + create_agent( agent_id=agent_id, developer_id=developer_id, data=CreateAgentRequest( @@ -94,7 +97,7 @@ def _(): client=client, ) - result = agent.get_agent( + result = get_agent( agent_id=agent_id, developer_id=developer_id, client=client ) @@ -110,7 +113,7 @@ def _(): developer_id = uuid4() # Create the agent - agent.create_agent( + create_agent( agent_id=agent_id, developer_id=developer_id, data=CreateAgentRequest( @@ -122,10 +125,10 @@ def _(): ) # Delete the agent - agent.delete_agent(agent_id=agent_id, developer_id=developer_id, client=client) + delete_agent(agent_id=agent_id, developer_id=developer_id, client=client) # Check that the agent is deleted - result = agent.get_agent( + result = get_agent( agent_id=agent_id, developer_id=developer_id, client=client ) @@ -138,7 +141,7 @@ def _(): agent_id = uuid4() developer_id = uuid4() - agent.create_agent( + create_agent( agent_id=agent_id, developer_id=developer_id, data=CreateAgentRequest( @@ -149,7 +152,7 @@ def _(): client=client, ) - result = agent.update_agent( + result = update_agent( agent_id=agent_id, developer_id=developer_id, data=UpdateAgentRequest( @@ -171,7 +174,7 @@ def _(): client = cozo_client() developer_id = uuid4() - result = agent.list_agents(developer_id=developer_id, client=client) + result = list_agents(developer_id=developer_id, client=client) assert isinstance(result, list) assert all(isinstance(agent, Agent) for agent in result) diff --git a/agents-api/tests/test_docs_queries.py b/agents-api/tests/test_docs_queries.py index 9ada53033..75732b95e 100644 --- a/agents-api/tests/test_docs_queries.py +++ b/agents-api/tests/test_docs_queries.py @@ -7,7 +7,6 @@ from agents_api.autogen.Docs import ( CreateDocRequest, - DeleteDocRequest, GetDocRequest, ListDocsRequest, SearchDocsByEmbeddingRequest, @@ -81,44 +80,6 @@ def _(): assert len(result["id"]) == 1, "Only 1 should have been found" -@test("model: delete docs") -def _(): - client = cozo_client() - - for owner_type in ("user", "agent"): - owner_id = uuid4() - doc_id = uuid4() - developer_id = uuid4() - - create_doc( - developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, - doc_id=doc_id, - data=CreateDocRequest(title="Hello", content=["World"]), - client=client, - ) - - delete_doc( - developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, - doc_id=doc_id, - data=DeleteDocRequest(), - client=client, - ) - - result = get_doc( - developer_id=developer_id, - owner_type=owner_type, - doc_id=doc_id, - data=GetDocRequest(), - client=client, - ) - - assert len(result["id"]) == 0, "Delete operation found the row" - - @test("model: list docs") def _(): client = cozo_client() diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index 20b02373b..bdddfd06c 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -15,6 +15,7 @@ from agents_api.models.session.delete_session import delete_session from agents_api.models.session.get_session import get_session from agents_api.models.session.list_sessions import list_sessions +from agents_api.autogen.openapi_model import Session MODEL = "julep-ai/samantha-1-turbo" diff --git a/agents-api/tests/test_task_queries.py b/agents-api/tests/test_task_queries.py index bb7d2c95f..291cc4a02 100644 --- a/agents-api/tests/test_task_queries.py +++ b/agents-api/tests/test_task_queries.py @@ -6,13 +6,6 @@ from ward import test from agents_api.autogen.openapi_model import Task -from agents_api.autogen.Tasks import ( - CreateTaskRequest, - DeleteTaskRequest, - GetTaskRequest, - ListTasksRequest, - UpdateTaskRequest, -) from agents_api.models.task.create_task import create_task from agents_api.models.task.delete_task import delete_task from agents_api.models.task.get_task import get_task @@ -42,11 +35,11 @@ def _(): developer_id=developer_id, agent_id=agent_id, task_id=task_id, - data=CreateTaskRequest( - name="test task", - description="test task about", - input_schema={"type": "object", "additionalProperties": True}, - ), + data={ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, client=client, ) @@ -61,7 +54,6 @@ def _(): get_task( developer_id=developer_id, task_id=task_id, - data=GetTaskRequest(), client=client, ) except Exception as e: @@ -79,18 +71,17 @@ def _(): developer_id=developer_id, agent_id=agent_id, task_id=task_id, - data=CreateTaskRequest( - name="test task", - description="test task about", - input_schema={"type": "object", "additionalProperties": True}, - ), + data={ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, client=client, ) result = get_task( developer_id=developer_id, task_id=task_id, - data=GetTaskRequest(), client=client, ) @@ -109,18 +100,17 @@ def _(): developer_id=developer_id, agent_id=agent_id, task_id=task_id, - data=CreateTaskRequest( - name="test task", - description="test task about", - input_schema={"type": "object", "additionalProperties": True}, - ), + data={ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, client=client, ) delete_task( developer_id=developer_id, task_id=task_id, - data=DeleteTaskRequest(), client=client, ) @@ -128,7 +118,6 @@ def _(): get_task( developer_id=developer_id, task_id=task_id, - data=GetTaskRequest(), client=client, ) except Exception as e: @@ -146,22 +135,22 @@ def _(): developer_id=developer_id, agent_id=agent_id, task_id=task_id, - data=CreateTaskRequest( - name="test task", - description="test task about", - input_schema={"type": "object", "additionalProperties": True}, - ), + data={ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, client=client, ) result = update_task( developer_id=developer_id, task_id=task_id, - data=UpdateTaskRequest( - name="updated task", - description="updated task about", - input_schema={"type": "object", "additionalProperties": True}, - ), + data={ + "name": "updated task", + "description": "updated task about", + "input_schema": {"type": "object", "additionalProperties": True}, + }, client=client, ) @@ -179,7 +168,6 @@ def _(): result = list_tasks( developer_id=developer_id, agent_id=agent_id, - data=ListTasksRequest(), client=client, ) diff --git a/agents-api/tests/test_tool_queries.py b/agents-api/tests/test_tool_queries.py index 474bdeae5..0d331e45c 100644 --- a/agents-api/tests/test_tool_queries.py +++ b/agents-api/tests/test_tool_queries.py @@ -5,13 +5,11 @@ from pycozo import Client from ward import test -from agents_api.autogen.openapi_model import ( +from agents_api.autogen.Tools import ( CreateToolsRequest, DeleteToolRequest, - FunctionDef, GetToolRequest, ListToolsRequest, - Tool, ) from agents_api.models.tools.create_tools import create_tools from agents_api.models.tools.delete_tool import delete_tool @@ -36,11 +34,11 @@ def _(): developer_id = uuid4() agent_id = uuid4() - tool = FunctionDef( - name="hello_world", - description="A function that prints hello world", - parameters={"type": "object", "properties": {}}, - ) + tool = { + "name": "hello_world", + "description": "A function that prints hello world", + "parameters": {"type": "object", "properties": {}}, + } result = create_tools( developer_id=developer_id, @@ -50,7 +48,7 @@ def _(): ) assert result is not None - assert isinstance(result[0], Tool) + assert isinstance(result[0], dict) @test("model: delete tool") @@ -60,11 +58,11 @@ def _(): agent_id = uuid4() tool_id = uuid4() - tool = FunctionDef( - name="hello_world", - description="A function that prints hello world", - parameters={"type": "object", "properties": {}}, - ) + tool = { + "name": "hello_world", + "description": "A function that prints hello world", + "parameters": {"type": "object", "properties": {}}, + } create_tools( developer_id=developer_id, @@ -82,7 +80,7 @@ def _(): ) assert result is not None - assert result.id == tool_id + assert result["id"] == str(tool_id) @test("model: get tool") @@ -92,11 +90,11 @@ def _(): agent_id = uuid4() tool_id = uuid4() - tool = FunctionDef( - name="hello_world", - description="A function that prints hello world", - parameters={"type": "object", "properties": {}}, - ) + tool = { + "name": "hello_world", + "description": "A function that prints hello world", + "parameters": {"type": "object", "properties": {}}, + } create_tools( developer_id=developer_id, @@ -114,7 +112,7 @@ def _(): ) assert result is not None - assert isinstance(result, Tool) + assert isinstance(result, dict) @test("model: list tools") @@ -123,11 +121,11 @@ def _(): developer_id = uuid4() agent_id = uuid4() - tool = FunctionDef( - name="hello_world", - description="A function that prints hello world", - parameters={"type": "object", "properties": {}}, - ) + tool = { + "name": "hello_world", + "description": "A function that prints hello world", + "parameters": {"type": "object", "properties": {}}, + } create_tools( developer_id=developer_id, @@ -144,4 +142,4 @@ def _(): ) assert result is not None - assert all(isinstance(tool, Tool) for tool in result) + assert all(isinstance(tool, dict) for tool in result) diff --git a/agents-api/tests/test_user_queries.py b/agents-api/tests/test_user_queries.py index 6f80116b9..898ebf7c0 100644 --- a/agents-api/tests/test_user_queries.py +++ b/agents-api/tests/test_user_queries.py @@ -7,7 +7,7 @@ from pycozo import Client from ward import raises, test -from agents_api.autogen.openapi_model import ( +from agents_api.autogen.Users import ( CreateUserRequest, GetUserRequest, ListUsersRequest, From ad27ec0caf3bbf0240f41885c16effc130e7c781 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 9 Aug 2024 15:19:06 -0400 Subject: [PATCH 017/110] fix(agents-api): Fixed tests Signed-off-by: Diwank Tomer --- .../agents_api/models/agent/create_agent.py | 7 +- .../agents_api/models/agent/delete_agent.py | 21 +- .../agents_api/models/agent/get_agent.py | 5 + .../agents_api/models/docs/delete_doc.py | 13 +- .../agents_api/models/entry/create_entries.py | 7 +- .../models/execution/create_execution.py | 10 +- .../agents_api/models/user/delete_user.py | 30 +- .../agents_api/models/user/update_user.py | 4 +- agents-api/agents_api/models/utils.py | 6 +- agents-api/tests/fixtures.py | 279 ++++++++++++++---- agents-api/tests/test_agent_queries.py | 127 ++------ agents-api/tests/test_docs_queries.py | 256 ++++++---------- agents-api/tests/test_entry_queries.py | 201 ++----------- agents-api/tests/test_execution_queries.py | 208 ++----------- agents-api/tests/test_session_queries.py | 122 +++----- agents-api/tests/test_task_queries.py | 160 ++++------ agents-api/tests/test_tool_queries.py | 125 +++----- agents-api/tests/test_user_queries.py | 138 ++------- 18 files changed, 599 insertions(+), 1120 deletions(-) diff --git a/agents-api/agents_api/models/agent/create_agent.py b/agents-api/agents_api/models/agent/create_agent.py index 404276a23..fd717462a 100644 --- a/agents-api/agents_api/models/agent/create_agent.py +++ b/agents-api/agents_api/models/agent/create_agent.py @@ -23,6 +23,11 @@ @rewrap_exceptions( { + lambda e: isinstance(e, QueryException) + and "asserted to return some results, but returned none" + in str(e): lambda *_: HTTPException( + detail="developer not found", status_code=403 + ), QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), TypeError: partialclass(HTTPException, status_code=400), @@ -62,7 +67,7 @@ def create_agent( ) data.default_settings = data.default_settings or {} - agent_data = data.model_dump() + agent_data = data.model_dump(exclude_unset=True) default_settings = agent_data.pop("default_settings") settings_cols, settings_vals = cozo_process_mutate_data( diff --git a/agents-api/agents_api/models/agent/delete_agent.py b/agents-api/agents_api/models/agent/delete_agent.py index a9af4e2bc..27e3ecc1c 100644 --- a/agents-api/agents_api/models/agent/delete_agent.py +++ b/agents-api/agents_api/models/agent/delete_agent.py @@ -23,6 +23,11 @@ @rewrap_exceptions( { + lambda e: isinstance(e, QueryException) + and "asserted to return some results, but returned none" + in str(e): lambda *_: HTTPException( + detail="developer not found or doesnt own resource", status_code=404 + ), QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), TypeError: partialclass(HTTPException, status_code=400), @@ -57,14 +62,18 @@ def delete_agent(*, developer_id: UUID, agent_id: UUID) -> tuple[list[str], dict verify_developer_owns_resource_query(developer_id, "agents", agent_id=agent_id), """ # Delete docs - ?[agent_id, doc_id] := - *agent_docs{ - agent_id, + ?[owner_id, owner_type, doc_id] := + *docs{ + owner_type, + owner_id, doc_id, - }, agent_id = to_uuid($agent_id) + }, + owner_id = to_uuid($agent_id), + owner_type = "agent" - :delete agent_docs { - agent_id, + :delete docs { + owner_type, + owner_id, doc_id } :returning diff --git a/agents-api/agents_api/models/agent/get_agent.py b/agents-api/agents_api/models/agent/get_agent.py index a57c14a31..d42e1694f 100644 --- a/agents-api/agents_api/models/agent/get_agent.py +++ b/agents-api/agents_api/models/agent/get_agent.py @@ -18,6 +18,11 @@ @rewrap_exceptions( { + lambda e: isinstance(e, QueryException) + and "asserted to return some results, but returned none" + in str(e): lambda *_: HTTPException( + detail="developer not found or doesnt own resource", status_code=404 + ), QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), TypeError: partialclass(HTTPException, status_code=400), diff --git a/agents-api/agents_api/models/docs/delete_doc.py b/agents-api/agents_api/models/docs/delete_doc.py index d754a671a..9dcaf0f33 100644 --- a/agents-api/agents_api/models/docs/delete_doc.py +++ b/agents-api/agents_api/models/docs/delete_doc.py @@ -12,6 +12,7 @@ partialclass, rewrap_exceptions, verify_developer_id_query, + verify_developer_owns_resource_query, wrap_in_class, ) @@ -37,6 +38,8 @@ def delete_doc( *, developer_id: UUID, + owner_id: UUID, + owner_type: str, doc_id: UUID, ) -> tuple[list[str], dict]: """Constructs and returns a datalog query for deleting documents and associated information snippets. @@ -52,6 +55,7 @@ def delete_doc( """ # Convert UUID parameters to string format for use in the datalog query doc_id = str(doc_id) + owner_id = str(owner_id) # The following query is divided into two main parts: # 1. Deleting information snippets associated with the document @@ -75,16 +79,19 @@ def delete_doc( delete_doc_query = """ # Delete the docs - ?[doc_id] <- [[ to_uuid($doc_id) ]] + ?[doc_id, owner_type, owner_id] <- [[ to_uuid($doc_id), $owner_type, to_uuid($owner_id) ]] - :delete docs { doc_id } + :delete docs { doc_id, owner_type, owner_id } :returning """ queries = [ verify_developer_id_query(developer_id), + verify_developer_owns_resource_query( + developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} + ), delete_snippets_query, delete_doc_query, ] - return (queries, {"doc_id": doc_id}) + return (queries, {"doc_id": doc_id, "owner_type": owner_type, "owner_id": owner_id}) diff --git a/agents-api/agents_api/models/entry/create_entries.py b/agents-api/agents_api/models/entry/create_entries.py index 7e582414e..72551dacc 100644 --- a/agents-api/agents_api/models/entry/create_entries.py +++ b/agents-api/agents_api/models/entry/create_entries.py @@ -1,3 +1,4 @@ +import json from uuid import UUID, uuid4 from beartype import beartype @@ -44,7 +45,7 @@ def create_entries( developer_id = str(developer_id) session_id = str(session_id) - data_dicts = [item.model_dump() for item in data] + data_dicts = [item.model_dump(exclude_unset=True) for item in data] for item in data_dicts: item["content"] = content_to_json(item["content"]) @@ -52,6 +53,10 @@ def create_entries( item["entry_id"] = item.pop("id", None) or str(uuid4()) item["created_at"] = (item.get("created_at") or utcnow()).timestamp() + if not item.get("token_count"): + item["token_count"] = len(json.dumps(item)) // 3.5 + item["tokenizer"] = "character_count" + cols, rows = cozo_process_mutate_data(data_dicts) # Construct a datalog query to insert the processed entries into the 'cozodb' database. diff --git a/agents-api/agents_api/models/execution/create_execution.py b/agents-api/agents_api/models/execution/create_execution.py index 575c4de4c..4e85a1db7 100644 --- a/agents-api/agents_api/models/execution/create_execution.py +++ b/agents-api/agents_api/models/execution/create_execution.py @@ -40,7 +40,7 @@ def create_execution( task_id: UUID, execution_id: UUID | None = None, data: Annotated[CreateExecutionRequest | dict, dict_like(CreateExecutionRequest)], - workflow_hande: WorkflowHandle, + workflow_handle: WorkflowHandle, ) -> tuple[list[str], dict]: execution_id = execution_id or uuid4() @@ -58,10 +58,10 @@ def create_execution( temporal_columns, temporal_values = cozo_process_mutate_data( { "execution_id": execution_id, - "id": workflow_hande.id, - "run_id": workflow_hande.run_id, - "first_execution_run_id": workflow_hande.first_execution_run_id, - "result_run_id": workflow_hande.result_run_id, + "id": workflow_handle.id, + "run_id": workflow_handle.run_id, + "first_execution_run_id": workflow_handle.first_execution_run_id, + "result_run_id": workflow_handle.result_run_id, } ) diff --git a/agents-api/agents_api/models/user/delete_user.py b/agents-api/agents_api/models/user/delete_user.py index dfb720cf8..170a1a399 100644 --- a/agents-api/agents_api/models/user/delete_user.py +++ b/agents-api/agents_api/models/user/delete_user.py @@ -58,41 +58,19 @@ def delete_user(*, developer_id: UUID, user_id: UUID) -> tuple[list[str], dict]: """ # Delete docs ?[user_id, doc_id] := - *user_docs{ - user_id, + *docs{ + owner_id: user_id, + owner_type: "user", doc_id, }, user_id = to_uuid($user_id) - :delete user_docs { + :delete docs { user_id, doc_id } :returning """, """ - # Delete tools - ?[user_id, tool_id] := - *tools{ - user_id, - tool_id, - }, user_id = to_uuid($user_id) - - :delete tools { - user_id, - tool_id - } - :returning - """, - """ - # Delete default user settings - ?[user_id] <- [[$user_id]] - - :delete user_default_settings { - user_id - } - :returning - """, - """ # Delete the user ?[user_id, developer_id] <- [[$user_id, $developer_id]] diff --git a/agents-api/agents_api/models/user/update_user.py b/agents-api/agents_api/models/user/update_user.py index f929b8d7e..7132e13a0 100644 --- a/agents-api/agents_api/models/user/update_user.py +++ b/agents-api/agents_api/models/user/update_user.py @@ -32,7 +32,7 @@ @cozo_query @beartype def update_user( - *, developer_id: UUID, user_id: UUID, update_user: UpdateUserRequest + *, developer_id: UUID, user_id: UUID, data: UpdateUserRequest ) -> tuple[list[str], dict]: """Updates user information in the 'cozodb' database. @@ -47,7 +47,7 @@ def update_user( """ user_id = str(user_id) developer_id = str(developer_id) - update_data = update_user.model_dump() + update_data = data.model_dump() # Prepares the update data by filtering out None values and adding user_id and developer_id. user_update_cols, user_update_vals = cozo_process_mutate_data( diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index 5eafe4d78..56cbefd55 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -188,7 +188,7 @@ def wrapper(*args: P.args, **kwargs: P.kwargs): transform = transform or (lambda x: x) if one: - assert len(data) >= 1 + assert len(data) >= 1, "Expected one result, got none" return cls(**transform(data[0])) return [cls(**item) for item in map(transform, data)] @@ -232,7 +232,9 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> T: else transform(error) ) - raise transform(new_error) from error + setattr(new_error, "__cause__", error) + + raise new_error from error raise diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 2f7cfbffe..c4b36bb46 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -1,18 +1,37 @@ from uuid import uuid4 +from cozo_migrate.api import apply, init from julep import AsyncClient, Client +from pycozo import Client as CozoClient +from temporalio.client import WorkflowHandle from ward import fixture -from agents_api.routers.sessions.session import BaseSession +from agents_api.autogen.openapi_model import ( + CreateAgentRequest, + CreateDocRequest, + CreateExecutionRequest, + CreateSessionRequest, + CreateTaskRequest, + CreateToolRequest, + CreateUserRequest, +) +from agents_api.models.agent.create_agent import create_agent +from agents_api.models.agent.delete_agent import delete_agent +from agents_api.models.docs.create_doc import create_doc +from agents_api.models.docs.delete_doc import delete_doc +from agents_api.models.execution.create_execution import create_execution +from agents_api.models.session.create_session import create_session +from agents_api.models.session.delete_session import delete_session +from agents_api.models.task.create_task import create_task +from agents_api.models.task.delete_task import delete_task +from agents_api.models.tools.create_tools import create_tools +from agents_api.models.tools.delete_tool import delete_tool +from agents_api.models.user.create_user import create_user +from agents_api.models.user.delete_user import delete_user # TODO: make clients connect to real service -@fixture(scope="global") -def base_session(): - return BaseSession(uuid4(), uuid4()) - - @fixture(scope="global") def client(): # Mock server base url @@ -31,64 +50,216 @@ def async_client(): return client -@fixture -def agent(client=client): - return client.agents.create( - name="Samantha", - about="about Samantha", - instructions=[ - "non-important content", - "important content", - ], - functions=[ - { - "description": "func desc", - "name": "some_func", - "parameters": {"param1": "string"}, - } - ], - default_settings={ - "frequency_penalty": 0.1, - "length_penalty": 0.9, - "presence_penalty": 0.8, - "repetition_penalty": 0.7, - "temperature": 0.6, - "top_p": 0.5, - }, - model="julep-ai/samantha-1-turbo", - docs=[ - { - "title": "some titie", - "content": "some content", - }, - ], +@fixture(scope="global") +def cozo_client(migrations_dir: str = "./migrations"): + # Create a new client for each test + # and initialize the schema. + client = CozoClient() + + init(client) + apply(client, migrations_dir=migrations_dir, all_=True) + + return client + + +@fixture(scope="global") +def test_developer_id(cozo_client=cozo_client): + developer_id = uuid4() + + cozo_client.run( + f""" + ?[developer_id, email] <- [["{str(developer_id)}", "developers@julep.ai"]] + :insert developers {{ developer_id, email }} + """ ) + yield developer_id -@fixture -def user(client=client): - return client.users.create( - name="test user", - about="test user about", + cozo_client.run( + f""" + ?[developer_id, email] <- [["{str(developer_id)}", "developers@julep.ai"]] + :delete developers {{ developer_id, email }} + """ ) -@fixture -def session(user=user, agent=agent, client=client): - return client.sessions.create( +@fixture(scope="global") +def test_agent(cozo_client=cozo_client, developer_id=test_developer_id): + agent = create_agent( + developer_id=developer_id, + data=CreateAgentRequest( + model="gpt-4o", + name="test agent", + about="test agent about", + ), + client=cozo_client, + ) + + yield agent + + delete_agent( + developer_id=developer_id, + agent_id=agent.id, + client=cozo_client, + ) + + +@fixture(scope="global") +def test_user(cozo_client=cozo_client, developer_id=test_developer_id): + user = create_user( + developer_id=developer_id, + data=CreateUserRequest( + name="test user", + about="test user about", + ), + client=cozo_client, + ) + + yield user + + delete_user( + developer_id=developer_id, user_id=user.id, + client=cozo_client, + ) + + +@fixture(scope="global") +def test_session( + cozo_client=cozo_client, + developer_id=test_developer_id, + test_user=test_user, + test_agent=test_agent, +): + session = create_session( + developer_id=developer_id, + data=CreateSessionRequest( + agent=test_agent.id, + user=test_user.id, + ), + client=cozo_client, + ) + + yield session + + delete_session( + developer_id=developer_id, + session_id=session.id, + client=cozo_client, + ) + + +@fixture(scope="global") +def test_doc( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + doc = create_doc( + developer_id=developer_id, + owner_type="agent", + owner_id=agent.id, + data=CreateDocRequest(title="Hello", content=["World"]), + client=client, + ) + + yield doc + + delete_doc( + developer_id=developer_id, + doc_id=doc.id, + owner_type="agent", + owner_id=agent.id, + client=client, + ) + + +@fixture(scope="global") +def test_task( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + task = create_task( + developer_id=developer_id, agent_id=agent.id, - situation="test situation", + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [], + } + ), + client=client, ) + yield task -@fixture -def task(agent=agent, client=client): - return client.tasks.create( + delete_task( + developer_id=developer_id, + task_id=task.id, + client=client, + ) + + +@fixture(scope="global") +def test_execution( + client=cozo_client, + developer_id=test_developer_id, + task=test_task, +): + workflow_handle = WorkflowHandle( + client=None, + id="blah", + ) + + execution = create_execution( + developer_id=developer_id, + task_id=task.id, + data=CreateExecutionRequest(input={"test": "test"}), + workflow_handle=workflow_handle, + client=client, + ) + + yield execution + + client.run(f""" + ?[execution_id] <- ["{str(execution.id)}"] + :delete executions {{ execution_id }} + """) + + +@fixture(scope="global") +def test_tool( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + function = { + "name": "hello_world1", + "description": "A function that prints hello world", + "parameters": {"type": "object", "properties": {}}, + } + + tool = { + "function": function, + "name": "hello_world1", + "type": "function", + } + + [tool, *_] = create_tools( + developer_id=developer_id, + agent_id=agent.id, + data=[CreateToolRequest(**tool)], + client=client, + ) + + yield tool + + delete_tool( + developer_id=developer_id, agent_id=agent.id, - name="task1", - description="task 1", - tools_available=["tool1"], - input_schema={}, - main=[], + tool_id=tool.id, + client=client, ) diff --git a/agents-api/tests/test_agent_queries.py b/agents-api/tests/test_agent_queries.py index 43b94e073..143788e63 100644 --- a/agents-api/tests/test_agent_queries.py +++ b/agents-api/tests/test_agent_queries.py @@ -1,12 +1,12 @@ # Tests for agent queries from uuid import uuid4 -from cozo_migrate.api import apply, init -from pycozo import Client from ward import test -from agents_api.autogen.Agents import ( +from agents_api.autogen.openapi_model import ( + Agent, CreateAgentRequest, + ResourceUpdatedResponse, UpdateAgentRequest, ) from agents_api.models.agent.create_agent import create_agent @@ -14,52 +14,30 @@ from agents_api.models.agent.get_agent import get_agent from agents_api.models.agent.list_agents import list_agents from agents_api.models.agent.update_agent import update_agent - -MODEL = "julep-ai/samantha-1-turbo" - - -def cozo_client(migrations_dir: str = "./migrations"): - # Create a new client for each test - # and initialize the schema. - client = Client() - - init(client) - apply(client, migrations_dir=migrations_dir, all_=True) - - return client +from tests.fixtures import cozo_client, test_agent, test_developer_id @test("model: create agent") -def _(): - client = cozo_client() - agent_id = uuid4() - developer_id = uuid4() - +def _(client=cozo_client, developer_id=test_developer_id): create_agent( - agent_id=agent_id, developer_id=developer_id, data=CreateAgentRequest( - model=MODEL, name="test agent", about="test agent about", + model="gpt-4o", ), client=client, ) @test("model: create agent with instructions") -def _(): - client = cozo_client() - agent_id = uuid4() - developer_id = uuid4() - +def _(client=cozo_client, developer_id=test_developer_id): create_agent( - agent_id=agent_id, developer_id=developer_id, data=CreateAgentRequest( - model=MODEL, name="test agent", about="test agent about", + model="gpt-4o", instructions=["test instruction"], ), client=client, @@ -67,93 +45,55 @@ def _(): @test("model: get agent not exists") -def _(): - client = cozo_client() +def _(client=cozo_client, developer_id=test_developer_id): agent_id = uuid4() - developer_id = uuid4() - result = get_agent( - agent_id=agent_id, developer_id=developer_id, client=client - ) + try: + get_agent(agent_id=agent_id, developer_id=developer_id, client=client) + except Exception: + pass - assert result is None + else: + assert None @test("model: get agent exists") -def _(): - client = cozo_client() - agent_id = uuid4() - developer_id = uuid4() - - create_agent( - agent_id=agent_id, - developer_id=developer_id, - data=CreateAgentRequest( - model=MODEL, - name="test agent", - about="test agent about", - default_settings={"temperature": 1.5}, - ), - client=client, - ) - - result = get_agent( - agent_id=agent_id, developer_id=developer_id, client=client - ) +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): + result = get_agent(agent_id=agent.id, developer_id=developer_id, client=client) assert result is not None assert isinstance(result, Agent) - assert result.default_settings.temperature == 1.5 @test("model: delete agent") -def _(): - client = cozo_client() - agent_id = uuid4() - developer_id = uuid4() - - # Create the agent - create_agent( - agent_id=agent_id, +def _(client=cozo_client, developer_id=test_developer_id): + temp_agent = create_agent( developer_id=developer_id, data=CreateAgentRequest( - model=MODEL, name="test agent", about="test agent about", + model="gpt-4o", + instructions=["test instruction"], ), client=client, ) # Delete the agent - delete_agent(agent_id=agent_id, developer_id=developer_id, client=client) + delete_agent(agent_id=temp_agent.id, developer_id=developer_id, client=client) # Check that the agent is deleted - result = get_agent( - agent_id=agent_id, developer_id=developer_id, client=client - ) - - assert result is None + try: + get_agent(agent_id=temp_agent.id, developer_id=developer_id, client=client) + except Exception: + pass + else: + raise AssertionError("Agent found") @test("model: update agent") -def _(): - client = cozo_client() - agent_id = uuid4() - developer_id = uuid4() - - create_agent( - agent_id=agent_id, - developer_id=developer_id, - data=CreateAgentRequest( - model=MODEL, - name="test agent", - about="test agent about", - ), - client=client, - ) - +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): result = update_agent( - agent_id=agent_id, + agent_id=agent.id, developer_id=developer_id, data=UpdateAgentRequest( name="updated agent", @@ -164,15 +104,12 @@ def _(): ) assert result is not None - assert isinstance(result, Agent) - assert result.default_settings.temperature == 1.5 + assert isinstance(result, ResourceUpdatedResponse) @test("model: list agents") -def _(): +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): """Tests listing all agents associated with a developer in the database. Verifies that the correct list of agents is retrieved.""" - client = cozo_client() - developer_id = uuid4() result = list_agents(developer_id=developer_id, client=client) diff --git a/agents-api/tests/test_docs_queries.py b/agents-api/tests/test_docs_queries.py index 75732b95e..1617e2586 100644 --- a/agents-api/tests/test_docs_queries.py +++ b/agents-api/tests/test_docs_queries.py @@ -1,188 +1,116 @@ # Tests for entry queries -from uuid import uuid4 -from cozo_migrate.api import apply, init -from pycozo import Client from ward import test -from agents_api.autogen.Docs import ( - CreateDocRequest, - GetDocRequest, - ListDocsRequest, - SearchDocsByEmbeddingRequest, -) +from agents_api.autogen.openapi_model import CreateDocRequest from agents_api.models.docs.create_doc import create_doc from agents_api.models.docs.delete_doc import delete_doc from agents_api.models.docs.embed_snippets import embed_snippets from agents_api.models.docs.get_doc import get_doc from agents_api.models.docs.list_docs import list_docs from agents_api.models.docs.search_docs_by_embedding import search_docs_by_embedding +from tests.fixtures import ( + cozo_client, + test_agent, + test_developer_id, + test_doc, + test_user, +) EMBEDDING_SIZE: int = 1024 -def cozo_client(migrations_dir: str = "./migrations"): - # Create a new client for each test - # and initialize the schema. - client = Client() - - init(client) - apply(client, migrations_dir=migrations_dir, all_=True) - - return client - - @test("model: create docs") -def _(): - client = cozo_client() - - for owner_type in ("user", "agent"): - owner_id = uuid4() - doc_id = uuid4() - developer_id = uuid4() - - create_doc( - developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, - doc_id=doc_id, - data=CreateDocRequest(title="Hello", content=["World"]), - client=client, - ) +def _( + client=cozo_client, developer_id=test_developer_id, agent=test_agent, user=test_user +): + create_doc( + developer_id=developer_id, + owner_type="agent", + owner_id=agent.id, + data=CreateDocRequest(title="Hello", content=["World"]), + client=client, + ) + + create_doc( + developer_id=developer_id, + owner_type="user", + owner_id=user.id, + data=CreateDocRequest(title="Hello", content=["World"]), + client=client, + ) @test("model: get docs") -def _(): - client = cozo_client() - - for owner_type in ("user", "agent"): - owner_id = uuid4() - doc_id = uuid4() - developer_id = uuid4() - - create_doc( - developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, - doc_id=doc_id, - data=CreateDocRequest(title="Hello", content=["World"]), - client=client, - ) - - result = get_doc( - developer_id=developer_id, - owner_type=owner_type, - doc_id=doc_id, - data=GetDocRequest(), - client=client, - ) - - assert len(result["id"]) == 1, "Only 1 should have been found" +def _(client=cozo_client, doc=test_doc, developer_id=test_developer_id): + get_doc( + developer_id=developer_id, + doc_id=doc.id, + client=client, + ) + + +@test("model: delete doc") +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): + doc = create_doc( + developer_id=developer_id, + owner_type="agent", + owner_id=agent.id, + data=CreateDocRequest(title="Hello", content=["World"]), + client=client, + ) + + delete_doc( + developer_id=developer_id, + doc_id=doc.id, + owner_type="agent", + owner_id=agent.id, + client=client, + ) @test("model: list docs") -def _(): - client = cozo_client() - - for owner_type in ("user", "agent"): - owner_id = uuid4() - doc_id = uuid4() - developer_id = uuid4() - - create_doc( - developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, - doc_id=doc_id, - data=CreateDocRequest(title="Hello", content=["World"]), - client=client, - ) - - result = list_docs( - developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, - data=ListDocsRequest(), - client=client, - ) - - assert len(result["id"]) == 1, "Only 1 should have been found" +def _( + client=cozo_client, developer_id=test_developer_id, doc=test_doc, agent=test_agent +): + result = list_docs( + developer_id=developer_id, + owner_type="agent", + owner_id=agent.id, + client=client, + ) + + assert len(result) >= 1 @test("model: search docs") -def _(): - client = cozo_client() - - for owner_type in ("user", "agent"): - owner_id = uuid4() - doc_id = uuid4() - developer_id = uuid4() - - create_doc( - developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, - doc_id=doc_id, - data=CreateDocRequest(title="Hello", content=["World"]), - client=client, - ) - - ### Add embedding to the snippet - embed_snippets( - developer_id=developer_id, - doc_id=doc_id, - snippet_indices=[0], - embeddings=[[1.0] * EMBEDDING_SIZE], - client=client, - ) - - ### Search - query_embedding = [0.99] * EMBEDDING_SIZE - - result = search_docs_by_embedding( - developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, - data=SearchDocsByEmbeddingRequest(query_embedding=query_embedding), - client=client, - ) - - assert len(result["id"]) == 1, "Only 1 should have been found" - - -@test("model: embed docs") -def _(): - client = cozo_client() - - for owner_type in ("user", "agent"): - owner_id = uuid4() - doc_id = uuid4() - developer_id = uuid4() - - snippets = [ - "Hello World", - "Hello Banana", - "Hello Apple", - ] - - create_doc( - developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, - doc_id=doc_id, - data=CreateDocRequest(title="Hi", content=snippets), - client=client, - ) - - ### Add embedding to the snippet - snippet_indices = [*range(len(snippets))] - - embeddings = [[1.0] * EMBEDDING_SIZE for _ in snippets] - - embed_snippets( - developer_id=developer_id, - doc_id=doc_id, - snippet_indices=snippet_indices, - embeddings=embeddings, - client=client, - ) +def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): + doc = create_doc( + developer_id=developer_id, + owner_type="agent", + owner_id=agent.id, + data=CreateDocRequest(title="Hello", content=["World"]), + client=client, + ) + + ### Add embedding to the snippet + embed_snippets( + developer_id=developer_id, + doc_id=doc.id, + snippet_indices=[0], + embeddings=[[1.0] * EMBEDDING_SIZE], + client=client, + ) + + ### Search + query_embedding = [0.99] * EMBEDDING_SIZE + + result = search_docs_by_embedding( + developer_id=developer_id, + owner_type="agent", + owner_id=agent.id, + query_embedding=query_embedding, + client=client, + ) + + assert len(result) >= 1 diff --git a/agents-api/tests/test_entry_queries.py b/agents-api/tests/test_entry_queries.py index 3b122651b..954defd4d 100644 --- a/agents-api/tests/test_entry_queries.py +++ b/agents-api/tests/test_entry_queries.py @@ -4,85 +4,55 @@ """ # Tests for entry queries -from uuid import uuid4 -from cozo_migrate.api import apply, init -from pycozo import Client from ward import test -from agents_api.autogen.Entries import ( - CreateEntriesRequest, - GetHistoryRequest, - ListEntriesRequest, -) -from agents_api.autogen.openapi_model import Entry, FunctionDef -from agents_api.models.agent.create_agent import create_agent -from agents_api.models.docs.create_doc import create_doc -from agents_api.models.docs.embed_snippets import embed_snippets +from agents_api.autogen.openapi_model import CreateEntryRequest from agents_api.models.entry.create_entries import create_entries -from agents_api.models.entry.get_history import get_history from agents_api.models.entry.list_entries import list_entries -from agents_api.models.session.create_session import create_session -from agents_api.models.tools.create_tools import create_tools -from agents_api.models.user.create_user import create_user +from tests.fixtures import cozo_client, test_developer_id, test_session MODEL = "julep-ai/samantha-1-turbo" -# Initializes a new CozoDB client for testing, applying all migrations. -def cozo_client(migrations_dir: str = "./migrations"): - # Create a new client for each test - # and initialize the schema. - client = Client() - - init(client) - apply(client, migrations_dir=migrations_dir, all_=True) - - return client - - @test("model: create entry") -def _(): +def _(client=cozo_client, developer_id=test_developer_id, session=test_session): """ Tests the addition of a new entry to the database. Verifies that the entry can be successfully added using the create_entries function. """ - client = cozo_client() - session_id = uuid4() - developer_id = uuid4() - test_entry = Entry( - session_id=session_id, + test_entry = CreateEntryRequest( + session_id=session.id, role="user", + source="internal", content="test entry content", ) create_entries( developer_id=developer_id, - session_id=session_id, - data=CreateEntriesRequest(entries=[test_entry]), + session_id=session.id, + data=[test_entry], client=client, ) @test("model: get entries") -def _(): +def _(client=cozo_client, developer_id=test_developer_id, session=test_session): """ Tests the retrieval of entries from the database. Verifies that entries matching specific criteria can be successfully retrieved. """ - client = cozo_client() - session_id = uuid4() - developer_id = uuid4() - test_entry = Entry( - session_id=session_id, + test_entry = CreateEntryRequest( + session_id=session.id, role="user", + source="api_request", content="test entry content", ) - internal_entry = Entry( - session_id=session_id, + internal_entry = CreateEntryRequest( + session_id=session.id, role="user", content="test entry content", source="internal", @@ -90,151 +60,16 @@ def _(): create_entries( developer_id=developer_id, - session_id=session_id, - data=CreateEntriesRequest(entries=[test_entry, internal_entry]), + session_id=session.id, + data=[test_entry, internal_entry], client=client, ) result = list_entries( developer_id=developer_id, - session_id=session_id, - data=ListEntriesRequest(), + session_id=session.id, client=client, ) # Asserts that only one entry is retrieved, matching the session_id. - assert len(result["id"]) == 1 - - -@test("model: procedural memory context") -def _(): - """ - Tests the procedural memory context in the database. - Verifies the functionality of retrieving relevant memory context based on embeddings. - """ - client = cozo_client() - developer_id = uuid4() - user_id = uuid4() - agent_id = uuid4() - session_id = uuid4() - uuid4() - user_doc_id = uuid4() - agent_doc_id = uuid4() - - # Setup: Creates a user, agent, session, function, and documents, then embeds tools and document snippets. - # Create stuff - test_entry = Entry( - session_id=session_id, - role="user", - content="test entry content", - source="api_request", - ) - - test_instruction1 = "test instruction" - test_instruction2 = "test instruction" - test_function = FunctionDef( - name="test function", - description="test function description", - parameters={"type": "object", "properties": {}}, - ) - - test_user_doc = "test user doc" - test_agent_doc = "test agent doc" - - [ - create_entries( - developer_id=developer_id, - session_id=session_id, - data=CreateEntriesRequest(entries=[test_entry]), - client=client, - ), - create_user( - developer_id=developer_id, - user_id=user_id, - data={ - "name": "test user", - "about": "test user about", - }, - client=client, - ), - create_agent( - developer_id=developer_id, - agent_id=agent_id, - data={ - "model": MODEL, - "name": "test agent", - "about": "test agent about", - "instructions": [test_instruction1, test_instruction2], - }, - client=client, - ), - create_session( - developer_id=developer_id, - session_id=session_id, - data={ - "users": [user_id], - "agents": [agent_id], - "situation": "test situation", - }, - client=client, - ), - create_tools( - developer_id=developer_id, - agent_id=agent_id, - data=[ - { - "name": test_function.name, - "description": test_function.description, - "parameters": test_function.parameters, - } - ], - client=client, - ), - create_doc( - developer_id=developer_id, - owner_type="agent", - owner_id=agent_id, - doc_id=agent_doc_id, - data={ - "title": test_agent_doc, - "content": [test_agent_doc], - }, - client=client, - ), - create_doc( - developer_id=developer_id, - owner_type="user", - owner_id=user_id, - doc_id=user_doc_id, - data={ - "title": test_user_doc, - "content": [test_user_doc], - }, - client=client, - ), - embed_snippets( - developer_id=developer_id, - doc_id=agent_doc_id, - snippet_indices=[0], - embeddings=[[1.0] * 1024], - client=client, - ), - embed_snippets( - developer_id=developer_id, - doc_id=user_doc_id, - snippet_indices=[0], - embeddings=[[1.0] * 1024], - client=client, - ), - ] - - # Executes the procedural memory context query to retrieve relevant memory context based on embeddings. - # Run the query - result = get_history( - developer_id=developer_id, - session_id=session_id, - data=GetHistoryRequest(), - client=client, - ) - - assert len(result["entries"]) == 1 + assert len(result) == 1 diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index 755c0b07f..38099f54c 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -1,97 +1,40 @@ # Tests for execution queries -from uuid import uuid4 -from cozo_migrate.api import apply, init -from pycozo import Client +from temporalio.client import WorkflowHandle from ward import test from agents_api.autogen.Executions import ( CreateExecutionRequest, - CreateExecutionTransitionRequest, - GetExecutionRequest, - GetExecutionTransitionRequest, - ListExecutionsRequest, - ListExecutionTransitionsRequest, ) -from agents_api.autogen.openapi_model import Execution, Transition +from agents_api.autogen.openapi_model import Execution from agents_api.models.execution.create_execution import create_execution -from agents_api.models.execution.create_execution_transition import ( - create_execution_transition, -) from agents_api.models.execution.get_execution import get_execution -from agents_api.models.execution.get_execution_transition import ( - get_execution_transition, -) -from agents_api.models.execution.list_execution_transitions import ( - list_execution_transitions, -) from agents_api.models.execution.list_executions import list_executions +from tests.fixtures import cozo_client, test_developer_id, test_execution, test_task MODEL = "julep-ai/samantha-1-turbo" -def cozo_client(migrations_dir: str = "./migrations"): - # Create a new client for each test - # and initialize the schema. - client = Client() - - init(client) - apply(client, migrations_dir=migrations_dir, all_=True) - - return client - - @test("model: create execution") -def _(): - client = cozo_client() - developer_id = uuid4() - task_id = uuid4() - execution_id = uuid4() - - create_execution( - developer_id=developer_id, - task_id=task_id, - execution_id=execution_id, - data=CreateExecutionRequest(input="test"), - client=client, +def _(client=cozo_client, developer_id=test_developer_id, task=test_task): + workflow_handle = WorkflowHandle( + client=None, + id="blah", ) - -@test("model: create execution with session") -def _(): - client = cozo_client() - developer_id = uuid4() - task_id = uuid4() - execution_id = uuid4() - session_id = uuid4() - create_execution( developer_id=developer_id, - task_id=task_id, - execution_id=execution_id, - data=CreateExecutionRequest(input="test", session_id=session_id), + task_id=task.id, + data=CreateExecutionRequest(input={"test": "test"}), + workflow_handle=workflow_handle, client=client, ) @test("model: get execution") -def _(): - client = cozo_client() - developer_id = uuid4() - task_id = uuid4() - execution_id = uuid4() - - create_execution( - developer_id=developer_id, - task_id=task_id, - execution_id=execution_id, - data=CreateExecutionRequest(input="test"), - client=client, - ) - +def _(client=cozo_client, developer_id=test_developer_id, execution=test_execution): result = get_execution( - execution_id=execution_id, - data=GetExecutionRequest(), + execution_id=execution.id, client=client, ) @@ -100,130 +43,19 @@ def _(): assert result.status == "queued" -@test("model: list executions empty") -def _(): - client = cozo_client() - developer_id = uuid4() - task_id = uuid4() - - result = list_executions( - developer_id=developer_id, - task_id=task_id, - data=ListExecutionsRequest(), - client=client, - ) - - assert isinstance(result, list) - assert len(result) == 0 - - @test("model: list executions") -def _(): - client = cozo_client() - developer_id = uuid4() - task_id = uuid4() - execution_id = uuid4() - - create_execution( - developer_id=developer_id, - task_id=task_id, - execution_id=execution_id, - data=CreateExecutionRequest(input="test"), - client=client, - ) - +def _( + client=cozo_client, + developer_id=test_developer_id, + execution=test_execution, + task=test_task, +): result = list_executions( developer_id=developer_id, - task_id=task_id, - data=ListExecutionsRequest(), + task_id=task.id, client=client, ) assert isinstance(result, list) - assert len(result) == 1 + assert len(result) >= 1 assert result[0].status == "queued" - - -@test("model: create execution transition") -def _(): - client = cozo_client() - developer_id = uuid4() - execution_id = uuid4() - transition_id = uuid4() - - create_execution_transition( - developer_id=developer_id, - execution_id=execution_id, - transition_id=transition_id, - data=CreateExecutionTransitionRequest( - type="step", - from_="test", - to="test", - outputs={"input": "test"}, - ), - client=client, - ) - - -@test("model: get execution transition") -def _(): - client = cozo_client() - developer_id = uuid4() - execution_id = uuid4() - transition_id = uuid4() - - create_execution_transition( - developer_id=developer_id, - execution_id=execution_id, - transition_id=transition_id, - data=CreateExecutionTransitionRequest( - type="step", - from_="test", - to="test", - outputs={"input": "test"}, - ), - client=client, - ) - - result = get_execution_transition( - developer_id=developer_id, - transition_id=transition_id, - data=GetExecutionTransitionRequest(), - client=client, - ) - - assert result is not None - assert isinstance(result, Transition) - assert result.type == "step" - - -@test("model: list execution transitions") -def _(): - client = cozo_client() - developer_id = uuid4() - execution_id = uuid4() - transition_id = uuid4() - - create_execution_transition( - developer_id=developer_id, - execution_id=execution_id, - transition_id=transition_id, - data=CreateExecutionTransitionRequest( - type="step", - from_="test", - to="test", - outputs={"input": "test"}, - ), - client=client, - ) - - result = list_execution_transitions( - developer_id=developer_id, - execution_id=execution_id, - data=ListExecutionTransitionsRequest(), - client=client, - ) - - assert isinstance(result, list) - assert len(result) == 1 - assert result[0].type == "step" diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index bdddfd06c..83ccb9782 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -1,50 +1,34 @@ # Tests for session queries from uuid import uuid4 -from cozo_migrate.api import apply, init -from pycozo import Client from ward import test -from agents_api.autogen.Sessions import ( - CreateSessionRequest, - DeleteSessionRequest, - GetSessionRequest, - ListSessionsRequest, -) +from agents_api.autogen.openapi_model import Session +from agents_api.autogen.Sessions import CreateSessionRequest from agents_api.models.session.create_session import create_session from agents_api.models.session.delete_session import delete_session from agents_api.models.session.get_session import get_session from agents_api.models.session.list_sessions import list_sessions -from agents_api.autogen.openapi_model import Session +from tests.fixtures import ( + cozo_client, + test_agent, + test_developer_id, + test_session, + test_user, +) MODEL = "julep-ai/samantha-1-turbo" -def cozo_client(migrations_dir: str = "./migrations"): - # Create a new client for each test - # and initialize the schema. - client = Client() - - init(client) - apply(client, migrations_dir=migrations_dir, all_=True) - - return client - - @test("model: create session") -def _(): - client = cozo_client() - session_id = uuid4() - agent_id = uuid4() - user_id = uuid4() - developer_id = uuid4() - +def _( + client=cozo_client, developer_id=test_developer_id, agent=test_agent, user=test_user +): create_session( - session_id=session_id, developer_id=developer_id, data=CreateSessionRequest( - users=[user_id], - agents=[agent_id], + users=[user.id], + agents=[agent.id], situation="test session about", ), client=client, @@ -52,17 +36,11 @@ def _(): @test("model: create session no user") -def _(): - client = cozo_client() - session_id = uuid4() - agent_id = uuid4() - developer_id = uuid4() - +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): create_session( - session_id=session_id, developer_id=developer_id, data=CreateSessionRequest( - agents=[agent_id], + agents=[agent.id], situation="test session about", ), client=client, @@ -70,45 +48,26 @@ def _(): @test("model: get session not exists") -def _(): - client = cozo_client() +def _(client=cozo_client, developer_id=test_developer_id): session_id = uuid4() - developer_id = uuid4() try: get_session( session_id=session_id, developer_id=developer_id, - data=GetSessionRequest(), client=client, ) - except Exception as e: - assert str(e) == "Session not found" + except Exception: + pass + else: + assert False, "Session should not exist" @test("model: get session exists") -def _(): - client = cozo_client() - session_id = uuid4() - agent_id = uuid4() - user_id = uuid4() - developer_id = uuid4() - - create_session( - session_id=session_id, - developer_id=developer_id, - data=CreateSessionRequest( - users=[user_id], - agents=[agent_id], - situation="test session about", - ), - client=client, - ) - +def _(client=cozo_client, developer_id=test_developer_id, session=test_session): result = get_session( - session_id=session_id, + session_id=session.id, developer_id=developer_id, - data=GetSessionRequest(), client=client, ) @@ -117,52 +76,41 @@ def _(): @test("model: delete session") -def _(): - client = cozo_client() - session_id = uuid4() - agent_id = uuid4() - user_id = uuid4() - developer_id = uuid4() - - create_session( - session_id=session_id, +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): + session = create_session( developer_id=developer_id, data=CreateSessionRequest( - users=[user_id], - agents=[agent_id], + agent=agent.id, situation="test session about", ), client=client, ) delete_session( - session_id=session_id, + session_id=session.id, developer_id=developer_id, - data=DeleteSessionRequest(), client=client, ) try: get_session( - session_id=session_id, + session_id=session.id, developer_id=developer_id, - data=GetSessionRequest(), client=client, ) - except Exception as e: - assert str(e) == "Session not found" + except Exception: + pass + else: + assert False, "Session should not exist" -@test("model: list sessions") -def _(): - client = cozo_client() - developer_id = uuid4() +@test("model: list sessions") +def _(client=cozo_client, developer_id=test_developer_id, session=test_session): result = list_sessions( developer_id=developer_id, - data=ListSessionsRequest(), client=client, ) assert isinstance(result, list) - assert len(result) == 0 + assert len(result) > 0 diff --git a/agents-api/tests/test_task_queries.py b/agents-api/tests/test_task_queries.py index 291cc4a02..dc9110d3a 100644 --- a/agents-api/tests/test_task_queries.py +++ b/agents-api/tests/test_task_queries.py @@ -1,53 +1,44 @@ # Tests for task queries from uuid import uuid4 -from cozo_migrate.api import apply, init -from pycozo import Client from ward import test -from agents_api.autogen.openapi_model import Task +from agents_api.autogen.openapi_model import ( + CreateTaskRequest, + ResourceUpdatedResponse, + Task, + UpdateTaskRequest, +) from agents_api.models.task.create_task import create_task from agents_api.models.task.delete_task import delete_task from agents_api.models.task.get_task import get_task from agents_api.models.task.list_tasks import list_tasks from agents_api.models.task.update_task import update_task - - -def cozo_client(migrations_dir: str = "./migrations"): - # Create a new client for each test - # and initialize the schema. - client = Client() - - init(client) - apply(client, migrations_dir=migrations_dir, all_=True) - - return client +from tests.fixtures import cozo_client, test_agent, test_developer_id, test_task @test("model: create task") -def _(): - client = cozo_client() - developer_id = uuid4() - agent_id = uuid4() +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): task_id = uuid4() create_task( developer_id=developer_id, - agent_id=agent_id, + agent_id=agent.id, task_id=task_id, - data={ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [], + } + ), client=client, ) @test("model: get task not exists") -def _(): - client = cozo_client() - developer_id = uuid4() +def _(client=cozo_client, developer_id=test_developer_id): task_id = uuid4() try: @@ -56,32 +47,17 @@ def _(): task_id=task_id, client=client, ) - except Exception as e: - assert str(e) == "Task not found" + except Exception: + pass + else: + assert False, "Task should not exist" @test("model: get task exists") -def _(): - client = cozo_client() - developer_id = uuid4() - agent_id = uuid4() - task_id = uuid4() - - create_task( - developer_id=developer_id, - agent_id=agent_id, - task_id=task_id, - data={ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, - client=client, - ) - +def _(client=cozo_client, developer_id=test_developer_id, task=test_task): result = get_task( developer_id=developer_id, - task_id=task_id, + task_id=task.id, client=client, ) @@ -90,86 +66,74 @@ def _(): @test("model: delete task") -def _(): - client = cozo_client() - developer_id = uuid4() - agent_id = uuid4() - task_id = uuid4() - - create_task( +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): + task = create_task( developer_id=developer_id, - agent_id=agent_id, - task_id=task_id, - data={ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [], + } + ), client=client, ) delete_task( developer_id=developer_id, - task_id=task_id, + agent_id=agent.id, + task_id=task.id, client=client, ) try: get_task( developer_id=developer_id, - task_id=task_id, + task_id=task.id, client=client, ) - except Exception as e: - assert str(e) == "Task not found" + except Exception: + pass + else: + assert False, "Task should not exist" -@test("model: update task") -def _(): - client = cozo_client() - developer_id = uuid4() - agent_id = uuid4() - task_id = uuid4() - - create_task( - developer_id=developer_id, - agent_id=agent_id, - task_id=task_id, - data={ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, - client=client, - ) +@test("model: update task") +def _( + client=cozo_client, developer_id=test_developer_id, agent=test_agent, task=test_task +): result = update_task( developer_id=developer_id, - task_id=task_id, - data={ - "name": "updated task", - "description": "updated task about", - "input_schema": {"type": "object", "additionalProperties": True}, - }, + task_id=task.id, + agent_id=agent.id, + data=UpdateTaskRequest( + **{ + "name": "updated task", + "description": "updated task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [], + } + ), client=client, ) assert result is not None - assert isinstance(result, Task) - assert result.name == "updated task" + assert isinstance(result, ResourceUpdatedResponse) @test("model: list tasks") -def _(): - client = cozo_client() - developer_id = uuid4() - agent_id = uuid4() - +def _( + client=cozo_client, developer_id=test_developer_id, task=test_task, agent=test_agent +): result = list_tasks( developer_id=developer_id, - agent_id=agent_id, + agent_id=agent.id, client=client, ) assert isinstance(result, list) + assert len(result) > 0 assert all(isinstance(task, Task) for task in result) diff --git a/agents-api/tests/test_tool_queries.py b/agents-api/tests/test_tool_queries.py index 0d331e45c..2720176f0 100644 --- a/agents-api/tests/test_tool_queries.py +++ b/agents-api/tests/test_tool_queries.py @@ -1,145 +1,94 @@ # Tests for tool queries -from uuid import uuid4 -from cozo_migrate.api import apply, init -from pycozo import Client from ward import test -from agents_api.autogen.Tools import ( - CreateToolsRequest, - DeleteToolRequest, - GetToolRequest, - ListToolsRequest, -) +from agents_api.autogen.openapi_model import CreateToolRequest, Tool from agents_api.models.tools.create_tools import create_tools from agents_api.models.tools.delete_tool import delete_tool from agents_api.models.tools.get_tool import get_tool from agents_api.models.tools.list_tools import list_tools - - -def cozo_client(migrations_dir: str = "./migrations"): - # Create a new client for each test - # and initialize the schema. - client = Client() - - init(client) - apply(client, migrations_dir=migrations_dir, all_=True) - - return client +from tests.fixtures import cozo_client, test_agent, test_developer_id, test_tool @test("model: create tool") -def _(): - client = cozo_client() - developer_id = uuid4() - agent_id = uuid4() - - tool = { +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): + function = { "name": "hello_world", "description": "A function that prints hello world", "parameters": {"type": "object", "properties": {}}, } + tool = { + "function": function, + "name": "hello_world", + "type": "function", + } + result = create_tools( developer_id=developer_id, - agent_id=agent_id, - data=CreateToolsRequest(tools=[tool]), + agent_id=agent.id, + data=[CreateToolRequest(**tool)], client=client, ) assert result is not None - assert isinstance(result[0], dict) + assert isinstance(result[0], Tool) @test("model: delete tool") -def _(): - client = cozo_client() - developer_id = uuid4() - agent_id = uuid4() - tool_id = uuid4() - - tool = { - "name": "hello_world", +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): + function = { + "name": "temp_temp", "description": "A function that prints hello world", "parameters": {"type": "object", "properties": {}}, } - create_tools( + tool = { + "function": function, + "name": "temp_temp", + "type": "function", + } + + [tool, *_] = create_tools( developer_id=developer_id, - agent_id=agent_id, - data=CreateToolsRequest(tools=[tool]), + agent_id=agent.id, + data=[CreateToolRequest(**tool)], client=client, ) result = delete_tool( developer_id=developer_id, - agent_id=agent_id, - tool_id=tool_id, - data=DeleteToolRequest(), + agent_id=agent.id, + tool_id=tool.id, client=client, ) assert result is not None - assert result["id"] == str(tool_id) @test("model: get tool") -def _(): - client = cozo_client() - developer_id = uuid4() - agent_id = uuid4() - tool_id = uuid4() - - tool = { - "name": "hello_world", - "description": "A function that prints hello world", - "parameters": {"type": "object", "properties": {}}, - } - - create_tools( - developer_id=developer_id, - agent_id=agent_id, - data=CreateToolsRequest(tools=[tool]), - client=client, - ) - +def _( + client=cozo_client, developer_id=test_developer_id, tool=test_tool, agent=test_agent +): result = get_tool( developer_id=developer_id, - agent_id=agent_id, - tool_id=tool_id, - data=GetToolRequest(), + agent_id=agent.id, + tool_id=tool.id, client=client, ) assert result is not None - assert isinstance(result, dict) @test("model: list tools") -def _(): - client = cozo_client() - developer_id = uuid4() - agent_id = uuid4() - - tool = { - "name": "hello_world", - "description": "A function that prints hello world", - "parameters": {"type": "object", "properties": {}}, - } - - create_tools( - developer_id=developer_id, - agent_id=agent_id, - data=CreateToolsRequest(tools=[tool]), - client=client, - ) - +def _( + client=cozo_client, developer_id=test_developer_id, agent=test_agent, tool=test_tool +): result = list_tools( developer_id=developer_id, - agent_id=agent_id, - data=ListToolsRequest(), + agent_id=agent.id, client=client, ) assert result is not None - assert all(isinstance(tool, dict) for tool in result) + assert all(isinstance(tool, Tool) for tool in result) diff --git a/agents-api/tests/test_user_queries.py b/agents-api/tests/test_user_queries.py index 898ebf7c0..055185b95 100644 --- a/agents-api/tests/test_user_queries.py +++ b/agents-api/tests/test_user_queries.py @@ -3,14 +3,11 @@ # Tests for user queries from uuid import uuid4 -from cozo_migrate.api import apply, init -from pycozo import Client -from ward import raises, test +from ward import test -from agents_api.autogen.Users import ( +from agents_api.autogen.openapi_model import ( CreateUserRequest, - GetUserRequest, - ListUsersRequest, + ResourceUpdatedResponse, UpdateUserRequest, User, ) @@ -18,29 +15,14 @@ from agents_api.models.user.get_user import get_user from agents_api.models.user.list_users import list_users from agents_api.models.user.update_user import update_user - - -def cozo_client(migrations_dir: str = "./migrations"): - """Initializes a new Cozo client for testing, applying all migrations to ensure the database schema is up to date.""" - # Create a new client for each test - # and initialize the schema. - client = Client() - - init(client) - apply(client, migrations_dir=migrations_dir, all_=True) - - return client +from tests.fixtures import cozo_client, test_developer_id, test_user @test("model: create user") -def _(): +def _(client=cozo_client, developer_id=test_developer_id): """Test that a user can be successfully created.""" - client = cozo_client() - user_id = uuid4() - developer_id = uuid4() create_user( - user_id=user_id, developer_id=developer_id, data=CreateUserRequest( name="test user", @@ -50,77 +32,13 @@ def _(): ) -@test("model: create user twice should fail") -def _(): - """Test that attempting to create the same user twice results in a failure.""" - client = cozo_client() - user_id = uuid4() - developer_id = uuid4() - - # Expect an exception to be raised as creating the same user twice should not be allowed. - # Should fail because the user already exists. - with raises(Exception): - create_user( - user_id=user_id, - developer_id=developer_id, - data=CreateUserRequest( - name="test user", - about="test user about", - ), - client=client, - ) - - create_user( - user_id=user_id, - developer_id=developer_id, - data=CreateUserRequest( - name="test user", - about="test user about", - ), - client=client, - ) - - -@test("model: update non-existent user should fail") -def _(): - """Test that attempting to update a non-existent user results in a failure.""" - client = cozo_client() - user_id = uuid4() - developer_id = uuid4() - - # Should fail because the user doesn't exist. - with raises(Exception): - update_user( - user_id=user_id, - developer_id=developer_id, - data=UpdateUserRequest( - name="test user", - about="test user about", - ), - client=client, - ) - - @test("model: update user") -def _(): +def _(client=cozo_client, developer_id=test_developer_id, user=test_user): """Test that an existing user's information can be successfully updated.""" - client = cozo_client() - user_id = uuid4() - developer_id = uuid4() - - create_user( - user_id=user_id, - developer_id=developer_id, - data=CreateUserRequest( - name="test user", - about="test user about", - ), - client=client, - ) # Verify that the 'updated_at' timestamp is greater than the 'created_at' timestamp, indicating a successful update. update_result = update_user( - user_id=user_id, + user_id=user.id, developer_id=developer_id, data=UpdateUserRequest( name="updated user", @@ -130,50 +48,38 @@ def _(): ) assert update_result is not None - assert isinstance(update_result, User) - assert update_result.updated_at > update_result.created_at + assert isinstance(update_result, ResourceUpdatedResponse) + assert update_result.updated_at > user.created_at @test("model: get user not exists") -def _(): +def _(client=cozo_client, developer_id=test_developer_id): """Test that retrieving a non-existent user returns an empty result.""" - client = cozo_client() + user_id = uuid4() - developer_id = uuid4() # Ensure that the query for an existing user returns exactly one result. try: get_user( user_id=user_id, developer_id=developer_id, - data=GetUserRequest(), client=client, ) - except Exception as e: - assert str(e) == "User not found" + except Exception: + pass + else: + assert ( + False + ), "Expected an exception to be raised when retrieving a non-existent user." @test("model: get user exists") -def _(): +def _(client=cozo_client, developer_id=test_developer_id, user=test_user): """Test that retrieving an existing user returns the correct user information.""" - client = cozo_client() - user_id = uuid4() - developer_id = uuid4() - - create_user( - user_id=user_id, - developer_id=developer_id, - data=CreateUserRequest( - name="test user", - about="test user about", - ), - client=client, - ) result = get_user( - user_id=user_id, + user_id=user.id, developer_id=developer_id, - data=GetUserRequest(), client=client, ) @@ -182,16 +88,14 @@ def _(): @test("model: list users") -def _(): +def _(client=cozo_client, developer_id=test_developer_id, user=test_user): """Test that listing users returns a collection of user information.""" - client = cozo_client() - developer_id = uuid4() result = list_users( developer_id=developer_id, - data=ListUsersRequest(), client=client, ) assert isinstance(result, list) + assert len(result) >= 1 assert all(isinstance(user, User) for user in result) From 440b5f79e42cf7f8ab4ecc564330545c8f578763 Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Fri, 9 Aug 2024 16:35:20 -0400 Subject: [PATCH 018/110] Add tests for various queries in `agents-api` * **`test_agent_queries.py`** - Add test for `create_or_update_agent` query - Add test for `patch_agent` query * **`test_docs_queries.py`** - Add test for `embed_snippets` query * **`test_entry_queries.py`** - Add test for `delete_entries` query * **`test_execution_queries.py`** - Add test for `create_execution_transition` query * **`test_session_queries.py`** - Add test for `create_or_update_session` query * **`test_task_queries.py`** - Add test for `create_or_update_task` query * **`test_tool_queries.py`** - Add test for `patch_tool` query * **`test_user_queries.py`** - Add test for `create_or_update_user` query --- agents-api/tests/test_agent_queries.py | 35 ++++++++++++++++ agents-api/tests/test_docs_queries.py | 17 ++++++++ agents-api/tests/test_entry_queries.py | 48 ++++++++++++++++++++++ agents-api/tests/test_execution_queries.py | 20 +++++++++ agents-api/tests/test_session_queries.py | 29 ++++++++++++- agents-api/tests/test_task_queries.py | 21 ++++++++++ agents-api/tests/test_tool_queries.py | 21 ++++++++++ agents-api/tests/test_user_queries.py | 16 ++++++++ 8 files changed, 206 insertions(+), 1 deletion(-) diff --git a/agents-api/tests/test_agent_queries.py b/agents-api/tests/test_agent_queries.py index 143788e63..6004dc029 100644 --- a/agents-api/tests/test_agent_queries.py +++ b/agents-api/tests/test_agent_queries.py @@ -6,13 +6,16 @@ from agents_api.autogen.openapi_model import ( Agent, CreateAgentRequest, + CreateOrUpdateAgentRequest, ResourceUpdatedResponse, UpdateAgentRequest, ) from agents_api.models.agent.create_agent import create_agent +from agents_api.models.agent.create_or_update_agent import create_or_update_agent from agents_api.models.agent.delete_agent import delete_agent from agents_api.models.agent.get_agent import get_agent from agents_api.models.agent.list_agents import list_agents +from agents_api.models.agent.patch_agent import patch_agent from agents_api.models.agent.update_agent import update_agent from tests.fixtures import cozo_client, test_agent, test_developer_id @@ -44,6 +47,21 @@ def _(client=cozo_client, developer_id=test_developer_id): ) +@test("model: create or update agent") +def _(client=cozo_client, developer_id=test_developer_id): + create_or_update_agent( + developer_id=developer_id, + agent_id=uuid4(), + data=CreateOrUpdateAgentRequest( + name="test agent", + about="test agent about", + model="gpt-4o", + instructions=["test instruction"], + ), + client=client, + ) + + @test("model: get agent not exists") def _(client=cozo_client, developer_id=test_developer_id): agent_id = uuid4() @@ -107,6 +125,23 @@ def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): assert isinstance(result, ResourceUpdatedResponse) +@test("model: patch agent") +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): + result = patch_agent( + agent_id=agent.id, + developer_id=developer_id, + data=UpdateAgentRequest( + name="patched agent", + about="patched agent about", + default_settings={"temperature": 1.0}, + ), + client=client, + ) + + assert result is not None + assert isinstance(result, ResourceUpdatedResponse) + + @test("model: list agents") def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): """Tests listing all agents associated with a developer in the database. Verifies that the correct list of agents is retrieved.""" diff --git a/agents-api/tests/test_docs_queries.py b/agents-api/tests/test_docs_queries.py index 1617e2586..5b9c28841 100644 --- a/agents-api/tests/test_docs_queries.py +++ b/agents-api/tests/test_docs_queries.py @@ -114,3 +114,20 @@ def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): ) assert len(result) >= 1 + + +@test("model: embed snippets") +def _(client=cozo_client, developer_id=test_developer_id, doc=test_doc): + snippet_indices = [0] + embeddings = [[1.0] * EMBEDDING_SIZE] + + result = embed_snippets( + developer_id=developer_id, + doc_id=doc.id, + snippet_indices=snippet_indices, + embeddings=embeddings, + client=client, + ) + + assert result is not None + assert result.id == doc.id diff --git a/agents-api/tests/test_entry_queries.py b/agents-api/tests/test_entry_queries.py index 954defd4d..bfa1753e8 100644 --- a/agents-api/tests/test_entry_queries.py +++ b/agents-api/tests/test_entry_queries.py @@ -10,6 +10,7 @@ from agents_api.autogen.openapi_model import CreateEntryRequest from agents_api.models.entry.create_entries import create_entries from agents_api.models.entry.list_entries import list_entries +from agents_api.models.entry.delete_entries import delete_entries from tests.fixtures import cozo_client, test_developer_id, test_session MODEL = "julep-ai/samantha-1-turbo" @@ -73,3 +74,50 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): # Asserts that only one entry is retrieved, matching the session_id. assert len(result) == 1 + + +@test("model: delete entries") +def _(client=cozo_client, developer_id=test_developer_id, session=test_session): + """ + Tests the deletion of entries from the database. + Verifies that entries can be successfully deleted using the delete_entries function. + """ + + test_entry = CreateEntryRequest( + session_id=session.id, + role="user", + source="api_request", + content="test entry content", + ) + + internal_entry = CreateEntryRequest( + session_id=session.id, + role="user", + content="test entry content", + source="internal", + ) + + created_entries = create_entries( + developer_id=developer_id, + session_id=session.id, + data=[test_entry, internal_entry], + client=client, + ) + + entry_ids = [entry.id for entry in created_entries] + + delete_entries( + developer_id=developer_id, + session_id=session.id, + entry_ids=entry_ids, + client=client, + ) + + result = list_entries( + developer_id=developer_id, + session_id=session.id, + client=client, + ) + + # Asserts that no entries are retrieved after deletion. + assert len(result) == 0 diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index 38099f54c..0c550f459 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -10,6 +10,7 @@ from agents_api.models.execution.create_execution import create_execution from agents_api.models.execution.get_execution import get_execution from agents_api.models.execution.list_executions import list_executions +from agents_api.models.execution.create_execution_transition import create_execution_transition from tests.fixtures import cozo_client, test_developer_id, test_execution, test_task MODEL = "julep-ai/samantha-1-turbo" @@ -59,3 +60,22 @@ def _( assert isinstance(result, list) assert len(result) >= 1 assert result[0].status == "queued" + + +@test("model: create execution transition") +def _(client=cozo_client, developer_id=test_developer_id, execution=test_execution): + result = create_execution_transition( + developer_id=developer_id, + execution_id=execution.id, + data={ + "type": "step", + "output": {"result": "test"}, + "current": [], + "next": [], + }, + client=client, + ) + + assert result is not None + assert result.type == "step" + assert result.output == {"result": "test"} diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index 83ccb9782..e95bd7299 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -3,9 +3,10 @@ from ward import test -from agents_api.autogen.openapi_model import Session +from agents_api.autogen.openapi_model import Session, CreateOrUpdateSessionRequest from agents_api.autogen.Sessions import CreateSessionRequest from agents_api.models.session.create_session import create_session +from agents_api.models.session.create_or_update_session import create_or_update_session from agents_api.models.session.delete_session import delete_session from agents_api.models.session.get_session import get_session from agents_api.models.session.list_sessions import list_sessions @@ -114,3 +115,29 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): assert isinstance(result, list) assert len(result) > 0 + + +@test("model: create or update session") +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent, user=test_user): + session_id = uuid4() + + create_or_update_session( + session_id=session_id, + developer_id=developer_id, + data=CreateOrUpdateSessionRequest( + users=[user.id], + agents=[agent.id], + situation="test session about", + ), + client=client, + ) + + result = get_session( + session_id=session_id, + developer_id=developer_id, + client=client, + ) + + assert result is not None + assert isinstance(result, Session) + assert result.id == session_id diff --git a/agents-api/tests/test_task_queries.py b/agents-api/tests/test_task_queries.py index dc9110d3a..6e083c22b 100644 --- a/agents-api/tests/test_task_queries.py +++ b/agents-api/tests/test_task_queries.py @@ -10,6 +10,7 @@ UpdateTaskRequest, ) from agents_api.models.task.create_task import create_task +from agents_api.models.task.create_or_update_task import create_or_update_task from agents_api.models.task.delete_task import delete_task from agents_api.models.task.get_task import get_task from agents_api.models.task.list_tasks import list_tasks @@ -37,6 +38,26 @@ def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): ) +@test("model: create or update task") +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): + task_id = uuid4() + + create_or_update_task( + developer_id=developer_id, + agent_id=agent.id, + task_id=task_id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [], + } + ), + client=client, + ) + + @test("model: get task not exists") def _(client=cozo_client, developer_id=test_developer_id): task_id = uuid4() diff --git a/agents-api/tests/test_tool_queries.py b/agents-api/tests/test_tool_queries.py index 2720176f0..fd5222cda 100644 --- a/agents-api/tests/test_tool_queries.py +++ b/agents-api/tests/test_tool_queries.py @@ -7,6 +7,7 @@ from agents_api.models.tools.delete_tool import delete_tool from agents_api.models.tools.get_tool import get_tool from agents_api.models.tools.list_tools import list_tools +from agents_api.models.tools.patch_tool import patch_tool from tests.fixtures import cozo_client, test_agent, test_developer_id, test_tool @@ -92,3 +93,23 @@ def _( assert result is not None assert all(isinstance(tool, Tool) for tool in result) + + +@test("model: patch tool") +def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent, tool=test_tool): + patch_data = { + "name": "patched_tool", + "description": "A patched function that prints hello world", + } + + result = patch_tool( + developer_id=developer_id, + agent_id=agent.id, + tool_id=tool.id, + patch_tool=patch_data, + client=client, + ) + + assert result is not None + assert result.name == "patched_tool" + assert result.description == "A patched function that prints hello world" diff --git a/agents-api/tests/test_user_queries.py b/agents-api/tests/test_user_queries.py index 055185b95..02272a8a6 100644 --- a/agents-api/tests/test_user_queries.py +++ b/agents-api/tests/test_user_queries.py @@ -12,6 +12,7 @@ User, ) from agents_api.models.user.create_user import create_user +from agents_api.models.user.create_or_update_user import create_or_update_user from agents_api.models.user.get_user import get_user from agents_api.models.user.list_users import list_users from agents_api.models.user.update_user import update_user @@ -32,6 +33,21 @@ def _(client=cozo_client, developer_id=test_developer_id): ) +@test("model: create or update user") +def _(client=cozo_client, developer_id=test_developer_id): + """Test that a user can be successfully created or updated.""" + + create_or_update_user( + developer_id=developer_id, + user_id=uuid4(), + data=CreateUserRequest( + name="test user", + about="test user about", + ), + client=client, + ) + + @test("model: update user") def _(client=cozo_client, developer_id=test_developer_id, user=test_user): """Test that an existing user's information can be successfully updated.""" From d305468c2b9bd46f13dd603cad9fa6160980568c Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 9 Aug 2024 17:39:02 -0400 Subject: [PATCH 019/110] fix(agents-api): Fixed tests Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Tools.py | 32 +- .../models/agent/create_or_update_agent.py | 4 +- .../execution/create_execution_transition.py | 4 +- .../agents_api/models/tools/patch_tool.py | 33 +- .../agents_api/models/tools/update_tool.py | 93 ++- agents-api/agents_api/models/utils.py | 4 +- agents-api/poetry.lock | 276 +++++---- agents-api/tests/fixtures.py | 2 +- agents-api/tests/test_agent_queries.py | 38 +- agents-api/tests/test_entry_queries.py | 6 +- agents-api/tests/test_execution_queries.py | 20 +- agents-api/tests/test_session_queries.py | 8 +- agents-api/tests/test_task_queries.py | 2 +- agents-api/tests/test_tool_queries.py | 72 ++- agents-api/tests/test_user_queries.py | 5 +- fern/fern.config.json | 2 +- sdks/python/poetry.lock | 579 ++++++++++-------- sdks/ts/src/api/index.ts | 2 - sdks/ts/src/api/models/Tools_FunctionDef.ts | 5 +- .../src/api/models/Tools_FunctionDefUpdate.ts | 23 - .../src/api/models/Tools_PatchToolRequest.ts | 4 +- sdks/ts/src/api/schemas/$Tools_FunctionDef.ts | 9 +- .../api/schemas/$Tools_FunctionDefUpdate.ts | 33 - .../api/schemas/$Tools_PatchToolRequest.ts | 2 +- typespec/tools/models.tsp | 4 +- 25 files changed, 711 insertions(+), 551 deletions(-) delete mode 100644 sdks/ts/src/api/models/Tools_FunctionDefUpdate.ts delete mode 100644 sdks/ts/src/api/schemas/$Tools_FunctionDefUpdate.ts diff --git a/agents-api/agents_api/autogen/Tools.py b/agents-api/agents_api/autogen/Tools.py index 16fbaeb7b..1f25759d5 100644 --- a/agents-api/agents_api/autogen/Tools.py +++ b/agents-api/agents_api/autogen/Tools.py @@ -46,35 +46,7 @@ class FunctionDef(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - name: Annotated[str, Field("overriden", pattern="^[^\\W0-9]\\w*$")] - """ - DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. - """ - description: Annotated[ - str | None, - Field( - None, - pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", - ), - ] - """ - Description of the function - """ - parameters: dict[str, Any] - """ - The parameters the function accepts - """ - - -class FunctionDefUpdate(BaseModel): - """ - Function definition - """ - - model_config = ConfigDict( - populate_by_name=True, - ) - name: Annotated[str, Field("overriden", pattern="^[^\\W0-9]\\w*$")] + name: Any | None = None """ DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. """ @@ -124,7 +96,7 @@ class PatchToolRequest(BaseModel): """ Name of the tool (must be unique for this agent and a valid python identifier string ) """ - function: FunctionDefUpdate | None = None + function: FunctionDef | None = None integration: Any | None = None system: Any | None = None api_call: Any | None = None diff --git a/agents-api/agents_api/models/agent/create_or_update_agent.py b/agents-api/agents_api/models/agent/create_or_update_agent.py index 2c05529f2..8cba24d2b 100644 --- a/agents-api/agents_api/models/agent/create_or_update_agent.py +++ b/agents-api/agents_api/models/agent/create_or_update_agent.py @@ -38,7 +38,7 @@ def create_or_update_agent( developer_id: UUID, agent_id: UUID, data: CreateOrUpdateAgentRequest, -) -> tuple[list[str], dict]: +) -> tuple[list[str | None], dict]: """ Constructs and executes a datalog query to create a new agent in the database. @@ -123,7 +123,7 @@ def create_or_update_agent( queries = [ verify_developer_id_query(developer_id), - default_settings and default_settings_query, + default_settings_query if default_settings else None, agent_query, ] diff --git a/agents-api/agents_api/models/execution/create_execution_transition.py b/agents-api/agents_api/models/execution/create_execution_transition.py index 606ca9406..06874016d 100644 --- a/agents-api/agents_api/models/execution/create_execution_transition.py +++ b/agents-api/agents_api/models/execution/create_execution_transition.py @@ -37,7 +37,9 @@ TypeError: partialclass(HTTPException, status_code=400), } ) -@wrap_in_class(Transition, transform=lambda d: {"id": d["transition_id"], **d}) +@wrap_in_class( + Transition, transform=lambda d: {"id": d["transition_id"], **d}, one=True +) @cozo_query @beartype def create_execution_transition( diff --git a/agents-api/agents_api/models/tools/patch_tool.py b/agents-api/agents_api/models/tools/patch_tool.py index d6c01c1df..acdbcf0b4 100644 --- a/agents-api/agents_api/models/tools/patch_tool.py +++ b/agents-api/agents_api/models/tools/patch_tool.py @@ -32,7 +32,7 @@ @cozo_query @beartype def patch_tool( - *, developer_id: UUID, agent_id: UUID, tool_id: UUID, patch_tool: PatchToolRequest + *, developer_id: UUID, agent_id: UUID, tool_id: UUID, data: PatchToolRequest ) -> tuple[list[str], dict]: """ # Execute the datalog query and return the results as a DataFrame @@ -41,14 +41,17 @@ def patch_tool( Parameters: - agent_id (UUID): The unique identifier of the agent. - tool_id (UUID): The unique identifier of the tool to be updated. - - patch_tool (PatchToolRequest): The request payload containing the updated tool information. + - data (PatchToolRequest): The request payload containing the updated tool information. Returns: - ResourceUpdatedResponse: The updated tool data. """ + agent_id = str(agent_id) + tool_id = str(tool_id) + # Extract the tool data from the payload - patch_data = patch_tool.model_dump(exclude_none=True) + patch_data = data.model_dump(exclude_none=True) # Assert that only one of the tool type fields is present tool_specs = [ @@ -64,16 +67,15 @@ def patch_tool( patch_data["type"] = patch_data.get("type", tool_type) assert patch_data["type"] == tool_type, "Invalid tool update" - if tool_spec is not None: - # Rename the tool definition to 'spec' - patch_data["spec"] = tool_spec + tool_spec = tool_spec or {} + if tool_spec: del patch_data[tool_type] tool_cols, tool_vals = cozo_process_mutate_data( { **patch_data, - "agent_id": str(agent_id), - "tool_id": str(tool_id), + "agent_id": agent_id, + "tool_id": tool_id, } ) @@ -81,11 +83,17 @@ def patch_tool( patch_query = f""" input[{tool_cols}] <- $input - ?[{tool_cols}, updated_at] := + ?[{tool_cols}, spec, updated_at] := + *tools {{ + agent_id: to_uuid($agent_id), + tool_id: to_uuid($tool_id), + spec: old_spec, + }}, input[{tool_cols}], + spec = concat(old_spec, $spec), updated_at = now() - :update tools {{ {tool_cols}, updated_at }} + :update tools {{ {tool_cols}, spec, updated_at }} :returning """ @@ -95,4 +103,7 @@ def patch_tool( patch_query, ] - return (queries, dict(input=tool_vals)) + return ( + queries, + dict(input=tool_vals, spec=tool_spec, agent_id=agent_id, tool_id=tool_id), + ) diff --git a/agents-api/agents_api/models/tools/update_tool.py b/agents-api/agents_api/models/tools/update_tool.py index aa1238643..1376aba37 100644 --- a/agents-api/agents_api/models/tools/update_tool.py +++ b/agents-api/agents_api/models/tools/update_tool.py @@ -1,15 +1,38 @@ from uuid import UUID from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError from ...autogen.openapi_model import ( - PatchToolRequest, ResourceUpdatedResponse, UpdateToolRequest, ) -from .patch_tool import patch_tool +from ...common.utils.cozo import cozo_process_mutate_data +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, + wrap_in_class, +) +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@wrap_in_class( + ResourceUpdatedResponse, + one=True, + transform=lambda d: {"id": d["tool_id"], "jobs": [], **d}, +) +@cozo_query @beartype def update_tool( *, @@ -18,12 +41,62 @@ def update_tool( tool_id: UUID, data: UpdateToolRequest, **kwargs, -) -> ResourceUpdatedResponse: - # Same as patch_tool_query, but with a different request payload - return patch_tool( - developer_id=developer_id, - agent_id=agent_id, - tool_id=tool_id, - patch_tool=PatchToolRequest(**data.model_dump()), - **kwargs, +) -> tuple[list[str], dict]: + agent_id = str(agent_id) + tool_id = str(tool_id) + + # Extract the tool data from the payload + update_data = data.model_dump(exclude_none=True) + + # Assert that only one of the tool type fields is present + tool_specs = [ + (tool_type, update_data.get(tool_type)) + for tool_type in ["function", "integration", "system", "api_call"] + if update_data.get(tool_type) is not None + ] + + assert len(tool_specs) <= 1, "Invalid tool update" + tool_type, tool_spec = tool_specs[0] if tool_specs else (None, None) + + if tool_type is not None: + update_data["type"] = update_data.get("type", tool_type) + assert update_data["type"] == tool_type, "Invalid tool update" + + update_data["spec"] = tool_spec + del update_data[tool_type] + + tool_cols, tool_vals = cozo_process_mutate_data( + { + **update_data, + "agent_id": agent_id, + "tool_id": tool_id, + } + ) + + # Construct the datalog query for updating the tool information + patch_query = f""" + input[{tool_cols}] <- $input + + ?[{tool_cols}, created_at, updated_at] := + *tools {{ + agent_id: to_uuid($agent_id), + tool_id: to_uuid($tool_id), + created_at + }}, + input[{tool_cols}], + updated_at = now() + + :put tools {{ {tool_cols}, created_at, updated_at }} + :returning + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query(developer_id, "agents", agent_id=agent_id), + patch_query, + ] + + return ( + queries, + dict(input=tool_vals, spec=tool_spec, agent_id=agent_id, tool_id=tool_id), ) diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index 56cbefd55..ed7b78e88 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -114,7 +114,7 @@ def cozo_query( func: Callable[P, tuple[str | list[str], dict]] | None = None, debug: bool | None = None, ): - def cozo_query_dec(func: Callable[P, tuple[str | list[str], dict]]): + def cozo_query_dec(func: Callable[P, tuple[str | list[Any], dict]]): """ Decorator that wraps a function that takes arbitrary arguments, and returns a (query string, variables) tuple. @@ -135,7 +135,7 @@ def wrapper( if isinstance(queries, str): query = queries else: - queries = [query for query in queries if query] + queries = [str(query) for query in queries if query] query = "}\n\n{\n".join(queries) query = f"{{ {query} }}" diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index ecfed6feb..125739747 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -13,87 +13,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.1" +version = "3.10.2" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:47b4c2412960e64d97258f40616efddaebcb34ff664c8a972119ed38fac2a62c"}, - {file = "aiohttp-3.10.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e7dbf637f87dd315fa1f36aaed8afa929ee2c607454fb7791e74c88a0d94da59"}, - {file = "aiohttp-3.10.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c8fb76214b5b739ce59e2236a6489d9dc3483649cfd6f563dbf5d8e40dbdd57d"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c577cdcf8f92862363b3d598d971c6a84ed8f0bf824d4cc1ce70c2fb02acb4a"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:777e23609899cb230ad2642b4bdf1008890f84968be78de29099a8a86f10b261"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b07286a1090483799599a2f72f76ac396993da31f6e08efedb59f40876c144fa"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9db600a86414a9a653e3c1c7f6a2f6a1894ab8f83d11505247bd1b90ad57157"}, - {file = "aiohttp-3.10.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01c3f1eb280008e51965a8d160a108c333136f4a39d46f516c64d2aa2e6a53f2"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f5dd109a925fee4c9ac3f6a094900461a2712df41745f5d04782ebcbe6479ccb"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:8c81ff4afffef9b1186639506d70ea90888218f5ddfff03870e74ec80bb59970"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:2a384dfbe8bfebd203b778a30a712886d147c61943675f4719b56725a8bbe803"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:b9fb6508893dc31cfcbb8191ef35abd79751db1d6871b3e2caee83959b4d91eb"}, - {file = "aiohttp-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:88596384c3bec644a96ae46287bb646d6a23fa6014afe3799156aef42669c6bd"}, - {file = "aiohttp-3.10.1-cp310-cp310-win32.whl", hash = "sha256:68164d43c580c2e8bf8e0eb4960142919d304052ccab92be10250a3a33b53268"}, - {file = "aiohttp-3.10.1-cp310-cp310-win_amd64.whl", hash = "sha256:d6bbe2c90c10382ca96df33b56e2060404a4f0f88673e1e84b44c8952517e5f3"}, - {file = "aiohttp-3.10.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f6979b4f20d3e557a867da9d9227de4c156fcdcb348a5848e3e6190fd7feb972"}, - {file = "aiohttp-3.10.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:03c0c380c83f8a8d4416224aafb88d378376d6f4cadebb56b060688251055cd4"}, - {file = "aiohttp-3.10.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c2b104e81b3c3deba7e6f5bc1a9a0e9161c380530479970766a6655b8b77c7c"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b023b68c61ab0cd48bd38416b421464a62c381e32b9dc7b4bdfa2905807452a4"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a07c76a82390506ca0eabf57c0540cf5a60c993c442928fe4928472c4c6e5e6"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:41d8dab8c64ded1edf117d2a64f353efa096c52b853ef461aebd49abae979f16"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:615348fab1a9ef7d0960a905e83ad39051ae9cb0d2837da739b5d3a7671e497a"}, - {file = "aiohttp-3.10.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:256ee6044214ee9d66d531bb374f065ee94e60667d6bbeaa25ca111fc3997158"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b7d5bb926805022508b7ddeaad957f1fce7a8d77532068d7bdb431056dc630cd"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:028faf71b338f069077af6315ad54281612705d68889f5d914318cbc2aab0d50"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:5c12310d153b27aa630750be44e79313acc4e864c421eb7d2bc6fa3429c41bf8"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:de1a91d5faded9054957ed0a9e01b9d632109341942fc123947ced358c5d9009"}, - {file = "aiohttp-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9c186b270979fb1dee3ababe2d12fb243ed7da08b30abc83ebac3a928a4ddb15"}, - {file = "aiohttp-3.10.1-cp311-cp311-win32.whl", hash = "sha256:4a9ce70f5e00380377aac0e568abd075266ff992be2e271765f7b35d228a990c"}, - {file = "aiohttp-3.10.1-cp311-cp311-win_amd64.whl", hash = "sha256:a77c79bac8d908d839d32c212aef2354d2246eb9deb3e2cb01ffa83fb7a6ea5d"}, - {file = "aiohttp-3.10.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2212296cdb63b092e295c3e4b4b442e7b7eb41e8a30d0f53c16d5962efed395d"}, - {file = "aiohttp-3.10.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4dcb127ca3eb0a61205818a606393cbb60d93b7afb9accd2fd1e9081cc533144"}, - {file = "aiohttp-3.10.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:cb8b79a65332e1a426ccb6290ce0409e1dc16b4daac1cc5761e059127fa3d134"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68cc24f707ed9cb961f6ee04020ca01de2c89b2811f3cf3361dc7c96a14bfbcc"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cb54f5725b4b37af12edf6c9e834df59258c82c15a244daa521a065fbb11717"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51d03e948e53b3639ce4d438f3d1d8202898ec6655cadcc09ec99229d4adc2a9"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:786299d719eb5d868f161aeec56d589396b053925b7e0ce36e983d30d0a3e55c"}, - {file = "aiohttp-3.10.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abda4009a30d51d3f06f36bc7411a62b3e647fa6cc935ef667e3e3d3a7dd09b1"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:67f7639424c313125213954e93a6229d3a1d386855d70c292a12628f600c7150"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:8e5a26d7aac4c0d8414a347da162696eea0629fdce939ada6aedf951abb1d745"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:120548d89f14b76a041088b582454d89389370632ee12bf39d919cc5c561d1ca"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:f5293726943bdcea24715b121d8c4ae12581441d22623b0e6ab12d07ce85f9c4"}, - {file = "aiohttp-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1f8605e573ed6c44ec689d94544b2c4bb1390aaa723a8b5a2cc0a5a485987a68"}, - {file = "aiohttp-3.10.1-cp312-cp312-win32.whl", hash = "sha256:e7168782621be4448d90169a60c8b37e9b0926b3b79b6097bc180c0a8a119e73"}, - {file = "aiohttp-3.10.1-cp312-cp312-win_amd64.whl", hash = "sha256:8fbf8c0ded367c5c8eaf585f85ca8dd85ff4d5b73fb8fe1e6ac9e1b5e62e11f7"}, - {file = "aiohttp-3.10.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:54b7f4a20d7cc6bfa4438abbde069d417bb7a119f870975f78a2b99890226d55"}, - {file = "aiohttp-3.10.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2fa643ca990323db68911b92f3f7a0ca9ae300ae340d0235de87c523601e58d9"}, - {file = "aiohttp-3.10.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d8311d0d690487359fe2247ec5d2cac9946e70d50dced8c01ce9e72341c21151"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222821c60b8f6a64c5908cb43d69c0ee978a1188f6a8433d4757d39231b42cdb"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7b55d9ede66af7feb6de87ff277e0ccf6d51c7db74cc39337fe3a0e31b5872d"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a95151a5567b3b00368e99e9c5334a919514f60888a6b6d2054fea5e66e527e"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e9e9171d2fe6bfd9d3838a6fe63b1e91b55e0bf726c16edf265536e4eafed19"}, - {file = "aiohttp-3.10.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a57e73f9523e980f6101dc9a83adcd7ac0006ea8bf7937ca3870391c7bb4f8ff"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:0df51a3d70a2bfbb9c921619f68d6d02591f24f10e9c76de6f3388c89ed01de6"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:b0de63ff0307eac3961b4af74382d30220d4813f36b7aaaf57f063a1243b4214"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:8db9b749f589b5af8e4993623dbda6716b2b7a5fcb0fa2277bf3ce4b278c7059"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:6b14c19172eb53b63931d3e62a9749d6519f7c121149493e6eefca055fcdb352"}, - {file = "aiohttp-3.10.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:5cd57ad998e3038aa87c38fe85c99ed728001bf5dde8eca121cadee06ee3f637"}, - {file = "aiohttp-3.10.1-cp38-cp38-win32.whl", hash = "sha256:df31641e3f02b77eb3c5fb63c0508bee0fc067cf153da0e002ebbb0db0b6d91a"}, - {file = "aiohttp-3.10.1-cp38-cp38-win_amd64.whl", hash = "sha256:93094eba50bc2ad4c40ff4997ead1fdcd41536116f2e7d6cfec9596a8ecb3615"}, - {file = "aiohttp-3.10.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:440954ddc6b77257e67170d57b1026aa9545275c33312357472504eef7b4cc0b"}, - {file = "aiohttp-3.10.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f9f8beed277488a52ee2b459b23c4135e54d6a819eaba2e120e57311015b58e9"}, - {file = "aiohttp-3.10.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d8a8221a63602008550022aa3a4152ca357e1dde7ab3dd1da7e1925050b56863"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a702bd3663b5cbf3916e84bf332400d24cdb18399f0877ca6b313ce6c08bfb43"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1988b370536eb14f0ce7f3a4a5b422ab64c4e255b3f5d7752c5f583dc8c967fc"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7ccf1f0a304352c891d124ac1a9dea59b14b2abed1704aaa7689fc90ef9c5be1"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc3ea6ef2a83edad84bbdb5d96e22f587b67c68922cd7b6f9d8f24865e655bcf"}, - {file = "aiohttp-3.10.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89b47c125ab07f0831803b88aeb12b04c564d5f07a1c1a225d4eb4d2f26e8b5e"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:21778552ef3d44aac3278cc6f6d13a6423504fa5f09f2df34bfe489ed9ded7f5"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:bde0693073fd5e542e46ea100aa6c1a5d36282dbdbad85b1c3365d5421490a92"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:bf66149bb348d8e713f3a8e0b4f5b952094c2948c408e1cfef03b49e86745d60"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:587237571a85716d6f71f60d103416c9df7d5acb55d96d3d3ced65f39bff9c0c"}, - {file = "aiohttp-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:bfe33cba6e127d0b5b417623c9aa621f0a69f304742acdca929a9fdab4593693"}, - {file = "aiohttp-3.10.1-cp39-cp39-win32.whl", hash = "sha256:9fbff00646cf8211b330690eb2fd64b23e1ce5b63a342436c1d1d6951d53d8dd"}, - {file = "aiohttp-3.10.1-cp39-cp39-win_amd64.whl", hash = "sha256:5951c328f9ac42d7bce7a6ded535879bc9ae13032818d036749631fa27777905"}, - {file = "aiohttp-3.10.1.tar.gz", hash = "sha256:8b0d058e4e425d3b45e8ec70d49b402f4d6b21041e674798b1f91ba027c73f28"}, + {file = "aiohttp-3.10.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95213b3d79c7e387144e9cb7b9d2809092d6ff2c044cb59033aedc612f38fb6d"}, + {file = "aiohttp-3.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1aa005f060aff7124cfadaa2493f00a4e28ed41b232add5869e129a2e395935a"}, + {file = "aiohttp-3.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eabe6bf4c199687592f5de4ccd383945f485779c7ffb62a9b9f1f8a3f9756df8"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e010736fc16d21125c7e2dc5c350cd43c528b85085c04bf73a77be328fe944"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99f81f9c1529fd8e03be4a7bd7df32d14b4f856e90ef6e9cbad3415dbfa9166c"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d611d1a01c25277bcdea06879afbc11472e33ce842322496b211319aa95441bb"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00191d38156e09e8c81ef3d75c0d70d4f209b8381e71622165f22ef7da6f101"}, + {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74c091a5ded6cb81785de2d7a8ab703731f26de910dbe0f3934eabef4ae417cc"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:18186a80ec5a701816adbf1d779926e1069392cf18504528d6e52e14b5920525"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5a7ceb2a0d2280f23a02c64cd0afdc922079bb950400c3dd13a1ab2988428aac"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8bd7be6ff6c162a60cb8fce65ee879a684fbb63d5466aba3fa5b9288eb04aefa"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fae962b62944eaebff4f4fddcf1a69de919e7b967136a318533d82d93c3c6bd1"}, + {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a0fde16d284efcacbe15fb0c1013f0967b6c3e379649239d783868230bf1db42"}, + {file = "aiohttp-3.10.2-cp310-cp310-win32.whl", hash = "sha256:f81cd85a0e76ec7b8e2b6636fe02952d35befda4196b8c88f3cec5b4fb512839"}, + {file = "aiohttp-3.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:54ba10eb5a3481c28282eb6afb5f709aedf53cf9c3a31875ffbdc9fc719ffd67"}, + {file = "aiohttp-3.10.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87fab7f948e407444c2f57088286e00e2ed0003ceaf3d8f8cc0f60544ba61d91"}, + {file = "aiohttp-3.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ec6ad66ed660d46503243cbec7b2b3d8ddfa020f984209b3b8ef7d98ce69c3f2"}, + {file = "aiohttp-3.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a4be88807283bd96ae7b8e401abde4ca0bab597ba73b5e9a2d98f36d451e9aac"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01c98041f90927c2cbd72c22a164bb816fa3010a047d264969cf82e1d4bcf8d1"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54e36c67e1a9273ecafab18d6693da0fb5ac48fd48417e4548ac24a918c20998"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7de3ddb6f424af54535424082a1b5d1ae8caf8256ebd445be68c31c662354720"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dd9c7db94b4692b827ce51dcee597d61a0e4f4661162424faf65106775b40e7"}, + {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e57e21e1167705f8482ca29cc5d02702208d8bf4aff58f766d94bcd6ead838cd"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a1a50e59b720060c29e2951fd9f13c01e1ea9492e5a527b92cfe04dd64453c16"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:686c87782481fda5ee6ba572d912a5c26d9f98cc5c243ebd03f95222af3f1b0f"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:dafb4abb257c0ed56dc36f4e928a7341b34b1379bd87e5a15ce5d883c2c90574"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:494a6f77560e02bd7d1ab579fdf8192390567fc96a603f21370f6e63690b7f3d"}, + {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6fe8503b1b917508cc68bf44dae28823ac05e9f091021e0c41f806ebbb23f92f"}, + {file = "aiohttp-3.10.2-cp311-cp311-win32.whl", hash = "sha256:4ddb43d06ce786221c0dfd3c91b4892c318eaa36b903f7c4278e7e2fa0dd5102"}, + {file = "aiohttp-3.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:ca2f5abcb0a9a47e56bac173c01e9f6c6e7f27534d91451c5f22e6a35a5a2093"}, + {file = "aiohttp-3.10.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:14eb6b17f6246959fb0b035d4f4ae52caa870c4edfb6170aad14c0de5bfbf478"}, + {file = "aiohttp-3.10.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:465e445ec348d4e4bd349edd8b22db75f025da9d7b6dc1369c48e7935b85581e"}, + {file = "aiohttp-3.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:341f8ece0276a828d95b70cd265d20e257f5132b46bf77d759d7f4e0443f2906"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c01fbb87b5426381cd9418b3ddcf4fc107e296fa2d3446c18ce6c76642f340a3"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c474af073e1a6763e1c5522bbb2d85ff8318197e4c6c919b8d7886e16213345"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d9076810a5621236e29b2204e67a68e1fe317c8727ee4c9abbfbb1083b442c38"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8f515d6859e673940e08de3922b9c4a2249653b0ac181169313bd6e4b1978ac"}, + {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:655e583afc639bef06f3b2446972c1726007a21003cd0ef57116a123e44601bc"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8da9449a575133828cc99985536552ea2dcd690e848f9d41b48d8853a149a959"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:19073d57d0feb1865d12361e2a1f5a49cb764bf81a4024a3b608ab521568093a"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8e98e1845805f184d91fda6f9ab93d7c7b0dddf1c07e0255924bfdb151a8d05"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:377220a5efde6f9497c5b74649b8c261d3cce8a84cb661be2ed8099a2196400a"}, + {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92f7f4a4dc9cdb5980973a74d43cdbb16286dacf8d1896b6c3023b8ba8436f8e"}, + {file = "aiohttp-3.10.2-cp312-cp312-win32.whl", hash = "sha256:9bb2834a6f11d65374ce97d366d6311a9155ef92c4f0cee543b2155d06dc921f"}, + {file = "aiohttp-3.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:518dc3cb37365255708283d1c1c54485bbacccd84f0a0fb87ed8917ba45eda5b"}, + {file = "aiohttp-3.10.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7f98e70bbbf693086efe4b86d381efad8edac040b8ad02821453083d15ec315f"}, + {file = "aiohttp-3.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f6f0b252a009e98fe84028a4ec48396a948e7a65b8be06ccfc6ef68cf1f614d"}, + {file = "aiohttp-3.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9360e3ffc7b23565600e729e8c639c3c50d5520e05fdf94aa2bd859eef12c407"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3988044d1635c7821dd44f0edfbe47e9875427464e59d548aece447f8c22800a"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a9d59da1543a6f1478c3436fd49ec59be3868bca561a33778b4391005e499d"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9f49bdb94809ac56e09a310a62f33e5f22973d6fd351aac72a39cd551e98194"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddfd2dca3f11c365d6857a07e7d12985afc59798458a2fdb2ffa4a0332a3fd43"}, + {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c1508ec97b2cd3e120bfe309a4ff8e852e8a7460f1ef1de00c2c0ed01e33c"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:49904f38667c44c041a0b44c474b3ae36948d16a0398a8f8cd84e2bb3c42a069"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:352f3a4e5f11f3241a49b6a48bc5b935fabc35d1165fa0d87f3ca99c1fcca98b"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:fc61f39b534c5d5903490478a0dd349df397d2284a939aa3cbaa2fb7a19b8397"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:ad2274e707be37420d0b6c3d26a8115295fe9d8e6e530fa6a42487a8ca3ad052"}, + {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c836bf3c7512100219fe1123743fd8dd9a2b50dd7cfb0c3bb10d041309acab4b"}, + {file = "aiohttp-3.10.2-cp38-cp38-win32.whl", hash = "sha256:53e8898adda402be03ff164b0878abe2d884e3ea03a4701e6ad55399d84b92dc"}, + {file = "aiohttp-3.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:7cc8f65f5b22304693de05a245b6736b14cb5bc9c8a03da6e2ae9ef15f8b458f"}, + {file = "aiohttp-3.10.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9dfc906d656e14004c5bc672399c1cccc10db38df2b62a13fb2b6e165a81c316"}, + {file = "aiohttp-3.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:91b10208b222ddf655c3a3d5b727879d7163db12b634492df41a9182a76edaae"}, + {file = "aiohttp-3.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fd16b5e1a7bdd14668cd6bde60a2a29b49147a535c74f50d8177d11b38433a7"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2bfdda4971bd79201f59adbad24ec2728875237e1c83bba5221284dbbf57bda"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69d73f869cf29e8a373127fc378014e2b17bcfbe8d89134bc6fb06a2f67f3cb3"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df59f8486507c421c0620a2c3dce81fbf1d54018dc20ff4fecdb2c106d6e6abc"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df930015db36b460aa9badbf35eccbc383f00d52d4b6f3de2ccb57d064a6ade"}, + {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:562b1153ab7f766ee6b8b357ec777a302770ad017cf18505d34f1c088fccc448"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d984db6d855de58e0fde1ef908d48fe9a634cadb3cf715962722b4da1c40619d"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:14dc3fcb0d877911d775d511eb617a486a8c48afca0a887276e63db04d3ee920"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b52a27a5c97275e254704e1049f4b96a81e67d6205f52fa37a4777d55b0e98ef"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:cd33d9de8cfd006a0d0fe85f49b4183c57e91d18ffb7e9004ce855e81928f704"}, + {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1238fc979160bc03a92fff9ad021375ff1c8799c6aacb0d8ea1b357ea40932bb"}, + {file = "aiohttp-3.10.2-cp39-cp39-win32.whl", hash = "sha256:e2f43d238eae4f0b04f58d4c0df4615697d4ca3e9f9b1963d49555a94f0f5a04"}, + {file = "aiohttp-3.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:947847f07a8f81d7b39b2d0202fd73e61962ebe17ac2d8566f260679e467da7b"}, + {file = "aiohttp-3.10.2.tar.gz", hash = "sha256:4d1f694b5d6e459352e5e925a42e05bac66655bfde44d81c59992463d2897014"}, ] [package.dependencies] @@ -2292,13 +2292,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.3" +version = "1.43.4" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.3-py3-none-any.whl", hash = "sha256:b626df5da48b794be4d1888eaecfe941d68a081bfeab50531e5395e32d0e7894"}, - {file = "litellm-1.43.3.tar.gz", hash = "sha256:b139fd3965cc1fdf98746dac67ce1f57f72c247083174ee8f1f46ed86fafdffc"}, + {file = "litellm-1.43.4-py3-none-any.whl", hash = "sha256:619cfaab189f921f66ff50c2b7a0e965e562c2a95b17c2ee24649826ba35da11"}, + {file = "litellm-1.43.4.tar.gz", hash = "sha256:949c51ad494b935d80da1cd18c3567e4ed181f8eb531ef4706e3be72afb0c43c"}, ] [package.dependencies] @@ -2904,13 +2904,13 @@ files = [ [[package]] name = "openai" -version = "1.40.1" +version = "1.40.2" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.1-py3-none-any.whl", hash = "sha256:cf5929076c6ca31c26f1ed207e9fd19eb05404cc9104f64c9d29bb0ac0c5bcd4"}, - {file = "openai-1.40.1.tar.gz", hash = "sha256:cb1294ac1f8c6a1acbb07e090698eb5ad74a7a88484e77126612a4f22579673d"}, + {file = "openai-1.40.2-py3-none-any.whl", hash = "sha256:38068f858f310b4fd4b0ea8734c3efcfde3c15a2978311e1453bd84817231b96"}, + {file = "openai-1.40.2.tar.gz", hash = "sha256:2180e9070bd36084328248b3ce668964e8ddd2e9019e1d426e31dc54cc117bb5"}, ] [package.dependencies] @@ -2928,62 +2928,68 @@ datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] [[package]] name = "orjson" -version = "3.10.6" +version = "3.10.7" description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" optional = false python-versions = ">=3.8" files = [ - {file = "orjson-3.10.6-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:fb0ee33124db6eaa517d00890fc1a55c3bfe1cf78ba4a8899d71a06f2d6ff5c7"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9c1c4b53b24a4c06547ce43e5fee6ec4e0d8fe2d597f4647fc033fd205707365"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eadc8fd310edb4bdbd333374f2c8fec6794bbbae99b592f448d8214a5e4050c0"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:61272a5aec2b2661f4fa2b37c907ce9701e821b2c1285d5c3ab0207ebd358d38"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57985ee7e91d6214c837936dc1608f40f330a6b88bb13f5a57ce5257807da143"}, - {file = "orjson-3.10.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:633a3b31d9d7c9f02d49c4ab4d0a86065c4a6f6adc297d63d272e043472acab5"}, - {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:1c680b269d33ec444afe2bdc647c9eb73166fa47a16d9a75ee56a374f4a45f43"}, - {file = "orjson-3.10.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f759503a97a6ace19e55461395ab0d618b5a117e8d0fbb20e70cfd68a47327f2"}, - {file = "orjson-3.10.6-cp310-none-win32.whl", hash = "sha256:95a0cce17f969fb5391762e5719575217bd10ac5a189d1979442ee54456393f3"}, - {file = "orjson-3.10.6-cp310-none-win_amd64.whl", hash = "sha256:df25d9271270ba2133cc88ee83c318372bdc0f2cd6f32e7a450809a111efc45c"}, - {file = "orjson-3.10.6-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b1ec490e10d2a77c345def52599311849fc063ae0e67cf4f84528073152bb2ba"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55d43d3feb8f19d07e9f01e5b9be4f28801cf7c60d0fa0d279951b18fae1932b"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ac3045267e98fe749408eee1593a142e02357c5c99be0802185ef2170086a863"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c27bc6a28ae95923350ab382c57113abd38f3928af3c80be6f2ba7eb8d8db0b0"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d27456491ca79532d11e507cadca37fb8c9324a3976294f68fb1eff2dc6ced5a"}, - {file = "orjson-3.10.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05ac3d3916023745aa3b3b388e91b9166be1ca02b7c7e41045da6d12985685f0"}, - {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1335d4ef59ab85cab66fe73fd7a4e881c298ee7f63ede918b7faa1b27cbe5212"}, - {file = "orjson-3.10.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4bbc6d0af24c1575edc79994c20e1b29e6fb3c6a570371306db0993ecf144dc5"}, - {file = "orjson-3.10.6-cp311-none-win32.whl", hash = "sha256:450e39ab1f7694465060a0550b3f6d328d20297bf2e06aa947b97c21e5241fbd"}, - {file = "orjson-3.10.6-cp311-none-win_amd64.whl", hash = "sha256:227df19441372610b20e05bdb906e1742ec2ad7a66ac8350dcfd29a63014a83b"}, - {file = "orjson-3.10.6-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:ea2977b21f8d5d9b758bb3f344a75e55ca78e3ff85595d248eee813ae23ecdfb"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6f3d167d13a16ed263b52dbfedff52c962bfd3d270b46b7518365bcc2121eed"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f710f346e4c44a4e8bdf23daa974faede58f83334289df80bc9cd12fe82573c7"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7275664f84e027dcb1ad5200b8b18373e9c669b2a9ec33d410c40f5ccf4b257e"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0943e4c701196b23c240b3d10ed8ecd674f03089198cf503105b474a4f77f21f"}, - {file = "orjson-3.10.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:446dee5a491b5bc7d8f825d80d9637e7af43f86a331207b9c9610e2f93fee22a"}, - {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:64c81456d2a050d380786413786b057983892db105516639cb5d3ee3c7fd5148"}, - {file = "orjson-3.10.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:960db0e31c4e52fa0fc3ecbaea5b2d3b58f379e32a95ae6b0ebeaa25b93dfd34"}, - {file = "orjson-3.10.6-cp312-none-win32.whl", hash = "sha256:a6ea7afb5b30b2317e0bee03c8d34c8181bc5a36f2afd4d0952f378972c4efd5"}, - {file = "orjson-3.10.6-cp312-none-win_amd64.whl", hash = "sha256:874ce88264b7e655dde4aeaacdc8fd772a7962faadfb41abe63e2a4861abc3dc"}, - {file = "orjson-3.10.6-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:66680eae4c4e7fc193d91cfc1353ad6d01b4801ae9b5314f17e11ba55e934183"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caff75b425db5ef8e8f23af93c80f072f97b4fb3afd4af44482905c9f588da28"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3722fddb821b6036fd2a3c814f6bd9b57a89dc6337b9924ecd614ebce3271394"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c2c116072a8533f2fec435fde4d134610f806bdac20188c7bd2081f3e9e0133f"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6eeb13218c8cf34c61912e9df2de2853f1d009de0e46ea09ccdf3d757896af0a"}, - {file = "orjson-3.10.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:965a916373382674e323c957d560b953d81d7a8603fbeee26f7b8248638bd48b"}, - {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:03c95484d53ed8e479cade8628c9cea00fd9d67f5554764a1110e0d5aa2de96e"}, - {file = "orjson-3.10.6-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:e060748a04cccf1e0a6f2358dffea9c080b849a4a68c28b1b907f272b5127e9b"}, - {file = "orjson-3.10.6-cp38-none-win32.whl", hash = "sha256:738dbe3ef909c4b019d69afc19caf6b5ed0e2f1c786b5d6215fbb7539246e4c6"}, - {file = "orjson-3.10.6-cp38-none-win_amd64.whl", hash = "sha256:d40f839dddf6a7d77114fe6b8a70218556408c71d4d6e29413bb5f150a692ff7"}, - {file = "orjson-3.10.6-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:697a35a083c4f834807a6232b3e62c8b280f7a44ad0b759fd4dce748951e70db"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd502f96bf5ea9a61cbc0b2b5900d0dd68aa0da197179042bdd2be67e51a1e4b"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:f215789fb1667cdc874c1b8af6a84dc939fd802bf293a8334fce185c79cd359b"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a2debd8ddce948a8c0938c8c93ade191d2f4ba4649a54302a7da905a81f00b56"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5410111d7b6681d4b0d65e0f58a13be588d01b473822483f77f513c7f93bd3b2"}, - {file = "orjson-3.10.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bb1f28a137337fdc18384079fa5726810681055b32b92253fa15ae5656e1dddb"}, - {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:bf2fbbce5fe7cd1aa177ea3eab2b8e6a6bc6e8592e4279ed3db2d62e57c0e1b2"}, - {file = "orjson-3.10.6-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:79b9b9e33bd4c517445a62b90ca0cc279b0f1f3970655c3df9e608bc3f91741a"}, - {file = "orjson-3.10.6-cp39-none-win32.whl", hash = "sha256:30b0a09a2014e621b1adf66a4f705f0809358350a757508ee80209b2d8dae219"}, - {file = "orjson-3.10.6-cp39-none-win_amd64.whl", hash = "sha256:49e3bc615652617d463069f91b867a4458114c5b104e13b7ae6872e5f79d0844"}, - {file = "orjson-3.10.6.tar.gz", hash = "sha256:e54b63d0a7c6c54a5f5f726bc93a2078111ef060fec4ecbf34c5db800ca3b3a7"}, + {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, + {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, + {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, + {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, + {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, + {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, + {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, + {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, + {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, + {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, + {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, + {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, + {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, + {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, + {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, + {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, + {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, + {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, + {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, + {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, + {file = "orjson-3.10.7-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0"}, + {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354"}, + {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866"}, + {file = "orjson-3.10.7-cp38-none-win32.whl", hash = "sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c"}, + {file = "orjson-3.10.7-cp38-none-win_amd64.whl", hash = "sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e"}, + {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, + {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, + {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, + {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, + {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, + {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, ] [[package]] diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index c4b36bb46..92de246c2 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -91,6 +91,7 @@ def test_agent(cozo_client=cozo_client, developer_id=test_developer_id): model="gpt-4o", name="test agent", about="test agent about", + metadata={"test": "test"}, ), client=cozo_client, ) @@ -237,7 +238,6 @@ def test_tool( agent=test_agent, ): function = { - "name": "hello_world1", "description": "A function that prints hello world", "parameters": {"type": "object", "properties": {}}, } diff --git a/agents-api/tests/test_agent_queries.py b/agents-api/tests/test_agent_queries.py index 6004dc029..b6d69c287 100644 --- a/agents-api/tests/test_agent_queries.py +++ b/agents-api/tests/test_agent_queries.py @@ -1,12 +1,13 @@ # Tests for agent queries from uuid import uuid4 -from ward import test +from ward import raises, test from agents_api.autogen.openapi_model import ( Agent, CreateAgentRequest, CreateOrUpdateAgentRequest, + PatchAgentRequest, ResourceUpdatedResponse, UpdateAgentRequest, ) @@ -66,13 +67,8 @@ def _(client=cozo_client, developer_id=test_developer_id): def _(client=cozo_client, developer_id=test_developer_id): agent_id = uuid4() - try: + with raises(Exception): get_agent(agent_id=agent_id, developer_id=developer_id, client=client) - except Exception: - pass - - else: - assert None @test("model: get agent exists") @@ -100,12 +96,8 @@ def _(client=cozo_client, developer_id=test_developer_id): delete_agent(agent_id=temp_agent.id, developer_id=developer_id, client=client) # Check that the agent is deleted - try: + with raises(Exception): get_agent(agent_id=temp_agent.id, developer_id=developer_id, client=client) - except Exception: - pass - else: - raise AssertionError("Agent found") @test("model: update agent") @@ -116,7 +108,8 @@ def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): data=UpdateAgentRequest( name="updated agent", about="updated agent about", - default_settings={"temperature": 1.5}, + default_settings={"temperature": 1.0}, + metadata={"hello": "world"}, ), client=client, ) @@ -124,16 +117,25 @@ def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): assert result is not None assert isinstance(result, ResourceUpdatedResponse) + agent = get_agent( + agent_id=agent.id, + developer_id=developer_id, + client=client, + ) + + assert "test" not in agent.metadata + @test("model: patch agent") def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): result = patch_agent( agent_id=agent.id, developer_id=developer_id, - data=UpdateAgentRequest( + data=PatchAgentRequest( name="patched agent", about="patched agent about", default_settings={"temperature": 1.0}, + metadata={"something": "else"}, ), client=client, ) @@ -141,6 +143,14 @@ def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): assert result is not None assert isinstance(result, ResourceUpdatedResponse) + agent = get_agent( + agent_id=agent.id, + developer_id=developer_id, + client=client, + ) + + assert "hello" in agent.metadata + @test("model: list agents") def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): diff --git a/agents-api/tests/test_entry_queries.py b/agents-api/tests/test_entry_queries.py index bfa1753e8..1c6fdf38e 100644 --- a/agents-api/tests/test_entry_queries.py +++ b/agents-api/tests/test_entry_queries.py @@ -9,8 +9,8 @@ from agents_api.autogen.openapi_model import CreateEntryRequest from agents_api.models.entry.create_entries import create_entries -from agents_api.models.entry.list_entries import list_entries from agents_api.models.entry.delete_entries import delete_entries +from agents_api.models.entry.list_entries import list_entries from tests.fixtures import cozo_client, test_developer_id, test_session MODEL = "julep-ai/samantha-1-turbo" @@ -93,7 +93,7 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): internal_entry = CreateEntryRequest( session_id=session.id, role="user", - content="test entry content", + content="internal entry content", source="internal", ) @@ -120,4 +120,4 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): ) # Asserts that no entries are retrieved after deletion. - assert len(result) == 0 + assert all(id not in [entry.id for entry in result] for id in entry_ids) diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index 0c550f459..b2ef1a04e 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -6,11 +6,13 @@ from agents_api.autogen.Executions import ( CreateExecutionRequest, ) -from agents_api.autogen.openapi_model import Execution +from agents_api.autogen.openapi_model import CreateTransitionRequest, Execution from agents_api.models.execution.create_execution import create_execution +from agents_api.models.execution.create_execution_transition import ( + create_execution_transition, +) from agents_api.models.execution.get_execution import get_execution from agents_api.models.execution.list_executions import list_executions -from agents_api.models.execution.create_execution_transition import create_execution_transition from tests.fixtures import cozo_client, test_developer_id, test_execution, test_task MODEL = "julep-ai/samantha-1-turbo" @@ -67,12 +69,14 @@ def _(client=cozo_client, developer_id=test_developer_id, execution=test_executi result = create_execution_transition( developer_id=developer_id, execution_id=execution.id, - data={ - "type": "step", - "output": {"result": "test"}, - "current": [], - "next": [], - }, + data=CreateTransitionRequest( + **{ + "type": "step", + "output": {"result": "test"}, + "current": ["main", 0], + "next": None, + } + ), client=client, ) diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index e95bd7299..01c2559de 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -3,10 +3,10 @@ from ward import test -from agents_api.autogen.openapi_model import Session, CreateOrUpdateSessionRequest +from agents_api.autogen.openapi_model import CreateOrUpdateSessionRequest, Session from agents_api.autogen.Sessions import CreateSessionRequest -from agents_api.models.session.create_session import create_session from agents_api.models.session.create_or_update_session import create_or_update_session +from agents_api.models.session.create_session import create_session from agents_api.models.session.delete_session import delete_session from agents_api.models.session.get_session import get_session from agents_api.models.session.list_sessions import list_sessions @@ -118,7 +118,9 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): @test("model: create or update session") -def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent, user=test_user): +def _( + client=cozo_client, developer_id=test_developer_id, agent=test_agent, user=test_user +): session_id = uuid4() create_or_update_session( diff --git a/agents-api/tests/test_task_queries.py b/agents-api/tests/test_task_queries.py index 6e083c22b..06c7c969b 100644 --- a/agents-api/tests/test_task_queries.py +++ b/agents-api/tests/test_task_queries.py @@ -9,8 +9,8 @@ Task, UpdateTaskRequest, ) -from agents_api.models.task.create_task import create_task from agents_api.models.task.create_or_update_task import create_or_update_task +from agents_api.models.task.create_task import create_task from agents_api.models.task.delete_task import delete_task from agents_api.models.task.get_task import get_task from agents_api.models.task.list_tasks import list_tasks diff --git a/agents-api/tests/test_tool_queries.py b/agents-api/tests/test_tool_queries.py index fd5222cda..c21b7fbfb 100644 --- a/agents-api/tests/test_tool_queries.py +++ b/agents-api/tests/test_tool_queries.py @@ -2,12 +2,18 @@ from ward import test -from agents_api.autogen.openapi_model import CreateToolRequest, Tool +from agents_api.autogen.openapi_model import ( + CreateToolRequest, + PatchToolRequest, + Tool, + UpdateToolRequest, +) from agents_api.models.tools.create_tools import create_tools from agents_api.models.tools.delete_tool import delete_tool from agents_api.models.tools.get_tool import get_tool from agents_api.models.tools.list_tools import list_tools from agents_api.models.tools.patch_tool import patch_tool +from agents_api.models.tools.update_tool import update_tool from tests.fixtures import cozo_client, test_agent, test_developer_id, test_tool @@ -96,20 +102,68 @@ def _( @test("model: patch tool") -def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent, tool=test_tool): - patch_data = { - "name": "patched_tool", - "description": "A patched function that prints hello world", - } +def _( + client=cozo_client, developer_id=test_developer_id, agent=test_agent, tool=test_tool +): + patch_data = PatchToolRequest( + **{ + "name": "patched_tool", + "function": { + "description": "A patched function that prints hello world", + }, + } + ) result = patch_tool( developer_id=developer_id, agent_id=agent.id, tool_id=tool.id, - patch_tool=patch_data, + data=patch_data, client=client, ) assert result is not None - assert result.name == "patched_tool" - assert result.description == "A patched function that prints hello world" + + tool = get_tool( + developer_id=developer_id, + agent_id=agent.id, + tool_id=tool.id, + client=client, + ) + + assert tool.name == "patched_tool" + assert tool.function.description == "A patched function that prints hello world" + assert tool.function.parameters + + +@test("model: update tool") +def _( + client=cozo_client, developer_id=test_developer_id, agent=test_agent, tool=test_tool +): + update_data = UpdateToolRequest( + name="updated_tool", + type="function", + function={ + "description": "An updated function that prints hello world", + }, + ) + + result = update_tool( + developer_id=developer_id, + agent_id=agent.id, + tool_id=tool.id, + data=update_data, + client=client, + ) + + assert result is not None + + tool = get_tool( + developer_id=developer_id, + agent_id=agent.id, + tool_id=tool.id, + client=client, + ) + + assert tool.name == "updated_tool" + assert not tool.function.parameters diff --git a/agents-api/tests/test_user_queries.py b/agents-api/tests/test_user_queries.py index 02272a8a6..ab5c62ed0 100644 --- a/agents-api/tests/test_user_queries.py +++ b/agents-api/tests/test_user_queries.py @@ -6,13 +6,14 @@ from ward import test from agents_api.autogen.openapi_model import ( + CreateOrUpdateUserRequest, CreateUserRequest, ResourceUpdatedResponse, UpdateUserRequest, User, ) -from agents_api.models.user.create_user import create_user from agents_api.models.user.create_or_update_user import create_or_update_user +from agents_api.models.user.create_user import create_user from agents_api.models.user.get_user import get_user from agents_api.models.user.list_users import list_users from agents_api.models.user.update_user import update_user @@ -40,7 +41,7 @@ def _(client=cozo_client, developer_id=test_developer_id): create_or_update_user( developer_id=developer_id, user_id=uuid4(), - data=CreateUserRequest( + data=CreateOrUpdateUserRequest( name="test user", about="test user about", ), diff --git a/fern/fern.config.json b/fern/fern.config.json index 1ccdf6af2..7a5d561b7 100644 --- a/fern/fern.config.json +++ b/fern/fern.config.json @@ -1,4 +1,4 @@ { "organization": "julep", - "version": "0.37.6" + "version": "0.37.16" } \ No newline at end of file diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 545eebbe1..a6d14d8b5 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -154,13 +154,13 @@ typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} [[package]] name = "attrs" -version = "24.1.0" +version = "24.2.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.7" files = [ - {file = "attrs-24.1.0-py3-none-any.whl", hash = "sha256:377b47448cb61fea38533f671fba0d0f8a96fd58facd4dc518e3dac9dbea0905"}, - {file = "attrs-24.1.0.tar.gz", hash = "sha256:adbdec84af72d38be7628e353a09b6a6790d15cd71819f6e9d7b0faa8a125745"}, + {file = "attrs-24.2.0-py3-none-any.whl", hash = "sha256:81921eb96de3191c8258c199618104dd27ac608d9366f5e35d011eae1867ede2"}, + {file = "attrs-24.2.0.tar.gz", hash = "sha256:5cfb1b9148b5b086569baec03f20d7b6bf3bcacc9a42bebf87ffaaca362f6346"}, ] [package.extras] @@ -173,13 +173,13 @@ tests-mypy = ["mypy (>=1.11.1)", "pytest-mypy-plugins"] [[package]] name = "babel" -version = "2.15.0" +version = "2.16.0" description = "Internationalization utilities" optional = false python-versions = ">=3.8" files = [ - {file = "Babel-2.15.0-py3-none-any.whl", hash = "sha256:08706bdad8d0a3413266ab61bd6c34d0c28d6e1e7badf40a2cebe67644e2e1fb"}, - {file = "babel-2.15.0.tar.gz", hash = "sha256:8daf0e265d05768bc6c7a314cf1321e9a123afc328cc635c18622a2f30a04413"}, + {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"}, + {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"}, ] [package.extras] @@ -312,63 +312,78 @@ files = [ [[package]] name = "cffi" -version = "1.16.0" +version = "1.17.0" description = "Foreign Function Interface for Python calling C code." optional = false python-versions = ">=3.8" files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, + {file = "cffi-1.17.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f9338cc05451f1942d0d8203ec2c346c830f8e86469903d5126c1f0a13a2bcbb"}, + {file = "cffi-1.17.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a0ce71725cacc9ebf839630772b07eeec220cbb5f03be1399e0457a1464f8e1a"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c815270206f983309915a6844fe994b2fa47e5d05c4c4cef267c3b30e34dbe42"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6bdcd415ba87846fd317bee0774e412e8792832e7805938987e4ede1d13046d"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a98748ed1a1df4ee1d6f927e151ed6c1a09d5ec21684de879c7ea6aa96f58f2"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a048d4f6630113e54bb4b77e315e1ba32a5a31512c31a273807d0027a7e69ab"}, + {file = "cffi-1.17.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24aa705a5f5bd3a8bcfa4d123f03413de5d86e497435693b638cbffb7d5d8a1b"}, + {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:856bf0924d24e7f93b8aee12a3a1095c34085600aa805693fb7f5d1962393206"}, + {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4304d4416ff032ed50ad6bb87416d802e67139e31c0bde4628f36a47a3164bfa"}, + {file = "cffi-1.17.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:331ad15c39c9fe9186ceaf87203a9ecf5ae0ba2538c9e898e3a6967e8ad3db6f"}, + {file = "cffi-1.17.0-cp310-cp310-win32.whl", hash = "sha256:669b29a9eca6146465cc574659058ed949748f0809a2582d1f1a324eb91054dc"}, + {file = "cffi-1.17.0-cp310-cp310-win_amd64.whl", hash = "sha256:48b389b1fd5144603d61d752afd7167dfd205973a43151ae5045b35793232aa2"}, + {file = "cffi-1.17.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5d97162c196ce54af6700949ddf9409e9833ef1003b4741c2b39ef46f1d9720"}, + {file = "cffi-1.17.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5ba5c243f4004c750836f81606a9fcb7841f8874ad8f3bf204ff5e56332b72b9"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bb9333f58fc3a2296fb1d54576138d4cf5d496a2cc118422bd77835e6ae0b9cb"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:435a22d00ec7d7ea533db494da8581b05977f9c37338c80bc86314bec2619424"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d1df34588123fcc88c872f5acb6f74ae59e9d182a2707097f9e28275ec26a12d"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df8bb0010fdd0a743b7542589223a2816bdde4d94bb5ad67884348fa2c1c67e8"}, + {file = "cffi-1.17.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a8b5b9712783415695663bd463990e2f00c6750562e6ad1d28e072a611c5f2a6"}, + {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ffef8fd58a36fb5f1196919638f73dd3ae0db1a878982b27a9a5a176ede4ba91"}, + {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e67d26532bfd8b7f7c05d5a766d6f437b362c1bf203a3a5ce3593a645e870b8"}, + {file = "cffi-1.17.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:45f7cd36186db767d803b1473b3c659d57a23b5fa491ad83c6d40f2af58e4dbb"}, + {file = "cffi-1.17.0-cp311-cp311-win32.whl", hash = "sha256:a9015f5b8af1bb6837a3fcb0cdf3b874fe3385ff6274e8b7925d81ccaec3c5c9"}, + {file = "cffi-1.17.0-cp311-cp311-win_amd64.whl", hash = "sha256:b50aaac7d05c2c26dfd50c3321199f019ba76bb650e346a6ef3616306eed67b0"}, + {file = "cffi-1.17.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aec510255ce690d240f7cb23d7114f6b351c733a74c279a84def763660a2c3bc"}, + {file = "cffi-1.17.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2770bb0d5e3cc0e31e7318db06efcbcdb7b31bcb1a70086d3177692a02256f59"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db9a30ec064129d605d0f1aedc93e00894b9334ec74ba9c6bdd08147434b33eb"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a47eef975d2b8b721775a0fa286f50eab535b9d56c70a6e62842134cf7841195"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f3e0992f23bbb0be00a921eae5363329253c3b86287db27092461c887b791e5e"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6107e445faf057c118d5050560695e46d272e5301feffda3c41849641222a828"}, + {file = "cffi-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb862356ee9391dc5a0b3cbc00f416b48c1b9a52d252d898e5b7696a5f9fe150"}, + {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c1c13185b90bbd3f8b5963cd8ce7ad4ff441924c31e23c975cb150e27c2bf67a"}, + {file = "cffi-1.17.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:17c6d6d3260c7f2d94f657e6872591fe8733872a86ed1345bda872cfc8c74885"}, + {file = "cffi-1.17.0-cp312-cp312-win32.whl", hash = "sha256:c3b8bd3133cd50f6b637bb4322822c94c5ce4bf0d724ed5ae70afce62187c492"}, + {file = "cffi-1.17.0-cp312-cp312-win_amd64.whl", hash = "sha256:dca802c8db0720ce1c49cce1149ff7b06e91ba15fa84b1d59144fef1a1bc7ac2"}, + {file = "cffi-1.17.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6ce01337d23884b21c03869d2f68c5523d43174d4fc405490eb0091057943118"}, + {file = "cffi-1.17.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cab2eba3830bf4f6d91e2d6718e0e1c14a2f5ad1af68a89d24ace0c6b17cced7"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:14b9cbc8f7ac98a739558eb86fabc283d4d564dafed50216e7f7ee62d0d25377"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b00e7bcd71caa0282cbe3c90966f738e2db91e64092a877c3ff7f19a1628fdcb"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41f4915e09218744d8bae14759f983e466ab69b178de38066f7579892ff2a555"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4760a68cab57bfaa628938e9c2971137e05ce48e762a9cb53b76c9b569f1204"}, + {file = "cffi-1.17.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:011aff3524d578a9412c8b3cfaa50f2c0bd78e03eb7af7aa5e0df59b158efb2f"}, + {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:a003ac9edc22d99ae1286b0875c460351f4e101f8c9d9d2576e78d7e048f64e0"}, + {file = "cffi-1.17.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ef9528915df81b8f4c7612b19b8628214c65c9b7f74db2e34a646a0a2a0da2d4"}, + {file = "cffi-1.17.0-cp313-cp313-win32.whl", hash = "sha256:70d2aa9fb00cf52034feac4b913181a6e10356019b18ef89bc7c12a283bf5f5a"}, + {file = "cffi-1.17.0-cp313-cp313-win_amd64.whl", hash = "sha256:b7b6ea9e36d32582cda3465f54c4b454f62f23cb083ebc7a94e2ca6ef011c3a7"}, + {file = "cffi-1.17.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:964823b2fc77b55355999ade496c54dde161c621cb1f6eac61dc30ed1b63cd4c"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:516a405f174fd3b88829eabfe4bb296ac602d6a0f68e0d64d5ac9456194a5b7e"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dec6b307ce928e8e112a6bb9921a1cb00a0e14979bf28b98e084a4b8a742bd9b"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4094c7b464cf0a858e75cd14b03509e84789abf7b79f8537e6a72152109c76e"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2404f3de742f47cb62d023f0ba7c5a916c9c653d5b368cc966382ae4e57da401"}, + {file = "cffi-1.17.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3aa9d43b02a0c681f0bfbc12d476d47b2b2b6a3f9287f11ee42989a268a1833c"}, + {file = "cffi-1.17.0-cp38-cp38-win32.whl", hash = "sha256:0bb15e7acf8ab35ca8b24b90af52c8b391690ef5c4aec3d31f38f0d37d2cc499"}, + {file = "cffi-1.17.0-cp38-cp38-win_amd64.whl", hash = "sha256:93a7350f6706b31f457c1457d3a3259ff9071a66f312ae64dc024f049055f72c"}, + {file = "cffi-1.17.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1a2ddbac59dc3716bc79f27906c010406155031a1c801410f1bafff17ea304d2"}, + {file = "cffi-1.17.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6327b572f5770293fc062a7ec04160e89741e8552bf1c358d1a23eba68166759"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbc183e7bef690c9abe5ea67b7b60fdbca81aa8da43468287dae7b5c046107d4"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5bdc0f1f610d067c70aa3737ed06e2726fd9d6f7bfee4a351f4c40b6831f4e82"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6d872186c1617d143969defeadac5a904e6e374183e07977eedef9c07c8953bf"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d46ee4764b88b91f16661a8befc6bfb24806d885e27436fdc292ed7e6f6d058"}, + {file = "cffi-1.17.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f76a90c345796c01d85e6332e81cab6d70de83b829cf1d9762d0a3da59c7932"}, + {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e60821d312f99d3e1569202518dddf10ae547e799d75aef3bca3a2d9e8ee693"}, + {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:eb09b82377233b902d4c3fbeeb7ad731cdab579c6c6fda1f763cd779139e47c3"}, + {file = "cffi-1.17.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:24658baf6224d8f280e827f0a50c46ad819ec8ba380a42448e24459daf809cf4"}, + {file = "cffi-1.17.0-cp39-cp39-win32.whl", hash = "sha256:0fdacad9e0d9fc23e519efd5ea24a70348305e8d7d85ecbb1a5fa66dc834e7fb"}, + {file = "cffi-1.17.0-cp39-cp39-win_amd64.whl", hash = "sha256:7cbc78dc018596315d4e7841c8c3a7ae31cc4d638c9b627f87d52e8abaaf2d29"}, + {file = "cffi-1.17.0.tar.gz", hash = "sha256:f3157624b7558b914cb039fd1af735e5e8049a87c817cc215109ad1c8779df76"}, ] [package.dependencies] @@ -577,33 +592,33 @@ develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest- [[package]] name = "debugpy" -version = "1.8.2" +version = "1.8.5" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" files = [ - {file = "debugpy-1.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:7ee2e1afbf44b138c005e4380097d92532e1001580853a7cb40ed84e0ef1c3d2"}, - {file = "debugpy-1.8.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3f8c3f7c53130a070f0fc845a0f2cee8ed88d220d6b04595897b66605df1edd6"}, - {file = "debugpy-1.8.2-cp310-cp310-win32.whl", hash = "sha256:f179af1e1bd4c88b0b9f0fa153569b24f6b6f3de33f94703336363ae62f4bf47"}, - {file = "debugpy-1.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:0600faef1d0b8d0e85c816b8bb0cb90ed94fc611f308d5fde28cb8b3d2ff0fe3"}, - {file = "debugpy-1.8.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:8a13417ccd5978a642e91fb79b871baded925d4fadd4dfafec1928196292aa0a"}, - {file = "debugpy-1.8.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acdf39855f65c48ac9667b2801234fc64d46778021efac2de7e50907ab90c634"}, - {file = "debugpy-1.8.2-cp311-cp311-win32.whl", hash = "sha256:2cbd4d9a2fc5e7f583ff9bf11f3b7d78dfda8401e8bb6856ad1ed190be4281ad"}, - {file = "debugpy-1.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:d3408fddd76414034c02880e891ea434e9a9cf3a69842098ef92f6e809d09afa"}, - {file = "debugpy-1.8.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:5d3ccd39e4021f2eb86b8d748a96c766058b39443c1f18b2dc52c10ac2757835"}, - {file = "debugpy-1.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62658aefe289598680193ff655ff3940e2a601765259b123dc7f89c0239b8cd3"}, - {file = "debugpy-1.8.2-cp312-cp312-win32.whl", hash = "sha256:bd11fe35d6fd3431f1546d94121322c0ac572e1bfb1f6be0e9b8655fb4ea941e"}, - {file = "debugpy-1.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:15bc2f4b0f5e99bf86c162c91a74c0631dbd9cef3c6a1d1329c946586255e859"}, - {file = "debugpy-1.8.2-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:5a019d4574afedc6ead1daa22736c530712465c0c4cd44f820d803d937531b2d"}, - {file = "debugpy-1.8.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40f062d6877d2e45b112c0bbade9a17aac507445fd638922b1a5434df34aed02"}, - {file = "debugpy-1.8.2-cp38-cp38-win32.whl", hash = "sha256:c78ba1680f1015c0ca7115671fe347b28b446081dada3fedf54138f44e4ba031"}, - {file = "debugpy-1.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:cf327316ae0c0e7dd81eb92d24ba8b5e88bb4d1b585b5c0d32929274a66a5210"}, - {file = "debugpy-1.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:1523bc551e28e15147815d1397afc150ac99dbd3a8e64641d53425dba57b0ff9"}, - {file = "debugpy-1.8.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e24ccb0cd6f8bfaec68d577cb49e9c680621c336f347479b3fce060ba7c09ec1"}, - {file = "debugpy-1.8.2-cp39-cp39-win32.whl", hash = "sha256:7f8d57a98c5a486c5c7824bc0b9f2f11189d08d73635c326abef268f83950326"}, - {file = "debugpy-1.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:16c8dcab02617b75697a0a925a62943e26a0330da076e2a10437edd9f0bf3755"}, - {file = "debugpy-1.8.2-py2.py3-none-any.whl", hash = "sha256:16e16df3a98a35c63c3ab1e4d19be4cbc7fdda92d9ddc059294f18910928e0ca"}, - {file = "debugpy-1.8.2.zip", hash = "sha256:95378ed08ed2089221896b9b3a8d021e642c24edc8fef20e5d4342ca8be65c00"}, + {file = "debugpy-1.8.5-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:7e4d594367d6407a120b76bdaa03886e9eb652c05ba7f87e37418426ad2079f7"}, + {file = "debugpy-1.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4413b7a3ede757dc33a273a17d685ea2b0c09dbd312cc03f5534a0fd4d40750a"}, + {file = "debugpy-1.8.5-cp310-cp310-win32.whl", hash = "sha256:dd3811bd63632bb25eda6bd73bea8e0521794cda02be41fa3160eb26fc29e7ed"}, + {file = "debugpy-1.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:b78c1250441ce893cb5035dd6f5fc12db968cc07f91cc06996b2087f7cefdd8e"}, + {file = "debugpy-1.8.5-cp311-cp311-macosx_12_0_universal2.whl", hash = "sha256:606bccba19f7188b6ea9579c8a4f5a5364ecd0bf5a0659c8a5d0e10dcee3032a"}, + {file = "debugpy-1.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db9fb642938a7a609a6c865c32ecd0d795d56c1aaa7a7a5722d77855d5e77f2b"}, + {file = "debugpy-1.8.5-cp311-cp311-win32.whl", hash = "sha256:4fbb3b39ae1aa3e5ad578f37a48a7a303dad9a3d018d369bc9ec629c1cfa7408"}, + {file = "debugpy-1.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:345d6a0206e81eb68b1493ce2fbffd57c3088e2ce4b46592077a943d2b968ca3"}, + {file = "debugpy-1.8.5-cp312-cp312-macosx_12_0_universal2.whl", hash = "sha256:5b5c770977c8ec6c40c60d6f58cacc7f7fe5a45960363d6974ddb9b62dbee156"}, + {file = "debugpy-1.8.5-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0a65b00b7cdd2ee0c2cf4c7335fef31e15f1b7056c7fdbce9e90193e1a8c8cb"}, + {file = "debugpy-1.8.5-cp312-cp312-win32.whl", hash = "sha256:c9f7c15ea1da18d2fcc2709e9f3d6de98b69a5b0fff1807fb80bc55f906691f7"}, + {file = "debugpy-1.8.5-cp312-cp312-win_amd64.whl", hash = "sha256:28ced650c974aaf179231668a293ecd5c63c0a671ae6d56b8795ecc5d2f48d3c"}, + {file = "debugpy-1.8.5-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:3df6692351172a42af7558daa5019651f898fc67450bf091335aa8a18fbf6f3a"}, + {file = "debugpy-1.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1cd04a73eb2769eb0bfe43f5bfde1215c5923d6924b9b90f94d15f207a402226"}, + {file = "debugpy-1.8.5-cp38-cp38-win32.whl", hash = "sha256:8f913ee8e9fcf9d38a751f56e6de12a297ae7832749d35de26d960f14280750a"}, + {file = "debugpy-1.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:a697beca97dad3780b89a7fb525d5e79f33821a8bc0c06faf1f1289e549743cf"}, + {file = "debugpy-1.8.5-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:0a1029a2869d01cb777216af8c53cda0476875ef02a2b6ff8b2f2c9a4b04176c"}, + {file = "debugpy-1.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e84c276489e141ed0b93b0af648eef891546143d6a48f610945416453a8ad406"}, + {file = "debugpy-1.8.5-cp39-cp39-win32.whl", hash = "sha256:ad84b7cde7fd96cf6eea34ff6c4a1b7887e0fe2ea46e099e53234856f9d99a34"}, + {file = "debugpy-1.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:7b0fe36ed9d26cb6836b0a51453653f8f2e347ba7348f2bbfe76bfeb670bfb1c"}, + {file = "debugpy-1.8.5-py2.py3-none-any.whl", hash = "sha256:55919dce65b471eff25901acf82d328bbd5b833526b6c1364bd5133754777a44"}, + {file = "debugpy-1.8.5.zip", hash = "sha256:b2112cfeb34b4507399d298fe7023a16656fc553ed5246536060ca7bd0e668d0"}, ] [[package]] @@ -971,6 +986,76 @@ MarkupSafe = ">=2.0" [package.extras] i18n = ["Babel (>=2.7)"] +[[package]] +name = "jiter" +version = "0.5.0" +description = "Fast iterable JSON parser." +optional = false +python-versions = ">=3.8" +files = [ + {file = "jiter-0.5.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:b599f4e89b3def9a94091e6ee52e1d7ad7bc33e238ebb9c4c63f211d74822c3f"}, + {file = "jiter-0.5.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a063f71c4b06225543dddadbe09d203dc0c95ba352d8b85f1221173480a71d5"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acc0d5b8b3dd12e91dd184b87273f864b363dfabc90ef29a1092d269f18c7e28"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c22541f0b672f4d741382a97c65609332a783501551445ab2df137ada01e019e"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:63314832e302cc10d8dfbda0333a384bf4bcfce80d65fe99b0f3c0da8945a91a"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a25fbd8a5a58061e433d6fae6d5298777c0814a8bcefa1e5ecfff20c594bd749"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:503b2c27d87dfff5ab717a8200fbbcf4714516c9d85558048b1fc14d2de7d8dc"}, + {file = "jiter-0.5.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6d1f3d27cce923713933a844872d213d244e09b53ec99b7a7fdf73d543529d6d"}, + {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:c95980207b3998f2c3b3098f357994d3fd7661121f30669ca7cb945f09510a87"}, + {file = "jiter-0.5.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:afa66939d834b0ce063f57d9895e8036ffc41c4bd90e4a99631e5f261d9b518e"}, + {file = "jiter-0.5.0-cp310-none-win32.whl", hash = "sha256:f16ca8f10e62f25fd81d5310e852df6649af17824146ca74647a018424ddeccf"}, + {file = "jiter-0.5.0-cp310-none-win_amd64.whl", hash = "sha256:b2950e4798e82dd9176935ef6a55cf6a448b5c71515a556da3f6b811a7844f1e"}, + {file = "jiter-0.5.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4c8e1ed0ef31ad29cae5ea16b9e41529eb50a7fba70600008e9f8de6376d553"}, + {file = "jiter-0.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6f16e21276074a12d8421692515b3fd6d2ea9c94fd0734c39a12960a20e85f3"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5280e68e7740c8c128d3ae5ab63335ce6d1fb6603d3b809637b11713487af9e6"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:583c57fc30cc1fec360e66323aadd7fc3edeec01289bfafc35d3b9dcb29495e4"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26351cc14507bdf466b5f99aba3df3143a59da75799bf64a53a3ad3155ecded9"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829df14d656b3fb87e50ae8b48253a8851c707da9f30d45aacab2aa2ba2d614"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a42a4bdcf7307b86cb863b2fb9bb55029b422d8f86276a50487982d99eed7c6e"}, + {file = "jiter-0.5.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04d461ad0aebf696f8da13c99bc1b3e06f66ecf6cfd56254cc402f6385231c06"}, + {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e6375923c5f19888c9226582a124b77b622f8fd0018b843c45eeb19d9701c403"}, + {file = "jiter-0.5.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:2cec323a853c24fd0472517113768c92ae0be8f8c384ef4441d3632da8baa646"}, + {file = "jiter-0.5.0-cp311-none-win32.whl", hash = "sha256:aa1db0967130b5cab63dfe4d6ff547c88b2a394c3410db64744d491df7f069bb"}, + {file = "jiter-0.5.0-cp311-none-win_amd64.whl", hash = "sha256:aa9d2b85b2ed7dc7697597dcfaac66e63c1b3028652f751c81c65a9f220899ae"}, + {file = "jiter-0.5.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:9f664e7351604f91dcdd557603c57fc0d551bc65cc0a732fdacbf73ad335049a"}, + {file = "jiter-0.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:044f2f1148b5248ad2c8c3afb43430dccf676c5a5834d2f5089a4e6c5bbd64df"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:702e3520384c88b6e270c55c772d4bd6d7b150608dcc94dea87ceba1b6391248"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:528d742dcde73fad9d63e8242c036ab4a84389a56e04efd854062b660f559544"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8cf80e5fe6ab582c82f0c3331df27a7e1565e2dcf06265afd5173d809cdbf9ba"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:44dfc9ddfb9b51a5626568ef4e55ada462b7328996294fe4d36de02fce42721f"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c451f7922992751a936b96c5f5b9bb9312243d9b754c34b33d0cb72c84669f4e"}, + {file = "jiter-0.5.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:308fce789a2f093dca1ff91ac391f11a9f99c35369117ad5a5c6c4903e1b3e3a"}, + {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7f5ad4a7c6b0d90776fdefa294f662e8a86871e601309643de30bf94bb93a64e"}, + {file = "jiter-0.5.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ea189db75f8eca08807d02ae27929e890c7d47599ce3d0a6a5d41f2419ecf338"}, + {file = "jiter-0.5.0-cp312-none-win32.whl", hash = "sha256:e3bbe3910c724b877846186c25fe3c802e105a2c1fc2b57d6688b9f8772026e4"}, + {file = "jiter-0.5.0-cp312-none-win_amd64.whl", hash = "sha256:a586832f70c3f1481732919215f36d41c59ca080fa27a65cf23d9490e75b2ef5"}, + {file = "jiter-0.5.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f04bc2fc50dc77be9d10f73fcc4e39346402ffe21726ff41028f36e179b587e6"}, + {file = "jiter-0.5.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6f433a4169ad22fcb550b11179bb2b4fd405de9b982601914ef448390b2954f3"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad4a6398c85d3a20067e6c69890ca01f68659da94d74c800298581724e426c7e"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6baa88334e7af3f4d7a5c66c3a63808e5efbc3698a1c57626541ddd22f8e4fbf"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ece0a115c05efca597c6d938f88c9357c843f8c245dbbb53361a1c01afd7148"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:335942557162ad372cc367ffaf93217117401bf930483b4b3ebdb1223dbddfa7"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:649b0ee97a6e6da174bffcb3c8c051a5935d7d4f2f52ea1583b5b3e7822fbf14"}, + {file = "jiter-0.5.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f4be354c5de82157886ca7f5925dbda369b77344b4b4adf2723079715f823989"}, + {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5206144578831a6de278a38896864ded4ed96af66e1e63ec5dd7f4a1fce38a3a"}, + {file = "jiter-0.5.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8120c60f8121ac3d6f072b97ef0e71770cc72b3c23084c72c4189428b1b1d3b6"}, + {file = "jiter-0.5.0-cp38-none-win32.whl", hash = "sha256:6f1223f88b6d76b519cb033a4d3687ca157c272ec5d6015c322fc5b3074d8a5e"}, + {file = "jiter-0.5.0-cp38-none-win_amd64.whl", hash = "sha256:c59614b225d9f434ea8fc0d0bec51ef5fa8c83679afedc0433905994fb36d631"}, + {file = "jiter-0.5.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:0af3838cfb7e6afee3f00dc66fa24695199e20ba87df26e942820345b0afc566"}, + {file = "jiter-0.5.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:550b11d669600dbc342364fd4adbe987f14d0bbedaf06feb1b983383dcc4b961"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:489875bf1a0ffb3cb38a727b01e6673f0f2e395b2aad3c9387f94187cb214bbf"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b250ca2594f5599ca82ba7e68785a669b352156260c5362ea1b4e04a0f3e2389"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ea18e01f785c6667ca15407cd6dabbe029d77474d53595a189bdc813347218e"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:462a52be85b53cd9bffd94e2d788a09984274fe6cebb893d6287e1c296d50653"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92cc68b48d50fa472c79c93965e19bd48f40f207cb557a8346daa020d6ba973b"}, + {file = "jiter-0.5.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1c834133e59a8521bc87ebcad773608c6fa6ab5c7a022df24a45030826cf10bc"}, + {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab3a71ff31cf2d45cb216dc37af522d335211f3a972d2fe14ea99073de6cb104"}, + {file = "jiter-0.5.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cccd3af9c48ac500c95e1bcbc498020c87e1781ff0345dd371462d67b76643eb"}, + {file = "jiter-0.5.0-cp39-none-win32.whl", hash = "sha256:368084d8d5c4fc40ff7c3cc513c4f73e02c85f6009217922d0823a48ee7adf61"}, + {file = "jiter-0.5.0-cp39-none-win_amd64.whl", hash = "sha256:ce03f7b4129eb72f1687fa11300fbf677b02990618428934662406d2a76742a1"}, + {file = "jiter-0.5.0.tar.gz", hash = "sha256:1d916ba875bcab5c5f7d927df998c4cb694d27dceddf3392e58beaf10563368a"}, +] + [[package]] name = "json5" version = "0.9.25" @@ -1666,23 +1751,24 @@ files = [ [[package]] name = "openai" -version = "1.39.0" +version = "1.40.2" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.39.0-py3-none-any.whl", hash = "sha256:a712553a131c59a249c474d0bb6a0414f41df36dc186d3a018fa7e600e57fb7f"}, - {file = "openai-1.39.0.tar.gz", hash = "sha256:0cea446082f50985f26809d704a97749cb366a1ba230ef432c684a9745b3f2d9"}, + {file = "openai-1.40.2-py3-none-any.whl", hash = "sha256:38068f858f310b4fd4b0ea8734c3efcfde3c15a2978311e1453bd84817231b96"}, + {file = "openai-1.40.2.tar.gz", hash = "sha256:2180e9070bd36084328248b3ce668964e8ddd2e9019e1d426e31dc54cc117bb5"}, ] [package.dependencies] anyio = ">=3.5.0,<5" distro = ">=1.7.0,<2" httpx = ">=0.23.0,<1" +jiter = ">=0.4.0,<1" pydantic = ">=1.9.0,<3" sniffio = "*" tqdm = ">4" -typing-extensions = ">=4.7,<5" +typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] @@ -2223,13 +2309,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyright" -version = "1.1.374" +version = "1.1.375" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.374-py3-none-any.whl", hash = "sha256:55752bcf7a3646d293cd76710a983b71e16f6128aab2d42468e6eb7e46c0a70d"}, - {file = "pyright-1.1.374.tar.gz", hash = "sha256:d01b2daf864ba5e0362e56b844984865970d7204158e61eb685e2dab7804cb82"}, + {file = "pyright-1.1.375-py3-none-any.whl", hash = "sha256:4c5e27eddeaee8b41cc3120736a1dda6ae120edf8523bb2446b6073a52f286e3"}, + {file = "pyright-1.1.375.tar.gz", hash = "sha256:7765557b0d6782b2fadabff455da2014476404c9e9214f49977a4e49dec19a0f"}, ] [package.dependencies] @@ -2363,61 +2449,64 @@ files = [ [[package]] name = "pyyaml" -version = "6.0.1" +version = "6.0.2" description = "YAML parser and emitter for Python" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {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_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"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086"}, + {file = "PyYAML-6.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b"}, + {file = "PyYAML-6.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180"}, + {file = "PyYAML-6.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68"}, + {file = "PyYAML-6.0.2-cp310-cp310-win32.whl", hash = "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99"}, + {file = "PyYAML-6.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774"}, + {file = "PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317"}, + {file = "PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4"}, + {file = "PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e"}, + {file = "PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5"}, + {file = "PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab"}, + {file = "PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425"}, + {file = "PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48"}, + {file = "PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b"}, + {file = "PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4"}, + {file = "PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba"}, + {file = "PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484"}, + {file = "PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc"}, + {file = "PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652"}, + {file = "PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183"}, + {file = "PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563"}, + {file = "PyYAML-6.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d"}, + {file = "PyYAML-6.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083"}, + {file = "PyYAML-6.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706"}, + {file = "PyYAML-6.0.2-cp38-cp38-win32.whl", hash = "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a"}, + {file = "PyYAML-6.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d"}, + {file = "PyYAML-6.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12"}, + {file = "PyYAML-6.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e"}, + {file = "PyYAML-6.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725"}, + {file = "PyYAML-6.0.2-cp39-cp39-win32.whl", hash = "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631"}, + {file = "PyYAML-6.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8"}, + {file = "pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e"}, ] [[package]] @@ -2622,114 +2711,114 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "rpds-py" -version = "0.19.1" +version = "0.20.0" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.8" files = [ - {file = "rpds_py-0.19.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:aaf71f95b21f9dc708123335df22e5a2fef6307e3e6f9ed773b2e0938cc4d491"}, - {file = "rpds_py-0.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ca0dda0c5715efe2ab35bb83f813f681ebcd2840d8b1b92bfc6fe3ab382fae4a"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81db2e7282cc0487f500d4db203edc57da81acde9e35f061d69ed983228ffe3b"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1a8dfa125b60ec00c7c9baef945bb04abf8ac772d8ebefd79dae2a5f316d7850"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:271accf41b02687cef26367c775ab220372ee0f4925591c6796e7c148c50cab5"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9bc4161bd3b970cd6a6fcda70583ad4afd10f2750609fb1f3ca9505050d4ef3"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0cf2a0dbb5987da4bd92a7ca727eadb225581dd9681365beba9accbe5308f7d"}, - {file = "rpds_py-0.19.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b5e28e56143750808c1c79c70a16519e9bc0a68b623197b96292b21b62d6055c"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:c7af6f7b80f687b33a4cdb0a785a5d4de1fb027a44c9a049d8eb67d5bfe8a687"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e429fc517a1c5e2a70d576077231538a98d59a45dfc552d1ac45a132844e6dfb"}, - {file = "rpds_py-0.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d2dbd8f4990d4788cb122f63bf000357533f34860d269c1a8e90ae362090ff3a"}, - {file = "rpds_py-0.19.1-cp310-none-win32.whl", hash = "sha256:e0f9d268b19e8f61bf42a1da48276bcd05f7ab5560311f541d22557f8227b866"}, - {file = "rpds_py-0.19.1-cp310-none-win_amd64.whl", hash = "sha256:df7c841813f6265e636fe548a49664c77af31ddfa0085515326342a751a6ba51"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:902cf4739458852fe917104365ec0efbea7d29a15e4276c96a8d33e6ed8ec137"}, - {file = "rpds_py-0.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f3d73022990ab0c8b172cce57c69fd9a89c24fd473a5e79cbce92df87e3d9c48"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3837c63dd6918a24de6c526277910e3766d8c2b1627c500b155f3eecad8fad65"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cdb7eb3cf3deb3dd9e7b8749323b5d970052711f9e1e9f36364163627f96da58"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:26ab43b6d65d25b1a333c8d1b1c2f8399385ff683a35ab5e274ba7b8bb7dc61c"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75130df05aae7a7ac171b3b5b24714cffeabd054ad2ebc18870b3aa4526eba23"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c34f751bf67cab69638564eee34023909380ba3e0d8ee7f6fe473079bf93f09b"}, - {file = "rpds_py-0.19.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f2671cb47e50a97f419a02cd1e0c339b31de017b033186358db92f4d8e2e17d8"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3c73254c256081704dba0a333457e2fb815364018788f9b501efe7c5e0ada401"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4383beb4a29935b8fa28aca8fa84c956bf545cb0c46307b091b8d312a9150e6a"}, - {file = "rpds_py-0.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:dbceedcf4a9329cc665452db1aaf0845b85c666e4885b92ee0cddb1dbf7e052a"}, - {file = "rpds_py-0.19.1-cp311-none-win32.whl", hash = "sha256:f0a6d4a93d2a05daec7cb885157c97bbb0be4da739d6f9dfb02e101eb40921cd"}, - {file = "rpds_py-0.19.1-cp311-none-win_amd64.whl", hash = "sha256:c149a652aeac4902ecff2dd93c3b2681c608bd5208c793c4a99404b3e1afc87c"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:56313be667a837ff1ea3508cebb1ef6681d418fa2913a0635386cf29cff35165"}, - {file = "rpds_py-0.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6d1d7539043b2b31307f2c6c72957a97c839a88b2629a348ebabe5aa8b626d6b"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e1dc59a5e7bc7f44bd0c048681f5e05356e479c50be4f2c1a7089103f1621d5"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b8f78398e67a7227aefa95f876481485403eb974b29e9dc38b307bb6eb2315ea"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ef07a0a1d254eeb16455d839cef6e8c2ed127f47f014bbda64a58b5482b6c836"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8124101e92c56827bebef084ff106e8ea11c743256149a95b9fd860d3a4f331f"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:08ce9c95a0b093b7aec75676b356a27879901488abc27e9d029273d280438505"}, - {file = "rpds_py-0.19.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0b02dd77a2de6e49078c8937aadabe933ceac04b41c5dde5eca13a69f3cf144e"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4dd02e29c8cbed21a1875330b07246b71121a1c08e29f0ee3db5b4cfe16980c4"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:9c7042488165f7251dc7894cd533a875d2875af6d3b0e09eda9c4b334627ad1c"}, - {file = "rpds_py-0.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f809a17cc78bd331e137caa25262b507225854073fd319e987bd216bed911b7c"}, - {file = "rpds_py-0.19.1-cp312-none-win32.whl", hash = "sha256:3ddab996807c6b4227967fe1587febade4e48ac47bb0e2d3e7858bc621b1cace"}, - {file = "rpds_py-0.19.1-cp312-none-win_amd64.whl", hash = "sha256:32e0db3d6e4f45601b58e4ac75c6f24afbf99818c647cc2066f3e4b192dabb1f"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:747251e428406b05fc86fee3904ee19550c4d2d19258cef274e2151f31ae9d38"}, - {file = "rpds_py-0.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:dc733d35f861f8d78abfaf54035461e10423422999b360966bf1c443cbc42705"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbda75f245caecff8faa7e32ee94dfaa8312a3367397975527f29654cd17a6ed"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd04d8cab16cab5b0a9ffc7d10f0779cf1120ab16c3925404428f74a0a43205a"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e2d66eb41ffca6cc3c91d8387509d27ba73ad28371ef90255c50cb51f8953301"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fdf4890cda3b59170009d012fca3294c00140e7f2abe1910e6a730809d0f3f9b"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1fa67ef839bad3815124f5f57e48cd50ff392f4911a9f3cf449d66fa3df62a5"}, - {file = "rpds_py-0.19.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b82c9514c6d74b89a370c4060bdb80d2299bc6857e462e4a215b4ef7aa7b090e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c7b07959866a6afb019abb9564d8a55046feb7a84506c74a6f197cbcdf8a208e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4f580ae79d0b861dfd912494ab9d477bea535bfb4756a2269130b6607a21802e"}, - {file = "rpds_py-0.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c6d20c8896c00775e6f62d8373aba32956aa0b850d02b5ec493f486c88e12859"}, - {file = "rpds_py-0.19.1-cp313-none-win32.whl", hash = "sha256:afedc35fe4b9e30ab240b208bb9dc8938cb4afe9187589e8d8d085e1aacb8309"}, - {file = "rpds_py-0.19.1-cp313-none-win_amd64.whl", hash = "sha256:1d4af2eb520d759f48f1073ad3caef997d1bfd910dc34e41261a595d3f038a94"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:34bca66e2e3eabc8a19e9afe0d3e77789733c702c7c43cd008e953d5d1463fde"}, - {file = "rpds_py-0.19.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:24f8ae92c7fae7c28d0fae9b52829235df83f34847aa8160a47eb229d9666c7b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71157f9db7f6bc6599a852852f3389343bea34315b4e6f109e5cbc97c1fb2963"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1d494887d40dc4dd0d5a71e9d07324e5c09c4383d93942d391727e7a40ff810b"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7b3661e6d4ba63a094138032c1356d557de5b3ea6fd3cca62a195f623e381c76"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97fbb77eaeb97591efdc654b8b5f3ccc066406ccfb3175b41382f221ecc216e8"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cc4bc73e53af8e7a42c8fd7923bbe35babacfa7394ae9240b3430b5dcf16b2a"}, - {file = "rpds_py-0.19.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:35af5e4d5448fa179fd7fff0bba0fba51f876cd55212f96c8bbcecc5c684ae5c"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3511f6baf8438326e351097cecd137eb45c5f019944fe0fd0ae2fea2fd26be39"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:57863d16187995c10fe9cf911b897ed443ac68189179541734502353af33e693"}, - {file = "rpds_py-0.19.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:9e318e6786b1e750a62f90c6f7fa8b542102bdcf97c7c4de2a48b50b61bd36ec"}, - {file = "rpds_py-0.19.1-cp38-none-win32.whl", hash = "sha256:53dbc35808c6faa2ce3e48571f8f74ef70802218554884787b86a30947842a14"}, - {file = "rpds_py-0.19.1-cp38-none-win_amd64.whl", hash = "sha256:8df1c283e57c9cb4d271fdc1875f4a58a143a2d1698eb0d6b7c0d7d5f49c53a1"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:e76c902d229a3aa9d5ceb813e1cbcc69bf5bda44c80d574ff1ac1fa3136dea71"}, - {file = "rpds_py-0.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:de1f7cd5b6b351e1afd7568bdab94934d656abe273d66cda0ceea43bbc02a0c2"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24fc5a84777cb61692d17988989690d6f34f7f95968ac81398d67c0d0994a897"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:74129d5ffc4cde992d89d345f7f7d6758320e5d44a369d74d83493429dad2de5"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e360188b72f8080fefa3adfdcf3618604cc8173651c9754f189fece068d2a45"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13e6d4840897d4e4e6b2aa1443e3a8eca92b0402182aafc5f4ca1f5e24f9270a"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f09529d2332264a902688031a83c19de8fda5eb5881e44233286b9c9ec91856d"}, - {file = "rpds_py-0.19.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0d4b52811dcbc1aba08fd88d475f75b4f6db0984ba12275d9bed1a04b2cae9b5"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dd635c2c4043222d80d80ca1ac4530a633102a9f2ad12252183bcf338c1b9474"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:f35b34a5184d5e0cc360b61664c1c06e866aab077b5a7c538a3e20c8fcdbf90b"}, - {file = "rpds_py-0.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d4ec0046facab83012d821b33cead742a35b54575c4edfb7ed7445f63441835f"}, - {file = "rpds_py-0.19.1-cp39-none-win32.whl", hash = "sha256:f5b8353ea1a4d7dfb59a7f45c04df66ecfd363bb5b35f33b11ea579111d4655f"}, - {file = "rpds_py-0.19.1-cp39-none-win_amd64.whl", hash = "sha256:1fb93d3486f793d54a094e2bfd9cd97031f63fcb5bc18faeb3dd4b49a1c06523"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7d5c7e32f3ee42f77d8ff1a10384b5cdcc2d37035e2e3320ded909aa192d32c3"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:89cc8921a4a5028d6dd388c399fcd2eef232e7040345af3d5b16c04b91cf3c7e"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bca34e913d27401bda2a6f390d0614049f5a95b3b11cd8eff80fe4ec340a1208"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5953391af1405f968eb5701ebbb577ebc5ced8d0041406f9052638bafe52209d"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:840e18c38098221ea6201f091fc5d4de6128961d2930fbbc96806fb43f69aec1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6d8b735c4d162dc7d86a9cf3d717f14b6c73637a1f9cd57fe7e61002d9cb1972"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce757c7c90d35719b38fa3d4ca55654a76a40716ee299b0865f2de21c146801c"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a9421b23c85f361a133aa7c5e8ec757668f70343f4ed8fdb5a4a14abd5437244"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3b823be829407393d84ee56dc849dbe3b31b6a326f388e171555b262e8456cc1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:5e58b61dcbb483a442c6239c3836696b79f2cd8e7eec11e12155d3f6f2d886d1"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39d67896f7235b2c886fb1ee77b1491b77049dcef6fbf0f401e7b4cbed86bbd4"}, - {file = "rpds_py-0.19.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8b32cd4ab6db50c875001ba4f5a6b30c0f42151aa1fbf9c2e7e3674893fb1dc4"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1c32e41de995f39b6b315d66c27dea3ef7f7c937c06caab4c6a79a5e09e2c415"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1a129c02b42d46758c87faeea21a9f574e1c858b9f358b6dd0bbd71d17713175"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:346557f5b1d8fd9966059b7a748fd79ac59f5752cd0e9498d6a40e3ac1c1875f"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:31e450840f2f27699d014cfc8865cc747184286b26d945bcea6042bb6aa4d26e"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:01227f8b3e6c8961490d869aa65c99653df80d2f0a7fde8c64ebddab2b9b02fd"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:69084fd29bfeff14816666c93a466e85414fe6b7d236cfc108a9c11afa6f7301"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4d2b88efe65544a7d5121b0c3b003ebba92bfede2ea3577ce548b69c5235185"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ea961a674172ed2235d990d7edf85d15d8dfa23ab8575e48306371c070cda67"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:5beffdbe766cfe4fb04f30644d822a1080b5359df7db3a63d30fa928375b2720"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:720f3108fb1bfa32e51db58b832898372eb5891e8472a8093008010911e324c5"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c2087dbb76a87ec2c619253e021e4fb20d1a72580feeaa6892b0b3d955175a71"}, - {file = "rpds_py-0.19.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2ddd50f18ebc05ec29a0d9271e9dbe93997536da3546677f8ca00b76d477680c"}, - {file = "rpds_py-0.19.1.tar.gz", hash = "sha256:31dd5794837f00b46f4096aa8ccaa5972f73a938982e32ed817bb520c465e520"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2"}, + {file = "rpds_py-0.20.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf"}, + {file = "rpds_py-0.20.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce"}, + {file = "rpds_py-0.20.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94"}, + {file = "rpds_py-0.20.0-cp310-none-win32.whl", hash = "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee"}, + {file = "rpds_py-0.20.0-cp310-none-win_amd64.whl", hash = "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489"}, + {file = "rpds_py-0.20.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209"}, + {file = "rpds_py-0.20.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad"}, + {file = "rpds_py-0.20.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58"}, + {file = "rpds_py-0.20.0-cp311-none-win32.whl", hash = "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0"}, + {file = "rpds_py-0.20.0-cp311-none-win_amd64.whl", hash = "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6"}, + {file = "rpds_py-0.20.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4"}, + {file = "rpds_py-0.20.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940"}, + {file = "rpds_py-0.20.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174"}, + {file = "rpds_py-0.20.0-cp312-none-win32.whl", hash = "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139"}, + {file = "rpds_py-0.20.0-cp312-none-win_amd64.whl", hash = "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29"}, + {file = "rpds_py-0.20.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879"}, + {file = "rpds_py-0.20.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2"}, + {file = "rpds_py-0.20.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57"}, + {file = "rpds_py-0.20.0-cp313-none-win32.whl", hash = "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a"}, + {file = "rpds_py-0.20.0-cp313-none-win_amd64.whl", hash = "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24"}, + {file = "rpds_py-0.20.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751"}, + {file = "rpds_py-0.20.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253"}, + {file = "rpds_py-0.20.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a"}, + {file = "rpds_py-0.20.0-cp38-none-win32.whl", hash = "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5"}, + {file = "rpds_py-0.20.0-cp38-none-win_amd64.whl", hash = "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22"}, + {file = "rpds_py-0.20.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda"}, + {file = "rpds_py-0.20.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420"}, + {file = "rpds_py-0.20.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b"}, + {file = "rpds_py-0.20.0-cp39-none-win32.whl", hash = "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7"}, + {file = "rpds_py-0.20.0-cp39-none-win_amd64.whl", hash = "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344"}, + {file = "rpds_py-0.20.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec"}, + {file = "rpds_py-0.20.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8"}, + {file = "rpds_py-0.20.0.tar.gz", hash = "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121"}, ] [[package]] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index b38e497f5..7cf1fe390 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -110,7 +110,6 @@ export type { Tools_ChosenFunctionCall } from "./models/Tools_ChosenFunctionCall export type { Tools_ChosenToolCall } from "./models/Tools_ChosenToolCall"; export type { Tools_FunctionCallOption } from "./models/Tools_FunctionCallOption"; export type { Tools_FunctionDef } from "./models/Tools_FunctionDef"; -export type { Tools_FunctionDefUpdate } from "./models/Tools_FunctionDefUpdate"; export type { Tools_FunctionTool } from "./models/Tools_FunctionTool"; export type { Tools_NamedFunctionChoice } from "./models/Tools_NamedFunctionChoice"; export type { Tools_NamedToolChoice } from "./models/Tools_NamedToolChoice"; @@ -225,7 +224,6 @@ export { $Tools_ChosenFunctionCall } from "./schemas/$Tools_ChosenFunctionCall"; export { $Tools_ChosenToolCall } from "./schemas/$Tools_ChosenToolCall"; export { $Tools_FunctionCallOption } from "./schemas/$Tools_FunctionCallOption"; export { $Tools_FunctionDef } from "./schemas/$Tools_FunctionDef"; -export { $Tools_FunctionDefUpdate } from "./schemas/$Tools_FunctionDefUpdate"; export { $Tools_FunctionTool } from "./schemas/$Tools_FunctionTool"; export { $Tools_NamedFunctionChoice } from "./schemas/$Tools_NamedFunctionChoice"; export { $Tools_NamedToolChoice } from "./schemas/$Tools_NamedToolChoice"; diff --git a/sdks/ts/src/api/models/Tools_FunctionDef.ts b/sdks/ts/src/api/models/Tools_FunctionDef.ts index 777aa3155..8e57315a3 100644 --- a/sdks/ts/src/api/models/Tools_FunctionDef.ts +++ b/sdks/ts/src/api/models/Tools_FunctionDef.ts @@ -3,7 +3,6 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; -import type { Common_validPythonIdentifier } from "./Common_validPythonIdentifier"; /** * Function definition */ @@ -11,7 +10,7 @@ export type Tools_FunctionDef = { /** * DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. */ - name?: Common_validPythonIdentifier; + name?: any; /** * Description of the function */ @@ -19,5 +18,5 @@ export type Tools_FunctionDef = { /** * The parameters the function accepts */ - parameters: Record; + parameters?: Record; }; diff --git a/sdks/ts/src/api/models/Tools_FunctionDefUpdate.ts b/sdks/ts/src/api/models/Tools_FunctionDefUpdate.ts deleted file mode 100644 index 7f4417abd..000000000 --- a/sdks/ts/src/api/models/Tools_FunctionDefUpdate.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; -import type { Common_validPythonIdentifier } from "./Common_validPythonIdentifier"; -/** - * Function definition - */ -export type Tools_FunctionDefUpdate = { - /** - * DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. - */ - name?: Common_validPythonIdentifier; - /** - * Description of the function - */ - description?: Common_identifierSafeUnicode; - /** - * The parameters the function accepts - */ - parameters?: Record; -}; diff --git a/sdks/ts/src/api/models/Tools_PatchToolRequest.ts b/sdks/ts/src/api/models/Tools_PatchToolRequest.ts index 0957c8db2..6ab88a01f 100644 --- a/sdks/ts/src/api/models/Tools_PatchToolRequest.ts +++ b/sdks/ts/src/api/models/Tools_PatchToolRequest.ts @@ -3,7 +3,7 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_validPythonIdentifier } from "./Common_validPythonIdentifier"; -import type { Tools_FunctionDefUpdate } from "./Tools_FunctionDefUpdate"; +import type { Tools_FunctionDef } from "./Tools_FunctionDef"; import type { Tools_ToolType } from "./Tools_ToolType"; /** * Payload for patching a tool @@ -17,7 +17,7 @@ export type Tools_PatchToolRequest = { * Name of the tool (must be unique for this agent and a valid python identifier string ) */ name?: Common_validPythonIdentifier; - function?: Tools_FunctionDefUpdate; + function?: Tools_FunctionDef; integration?: any; system?: any; api_call?: any; diff --git a/sdks/ts/src/api/schemas/$Tools_FunctionDef.ts b/sdks/ts/src/api/schemas/$Tools_FunctionDef.ts index 0d9e5f2b3..57fba7cdc 100644 --- a/sdks/ts/src/api/schemas/$Tools_FunctionDef.ts +++ b/sdks/ts/src/api/schemas/$Tools_FunctionDef.ts @@ -6,13 +6,9 @@ export const $Tools_FunctionDef = { description: `Function definition`, properties: { name: { - type: "all-of", description: `DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons.`, - contains: [ - { - type: "Common_validPythonIdentifier", - }, - ], + properties: {}, + isNullable: true, }, description: { type: "all-of", @@ -28,7 +24,6 @@ export const $Tools_FunctionDef = { contains: { properties: {}, }, - isRequired: true, }, }, } as const; diff --git a/sdks/ts/src/api/schemas/$Tools_FunctionDefUpdate.ts b/sdks/ts/src/api/schemas/$Tools_FunctionDefUpdate.ts deleted file mode 100644 index c79a3478e..000000000 --- a/sdks/ts/src/api/schemas/$Tools_FunctionDefUpdate.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tools_FunctionDefUpdate = { - description: `Function definition`, - properties: { - name: { - type: "all-of", - description: `DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons.`, - contains: [ - { - type: "Common_validPythonIdentifier", - }, - ], - }, - description: { - type: "all-of", - description: `Description of the function`, - contains: [ - { - type: "Common_identifierSafeUnicode", - }, - ], - }, - parameters: { - type: "dictionary", - contains: { - properties: {}, - }, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tools_PatchToolRequest.ts b/sdks/ts/src/api/schemas/$Tools_PatchToolRequest.ts index 4170be793..316eccd14 100644 --- a/sdks/ts/src/api/schemas/$Tools_PatchToolRequest.ts +++ b/sdks/ts/src/api/schemas/$Tools_PatchToolRequest.ts @@ -24,7 +24,7 @@ export const $Tools_PatchToolRequest = { ], }, function: { - type: "Tools_FunctionDefUpdate", + type: "Tools_FunctionDef", }, integration: { properties: {}, diff --git a/typespec/tools/models.tsp b/typespec/tools/models.tsp index 509520304..1938611c9 100644 --- a/typespec/tools/models.tsp +++ b/typespec/tools/models.tsp @@ -32,13 +32,13 @@ alias ToolChoiceOption = "auto" | "none" | NamedToolChoice; /** Function definition */ model FunctionDef { /** DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. */ - name?: validPythonIdentifier = "overriden"; + name?: null = null; /** Description of the function */ description?: identifierSafeUnicode; /** The parameters the function accepts */ - parameters: FunctionParameters; + parameters?: FunctionParameters; } From afe3be8f37c79b5a64a785e9cf521696f02c16a2 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 9 Aug 2024 18:44:51 -0400 Subject: [PATCH 020/110] feat(agents-api): Add route tests Signed-off-by: Diwank Tomer --- agents-api/agents_api/clients/cozo.py | 9 +- agents-api/agents_api/env.py | 9 +- agents-api/agents_api/models/utils.py | 8 +- .../agents_api/routers/agents/create_agent.py | 3 +- agents-api/tests/fixtures.py | 47 +- agents-api/tests/test_agent_routes.py | 209 +++++++++ agents-api/tests/test_agents.py | 433 ------------------ agents-api/tests/test_sessions.py | 307 ------------- agents-api/tests/test_tasks.py | 159 ------- agents-api/tests/test_users.py | 191 -------- 10 files changed, 256 insertions(+), 1119 deletions(-) create mode 100644 agents-api/tests/test_agent_routes.py delete mode 100644 agents-api/tests/test_agents.py delete mode 100644 agents-api/tests/test_sessions.py delete mode 100644 agents-api/tests/test_tasks.py delete mode 100644 agents-api/tests/test_users.py diff --git a/agents-api/agents_api/clients/cozo.py b/agents-api/agents_api/clients/cozo.py index c9fafe710..d582e5db4 100644 --- a/agents-api/agents_api/clients/cozo.py +++ b/agents-api/agents_api/clients/cozo.py @@ -1,9 +1,16 @@ from pycozo.client import Client from ..env import cozo_auth, cozo_host +from ..web import app options = {"host": cozo_host} if cozo_auth: options.update({"auth": cozo_auth}) -client = Client("http", options=options) + +def get_cozo_client(): + client = getattr(app.state, "cozo_client", Client("http", options=options)) + if not hasattr(app.state, "cozo_client"): + app.state.cozo_client = client + + return client diff --git a/agents-api/agents_api/env.py b/agents-api/agents_api/env.py index 7fa4fe94c..6ba8f0b90 100644 --- a/agents-api/agents_api/env.py +++ b/agents-api/agents_api/env.py @@ -3,13 +3,13 @@ It utilizes the environs library for environment variable parsing. """ +import random from pprint import pprint from environs import Env # Initialize the Env object for environment variable parsing. env = Env() -env.read_env() # Debug mode debug: bool = env.bool("AGENTS_API_DEBUG", default=False) @@ -32,7 +32,12 @@ temporal_task_queue = env.str("TEMPORAL_TASK_QUEUE", default="memory-task-queue") # auth -api_key: str = env.str("AGENTS_API_KEY") +_random_generated_key = "".join(str(random.randint(0, 9)) for _ in range(32)) +api_key: str = env.str("AGENTS_API_KEY", _random_generated_key) + +if api_key == _random_generated_key: + print(f"Generated API key since not set in the environment: {api_key}") + api_key_header_name: str = env.str("AGENTS_API_KEY_HEADER_NAME", default="X-Auth-Key") skip_check_developer_headers: bool = env.bool( "SKIP_CHECK_DEVELOPER_HEADERS", default=False diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index ed7b78e88..c9bfb199f 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -7,7 +7,6 @@ import pandas as pd from pydantic import BaseModel -from ..clients.cozo import client as cozo_client from ..common.utils.cozo import uuid_int_list_to_uuid4 P = ParamSpec("P") @@ -127,9 +126,7 @@ def cozo_query_dec(func: Callable[P, tuple[str | list[Any], dict]]): from pprint import pprint @wraps(func) - def wrapper( - *args: P.args, client=cozo_client, **kwargs: P.kwargs - ) -> pd.DataFrame: + def wrapper(*args: P.args, client=None, **kwargs: P.kwargs) -> pd.DataFrame: queries, variables = func(*args, **kwargs) if isinstance(queries, str): @@ -146,6 +143,9 @@ def wrapper( ) ) + from ..clients.cozo import get_cozo_client + + client = client or get_cozo_client() result = client.run(query, variables) # Need to fix the UUIDs in the result diff --git a/agents-api/agents_api/routers/agents/create_agent.py b/agents-api/agents_api/routers/agents/create_agent.py index f6e60e728..56e2eadf7 100644 --- a/agents-api/agents_api/routers/agents/create_agent.py +++ b/agents-api/agents_api/routers/agents/create_agent.py @@ -19,9 +19,10 @@ async def create_agent( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], data: CreateAgentRequest, ) -> ResourceCreatedResponse: + print("create_agent", x_developer_id, data) agent = models.agent.create_agent( developer_id=x_developer_id, data=data, ) - return ResourceCreatedResponse(id=agent.id, created_at=agent.created_at) + return ResourceCreatedResponse(id=agent.id, created_at=agent.created_at, jobs=[]) diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 92de246c2..b0b7faceb 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -1,7 +1,7 @@ from uuid import uuid4 from cozo_migrate.api import apply, init -from julep import AsyncClient, Client +from fastapi.testclient import TestClient from pycozo import Client as CozoClient from temporalio.client import WorkflowHandle from ward import fixture @@ -15,6 +15,7 @@ CreateToolRequest, CreateUserRequest, ) +from agents_api.env import api_key, api_key_header_name from agents_api.models.agent.create_agent import create_agent from agents_api.models.agent.delete_agent import delete_agent from agents_api.models.docs.create_doc import create_doc @@ -28,26 +29,7 @@ from agents_api.models.tools.delete_tool import delete_tool from agents_api.models.user.create_user import create_user from agents_api.models.user.delete_user import delete_user - -# TODO: make clients connect to real service - - -@fixture(scope="global") -def client(): - # Mock server base url - base_url = "http://localhost:8080" - client = Client(api_key="thisisnotarealapikey", base_url=base_url) - - return client - - -@fixture -def async_client(): - # Mock server base url - base_url = "http://localhost:8080" - client = AsyncClient(api_key="thisisnotarealapikey", base_url=base_url) - - return client +from agents_api.web import app @fixture(scope="global") @@ -263,3 +245,26 @@ def test_tool( tool_id=tool.id, client=client, ) + + +@fixture(scope="global") +def client(cozo_client=cozo_client): + client = TestClient(app=app) + app.state.cozo_client = cozo_client + + return client + + +@fixture(scope="global") +def make_request(client=client, developer_id=test_developer_id): + def _make_request(method, url, **kwargs): + headers = kwargs.pop("headers", {}) + headers = { + **headers, + "X-Developer-Id": str(developer_id), + api_key_header_name: api_key, + } + + return client.request(method, url, headers=headers, **kwargs) + + return _make_request diff --git a/agents-api/tests/test_agent_routes.py b/agents-api/tests/test_agent_routes.py new file mode 100644 index 000000000..3af2b83e6 --- /dev/null +++ b/agents-api/tests/test_agent_routes.py @@ -0,0 +1,209 @@ +# Tests for agent queries +from uuid import uuid4 + +from ward import test + +from tests.fixtures import client, make_request, test_agent + + +@test("route: unauthorized should fail") +def _(client=client): + data = dict( + name="test agent", + about="test agent about", + model="gpt-4o", + ) + + response = client.request( + method="POST", + url="/agents", + data=data, + ) + + assert response.status_code == 403 + + +@test("route: create agent") +def _(make_request=make_request): + data = dict( + name="test agent", + about="test agent about", + model="gpt-4o", + ) + + response = make_request( + method="POST", + url="/agents", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: create agent with instructions") +def _(make_request=make_request): + data = dict( + name="test agent", + about="test agent about", + model="gpt-4o", + instructions=["test instruction"], + ) + + response = make_request( + method="POST", + url="/agents", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: create or update agent") +def _(make_request=make_request): + data = dict( + name="test agent", + about="test agent about", + model="gpt-4o", + instructions=["test instruction"], + ) + + response = make_request( + method="POST", + url="/agents", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: get agent not exists") +def _(make_request=make_request): + agent_id = str(uuid4()) + + response = make_request( + method="GET", + url=f"/agents/{agent_id}", + ) + + assert response.status_code == 404 + + +@test("route: get agent exists") +def _(make_request=make_request, agent=test_agent): + agent_id = str(agent.id) + + response = make_request( + method="GET", + url=f"/agents/{agent_id}", + ) + + assert response.status_code != 404 + + +@test("route: delete agent") +def _(make_request=make_request): + data = dict( + name="test agent", + about="test agent about", + model="gpt-4o", + instructions=["test instruction"], + ) + + response = make_request( + method="POST", + url="/agents", + json=data, + ) + agent_id = response.json()["id"] + + response = make_request( + method="DELETE", + url=f"/agents/{agent_id}", + ) + + assert response.status_code == 202 + + response = make_request( + method="GET", + url=f"/agents/{agent_id}", + ) + + assert response.status_code == 404 + + +@test("route: update agent") +def _(make_request=make_request, agent=test_agent): + data = dict( + name="updated agent", + about="updated agent about", + default_settings={"temperature": 1.0}, + metadata={"hello": "world"}, + ) + + agent_id = str(agent.id) + response = make_request( + method="PUT", + url=f"/agents/{agent_id}", + json=data, + ) + + assert response.status_code == 200 + + agent_id = response.json()["id"] + + response = make_request( + method="GET", + url=f"/agents/{agent_id}", + ) + + assert response.status_code == 200 + agent = response.json() + + assert "test" not in agent["metadata"] + + +@test("model: patch agent") +def _(make_request=make_request, agent=test_agent): + agent_id = str(agent.id) + + data = dict( + name="patched agent", + about="patched agent about", + default_settings={"temperature": 1.0}, + metadata={"something": "else"}, + ) + + response = make_request( + method="PATCH", + url=f"/agents/{agent_id}", + json=data, + ) + + assert response.status_code == 200 + + agent_id = response.json()["id"] + + response = make_request( + method="GET", + url=f"/agents/{agent_id}", + ) + + assert response.status_code == 200 + agent = response.json() + + assert "hello" in agent["metadata"] + + +@test("model: list agents") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/agents", + ) + + assert response.status_code == 200 + response = response.json() + agents = response["items"] + + assert isinstance(agents, list) + assert len(agents) > 0 diff --git a/agents-api/tests/test_agents.py b/agents-api/tests/test_agents.py deleted file mode 100644 index 046aa188d..000000000 --- a/agents-api/tests/test_agents.py +++ /dev/null @@ -1,433 +0,0 @@ -# import uuid - -# from julep.api import Agent, ResourceCreatedResponse, ResourceUpdatedResponse -# from julep.api.core import ApiError -# from ward import test - -# from tests.fixtures import agent, async_client, client - - -# @test("create new agent with tools") -# def _(client=client): -# agent = client.agents.create( -# name="Samantha", -# about="about Samantha", -# instructions=[ -# "non-important content", -# "important content", -# ], -# tools=[ -# { -# "type": "function", -# "function": { -# "description": "func desc", -# "name": "some_func", -# "parameters": {"param1": "string"}, -# }, -# } -# ], -# default_settings={ -# "frequency_penalty": 0.1, -# "length_penalty": 0.9, -# "presence_penalty": 0.8, -# "repetition_penalty": 0.7, -# "temperature": 0.6, -# "top_p": 0.5, -# }, -# model="julep-ai/samantha-1-turbo", -# docs=[ -# { -# "title": "some titie", -# "content": "some content", -# }, -# ], -# ) - -# assert isinstance(agent, ResourceCreatedResponse) -# assert agent.created_at -# assert bool(uuid.UUID(str(agent.id), version=4)) - - -# @test("async create new agent with tools") -# async def _(client=async_client): -# agent = await client.agents.create( -# name="Samantha", -# about="about Samantha", -# instructions=[ -# "non-important content", -# "important content", -# ], -# tools=[ -# { -# "type": "function", -# "function": { -# "description": "func desc", -# "name": "some_func", -# "parameters": {"param1": "string"}, -# }, -# } -# ], -# default_settings={ -# "frequency_penalty": 0.1, -# "length_penalty": 0.9, -# "presence_penalty": 0.8, -# "repetition_penalty": 0.7, -# "temperature": 0.6, -# "top_p": 0.5, -# }, -# model="julep-ai/samantha-1-turbo", -# docs=[ -# { -# "title": "some titie", -# "content": "some content", -# }, -# ], -# ) - -# assert isinstance(agent, ResourceCreatedResponse) -# assert agent.created_at -# assert bool(uuid.UUID(str(agent.id), version=4)) - - -# @test("create new agent with functions") -# def _(client=client): -# agent = client.agents.create( -# name="Samantha", -# about="about Samantha", -# instructions=[ -# "non-important content", -# "important content", -# ], -# functions=[ -# { -# "description": "func desc", -# "name": "some_func", -# "parameters": {"param1": "string"}, -# } -# ], -# default_settings={ -# "frequency_penalty": 0.1, -# "length_penalty": 0.9, -# "presence_penalty": 0.8, -# "repetition_penalty": 0.7, -# "temperature": 0.6, -# "top_p": 0.5, -# }, -# model="julep-ai/samantha-1-turbo", -# docs=[ -# { -# "title": "some titie", -# "content": "some content", -# }, -# ], -# ) - -# assert isinstance(agent, ResourceCreatedResponse) -# assert agent.created_at -# assert bool(uuid.UUID(str(agent.id), version=4)) - - -# @test("async create new agent with functions") -# async def _(client=async_client): -# agent = await client.agents.create( -# name="Samantha", -# about="about Samantha", -# instructions=[ -# "non-important content", -# "important content", -# ], -# functions=[ -# { -# "description": "func desc", -# "name": "some_func", -# "parameters": {"param1": "string"}, -# } -# ], -# default_settings={ -# "frequency_penalty": 0.1, -# "length_penalty": 0.9, -# "presence_penalty": 0.8, -# "repetition_penalty": 0.7, -# "temperature": 0.6, -# "top_p": 0.5, -# }, -# model="julep-ai/samantha-1-turbo", -# docs=[ -# { -# "title": "some titie", -# "content": "some content", -# }, -# ], -# ) - -# assert isinstance(agent, ResourceCreatedResponse) -# assert agent.created_at -# assert bool(uuid.UUID(str(agent.id), version=4)) - - -# @test("create new agent with functions and tools") -# def _(client=client): -# try: -# client.agents.create( -# name="Samantha", -# about="about Samantha", -# instructions=[ -# "non-important content", -# "important content", -# ], -# tools=[ -# { -# "type": "function", -# "function": { -# "description": "func desc", -# "name": "some_func", -# "parameters": {"param1": "string"}, -# }, -# } -# ], -# functions=[ -# { -# "description": "func desc", -# "name": "some_func", -# "parameters": {"param1": "string"}, -# } -# ], -# default_settings={ -# "frequency_penalty": 0.1, -# "length_penalty": 0.9, -# "presence_penalty": 0.8, -# "repetition_penalty": 0.7, -# "temperature": 0.6, -# "top_p": 0.5, -# }, -# model="julep-ai/samantha-1-turbo", -# docs=[ -# { -# "title": "some titie", -# "content": "some content", -# }, -# ], -# ) -# except Exception: -# assert True -# else: -# assert False - - -# @test("async create new agent with functions and tools") -# async def _(client=async_client): -# try: -# await client.agents.create( -# name="Samantha", -# about="about Samantha", -# instructions=[ -# "non-important content", -# "important content", -# ], -# tools=[ -# { -# "type": "function", -# "function": { -# "description": "func desc", -# "name": "some_func", -# "parameters": {"param1": "string"}, -# }, -# } -# ], -# functions=[ -# { -# "description": "func desc", -# "name": "some_func", -# "parameters": {"param1": "string"}, -# } -# ], -# default_settings={ -# "frequency_penalty": 0.1, -# "length_penalty": 0.9, -# "presence_penalty": 0.8, -# "repetition_penalty": 0.7, -# "temperature": 0.6, -# "top_p": 0.5, -# }, -# model="julep-ai/samantha-1-turbo", -# docs=[ -# { -# "title": "some titie", -# "content": "some content", -# }, -# ], -# ) -# except Exception: -# assert True -# else: -# assert False - - -# @test("update existing agent") -# def _(client=client, existing_agent=agent): -# response = client.agents.update( -# agent_id=agent.id, -# name="test user", -# about="test user about", -# instructions=["test agent instructions"], -# default_settings={"temperature": 0.5}, -# model="some model", -# ) - -# assert isinstance(response, ResourceUpdatedResponse) -# assert response.updated_at != existing_agent.updated_at -# assert response.id == existing_agent.id - - -# @test("async update existing agent") -# async def _(client=async_client, existing_agent=agent): -# response = await client.agents.update( -# agent_id=agent.id, -# name="test user", -# about="test user about", -# instructions=["test agent instructions"], -# default_settings={"temperature": 0.5}, -# model="some model", -# ) - -# assert isinstance(response, ResourceUpdatedResponse) -# assert response.updated_at != existing_agent.updated_at -# assert response.id == existing_agent.id - - -# @test("update non-existing agent") -# def _(client=client): -# try: -# client.agents.update( -# agent_id=uuid.uuid4(), -# name="test user", -# about="test user about", -# instructions=["test agent instructions"], -# default_settings={"temperature": 0.5}, -# model="some model", -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("async update non-existing agent") -# async def _(client=async_client): -# try: -# await client.agents.update( -# agent_id=uuid.uuid4(), -# name="test user", -# about="test user about", -# instructions=["test agent instructions"], -# default_settings={"temperature": 0.5}, -# model="some model", -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("delete existing agent") -# def _(client=client, existing_agent=agent): -# response = client.agents.delete( -# existing_agent.id, -# ) - -# assert response is None - - -# @test("async delete existing agent") -# async def _(client=async_client, existing_agent=agent): -# response = await client.agents.delete( -# existing_agent.id, -# ) - -# assert response is None - - -# @test("delete non-existing agent") -# def _(client=client): -# try: -# client.agents.delete( -# uuid.uuid4(), -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("async delete non-existing agent") -# async def _(client=async_client): -# try: -# await client.agents.delete( -# uuid.uuid4(), -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("get existing agent") -# def _(client=client, existing_agent=agent): -# response = client.agents.get(existing_agent.id) -# assert isinstance(response, Agent) -# assert response.id == existing_agent.id - - -# @test("async get existing agent") -# async def _(client=async_client, existing_agent=agent): -# response = await client.agents.get(existing_agent.id) -# assert isinstance(response, Agent) -# assert response.id == existing_agent.id - - -# @test("get non-existing agent") -# def _(client=client): -# try: -# client.agents.get(uuid.uuid4()) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("async get non-existing agent") -# async def _(client=async_client): -# try: -# await client.agents.get(uuid.uuid4()) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("list agents") -# def _(client=client, existing_agent=agent): -# response = client.agents.list() -# assert len(response) > 0 -# assert isinstance(response[0], Agent) -# assert response[0].id == existing_agent.id - - -# @test("async list agents") -# async def _(client=async_client, existing_agent=agent): -# response = await client.agents.list() -# assert len(response) > 0 -# assert isinstance(response[0], Agent) -# assert response[0].id == existing_agent.id diff --git a/agents-api/tests/test_sessions.py b/agents-api/tests/test_sessions.py deleted file mode 100644 index e036f75a2..000000000 --- a/agents-api/tests/test_sessions.py +++ /dev/null @@ -1,307 +0,0 @@ -# import uuid - -# from julep.api import ( -# ChatMlMessage, -# ChatResponse, -# ChatSettingsResponseFormat, -# ChatSettingsResponseFormatType, -# InputChatMlMessage, -# InputChatMlMessageRole, -# ResourceCreatedResponse, -# ResourceUpdatedResponse, -# Session, -# Suggestion, -# Tool, -# ToolChoiceOption, -# ) -# from julep.api.core import ApiError -# from ward import test - -# from tests.fixtures import agent, async_client, client, session, user - - -# @test("get existing session") -# def _(existing_session=session, client=client): -# response = client.sessions.get(id=existing_session.id) - -# assert isinstance(response, Session) -# assert response.id == existing_session.id - - -# @test("async get existing sessions") -# async def _(existing_session=session, client=async_client): -# response = await client.sessions.get(id=existing_session.id) - -# assert isinstance(response, Session) -# assert response.id == existing_session.id - - -# @test("get non-existing session") -# def _(client=client): -# try: -# client.sessions.get(id=uuid.uuid4()) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("async get non-existing sessions") -# async def _(existing_session=session, client=async_client): -# try: -# await client.sessions.get(id=uuid.uuid4()) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("create sessions") -# def _(user=user, agent=agent, client=client): -# response = client.sessions.create( -# user_id=user.id, -# agent_id=agent.id, -# situation="test situation", -# ) - -# assert isinstance(response, ResourceCreatedResponse) -# assert response.created_at -# bool(uuid.UUID(str(response.id), version=4)) - - -# @test("async create sessions") -# async def _(user=user, agent=agent, client=async_client): -# response = await client.sessions.create( -# user_id=user.id, -# agent_id=agent.id, -# situation="test situation", -# ) - -# assert isinstance(response, ResourceCreatedResponse) -# assert response.created_at -# bool(uuid.UUID(str(response.id), version=4)) - - -# @test("list sessions") -# def _(existing_session=session, client=client): -# response = client.sessions.list() - -# assert len(response) > 0 -# assert isinstance(response[0], Session) -# assert response[0].id == existing_session.id - - -# @test("async list sessions") -# async def _(existing_session=session, client=async_client): -# response = await client.sessions.list() - -# assert len(response) > 0 -# assert isinstance(response[0], Session) -# assert response[0].id == existing_session.id - - -# @test("update existing session") -# def _(existing_session=session, client=client): -# response = client.sessions.update( -# session_id=existing_session.id, -# situation="test situation", -# ) - -# assert isinstance(response, ResourceUpdatedResponse) -# assert response.updated_at -# assert response.updated_at != existing_session.updated_at -# assert response.id == existing_session.id - - -# @test("async update existing session") -# async def _(existing_session=session, client=async_client): -# response = await client.sessions.update( -# session_id=existing_session.id, -# situation="test situation", -# ) - -# assert isinstance(response, ResourceUpdatedResponse) -# assert response.updated_at -# assert response.updated_at != existing_session.updated_at -# assert response.id == existing_session.id - - -# @test("update non-existing session") -# def _(client=client): -# try: -# client.sessions.update( -# session_id=uuid.uuid4(), -# situation="test situation", -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("async update non-existing session") -# async def _(client=async_client): -# try: -# await client.sessions.update( -# session_id=uuid.uuid4(), -# situation="test situation", -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("delete existing sessions") -# def _(existing_session=session, client=client): -# response = client.sessions.delete( -# session_id=existing_session.id, -# ) - -# assert response is None - - -# @test("async delete existing sessions") -# async def _(existing_session=session, client=client): -# response = await client.sessions.delete( -# session_id=existing_session.id, -# ) - -# assert response is None - - -# # TODO: implement below tests properly -# @test("sessions.chat") -# def _(client=client): -# response = client.sessions.chat( -# session_id=str(uuid.uuid4()), -# messages=[ -# InputChatMlMessage( -# role=InputChatMlMessageRole.USER, -# content="test content", -# name="tets name", -# ) -# ], -# tools=[ -# Tool( -# **{ -# "type": "function", -# "function": { -# "description": "test description", -# "name": "test name", -# "parameters": {"test_arg": "test val"}, -# }, -# "id": str(uuid.uuid4()), -# }, -# ) -# ], -# tool_choice=ToolChoiceOption("auto"), -# frequency_penalty=0.5, -# length_penalty=0.5, -# logit_bias={"test": 1}, -# max_tokens=120, -# presence_penalty=0.5, -# repetition_penalty=0.5, -# response_format=ChatSettingsResponseFormat( -# type=ChatSettingsResponseFormatType.TEXT, -# ), -# seed=1, -# stop=["<"], -# stream=False, -# temperature=0.7, -# top_p=0.9, -# recall=False, -# remember=False, -# ) - -# assert isinstance(response, ChatResponse) - - -# @test("async sessions.chat") -# async def _(client=async_client): -# response = await client.sessions.chat( -# session_id=str(uuid.uuid4()), -# messages=[ -# InputChatMlMessage( -# role=InputChatMlMessageRole.USER, -# content="test content", -# name="tets name", -# ) -# ], -# tools=[ -# Tool( -# **{ -# "type": "function", -# "function": { -# "description": "test description", -# "name": "test name", -# "parameters": {"test_arg": "test val"}, -# }, -# "id": str(uuid.uuid4()), -# }, -# ) -# ], -# tool_choice=ToolChoiceOption("auto"), -# frequency_penalty=0.5, -# length_penalty=0.5, -# logit_bias={"test": 1}, -# max_tokens=120, -# presence_penalty=0.5, -# repetition_penalty=0.5, -# response_format=ChatSettingsResponseFormat( -# type=ChatSettingsResponseFormatType.TEXT, -# ), -# seed=1, -# stop=["<"], -# stream=False, -# temperature=0.7, -# top_p=0.9, -# recall=False, -# remember=False, -# ) - -# assert isinstance(response, ChatResponse) - - -# @test("sessions.suggestions") -# def _(client=client): -# response = client.sessions.suggestions( -# session_id=uuid.uuid4(), -# ) -# assert len(response) > 0 -# assert isinstance(response[0], Suggestion) - - -# @test("async sessions.suggestions") -# async def _(client=async_client): -# response = await client.sessions.suggestions( -# session_id=uuid.uuid4(), -# ) -# assert len(response) > 0 -# assert isinstance(response[0], Suggestion) - - -# @test("sessions.history") -# def _(client=client): -# response = client.sessions.history( -# session_id=uuid.uuid4(), -# ) -# assert len(response) > 0 -# assert isinstance(response[0], ChatMlMessage) - - -# @test("async sessions.list") -# async def _(client=async_client): -# response = await client.sessions.history( -# session_id=uuid.uuid4(), -# ) -# assert len(response) > 0 -# assert isinstance(response[0], ChatMlMessage) diff --git a/agents-api/tests/test_tasks.py b/agents-api/tests/test_tasks.py deleted file mode 100644 index 6992ba327..000000000 --- a/agents-api/tests/test_tasks.py +++ /dev/null @@ -1,159 +0,0 @@ -# import uuid -# from typing import List - -# from julep.api.types import Execution, Task -# from ward import test - -# from tests.fixtures import agent, async_client, client, task - - -# @test("create task") -# def _(client=client, agent=agent): -# task = client.tasks.create( -# agent_id=agent.id, -# name="task1", -# description="task 1", -# tools_available=["tool1"], -# input_schema={}, -# main=[], -# ) - -# assert isinstance(task, Task) -# assert task.created_at -# assert bool(uuid.UUID(str(task.id), version=4)) - -# assert task.agent_id == agent.id -# assert task.name == "task1" -# assert task.description == "task 1" -# assert task.tools_available == ["tool1"] -# assert task.input_schema == {} -# assert task.main == [] - - -# @test("get task") -# def _(client=client, agent=agent, task=task): -# task = client.tasks.get( -# agent_id=agent.id, -# task_id=task.id, -# ) - -# assert isinstance(task, Task) -# assert task.created_at -# assert bool(uuid.UUID(str(task.id), version=4)) - -# assert task.agent_id == agent.id -# assert task.name == "task1" -# assert task.description == "task 1" -# assert task.tools_available == ["tool1"] -# assert task.input_schema == {} -# assert task.main == [] - - -# @test("list task") -# def _(client=client, agent=agent): -# tasks = client.tasks.list( -# agent_id=agent.id, -# ) - -# assert isinstance(tasks, List[Task]) -# assert len(tasks) > 0 - -# task = tasks[0] - -# assert task.created_at -# assert bool(uuid.UUID(str(task.id), version=4)) - -# assert task.agent_id == agent.id -# assert task.name == "task1" -# assert task.description == "task 1" -# assert task.tools_available == ["tool1"] -# assert task.input_schema == {} -# assert task.main == [] - - -# @test("start task execution") -# def _(client=client, agent=agent, task=task): -# execution = client.tasks.start_task_execution( -# agent_id=agent.id, -# task_id=task.id, -# arguments={}, -# status="enqueued", -# ) - -# assert isinstance(execution, Execution) - - -# @test("create task") -# async def _(client=async_client, agent=agent): -# task = await client.tasks.create( -# agent_id=agent.id, -# name="task1", -# description="task 1", -# tools_available=["tool1"], -# input_schema={}, -# main=[], -# ) - -# assert isinstance(task, Task) -# assert task.created_at -# assert bool(uuid.UUID(str(task.id), version=4)) - -# assert task.agent_id == agent.id -# assert task.name == "task1" -# assert task.description == "task 1" -# assert task.tools_available == ["tool1"] -# assert task.input_schema == {} -# assert task.main == [] - - -# @test("get task") -# async def _(client=async_client, agent=agent, task=task): -# task = await client.tasks.get( -# agent_id=agent.id, -# task_id=task.id, -# ) - -# assert isinstance(task, Task) -# assert task.created_at -# assert bool(uuid.UUID(str(task.id), version=4)) - -# assert task.agent_id == agent.id -# assert task.name == "task1" -# assert task.description == "task 1" -# assert task.tools_available == ["tool1"] -# assert task.input_schema == {} -# assert task.main == [] - - -# @test("list task") -# async def _(client=async_client, agent=agent): -# tasks = await client.tasks.list( -# agent_id=agent.id, -# ) - -# assert isinstance(tasks, List[Task]) -# assert len(tasks) > 0 - -# task = tasks[0] - -# assert task.created_at -# assert bool(uuid.UUID(str(task.id), version=4)) - -# assert task.agent_id == agent.id -# assert task.name == "task1" -# assert task.description == "task 1" -# assert task.tools_available == ["tool1"] -# assert task.input_schema == {} -# assert task.main == [] - - -# @test("start task execution") -# async def _(client=async_client, agent=agent, task=task): -# execution = await client.tasks.start_task_execution( -# agent_id=agent.id, -# task_id=task.id, -# arguments={}, -# status="enqueued", -# ) - -# assert isinstance(execution, Execution) diff --git a/agents-api/tests/test_users.py b/agents-api/tests/test_users.py deleted file mode 100644 index 8cd074bdb..000000000 --- a/agents-api/tests/test_users.py +++ /dev/null @@ -1,191 +0,0 @@ -# import uuid - -# from julep.api import ResourceCreatedResponse, ResourceUpdatedResponse, User -# from julep.api.core import ApiError -# from ward import test - -# from tests.fixtures import async_client, client, user - - -# @test("create user") -# def _(client=client): -# response = client.users.create( -# name="test user", -# about="test user about", -# ) - -# assert isinstance(response, ResourceCreatedResponse) -# assert response.created_at -# assert bool(uuid.UUID(str(response.id), version=4)) - - -# @test("async create user") -# async def _(client=async_client): -# response = await client.users.create( -# name="test user", -# about="test user about", -# ) - -# assert isinstance(response, ResourceCreatedResponse) -# assert response.created_at -# assert bool(uuid.UUID(str(response.id), version=4)) - - -# @test("get existing user") -# def _(existing_user=user, client=client): -# response = client.users.get(existing_user.id) -# assert isinstance(response, User) -# assert existing_user.id == response.id - - -# @test("async get existing user") -# async def _(existing_user=user, client=async_client): -# response = await client.users.get(existing_user.id) -# assert isinstance(response, User) -# assert existing_user.id == response.id - - -# @test("get non-existing user") -# def _(client=client): -# try: -# client.users.get(uuid.uuid4()) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("async get non-existing user") -# async def _(client=async_client): -# try: -# await client.users.get(uuid.uuid4()) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("update existing user") -# def _(existing_user=user, client=client): -# response = client.users.update( -# user_id=existing_user.id, -# name="test user", -# about="test user about", -# ) - -# assert isinstance(response, ResourceUpdatedResponse) -# assert response.updated_at -# assert response.updated_at != existing_user.updated_at -# assert response.id == existing_user.id - - -# @test("async update existing user") -# async def _(existing_user=user, async_client=client): -# response = await client.users.update( -# user_id=existing_user.id, -# name="test user", -# about="test user about", -# ) - -# assert isinstance(response, ResourceUpdatedResponse) -# assert response.updated_at -# assert response.updated_at != existing_user.updated_at -# assert response.id == existing_user.id - - -# @test("update non-existing user") -# def _(client=client): -# try: -# client.users.update( -# user_id=uuid.uuid4(), -# name="test user", -# about="test user about", -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("async update non-existing user") -# async def _(client=async_client): -# try: -# await client.users.update( -# user_id=uuid.uuid4(), -# name="test user", -# about="test user about", -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("delete existing user") -# def _(existing_user=user, client=client): -# response = client.users.delete( -# user_id=existing_user.id, -# ) - -# assert response is None - - -# @test("async delete existing user") -# async def _(existing_user=user, client=async_client): -# response = await client.users.delete( -# user_id=existing_user.id, -# ) - -# assert response is None - - -# @test("delete non-existing user") -# def _(client=client): -# try: -# client.users.delete( -# user_id=uuid.uuid4(), -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("async delete non-existing user") -# async def _(client=async_client): -# try: -# await client.users.delete( -# user_id=uuid.uuid4(), -# ) -# except ApiError as e: -# assert e.status_code == 404 -# except Exception: -# assert False -# else: -# assert False - - -# @test("list users") -# def _(existing_user=user, client=client): -# response = client.users.list() -# assert len(response) > 0 -# assert isinstance(response[0], User) -# assert response[0].id == existing_user.id - - -# @test("async list users") -# async def _(existing_user=user, client=async_client): -# response = await client.users.list() -# assert len(response) > 0 -# assert isinstance(response[0], User) -# assert response[0].id == existing_user.id From dd520455ce8c3b46b64043713f9ccca9d0242f5b Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Fri, 9 Aug 2024 18:48:13 -0400 Subject: [PATCH 021/110] --- agents-api/tests/test_user_routes.py | 167 +++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 agents-api/tests/test_user_routes.py diff --git a/agents-api/tests/test_user_routes.py b/agents-api/tests/test_user_routes.py new file mode 100644 index 000000000..88f14d85a --- /dev/null +++ b/agents-api/tests/test_user_routes.py @@ -0,0 +1,167 @@ +# Tests for user routes +from uuid import uuid4 + +from ward import test + +from tests.fixtures import client, make_request, test_user + + +@test("route: unauthorized should fail") +def _(client=client): + data = dict( + name="test user", + about="test user about", + ) + + response = client.request( + method="POST", + url="/users", + data=data, + ) + + assert response.status_code == 403 + + +@test("route: create user") +def _(make_request=make_request): + data = dict( + name="test user", + about="test user about", + ) + + response = make_request( + method="POST", + url="/users", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: get user not exists") +def _(make_request=make_request): + user_id = str(uuid4()) + + response = make_request( + method="GET", + url=f"/users/{user_id}", + ) + + assert response.status_code == 404 + + +@test("route: get user exists") +def _(make_request=make_request, user=test_user): + user_id = str(user.id) + + response = make_request( + method="GET", + url=f"/users/{user_id}", + ) + + assert response.status_code != 404 + + +@test("route: delete user") +def _(make_request=make_request): + data = dict( + name="test user", + about="test user about", + ) + + response = make_request( + method="POST", + url="/users", + json=data, + ) + user_id = response.json()["id"] + + response = make_request( + method="DELETE", + url=f"/users/{user_id}", + ) + + assert response.status_code == 202 + + response = make_request( + method="GET", + url=f"/users/{user_id}", + ) + + assert response.status_code == 404 + + +@test("route: update user") +def _(make_request=make_request, user=test_user): + data = dict( + name="updated user", + about="updated user about", + ) + + user_id = str(user.id) + response = make_request( + method="PUT", + url=f"/users/{user_id}", + json=data, + ) + + assert response.status_code == 200 + + user_id = response.json()["id"] + + response = make_request( + method="GET", + url=f"/users/{user_id}", + ) + + assert response.status_code == 200 + user = response.json() + + assert user["name"] == "updated user" + assert user["about"] == "updated user about" + + +@test("model: patch user") +def _(make_request=make_request, user=test_user): + user_id = str(user.id) + + data = dict( + name="patched user", + about="patched user about", + ) + + response = make_request( + method="PATCH", + url=f"/users/{user_id}", + json=data, + ) + + assert response.status_code == 200 + + user_id = response.json()["id"] + + response = make_request( + method="GET", + url=f"/users/{user_id}", + ) + + assert response.status_code == 200 + user = response.json() + + assert user["name"] == "patched user" + assert user["about"] == "patched user about" + + +@test("model: list users") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/users", + ) + + assert response.status_code == 200 + response = response.json() + users = response["items"] + + assert isinstance(users, list) + assert len(users) > 0 From d0b3e6bac438e578986f23eb0214c1db8db9c54a Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 9 Aug 2024 19:15:53 -0400 Subject: [PATCH 022/110] fix(agents-api): Fixed tests Signed-off-by: Diwank Tomer --- agents-api/agents_api/models/agent/get_agent.py | 6 +++++- agents-api/agents_api/models/user/create_user.py | 5 +++++ agents-api/agents_api/models/user/delete_user.py | 13 ++++++++----- agents-api/agents_api/models/user/get_user.py | 9 +++++++++ agents-api/agents_api/models/utils.py | 7 +++++-- .../routers/agents/create_or_update_agent.py | 2 +- agents-api/agents_api/routers/users/create_user.py | 2 +- agents-api/agents_api/routers/users/delete_user.py | 1 + agents-api/agents_api/routers/users/list_users.py | 7 ++++--- agents-api/agents_api/web.py | 3 ++- agents-api/tests/test_agent_routes.py | 8 +++++--- 11 files changed, 46 insertions(+), 17 deletions(-) diff --git a/agents-api/agents_api/models/agent/get_agent.py b/agents-api/agents_api/models/agent/get_agent.py index d42e1694f..ceef527f3 100644 --- a/agents-api/agents_api/models/agent/get_agent.py +++ b/agents-api/agents_api/models/agent/get_agent.py @@ -18,10 +18,14 @@ @rewrap_exceptions( { + lambda e: isinstance(e, QueryException) + and "Developer not found" in str(e): lambda *_: HTTPException( + detail="developer does not exist", status_code=403 + ), lambda e: isinstance(e, QueryException) and "asserted to return some results, but returned none" in str(e): lambda *_: HTTPException( - detail="developer not found or doesnt own resource", status_code=404 + detail="developer doesnt own resource", status_code=404 ), QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), diff --git a/agents-api/agents_api/models/user/create_user.py b/agents-api/agents_api/models/user/create_user.py index fda675231..f9c5af19e 100644 --- a/agents-api/agents_api/models/user/create_user.py +++ b/agents-api/agents_api/models/user/create_user.py @@ -22,6 +22,11 @@ @rewrap_exceptions( { + lambda e: isinstance(e, QueryException) + and "asserted to return some results, but returned none" + in str(e): lambda *_: HTTPException( + detail="developer not found", status_code=403 + ), QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), TypeError: partialclass(HTTPException, status_code=400), diff --git a/agents-api/agents_api/models/user/delete_user.py b/agents-api/agents_api/models/user/delete_user.py index 170a1a399..04f96e630 100644 --- a/agents-api/agents_api/models/user/delete_user.py +++ b/agents-api/agents_api/models/user/delete_user.py @@ -57,15 +57,18 @@ def delete_user(*, developer_id: UUID, user_id: UUID) -> tuple[list[str], dict]: verify_developer_owns_resource_query(developer_id, "users", user_id=user_id), """ # Delete docs - ?[user_id, doc_id] := + ?[owner_type, owner_id, doc_id] := *docs{ - owner_id: user_id, - owner_type: "user", + owner_id, + owner_type, doc_id, - }, user_id = to_uuid($user_id) + }, + owner_id = to_uuid($user_id), + owner_type = "user" :delete docs { - user_id, + owner_type, + owner_id, doc_id } :returning diff --git a/agents-api/agents_api/models/user/get_user.py b/agents-api/agents_api/models/user/get_user.py index fa2dd5d38..d4556a365 100644 --- a/agents-api/agents_api/models/user/get_user.py +++ b/agents-api/agents_api/models/user/get_user.py @@ -18,6 +18,15 @@ @rewrap_exceptions( { + lambda e: isinstance(e, QueryException) + and "Developer not found" in str(e): lambda *_: HTTPException( + detail="developer does not exist", status_code=403 + ), + lambda e: isinstance(e, QueryException) + and "asserted to return some results, but returned none" + in str(e): lambda *_: HTTPException( + detail="developer doesnt own resource", status_code=404 + ), QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), TypeError: partialclass(HTTPException, status_code=400), diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index c9bfb199f..126d797a3 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -65,12 +65,15 @@ class NewCls(cls): def verify_developer_id_query(developer_id: UUID | str) -> str: return f""" - ?[developer_id] := + matched[count(developer_id)] := *developers{{ developer_id, }}, developer_id = to_uuid("{str(developer_id)}") - :assert some + ?[exists] := + matched[num], + exists = num > 0, + assert(exists, "Developer does not exist") """ diff --git a/agents-api/agents_api/routers/agents/create_or_update_agent.py b/agents-api/agents_api/routers/agents/create_or_update_agent.py index 0c40c98b8..fc2fa5563 100644 --- a/agents-api/agents_api/routers/agents/create_or_update_agent.py +++ b/agents-api/agents_api/routers/agents/create_or_update_agent.py @@ -27,4 +27,4 @@ async def create_or_update_agent( data=data, ) - return ResourceCreatedResponse(id=agent.id, created_at=agent.created_at) + return ResourceCreatedResponse(id=agent.id, created_at=agent.created_at, jobs=[]) diff --git a/agents-api/agents_api/routers/users/create_user.py b/agents-api/agents_api/routers/users/create_user.py index 07e763b66..fcf8bf89b 100644 --- a/agents-api/agents_api/routers/users/create_user.py +++ b/agents-api/agents_api/routers/users/create_user.py @@ -20,4 +20,4 @@ async def create_user( data=data, ) - return ResourceCreatedResponse(id=user.id, created_at=user.created_at) + return ResourceCreatedResponse(id=user.id, created_at=user.created_at, jobs=[]) diff --git a/agents-api/agents_api/routers/users/delete_user.py b/agents-api/agents_api/routers/users/delete_user.py index fd1d02a94..3a63e42e9 100644 --- a/agents-api/agents_api/routers/users/delete_user.py +++ b/agents-api/agents_api/routers/users/delete_user.py @@ -14,4 +14,5 @@ async def delete_user( user_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] ) -> ResourceDeletedResponse: + print(user_id) return delete_user_query(developer_id=x_developer_id, user_id=user_id) diff --git a/agents-api/agents_api/routers/users/list_users.py b/agents-api/agents_api/routers/users/list_users.py index aa57883b3..bfa5be128 100644 --- a/agents-api/agents_api/routers/users/list_users.py +++ b/agents-api/agents_api/routers/users/list_users.py @@ -1,6 +1,6 @@ import json from json import JSONDecodeError -from typing import Annotated, List, Literal +from typing import Annotated, Literal from fastapi import Depends, HTTPException, status from pydantic import UUID4 @@ -19,7 +19,7 @@ async def list_users( sort_by: Literal["created_at", "updated_at"] = "created_at", direction: Literal["asc", "desc"] = "desc", metadata_filter: str = "{}", -) -> List[User]: +) -> ListResponse[User]: try: metadata_filter = json.loads(metadata_filter) except JSONDecodeError: @@ -37,4 +37,5 @@ async def list_users( metadata_filter=metadata_filter, ) - return ListResponse[User](items=users) + result = ListResponse[User](items=users) + return result diff --git a/agents-api/agents_api/web.py b/agents-api/agents_api/web.py index ab805a67c..df09fa587 100644 --- a/agents-api/agents_api/web.py +++ b/agents-api/agents_api/web.py @@ -22,6 +22,7 @@ from agents_api.routers import ( agents, tasks, + users, ) if not sentry_dsn: @@ -88,7 +89,7 @@ def register_exceptions(app: FastAPI): app.include_router(agents.router) # app.include_router(sessions.router) -# app.include_router(users.router) +app.include_router(users.router) # app.include_router(jobs.router) app.include_router(tasks.router) diff --git a/agents-api/tests/test_agent_routes.py b/agents-api/tests/test_agent_routes.py index 3af2b83e6..53951a855 100644 --- a/agents-api/tests/test_agent_routes.py +++ b/agents-api/tests/test_agent_routes.py @@ -60,6 +60,8 @@ def _(make_request=make_request): @test("route: create or update agent") def _(make_request=make_request): + agent_id = str(uuid4()) + data = dict( name="test agent", about="test agent about", @@ -69,7 +71,7 @@ def _(make_request=make_request): response = make_request( method="POST", - url="/agents", + url=f"/agents/{agent_id}", json=data, ) @@ -162,7 +164,7 @@ def _(make_request=make_request, agent=test_agent): assert "test" not in agent["metadata"] -@test("model: patch agent") +@test("route: patch agent") def _(make_request=make_request, agent=test_agent): agent_id = str(agent.id) @@ -194,7 +196,7 @@ def _(make_request=make_request, agent=test_agent): assert "hello" in agent["metadata"] -@test("model: list agents") +@test("route: list agents") def _(make_request=make_request): response = make_request( method="GET", From 4e6656ca2e3a057300409cd0f7ca2b6b8ba7152b Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Sat, 10 Aug 2024 15:44:41 +0300 Subject: [PATCH 023/110] fix: Apply fixes --- agents-api/agents_api/autogen/Chat.py | 2 +- agents-api/agents_api/autogen/Entries.py | 2 +- .../agents_api/models/agent/create_agent.py | 2 +- .../agents_api/models/agent/update_agent.py | 6 +- .../routers/agents/create_agent_tool.py | 2 +- .../routers/sessions/create_session.py | 1 + .../routers/sessions/get_session_history.py | 4 +- .../routers/users/create_or_update_user.py | 2 +- .../agents_api/routers/users/update_user.py | 2 +- agents-api/agents_api/web.py | 16 ++++- agents-api/poetry.lock | 22 +++---- scripts/generate_openapi_code.sh | 2 +- sdks/python/julep/api/__init__.py | 2 - sdks/python/julep/api/client.py | 9 ++- sdks/python/julep/api/reference.md | 2 +- sdks/python/julep/api/types/__init__.py | 2 - .../julep/api/types/tools_function_def.py | 11 ++-- .../api/types/tools_function_def_update.py | 65 ------------------- sdks/python/poetry.lock | 14 ++-- 19 files changed, 55 insertions(+), 113 deletions(-) mode change 100644 => 100755 scripts/generate_openapi_code.sh delete mode 100644 sdks/python/julep/api/types/tools_function_def_update.py diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index c7c25c7ad..6669fbcec 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel from .Docs import DocReference from .Entries import ChatMLMessage diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index 6c8bf9ca4..d56b333d8 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, RootModel from .Tools import ChosenToolCall, Tool, ToolResponse diff --git a/agents-api/agents_api/models/agent/create_agent.py b/agents-api/agents_api/models/agent/create_agent.py index fd717462a..3ac561a0b 100644 --- a/agents-api/agents_api/models/agent/create_agent.py +++ b/agents-api/agents_api/models/agent/create_agent.py @@ -65,7 +65,7 @@ def create_agent( if isinstance(data.instructions, list) else [data.instructions] ) - data.default_settings = data.default_settings or {} + data.default_settings = data.default_settings agent_data = data.model_dump(exclude_unset=True) default_settings = agent_data.pop("default_settings") diff --git a/agents-api/agents_api/models/agent/update_agent.py b/agents-api/agents_api/models/agent/update_agent.py index f699be248..9cd8f04d9 100644 --- a/agents-api/agents_api/models/agent/update_agent.py +++ b/agents-api/agents_api/models/agent/update_agent.py @@ -49,7 +49,11 @@ def update_agent( Returns: ResourceUpdatedResponse: The updated agent data. """ - default_settings = data.default_settings.model_dump(exclude_none=True) + default_settings = ( + data.default_settings.model_dump(exclude_none=True) + if data.default_settings + else {} + ) update_data = data.model_dump() # Remove default settings from the agent update data diff --git a/agents-api/agents_api/routers/agents/create_agent_tool.py b/agents-api/agents_api/routers/agents/create_agent_tool.py index aaa434743..46442ba01 100644 --- a/agents-api/agents_api/routers/agents/create_agent_tool.py +++ b/agents-api/agents_api/routers/agents/create_agent_tool.py @@ -25,6 +25,6 @@ async def create_agent_tool( developer_id=x_developer_id, agent_id=agent_id, data=[data], - ) + )[0] return ResourceCreatedResponse(id=tool.id, created_at=tool.created_at) diff --git a/agents-api/agents_api/routers/sessions/create_session.py b/agents-api/agents_api/routers/sessions/create_session.py index cf4ba119d..2262d8e7c 100644 --- a/agents-api/agents_api/routers/sessions/create_session.py +++ b/agents-api/agents_api/routers/sessions/create_session.py @@ -26,4 +26,5 @@ async def create_session( return ResourceCreatedResponse( id=session.id, created_at=session.created_at, + jobs=[], ) diff --git a/agents-api/agents_api/routers/sessions/get_session_history.py b/agents-api/agents_api/routers/sessions/get_session_history.py index c960f001e..64be57fcb 100644 --- a/agents-api/agents_api/routers/sessions/get_session_history.py +++ b/agents-api/agents_api/routers/sessions/get_session_history.py @@ -3,7 +3,7 @@ from fastapi import Depends from pydantic import UUID4 -from ...autogen.openapi_model import Session +from ...autogen.openapi_model import History from ...dependencies.developer_id import get_developer_id from ...models.entry.get_history import get_history as get_history_query from .router import router @@ -12,5 +12,5 @@ @router.get("/sessions/{session_id}/history", tags=["sessions"]) async def get_session_history( session_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] -) -> Session: +) -> History: return get_history_query(developer_id=x_developer_id, session_id=session_id) diff --git a/agents-api/agents_api/routers/users/create_or_update_user.py b/agents-api/agents_api/routers/users/create_or_update_user.py index 928bee62f..331f2c4d7 100644 --- a/agents-api/agents_api/routers/users/create_or_update_user.py +++ b/agents-api/agents_api/routers/users/create_or_update_user.py @@ -24,4 +24,4 @@ async def create_or_update_user( data=data, ) - return ResourceCreatedResponse(id=user.id, created_at=user.created_at) + return ResourceCreatedResponse(id=user.id, created_at=user.created_at, jobs=[]) diff --git a/agents-api/agents_api/routers/users/update_user.py b/agents-api/agents_api/routers/users/update_user.py index 258023173..d80d68f83 100644 --- a/agents-api/agents_api/routers/users/update_user.py +++ b/agents-api/agents_api/routers/users/update_user.py @@ -18,5 +18,5 @@ async def update_user( return update_user_query( developer_id=x_developer_id, user_id=user_id, - data=data, + update_user=data, ) diff --git a/agents-api/agents_api/web.py b/agents-api/agents_api/web.py index df09fa587..23c34a970 100644 --- a/agents-api/agents_api/web.py +++ b/agents-api/agents_api/web.py @@ -8,7 +8,7 @@ import sentry_sdk import uvicorn from fastapi import Depends, FastAPI, Request, status -from fastapi.exceptions import RequestValidationError +from fastapi.exceptions import HTTPException, RequestValidationError from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import JSONResponse from litellm.exceptions import APIError @@ -21,6 +21,8 @@ from agents_api.exceptions import PromptTooBigError from agents_api.routers import ( agents, + jobs, + sessions, tasks, users, ) @@ -88,12 +90,20 @@ def register_exceptions(app: FastAPI): register_exceptions(app) app.include_router(agents.router) -# app.include_router(sessions.router) +app.include_router(sessions.router) app.include_router(users.router) -# app.include_router(jobs.router) +app.include_router(jobs.router) app.include_router(tasks.router) +@app.exception_handler(HTTPException) +async def http_exception_handler(request, exc: HTTPException): # pylint: disable=unused-argument + return JSONResponse( + status_code=exc.status_code, + content={"error": {"message": str(exc)}}, + ) + + @app.exception_handler(RPCError) async def validation_error_handler(request: Request, exc: RPCError): return JSONResponse( diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 125739747..7afec2778 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -2292,13 +2292,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.4" +version = "1.43.6" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.4-py3-none-any.whl", hash = "sha256:619cfaab189f921f66ff50c2b7a0e965e562c2a95b17c2ee24649826ba35da11"}, - {file = "litellm-1.43.4.tar.gz", hash = "sha256:949c51ad494b935d80da1cd18c3567e4ed181f8eb531ef4706e3be72afb0c43c"}, + {file = "litellm-1.43.6-py3-none-any.whl", hash = "sha256:d1a8eebba7e22c785b413fe7908bcd30356ed652958bd55b4c8982ccede66933"}, + {file = "litellm-1.43.6.tar.gz", hash = "sha256:9a94e9c73d029c90de11c9cdbf633531ff0d89b87d7df8bd87ff43d7d3b7c205"}, ] [package.dependencies] @@ -2904,13 +2904,13 @@ files = [ [[package]] name = "openai" -version = "1.40.2" +version = "1.40.3" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.2-py3-none-any.whl", hash = "sha256:38068f858f310b4fd4b0ea8734c3efcfde3c15a2978311e1453bd84817231b96"}, - {file = "openai-1.40.2.tar.gz", hash = "sha256:2180e9070bd36084328248b3ce668964e8ddd2e9019e1d426e31dc54cc117bb5"}, + {file = "openai-1.40.3-py3-none-any.whl", hash = "sha256:09396cb6e2e15c921a5d872bf92841a60a9425da10dcd962b45fe7c4f48f8395"}, + {file = "openai-1.40.3.tar.gz", hash = "sha256:f2ffe907618240938c59d7ccc67dd01dc8c50be203c0077240db6758d2f02480"}, ] [package.dependencies] @@ -4395,7 +4395,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} +greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\")"} typing-extensions = ">=4.6.0" [package.extras] @@ -5005,13 +5005,13 @@ files = [ [[package]] name = "webcolors" -version = "24.6.0" +version = "24.8.0" description = "A library for working with the color formats defined by HTML and CSS." optional = false python-versions = ">=3.8" files = [ - {file = "webcolors-24.6.0-py3-none-any.whl", hash = "sha256:8cf5bc7e28defd1d48b9e83d5fc30741328305a8195c29a8e668fa45586568a1"}, - {file = "webcolors-24.6.0.tar.gz", hash = "sha256:1d160d1de46b3e81e58d0a280d0c78b467dc80f47294b91b1ad8029d2cedb55b"}, + {file = "webcolors-24.8.0-py3-none-any.whl", hash = "sha256:fc4c3b59358ada164552084a8ebee637c221e4059267d0f8325b3b560f6c7f0a"}, + {file = "webcolors-24.8.0.tar.gz", hash = "sha256:08b07af286a01bcd30d583a7acadf629583d1f79bfef27dd2c2c5c263817277d"}, ] [package.extras] diff --git a/scripts/generate_openapi_code.sh b/scripts/generate_openapi_code.sh old mode 100644 new mode 100755 index b58b8ed45..8ea3acf8f --- a/scripts/generate_openapi_code.sh +++ b/scripts/generate_openapi_code.sh @@ -7,7 +7,7 @@ cd typespec/ && \ tsp compile . cd - -fern generate --local +fern generate cd sdks/python && \ poetry update && \ diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index 30f156d29..0e6255648 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -195,7 +195,6 @@ ToolsChosenToolCall_Function, ToolsFunctionCallOption, ToolsFunctionDef, - ToolsFunctionDefUpdate, ToolsFunctionTool, ToolsNamedFunctionChoice, ToolsNamedToolChoice, @@ -412,7 +411,6 @@ "ToolsChosenToolCall_Function", "ToolsFunctionCallOption", "ToolsFunctionDef", - "ToolsFunctionDefUpdate", "ToolsFunctionTool", "ToolsNamedFunctionChoice", "ToolsNamedToolChoice", diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index 207aa7c32..c074e5c3b 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -98,7 +98,6 @@ from .types.tasks_task_tool import TasksTaskTool from .types.tasks_update_task_request_main_item import TasksUpdateTaskRequestMainItem from .types.tools_function_def import ToolsFunctionDef -from .types.tools_function_def_update import ToolsFunctionDefUpdate from .types.tools_tool_type import ToolsToolType from .types.user_docs_route_list_request_direction import ( UserDocsRouteListRequestDirection, @@ -1585,7 +1584,7 @@ def agent_tools_route_patch( *, type: typing.Optional[ToolsToolType] = OMIT, name: typing.Optional[CommonValidPythonIdentifier] = OMIT, - function: typing.Optional[ToolsFunctionDefUpdate] = OMIT, + function: typing.Optional[ToolsFunctionDef] = OMIT, integration: typing.Optional[typing.Any] = OMIT, system: typing.Optional[typing.Any] = OMIT, api_call: typing.Optional[typing.Any] = OMIT, @@ -1608,7 +1607,7 @@ def agent_tools_route_patch( name : typing.Optional[CommonValidPythonIdentifier] Name of the tool (must be unique for this agent and a valid python identifier string ) - function : typing.Optional[ToolsFunctionDefUpdate] + function : typing.Optional[ToolsFunctionDef] integration : typing.Optional[typing.Any] @@ -5177,7 +5176,7 @@ async def agent_tools_route_patch( *, type: typing.Optional[ToolsToolType] = OMIT, name: typing.Optional[CommonValidPythonIdentifier] = OMIT, - function: typing.Optional[ToolsFunctionDefUpdate] = OMIT, + function: typing.Optional[ToolsFunctionDef] = OMIT, integration: typing.Optional[typing.Any] = OMIT, system: typing.Optional[typing.Any] = OMIT, api_call: typing.Optional[typing.Any] = OMIT, @@ -5200,7 +5199,7 @@ async def agent_tools_route_patch( name : typing.Optional[CommonValidPythonIdentifier] Name of the tool (must be unique for this agent and a valid python identifier string ) - function : typing.Optional[ToolsFunctionDefUpdate] + function : typing.Optional[ToolsFunctionDef] integration : typing.Optional[typing.Any] diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index c7b27f1a6..cc0693d02 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -2158,7 +2158,7 @@ client.agent_tools_route_patch(
-**function:** `typing.Optional[ToolsFunctionDefUpdate]` +**function:** `typing.Optional[ToolsFunctionDef]`
diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index a2ab207d0..fec55842f 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -253,7 +253,6 @@ from .tools_chosen_tool_call import ToolsChosenToolCall, ToolsChosenToolCall_Function from .tools_function_call_option import ToolsFunctionCallOption from .tools_function_def import ToolsFunctionDef -from .tools_function_def_update import ToolsFunctionDefUpdate from .tools_function_tool import ToolsFunctionTool from .tools_named_function_choice import ToolsNamedFunctionChoice from .tools_named_tool_choice import ToolsNamedToolChoice, ToolsNamedToolChoice_Function @@ -467,7 +466,6 @@ "ToolsChosenToolCall_Function", "ToolsFunctionCallOption", "ToolsFunctionDef", - "ToolsFunctionDefUpdate", "ToolsFunctionTool", "ToolsNamedFunctionChoice", "ToolsNamedToolChoice", diff --git a/sdks/python/julep/api/types/tools_function_def.py b/sdks/python/julep/api/types/tools_function_def.py index bc446685b..0c7e71ee3 100644 --- a/sdks/python/julep/api/types/tools_function_def.py +++ b/sdks/python/julep/api/types/tools_function_def.py @@ -6,7 +6,6 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode -from .common_valid_python_identifier import CommonValidPythonIdentifier class ToolsFunctionDef(pydantic_v1.BaseModel): @@ -14,11 +13,7 @@ class ToolsFunctionDef(pydantic_v1.BaseModel): Function definition """ - name: typing.Optional[CommonValidPythonIdentifier] = pydantic_v1.Field(default=None) - """ - DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. - """ - + name: typing.Optional[typing.Any] = None description: typing.Optional[CommonIdentifierSafeUnicode] = pydantic_v1.Field( default=None ) @@ -26,7 +21,9 @@ class ToolsFunctionDef(pydantic_v1.BaseModel): Description of the function """ - parameters: typing.Dict[str, typing.Any] = pydantic_v1.Field() + parameters: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( + default=None + ) """ The parameters the function accepts """ diff --git a/sdks/python/julep/api/types/tools_function_def_update.py b/sdks/python/julep/api/types/tools_function_def_update.py deleted file mode 100644 index 9cfc3bd81..000000000 --- a/sdks/python/julep/api/types/tools_function_def_update.py +++ /dev/null @@ -1,65 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode -from .common_valid_python_identifier import CommonValidPythonIdentifier - - -class ToolsFunctionDefUpdate(pydantic_v1.BaseModel): - """ - Function definition - """ - - name: typing.Optional[CommonValidPythonIdentifier] = pydantic_v1.Field(default=None) - """ - DO NOT USE: This will be overriden by the tool name. Here only for compatibility reasons. - """ - - description: typing.Optional[CommonIdentifierSafeUnicode] = pydantic_v1.Field( - default=None - ) - """ - Description of the function - """ - - parameters: typing.Optional[typing.Dict[str, typing.Any]] = pydantic_v1.Field( - default=None - ) - """ - The parameters the function accepts - """ - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index a6d14d8b5..8db25188c 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "annotated-types" @@ -1751,13 +1751,13 @@ files = [ [[package]] name = "openai" -version = "1.40.2" +version = "1.40.3" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.2-py3-none-any.whl", hash = "sha256:38068f858f310b4fd4b0ea8734c3efcfde3c15a2978311e1453bd84817231b96"}, - {file = "openai-1.40.2.tar.gz", hash = "sha256:2180e9070bd36084328248b3ce668964e8ddd2e9019e1d426e31dc54cc117bb5"}, + {file = "openai-1.40.3-py3-none-any.whl", hash = "sha256:09396cb6e2e15c921a5d872bf92841a60a9425da10dcd962b45fe7c4f48f8395"}, + {file = "openai-1.40.3.tar.gz", hash = "sha256:f2ffe907618240938c59d7ccc67dd01dc8c50be203c0077240db6758d2f02480"}, ] [package.dependencies] @@ -3220,13 +3220,13 @@ files = [ [[package]] name = "webcolors" -version = "24.6.0" +version = "24.8.0" description = "A library for working with the color formats defined by HTML and CSS." optional = false python-versions = ">=3.8" files = [ - {file = "webcolors-24.6.0-py3-none-any.whl", hash = "sha256:8cf5bc7e28defd1d48b9e83d5fc30741328305a8195c29a8e668fa45586568a1"}, - {file = "webcolors-24.6.0.tar.gz", hash = "sha256:1d160d1de46b3e81e58d0a280d0c78b467dc80f47294b91b1ad8029d2cedb55b"}, + {file = "webcolors-24.8.0-py3-none-any.whl", hash = "sha256:fc4c3b59358ada164552084a8ebee637c221e4059267d0f8325b3b560f6c7f0a"}, + {file = "webcolors-24.8.0.tar.gz", hash = "sha256:08b07af286a01bcd30d583a7acadf629583d1f79bfef27dd2c2c5c263817277d"}, ] [package.extras] From 128f07495c6c3dbc09b0593a6ac7393d82cb90f4 Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Sat, 10 Aug 2024 11:57:52 -0400 Subject: [PATCH 024/110] Add tests for docs routes Add tests for docs routes in `agents-api/tests/test_docs_routes.py`. * **Create Docs Tests** - Add test for creating user doc. - Add test for creating agent doc. * **Delete Doc Test** - Add test for deleting a doc. * **Get Doc Test** - Add test for getting a doc. * **List Docs Tests** - Add test for listing user docs. - Add test for listing agent docs. * **Search Docs Tests** - Add test for searching user docs. - Add test for searching agent docs. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/julep-ai/julep?shareId=XXXX-XXXX-XXXX-XXXX). --- agents-api/tests/test_docs_routes.py | 179 +++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 agents-api/tests/test_docs_routes.py diff --git a/agents-api/tests/test_docs_routes.py b/agents-api/tests/test_docs_routes.py new file mode 100644 index 000000000..388590fe8 --- /dev/null +++ b/agents-api/tests/test_docs_routes.py @@ -0,0 +1,179 @@ +from uuid import uuid4 + +from ward import test + +from agents_api.autogen.openapi_model import CreateDocRequest, TextOnlyDocSearchRequest +from tests.fixtures import make_request, test_agent, test_user + + +@test("route: create user doc") +def _(make_request=make_request, user=test_user): + data = dict( + title="Test User Doc", + content=["This is a test user document."], + ) + + response = make_request( + method="POST", + url=f"/users/{user.id}/docs", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: create agent doc") +def _(make_request=make_request, agent=test_agent): + data = dict( + title="Test Agent Doc", + content=["This is a test agent document."], + ) + + response = make_request( + method="POST", + url=f"/agents/{agent.id}/docs", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: delete doc") +def _(make_request=make_request, agent=test_agent): + data = dict( + title="Test Agent Doc", + content=["This is a test agent document."], + ) + + response = make_request( + method="POST", + url=f"/agents/{agent.id}/docs", + json=data, + ) + doc_id = response.json()["id"] + + response = make_request( + method="DELETE", + url=f"/docs/{doc_id}", + ) + + assert response.status_code == 202 + + response = make_request( + method="GET", + url=f"/docs/{doc_id}", + ) + + assert response.status_code == 404 + + +@test("route: get doc") +def _(make_request=make_request, agent=test_agent): + data = dict( + title="Test Agent Doc", + content=["This is a test agent document."], + ) + + response = make_request( + method="POST", + url=f"/agents/{agent.id}/docs", + json=data, + ) + doc_id = response.json()["id"] + + response = make_request( + method="GET", + url=f"/docs/{doc_id}", + ) + + assert response.status_code == 200 + + +@test("route: list user docs") +def _(make_request=make_request, user=test_user): + response = make_request( + method="GET", + url=f"/users/{user.id}/docs", + ) + + assert response.status_code == 200 + response = response.json() + docs = response["items"] + + assert isinstance(docs, list) + + +@test("route: list agent docs") +def _(make_request=make_request, agent=test_agent): + response = make_request( + method="GET", + url=f"/agents/{agent.id}/docs", + ) + + assert response.status_code == 200 + response = response.json() + docs = response["items"] + + assert isinstance(docs, list) + + +@test("route: search user docs") +def _(make_request=make_request, user=test_user): + data = dict( + title="Test User Doc", + content=["This is a test user document."], + ) + + response = make_request( + method="POST", + url=f"/users/{user.id}/docs", + json=data, + ) + + search_params = TextOnlyDocSearchRequest( + text="test", + limit=1, + ) + + response = make_request( + method="POST", + url=f"/users/{user.id}/search", + json=search_params.dict(), + ) + + assert response.status_code == 200 + response = response.json() + docs = response["docs"] + + assert isinstance(docs, list) + + +@test("route: search agent docs") +def _(make_request=make_request, agent=test_agent): + data = dict( + title="Test Agent Doc", + content=["This is a test agent document."], + ) + + response = make_request( + method="POST", + url=f"/agents/{agent.id}/docs", + json=data, + ) + + search_params = TextOnlyDocSearchRequest( + text="test", + limit=1, + ) + + response = make_request( + method="POST", + url=f"/agents/{agent.id}/search", + json=search_params.dict(), + ) + + assert response.status_code == 200 + response = response.json() + docs = response["docs"] + + assert isinstance(docs, list) From e0c31400064cbc568d5d07f62a4e189bc39b4216 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 10 Aug 2024 13:36:44 -0400 Subject: [PATCH 025/110] feat(agents-api): Add docs tests Signed-off-by: Diwank Tomer --- agents-api/agents_api/common/utils/debug.py | 4 +- .../agents_api/models/agent/create_agent.py | 3 +- .../agents_api/models/docs/create_doc.py | 19 +- agents-api/agents_api/models/docs/get_doc.py | 3 + .../models/docs/search_docs_by_text.py | 18 + .../agents_api/routers/docs/__init__.py | 2 +- .../agents_api/routers/docs/create_doc.py | 4 +- .../agents_api/routers/docs/delete_doc.py | 33 +- .../agents_api/routers/docs/search_docs.py | 2 + .../agents_api/routers/users/update_user.py | 2 +- agents-api/agents_api/web.py | 2 + ...igrate_1723307805_add_lsh_index_to_docs.py | 44 +++ agents-api/poetry.lock | 4 +- agents-api/tests/fixtures.py | 25 ++ agents-api/tests/test_docs_routes.py | 61 ++-- sdks/python/julep/api/client.py | 320 ++++++++++++------ sdks/python/julep/api/reference.md | 231 +++++++++---- sdks/ts/src/api/services/DefaultService.ts | 75 ++-- typespec/docs/endpoints.tsp | 5 +- 19 files changed, 615 insertions(+), 242 deletions(-) create mode 100644 agents-api/migrations/migrate_1723307805_add_lsh_index_to_docs.py diff --git a/agents-api/agents_api/common/utils/debug.py b/agents-api/agents_api/common/utils/debug.py index c6e73f263..0be9eabb4 100644 --- a/agents-api/agents_api/common/utils/debug.py +++ b/agents-api/agents_api/common/utils/debug.py @@ -6,7 +6,9 @@ def pdb_on_exception(fn): def wrapper(*args, **kwargs): try: return fn(*args, **kwargs) - except Exception: + except Exception as exc: + print(repr(getattr(exc, "__cause__", exc))) + import pdb import traceback diff --git a/agents-api/agents_api/models/agent/create_agent.py b/agents-api/agents_api/models/agent/create_agent.py index 3ac561a0b..6b649afbb 100644 --- a/agents-api/agents_api/models/agent/create_agent.py +++ b/agents-api/agents_api/models/agent/create_agent.py @@ -60,12 +60,13 @@ def create_agent( # Extract the agent data from the payload data.metadata = data.metadata or {} + data.default_settings = data.default_settings or {} + data.instructions = ( data.instructions if isinstance(data.instructions, list) else [data.instructions] ) - data.default_settings = data.default_settings agent_data = data.model_dump(exclude_unset=True) default_settings = agent_data.pop("default_settings") diff --git a/agents-api/agents_api/models/docs/create_doc.py b/agents-api/agents_api/models/docs/create_doc.py index 23c849515..3a86ad0e5 100644 --- a/agents-api/agents_api/models/docs/create_doc.py +++ b/agents-api/agents_api/models/docs/create_doc.py @@ -30,7 +30,6 @@ one=True, transform=lambda d: { "id": UUID(d["doc_id"]), - "content": [], # <-- Note: we do not return content on creation **d, }, ) @@ -87,6 +86,10 @@ def create_doc( create_snippets_query = f""" ?[{snippet_cols}] <- $snippet_rows + + :create _snippets {{ {snippet_cols} }} + }} {{ + ?[{snippet_cols}] <- $snippet_rows :insert snippets {{ {snippet_cols} }} :returning """ @@ -94,8 +97,22 @@ def create_doc( # Construct the datalog query for creating the document and its snippets. create_doc_query = f""" ?[{doc_cols}] <- $doc_rows + + :create _docs {{ {doc_cols} }} + }} {{ + ?[{doc_cols}] <- $doc_rows :insert docs {{ {doc_cols} }} :returning + }} {{ + snippet_rows[collect(content)] := + *_snippets {{ + content + }} + + ?[{doc_cols}, content, created_at] := + *_docs {{ {doc_cols} }}, + snippet_rows[content], + created_at = now() """ queries = [ diff --git a/agents-api/agents_api/models/docs/get_doc.py b/agents-api/agents_api/models/docs/get_doc.py index fcac5b0a4..4a06333c5 100644 --- a/agents-api/agents_api/models/docs/get_doc.py +++ b/agents-api/agents_api/models/docs/get_doc.py @@ -19,6 +19,9 @@ @rewrap_exceptions( { + lambda e: isinstance(e, AssertionError) + and "Expected one result" + in repr(e): partialclass(HTTPException, status_code=404), QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), TypeError: partialclass(HTTPException, status_code=400), diff --git a/agents-api/agents_api/models/docs/search_docs_by_text.py b/agents-api/agents_api/models/docs/search_docs_by_text.py index 901e3ea31..8befeb07d 100644 --- a/agents-api/agents_api/models/docs/search_docs_by_text.py +++ b/agents-api/agents_api/models/docs/search_docs_by_text.py @@ -76,6 +76,24 @@ def search_docs_by_text( doc_id }} + search_result[ + doc_id, + snippet_data, + distance, + ] := + input[owner_id, query], + candidate[doc_id], + ~snippets:lsh {{ + doc_id, + index, + content + | + query: query, + k: {k}, + }}, + distance = 10000000, # Very large distance to depict no distance + snippet_data = [index, content] + search_result[ doc_id, snippet_data, diff --git a/agents-api/agents_api/routers/docs/__init__.py b/agents-api/agents_api/routers/docs/__init__.py index 2db2d042a..0d9fe8b5c 100644 --- a/agents-api/agents_api/routers/docs/__init__.py +++ b/agents-api/agents_api/routers/docs/__init__.py @@ -1,6 +1,6 @@ # ruff: noqa: F401 from .create_doc import create_agent_doc, create_user_doc -from .delete_doc import delete_doc +from .delete_doc import delete_agent_doc, delete_user_doc from .get_doc import get_doc from .list_docs import list_agent_docs, list_user_docs from .router import router diff --git a/agents-api/agents_api/routers/docs/create_doc.py b/agents-api/agents_api/routers/docs/create_doc.py index d99468733..0b43fe8eb 100644 --- a/agents-api/agents_api/routers/docs/create_doc.py +++ b/agents-api/agents_api/routers/docs/create_doc.py @@ -23,7 +23,7 @@ async def create_user_doc( data=data, ) - return ResourceCreatedResponse(id=doc.id, created_at=doc.created_at) + return ResourceCreatedResponse(id=doc.id, created_at=doc.created_at, jobs=[]) @router.post("/agents/{agent_id}/docs", status_code=HTTP_201_CREATED, tags=["docs"]) @@ -39,4 +39,4 @@ async def create_agent_doc( data=data, ) - return ResourceCreatedResponse(id=doc.id, created_at=doc.created_at) + return ResourceCreatedResponse(id=doc.id, created_at=doc.created_at, jobs=[]) diff --git a/agents-api/agents_api/routers/docs/delete_doc.py b/agents-api/agents_api/routers/docs/delete_doc.py index d1ae4416f..c31bf4051 100644 --- a/agents-api/agents_api/routers/docs/delete_doc.py +++ b/agents-api/agents_api/routers/docs/delete_doc.py @@ -10,8 +10,33 @@ from .router import router -@router.delete("/docs/{doc_id}", status_code=HTTP_202_ACCEPTED, tags=["docs"]) -async def delete_doc( - doc_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] +@router.delete( + "/agents/{agent_id}/docs/{doc_id}", status_code=HTTP_202_ACCEPTED, tags=["docs"] +) +async def delete_agent_doc( + doc_id: UUID4, + agent_id: UUID4, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> ResourceDeletedResponse: - return delete_doc_query(developer_id=x_developer_id, doc_id=doc_id) + return delete_doc_query( + developer_id=x_developer_id, + owner_id=agent_id, + owner_type="agent", + doc_id=doc_id, + ) + + +@router.delete( + "/users/{user_id}/docs/{doc_id}", status_code=HTTP_202_ACCEPTED, tags=["docs"] +) +async def delete_user_doc( + doc_id: UUID4, + user_id: UUID4, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], +) -> ResourceDeletedResponse: + return delete_doc_query( + developer_id=x_developer_id, + owner_id=user_id, + owner_type="user", + doc_id=doc_id, + ) diff --git a/agents-api/agents_api/routers/docs/search_docs.py b/agents-api/agents_api/routers/docs/search_docs.py index 8de06dd17..0e5430a7a 100644 --- a/agents-api/agents_api/routers/docs/search_docs.py +++ b/agents-api/agents_api/routers/docs/search_docs.py @@ -74,6 +74,7 @@ async def search_user_docs( owner_id=user_id, **params, ) + end = time.time() time_taken = end - start @@ -101,6 +102,7 @@ async def search_agent_docs( owner_id=agent_id, **params, ) + end = time.time() time_taken = end - start diff --git a/agents-api/agents_api/routers/users/update_user.py b/agents-api/agents_api/routers/users/update_user.py index d80d68f83..258023173 100644 --- a/agents-api/agents_api/routers/users/update_user.py +++ b/agents-api/agents_api/routers/users/update_user.py @@ -18,5 +18,5 @@ async def update_user( return update_user_query( developer_id=x_developer_id, user_id=user_id, - update_user=data, + data=data, ) diff --git a/agents-api/agents_api/web.py b/agents-api/agents_api/web.py index 23c34a970..2b85ece5f 100644 --- a/agents-api/agents_api/web.py +++ b/agents-api/agents_api/web.py @@ -21,6 +21,7 @@ from agents_api.exceptions import PromptTooBigError from agents_api.routers import ( agents, + docs, jobs, sessions, tasks, @@ -93,6 +94,7 @@ def register_exceptions(app: FastAPI): app.include_router(sessions.router) app.include_router(users.router) app.include_router(jobs.router) +app.include_router(docs.router) app.include_router(tasks.router) diff --git a/agents-api/migrations/migrate_1723307805_add_lsh_index_to_docs.py b/agents-api/migrations/migrate_1723307805_add_lsh_index_to_docs.py new file mode 100644 index 000000000..1268fa5c4 --- /dev/null +++ b/agents-api/migrations/migrate_1723307805_add_lsh_index_to_docs.py @@ -0,0 +1,44 @@ +#/usr/bin/env python3 + +MIGRATION_ID = "add_lsh_index_to_docs" +CREATED_AT = 1723307805.007054 + +# See: https://docs.cozodb.org/en/latest/vector.html#full-text-search-fts +snippets_lsh_index = dict( + up=""" + ::lsh create snippets:lsh { + extractor: content, + tokenizer: Simple, + filters: [Stopwords('en')], + n_perm: 200, + target_threshold: 0.9, + n_gram: 3, + false_positive_weight: 1.0, + false_negative_weight: 1.0, + } + """, + down=""" + ::lsh drop snippets:lsh + """, +) + +queries = [ + snippets_lsh_index, +] + + +def run(client, queries): + joiner = "}\n\n{" + + query = joiner.join(queries) + query = f"{{\n{query}\n}}" + + client.run(query) + + +def up(client): + run(client, [q["up"] for q in queries]) + + +def down(client): + run(client, [q["down"] for q in reversed(queries)]) diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 7afec2778..38ffe14d1 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -4395,7 +4395,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\")"} +greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} typing-extensions = ">=4.6.0" [package.extras] diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index b0b7faceb..e04cb316d 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -157,6 +157,31 @@ def test_doc( ) +@fixture(scope="global") +def test_user_doc( + client=cozo_client, + developer_id=test_developer_id, + user=test_user, +): + doc = create_doc( + developer_id=developer_id, + owner_type="user", + owner_id=user.id, + data=CreateDocRequest(title="Hello", content=["World"]), + client=client, + ) + + yield doc + + delete_doc( + developer_id=developer_id, + doc_id=doc.id, + owner_type="user", + owner_id=user.id, + client=client, + ) + + @fixture(scope="global") def test_task( client=cozo_client, diff --git a/agents-api/tests/test_docs_routes.py b/agents-api/tests/test_docs_routes.py index 388590fe8..3bd68cb0b 100644 --- a/agents-api/tests/test_docs_routes.py +++ b/agents-api/tests/test_docs_routes.py @@ -1,9 +1,6 @@ -from uuid import uuid4 - from ward import test -from agents_api.autogen.openapi_model import CreateDocRequest, TextOnlyDocSearchRequest -from tests.fixtures import make_request, test_agent, test_user +from tests.fixtures import make_request, test_agent, test_user, test_doc, test_user_doc @test("route: create user doc") @@ -37,6 +34,10 @@ def _(make_request=make_request, agent=test_agent): assert response.status_code == 201 + # FIXME: Should create a job to process the document + # result = response.json() + # assert len(result["jobs"]) > 0 + @test("route: delete doc") def _(make_request=make_request, agent=test_agent): @@ -54,7 +55,7 @@ def _(make_request=make_request, agent=test_agent): response = make_request( method="DELETE", - url=f"/docs/{doc_id}", + url=f"/agents/{agent.id}/docs/{doc_id}", ) assert response.status_code == 202 @@ -117,28 +118,17 @@ def _(make_request=make_request, agent=test_agent): assert isinstance(docs, list) -@test("route: search user docs") -def _(make_request=make_request, user=test_user): - data = dict( - title="Test User Doc", - content=["This is a test user document."], - ) - - response = make_request( - method="POST", - url=f"/users/{user.id}/docs", - json=data, - ) - - search_params = TextOnlyDocSearchRequest( - text="test", +@test("route: search agent docs") +def _(make_request=make_request, agent=test_agent, doc=test_doc): + search_params = dict( + text=doc.content[0], limit=1, ) response = make_request( method="POST", - url=f"/users/{user.id}/search", - json=search_params.dict(), + url=f"/agents/{agent.id}/search", + json=search_params, ) assert response.status_code == 200 @@ -146,30 +136,20 @@ def _(make_request=make_request, user=test_user): docs = response["docs"] assert isinstance(docs, list) + assert len(docs) >= 1 -@test("route: search agent docs") -def _(make_request=make_request, agent=test_agent): - data = dict( - title="Test Agent Doc", - content=["This is a test agent document."], - ) - - response = make_request( - method="POST", - url=f"/agents/{agent.id}/docs", - json=data, - ) - - search_params = TextOnlyDocSearchRequest( - text="test", +@test("route: search user docs") +def _(make_request=make_request, user=test_user, doc=test_user_doc): + search_params = dict( + text=doc.content[0], limit=1, ) response = make_request( method="POST", - url=f"/agents/{agent.id}/search", - json=search_params.dict(), + url=f"/users/{user.id}/search", + json=search_params, ) assert response.status_code == 200 @@ -177,3 +157,6 @@ def _(make_request=make_request, agent=test_agent): docs = response["docs"] assert isinstance(docs, list) + + # FIXME: This test is failing because the search is not returning the expected results + # assert len(docs) >= 1 diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index c074e5c3b..f560e0bfe 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -820,6 +820,58 @@ def agent_docs_route_create( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def agent_docs_route_delete( + self, + id: CommonUuid, + child_id: CommonUuid, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceDeletedResponse: + """ + Delete a Doc for this Agent + + Parameters + ---------- + id : CommonUuid + ID of parent resource + + child_id : CommonUuid + ID of the resource to be deleted + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceDeletedResponse + The request has been accepted for processing, but processing has not yet completed. + + Examples + -------- + from julep.client import JulepApi + + client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + client.agent_docs_route_delete( + id="id", + child_id="child_id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"agents/{jsonable_encoder(id)}/docs/{jsonable_encoder(child_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceDeletedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def agents_docs_search_route_search( self, id: CommonUuid, @@ -1799,50 +1851,6 @@ def individual_docs_route_get( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - def individual_docs_route_delete( - self, id: CommonUuid, *, request_options: typing.Optional[RequestOptions] = None - ) -> CommonResourceDeletedResponse: - """ - Delete an existing Doc by id - - Parameters - ---------- - id : CommonUuid - ID of the resource - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CommonResourceDeletedResponse - The request has been accepted for processing, but processing has not yet completed. - - Examples - -------- - from julep.client import JulepApi - - client = JulepApi( - auth_key="YOUR_AUTH_KEY", - api_key="YOUR_API_KEY", - ) - client.individual_docs_route_delete( - id="id", - ) - """ - _response = self._client_wrapper.httpx_client.request( - f"docs/{jsonable_encoder(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CommonResourceDeletedResponse, _response.json()) # type: ignore - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) - def embed_route_embed( self, *, @@ -3497,6 +3505,58 @@ def user_docs_route_create( raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + def user_docs_route_delete( + self, + id: CommonUuid, + child_id: CommonUuid, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceDeletedResponse: + """ + Delete a Doc for this User + + Parameters + ---------- + id : CommonUuid + ID of parent resource + + child_id : CommonUuid + ID of the resource to be deleted + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceDeletedResponse + The request has been accepted for processing, but processing has not yet completed. + + Examples + -------- + from julep.client import JulepApi + + client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + client.user_docs_route_delete( + id="id", + child_id="child_id", + ) + """ + _response = self._client_wrapper.httpx_client.request( + f"users/{jsonable_encoder(id)}/docs/{jsonable_encoder(child_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceDeletedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + def user_docs_search_route_search( self, id: CommonUuid, @@ -4332,6 +4392,66 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def agent_docs_route_delete( + self, + id: CommonUuid, + child_id: CommonUuid, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceDeletedResponse: + """ + Delete a Doc for this Agent + + Parameters + ---------- + id : CommonUuid + ID of parent resource + + child_id : CommonUuid + ID of the resource to be deleted + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceDeletedResponse + The request has been accepted for processing, but processing has not yet completed. + + Examples + -------- + import asyncio + + from julep.client import AsyncJulepApi + + client = AsyncJulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.agent_docs_route_delete( + id="id", + child_id="child_id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"agents/{jsonable_encoder(id)}/docs/{jsonable_encoder(child_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceDeletedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def agents_docs_search_route_search( self, id: CommonUuid, @@ -5415,58 +5535,6 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) - async def individual_docs_route_delete( - self, id: CommonUuid, *, request_options: typing.Optional[RequestOptions] = None - ) -> CommonResourceDeletedResponse: - """ - Delete an existing Doc by id - - Parameters - ---------- - id : CommonUuid - ID of the resource - - request_options : typing.Optional[RequestOptions] - Request-specific configuration. - - Returns - ------- - CommonResourceDeletedResponse - The request has been accepted for processing, but processing has not yet completed. - - Examples - -------- - import asyncio - - from julep.client import AsyncJulepApi - - client = AsyncJulepApi( - auth_key="YOUR_AUTH_KEY", - api_key="YOUR_API_KEY", - ) - - - async def main() -> None: - await client.individual_docs_route_delete( - id="id", - ) - - - asyncio.run(main()) - """ - _response = await self._client_wrapper.httpx_client.request( - f"docs/{jsonable_encoder(id)}", - method="DELETE", - request_options=request_options, - ) - try: - if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CommonResourceDeletedResponse, _response.json()) # type: ignore - _response_json = _response.json() - except JSONDecodeError: - raise ApiError(status_code=_response.status_code, body=_response.text) - raise ApiError(status_code=_response.status_code, body=_response_json) - async def embed_route_embed( self, *, @@ -7337,6 +7405,66 @@ async def main() -> None: raise ApiError(status_code=_response.status_code, body=_response.text) raise ApiError(status_code=_response.status_code, body=_response_json) + async def user_docs_route_delete( + self, + id: CommonUuid, + child_id: CommonUuid, + *, + request_options: typing.Optional[RequestOptions] = None, + ) -> CommonResourceDeletedResponse: + """ + Delete a Doc for this User + + Parameters + ---------- + id : CommonUuid + ID of parent resource + + child_id : CommonUuid + ID of the resource to be deleted + + request_options : typing.Optional[RequestOptions] + Request-specific configuration. + + Returns + ------- + CommonResourceDeletedResponse + The request has been accepted for processing, but processing has not yet completed. + + Examples + -------- + import asyncio + + from julep.client import AsyncJulepApi + + client = AsyncJulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", + ) + + + async def main() -> None: + await client.user_docs_route_delete( + id="id", + child_id="child_id", + ) + + + asyncio.run(main()) + """ + _response = await self._client_wrapper.httpx_client.request( + f"users/{jsonable_encoder(id)}/docs/{jsonable_encoder(child_id)}", + method="DELETE", + request_options=request_options, + ) + try: + if 200 <= _response.status_code < 300: + return pydantic_v1.parse_obj_as(CommonResourceDeletedResponse, _response.json()) # type: ignore + _response_json = _response.json() + except JSONDecodeError: + raise ApiError(status_code=_response.status_code, body=_response.text) + raise ApiError(status_code=_response.status_code, body=_response_json) + async def user_docs_search_route_search( self, id: CommonUuid, diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index cc0693d02..33e041f65 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -936,6 +936,86 @@ client.agent_docs_route_create(
+ +
+ + +
client.agent_docs_route_delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a Doc for this Agent +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from julep.client import JulepApi + +client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", +) +client.agent_docs_route_delete( + id="id", + child_id="child_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `CommonUuid` — ID of parent resource + +
+
+ +
+
+ +**child_id:** `CommonUuid` — ID of the resource to be deleted + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
@@ -2416,77 +2496,6 @@ client.individual_docs_route_get(
- - - - -
client.individual_docs_route_delete(...) -
-
- -#### 📝 Description - -
-
- -
-
- -Delete an existing Doc by id -
-
-
-
- -#### 🔌 Usage - -
-
- -
-
- -```python -from julep.client import JulepApi - -client = JulepApi( - auth_key="YOUR_AUTH_KEY", - api_key="YOUR_API_KEY", -) -client.individual_docs_route_delete( - id="id", -) - -``` -
-
-
-
- -#### ⚙️ Parameters - -
-
- -
-
- -**id:** `CommonUuid` — ID of the resource - -
-
- -
-
- -**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. - -
-
-
-
- -
@@ -5012,6 +5021,86 @@ client.user_docs_route_create( + + + + +
client.user_docs_route_delete(...) +
+
+ +#### 📝 Description + +
+
+ +
+
+ +Delete a Doc for this User +
+
+
+
+ +#### 🔌 Usage + +
+
+ +
+
+ +```python +from julep.client import JulepApi + +client = JulepApi( + auth_key="YOUR_AUTH_KEY", + api_key="YOUR_API_KEY", +) +client.user_docs_route_delete( + id="id", + child_id="child_id", +) + +``` +
+
+
+
+ +#### ⚙️ Parameters + +
+
+ +
+
+ +**id:** `CommonUuid` — ID of parent resource + +
+
+ +
+
+ +**child_id:** `CommonUuid` — ID of the resource to be deleted + +
+
+ +
+
+ +**request_options:** `typing.Optional[RequestOptions]` — Request-specific configuration. + +
+
+
+
+ +
diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index c71a6d7e9..b671d3fb5 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -313,6 +313,33 @@ export class DefaultService { mediaType: "application/json", }); } + /** + * Delete a Doc for this Agent + * @returns Common_ResourceDeletedResponse The request has been accepted for processing, but processing has not yet completed. + * @throws ApiError + */ + public agentDocsRouteDelete({ + id, + childId, + }: { + /** + * ID of parent resource + */ + id: Common_uuid; + /** + * ID of the resource to be deleted + */ + childId: Common_uuid; + }): CancelablePromise { + return this.httpRequest.request({ + method: "DELETE", + url: "/agents/{id}/docs/{child_id}", + path: { + id: id, + child_id: childId, + }, + }); + } /** * Search Docs owned by an Agent * @returns Docs_DocSearchResponse The request has succeeded. @@ -730,27 +757,6 @@ export class DefaultService { }, }); } - /** - * Delete an existing Doc by id - * @returns Common_ResourceDeletedResponse The request has been accepted for processing, but processing has not yet completed. - * @throws ApiError - */ - public individualDocsRouteDelete({ - id, - }: { - /** - * ID of the resource - */ - id: Common_uuid; - }): CancelablePromise { - return this.httpRequest.request({ - method: "DELETE", - url: "/docs/{id}", - path: { - id: id, - }, - }); - } /** * Embed a query for search * @returns Docs_EmbedQueryResponse The request has succeeded. @@ -1722,6 +1728,33 @@ export class DefaultService { mediaType: "application/json", }); } + /** + * Delete a Doc for this User + * @returns Common_ResourceDeletedResponse The request has been accepted for processing, but processing has not yet completed. + * @throws ApiError + */ + public userDocsRouteDelete({ + id, + childId, + }: { + /** + * ID of parent resource + */ + id: Common_uuid; + /** + * ID of the resource to be deleted + */ + childId: Common_uuid; + }): CancelablePromise { + return this.httpRequest.request({ + method: "DELETE", + url: "/users/{id}/docs/{child_id}", + path: { + id: id, + child_id: childId, + }, + }); + } /** * Search Docs owned by a User * @returns Docs_DocSearchResponse The request has succeeded. diff --git a/typespec/docs/endpoints.tsp b/typespec/docs/endpoints.tsp index 143d18f33..3600319c1 100644 --- a/typespec/docs/endpoints.tsp +++ b/typespec/docs/endpoints.tsp @@ -19,15 +19,16 @@ namespace Docs; interface UserEndpoints extends ChildLimitOffsetPagination, + ChildDeleteEndpoint<"Delete a Doc for this User">, ChildCreateEndpoint {} interface AgentEndpoints extends ChildLimitOffsetPagination, + ChildDeleteEndpoint<"Delete a Doc for this Agent">, ChildCreateEndpoint {} interface IndividualDocEndpoints - extends GetEndpoint, - DeleteEndpoint<"Delete an existing Doc by id"> {} + extends GetEndpoint {} interface SearchEndpoints { @doc(DocString) From 28afb53a9b0f397e8d6e5caadd2119443b2c2992 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Sat, 10 Aug 2024 20:52:07 +0300 Subject: [PATCH 026/110] feat: Implement error step --- .../agents_api/activities/summarization.py | 22 +++++-- .../activities/task_steps/__init__.py | 57 ++++++++++++++----- agents-api/agents_api/worker/__main__.py | 2 - .../agents_api/workflows/task_execution.py | 17 +++--- 4 files changed, 69 insertions(+), 29 deletions(-) diff --git a/agents-api/agents_api/activities/summarization.py b/agents-api/agents_api/activities/summarization.py index 81e694bd5..2e61ab9e4 100644 --- a/agents-api/agents_api/activities/summarization.py +++ b/agents-api/agents_api/activities/summarization.py @@ -5,14 +5,16 @@ from typing import Callable from uuid import UUID +import pandas as pd from litellm import acompletion from temporalio import activity from agents_api.common.protocol.entries import Entry -from agents_api.models.entry.entries_summarization import ( - entries_summarization_query, - get_toplevel_entries_query, -) + +# from agents_api.models.entry.entries_summarization import ( +# entries_summarization_query, +# get_toplevel_entries_query, +# ) from agents_api.rec_sum.entities import get_entities from agents_api.rec_sum.summarize import summarize_messages from agents_api.rec_sum.trim import trim_messages @@ -20,6 +22,18 @@ from ..env import model_api_key, model_inference_url, summarization_model_name from ..model_registry import LOCAL_MODELS + +# TODO: remove stubs +def entries_summarization_query(*args, **kwargs): + return pd.DataFrame() + + +def get_toplevel_entries_query(*args, **kwargs): + return pd.DataFrame() + + +# + example_previous_memory = """ Speaker 1: Composes and listens to music. Likes to buy basketball shoes but doesn't wear them often. """.strip() diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index afd0de233..9640dc6d3 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -1,4 +1,5 @@ import asyncio +from typing import Literal from uuid import uuid4 from openai.types.chat.chat_completion import ChatCompletion @@ -7,8 +8,8 @@ from ...autogen.openapi_model import ( CreateTransitionRequest, + ErrorWorkflowStep, EvaluateStep, - # ErrorWorkflowStep, IfElseWorkflowStep, InputChatMLMessage, PromptStep, @@ -28,8 +29,28 @@ from ...models.execution.update_execution import ( update_execution as update_execution_query, ) -from ...routers.sessions.protocol import Settings -from ...routers.sessions.session import llm_generate + +# from ...routers.sessions.protocol import Settings +# from ...routers.sessions.session import llm_generate + + +# TODO: remove stubs +class Settings: + def __init__(self, *args, **kwargs): + pass + + +def llm_generate(*args, **kwargs): + return ChatCompletion( + id="", + choices=[], + created=0, + model="", + object="chat.completion", + ) + + +# @activity.defn @@ -40,7 +61,12 @@ async def prompt_step(context: StepContext) -> dict: context_data: dict = context.model_dump() # Render template messages - template_messages: list[InputChatMLMessage] = context.definition.prompt + prompt = ( + [InputChatMLMessage(content=context.definition.prompt)] + if isinstance(context.definition.prompt, str) + else context.definition.prompt + ) + template_messages: list[InputChatMLMessage] = prompt messages = await asyncio.gather( *[ render_template(msg.content, context_data, skip_vars=["developer_id"]) @@ -100,13 +126,7 @@ async def tool_call_step(context: StepContext) -> dict: # get tool by id # call tool - -# @activity.defn -# async def error_step(context: StepContext) -> dict: -# if not isinstance(context.definition, ErrorWorkflowStep): -# return {} - -# return {"error": context.definition.error} + return {} @activity.defn @@ -127,7 +147,16 @@ async def if_else_step(context: StepContext) -> dict: async def transition_step( context: StepContext, transition_info: TransitionInfo, -) -> dict: + execution_status: Literal[ + "queued", + "starting", + "running", + "awaiting_input", + "succeeded", + "failed", + "cancelled", + ] = "awaiting_input", +): print("Running transition step") # raise NotImplementedError() @@ -152,10 +181,10 @@ async def transition_step( task_id=context.task.id, execution_id=context.execution.id, data=UpdateExecutionRequest( - status="awaiting_input", + status=execution_status, ), ) # Raise if it's a waiting step - if transition_info.type == "awaiting_input": + if execution_status == "awaiting_input": activity.raise_complete_async() diff --git a/agents-api/agents_api/worker/__main__.py b/agents-api/agents_api/worker/__main__.py index 271158385..544a28b4d 100644 --- a/agents-api/agents_api/worker/__main__.py +++ b/agents-api/agents_api/worker/__main__.py @@ -20,7 +20,6 @@ from ..activities.summarization import summarization from ..activities.task_steps import ( evaluate_step, - # error_step, if_else_step, prompt_step, tool_call_step, @@ -77,7 +76,6 @@ async def main(): evaluate_step, yield_step, tool_call_step, - # error_step, if_else_step, transition_step, ] diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 13c1af9f9..1b1d94bd3 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -14,7 +14,7 @@ transition_step, ) from ..autogen.openapi_model import ( - # ErrorWorkflowStep, + ErrorWorkflowStep, EvaluateStep, IfElseWorkflowStep, PromptStep, @@ -57,7 +57,7 @@ async def run( inputs=previous_inputs, ) - should_wait = False + should_wait, is_error = False, False # Run the step match step: case PromptStep(): @@ -88,12 +88,8 @@ async def run( context, schedule_to_close_timeout=timedelta(seconds=600), ) - # case ErrorWorkflowStep(): - # result = await workflow.execute_activity( - # error_step, - # context, - # schedule_to_close_timeout=timedelta(seconds=600), - # ) + case ErrorWorkflowStep(): + is_error = True case IfElseWorkflowStep(): outputs = await workflow.execute_activity( if_else_step, @@ -116,7 +112,9 @@ async def run( is_last = step_idx + 1 == len(current_workflow) # Transition type transition_type = ( - "awaiting_input" if should_wait else ("finish" if is_last else "step") + "awaiting_input" + if should_wait + else ("finish" if is_last else ("error" if is_error else "step")) ) # Transition to the next step @@ -131,6 +129,7 @@ async def run( args=[ context, transition_info, + "failed" if is_error else "awaiting_input", ], schedule_to_close_timeout=timedelta(seconds=600), ) From 2d379b8f3a49630bbcdf4c97efbfd3db94f16ed9 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 10 Aug 2024 19:18:30 -0400 Subject: [PATCH 027/110] feat(agents-api): Fix prepare_chat_context Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Agents.py | 18 +- agents-api/agents_api/autogen/Chat.py | 300 ++++++++++++--- agents-api/agents_api/autogen/Entries.py | 2 +- agents-api/agents_api/autogen/Tasks.py | 202 +--------- .../agents_api/autogen/openapi_model.py | 3 + .../agents_api/common/protocol/sessions.py | 19 +- agents-api/agents_api/models/docs/get_doc.py | 5 +- .../models/session/prepare_chat_context.py | 71 ++-- .../agents_api/routers/sessions/__init__.py | 1 + .../agents_api/routers/sessions/chat.py | 33 ++ ...igrate_1723307805_add_lsh_index_to_docs.py | 2 +- agents-api/poetry.lock | 160 ++++---- agents-api/tests/test_docs_routes.py | 2 +- agents-api/tests/test_session_queries.py | 27 ++ fern/generators.yml | 5 - sdks/python/julep/api/__init__.py | 44 +-- sdks/python/julep/api/client.py | 353 ++++++++++++++---- sdks/python/julep/api/reference.md | 204 +++++++++- sdks/python/julep/api/types/__init__.py | 60 +-- sdks/python/julep/api/types/agents_agent.py | 4 +- .../types/agents_agent_default_settings.py | 11 - .../api/types/agents_create_agent_request.py | 8 +- ...s_create_agent_request_default_settings.py | 11 - ...ts_patch_agent_request_default_settings.py | 11 - .../api/types/agents_update_agent_request.py | 8 +- ...s_update_agent_request_default_settings.py | 11 - ...oice.py => chat_chat_input_tool_choice.py} | 2 +- ...ettings_agent.py => chat_chat_settings.py} | 26 +- ...tings.py => chat_default_chat_settings.py} | 30 +- .../types/chat_generation_preset_settings.py | 46 --- .../api/types/chat_message_chat_response.py | 4 +- ...chat_message_chat_response_choices_item.py | 10 + .../julep/api/types/chat_open_ai_settings.py | 60 --- .../api/types/chat_route_generate_request.py | 15 - .../chat_route_generate_request_agent.py | 154 -------- ...oute_generate_request_frequency_penalty.py | 149 -------- ...e_request_frequency_penalty_tool_choice.py | 9 - .../chat_route_generate_request_preset.py | 135 ------- ...ute_generate_request_preset_tool_choice.py | 9 - .../tasks_create_task_request_main_item.py | 4 +- .../tasks_patch_task_request_main_item.py | 4 +- .../julep/api/types/tasks_prompt_step.py | 4 +- .../api/types/tasks_prompt_step_settings.py | 15 - ..._prompt_step_settings_frequency_penalty.py | 110 ------ .../tasks_prompt_step_settings_preset.py | 96 ----- .../julep/api/types/tasks_task_main_item.py | 4 +- .../tasks_update_task_request_main_item.py | 4 +- sdks/ts/src/api/index.ts | 12 +- sdks/ts/src/api/models/Agents_Agent.ts | 9 +- .../api/models/Agents_CreateAgentRequest.ts | 9 +- .../api/models/Agents_PatchAgentRequest.ts | 9 +- .../api/models/Agents_UpdateAgentRequest.ts | 9 +- sdks/ts/src/api/models/Chat_ChatInput.ts | 102 +++++ sdks/ts/src/api/models/Chat_ChatSettings.ts | 75 ++++ ...ettings.ts => Chat_DefaultChatSettings.ts} | 26 +- .../models/Chat_GenerationPresetSettings.ts | 11 - .../api/models/Chat_MessageChatResponse.ts | 5 +- sdks/ts/src/api/models/Chat_OpenAISettings.ts | 22 -- sdks/ts/src/api/models/Tasks_PromptStep.ts | 150 +------- sdks/ts/src/api/schemas/$Agents_Agent.ts | 10 +- .../api/schemas/$Agents_CreateAgentRequest.ts | 10 +- .../api/schemas/$Agents_PatchAgentRequest.ts | 10 +- .../api/schemas/$Agents_UpdateAgentRequest.ts | 10 +- sdks/ts/src/api/schemas/$Chat_ChatInput.ts | 160 ++++++++ sdks/ts/src/api/schemas/$Chat_ChatSettings.ts | 118 ++++++ ...ttings.ts => $Chat_DefaultChatSettings.ts} | 34 +- .../schemas/$Chat_GenerationPresetSettings.ts | 17 - .../api/schemas/$Chat_MessageChatResponse.ts | 10 +- .../src/api/schemas/$Chat_OpenAISettings.ts | 34 -- sdks/ts/src/api/schemas/$Tasks_PromptStep.ts | 249 +----------- sdks/ts/src/api/services/DefaultService.ts | 224 +---------- typespec/chat/models.tsp | 43 +-- 72 files changed, 1567 insertions(+), 2266 deletions(-) create mode 100644 agents-api/agents_api/routers/sessions/chat.py delete mode 100644 sdks/python/julep/api/types/agents_agent_default_settings.py delete mode 100644 sdks/python/julep/api/types/agents_create_agent_request_default_settings.py delete mode 100644 sdks/python/julep/api/types/agents_patch_agent_request_default_settings.py delete mode 100644 sdks/python/julep/api/types/agents_update_agent_request_default_settings.py rename sdks/python/julep/api/types/{chat_route_generate_request_agent_tool_choice.py => chat_chat_input_tool_choice.py} (79%) rename sdks/python/julep/api/types/{tasks_prompt_step_settings_agent.py => chat_chat_settings.py} (82%) rename sdks/python/julep/api/types/{chat_v_llm_settings.py => chat_default_chat_settings.py} (71%) delete mode 100644 sdks/python/julep/api/types/chat_generation_preset_settings.py create mode 100644 sdks/python/julep/api/types/chat_message_chat_response_choices_item.py delete mode 100644 sdks/python/julep/api/types/chat_open_ai_settings.py delete mode 100644 sdks/python/julep/api/types/chat_route_generate_request.py delete mode 100644 sdks/python/julep/api/types/chat_route_generate_request_agent.py delete mode 100644 sdks/python/julep/api/types/chat_route_generate_request_frequency_penalty.py delete mode 100644 sdks/python/julep/api/types/chat_route_generate_request_frequency_penalty_tool_choice.py delete mode 100644 sdks/python/julep/api/types/chat_route_generate_request_preset.py delete mode 100644 sdks/python/julep/api/types/chat_route_generate_request_preset_tool_choice.py delete mode 100644 sdks/python/julep/api/types/tasks_prompt_step_settings.py delete mode 100644 sdks/python/julep/api/types/tasks_prompt_step_settings_frequency_penalty.py delete mode 100644 sdks/python/julep/api/types/tasks_prompt_step_settings_preset.py create mode 100644 sdks/ts/src/api/models/Chat_ChatInput.ts create mode 100644 sdks/ts/src/api/models/Chat_ChatSettings.ts rename sdks/ts/src/api/models/{Chat_vLLMSettings.ts => Chat_DefaultChatSettings.ts} (59%) delete mode 100644 sdks/ts/src/api/models/Chat_GenerationPresetSettings.ts delete mode 100644 sdks/ts/src/api/models/Chat_OpenAISettings.ts create mode 100644 sdks/ts/src/api/schemas/$Chat_ChatInput.ts create mode 100644 sdks/ts/src/api/schemas/$Chat_ChatSettings.ts rename sdks/ts/src/api/schemas/{$Chat_vLLMSettings.ts => $Chat_DefaultChatSettings.ts} (60%) delete mode 100644 sdks/ts/src/api/schemas/$Chat_GenerationPresetSettings.ts delete mode 100644 sdks/ts/src/api/schemas/$Chat_OpenAISettings.ts diff --git a/agents-api/agents_api/autogen/Agents.py b/agents-api/agents_api/autogen/Agents.py index 71b53c8df..1bd2d23f7 100644 --- a/agents-api/agents_api/autogen/Agents.py +++ b/agents-api/agents_api/autogen/Agents.py @@ -8,7 +8,7 @@ from pydantic import AwareDatetime, BaseModel, ConfigDict, Field -from .Chat import GenerationPresetSettings, OpenAISettings, VLLMSettings +from .Chat import DefaultChatSettings class Agent(BaseModel): @@ -47,9 +47,7 @@ class Agent(BaseModel): """ Instructions for the agent """ - default_settings: ( - GenerationPresetSettings | OpenAISettings | VLLMSettings | None - ) = None + default_settings: DefaultChatSettings | None = None """ Default settings for all sessions created by this agent """ @@ -86,9 +84,7 @@ class CreateAgentRequest(BaseModel): """ Instructions for the agent """ - default_settings: ( - GenerationPresetSettings | OpenAISettings | VLLMSettings | None - ) = None + default_settings: DefaultChatSettings | None = None """ Default settings for all sessions created by this agent """ @@ -125,9 +121,7 @@ class PatchAgentRequest(BaseModel): """ Instructions for the agent """ - default_settings: ( - GenerationPresetSettings | OpenAISettings | VLLMSettings | None - ) = None + default_settings: DefaultChatSettings | None = None """ Default settings for all sessions created by this agent """ @@ -164,9 +158,7 @@ class UpdateAgentRequest(BaseModel): """ Instructions for the agent """ - default_settings: ( - GenerationPresetSettings | OpenAISettings | VLLMSettings | None - ) = None + default_settings: DefaultChatSettings | None = None """ Default settings for all sessions created by this agent """ diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 6669fbcec..f46c6289a 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -6,10 +6,12 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from .Common import LogitBias from .Docs import DocReference -from .Entries import ChatMLMessage +from .Entries import ChatMLMessage, InputChatMLMessage +from .Tools import FunctionTool, NamedToolChoice class BaseChatOutput(BaseModel): @@ -62,6 +64,119 @@ class BaseTokenLogProb(BaseModel): bytes: Annotated[list[int] | None, Field(...)] +class ChatInput(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + messages: Annotated[list[InputChatMLMessage], Field(min_length=1)] + """ + A list of new input messages comprising the conversation so far. + """ + tools: Annotated[list[FunctionTool] | None, Field(None, min_length=1)] + """ + (Advanced) List of tools that are provided in addition to agent's default set of tools. + """ + tool_choice: Literal["auto", "none"] | NamedToolChoice | None = None + """ + Can be one of existing tools given to the agent earlier or the ones provided in this request. + """ + recall: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] + """ + Whether previous memories should be recalled or not (will be enabled in a future release) + """ + remember: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] + """ + Whether this interaction should form new memories or not (will be enabled in a future release) + """ + save: bool = True + """ + Whether this interaction should be stored in the session history or not + """ + model: Annotated[ + str | None, + Field( + None, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] + """ + Identifier of the model to be used + """ + stream: bool = False + """ + Indicates if the server should stream the response as it's generated + """ + stop: Annotated[list[str] | None, Field(None, max_length=4, min_length=1)] + """ + Up to 4 sequences where the API will stop generating further tokens. + """ + seed: Annotated[int | None, Field(None, ge=-1, le=1000)] + """ + If specified, the system will make a best effort to sample deterministically for that particular seed value + """ + max_tokens: Annotated[int | None, Field(None, ge=1)] + """ + The maximum number of tokens to generate in the chat completion + """ + logit_bias: dict[str, LogitBias] | None = None + """ + Modify the likelihood of specified tokens appearing in the completion + """ + response_format: CompletionResponseFormat | None = None + """ + Response format (set to `json_object` to restrict output to JSON) + """ + agent: UUID | None = None + """ + Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + """ + preset: ( + Literal[ + "problem_solving", + "conversational", + "fun", + "prose", + "creative", + "business", + "deterministic", + "code", + "multilingual", + ] + | None + ) = None + """ + Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + """ + frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] + """ + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + """ + top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + """ + Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + """ + repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + length_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + """ + min_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + """ + Minimum probability compared to leading token to be considered + """ + + class ChatOutputChunk(BaseChatOutput): """ Streaming chat completion output @@ -76,6 +191,95 @@ class ChatOutputChunk(BaseChatOutput): """ +class ChatSettings(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + model: Annotated[ + str | None, + Field( + None, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] + """ + Identifier of the model to be used + """ + stream: bool = False + """ + Indicates if the server should stream the response as it's generated + """ + stop: Annotated[list[str] | None, Field(None, max_length=4, min_length=1)] + """ + Up to 4 sequences where the API will stop generating further tokens. + """ + seed: Annotated[int | None, Field(None, ge=-1, le=1000)] + """ + If specified, the system will make a best effort to sample deterministically for that particular seed value + """ + max_tokens: Annotated[int | None, Field(None, ge=1)] + """ + The maximum number of tokens to generate in the chat completion + """ + logit_bias: dict[str, LogitBias] | None = None + """ + Modify the likelihood of specified tokens appearing in the completion + """ + response_format: CompletionResponseFormat | None = None + """ + Response format (set to `json_object` to restrict output to JSON) + """ + agent: UUID | None = None + """ + Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + """ + preset: ( + Literal[ + "problem_solving", + "conversational", + "fun", + "prose", + "creative", + "business", + "deterministic", + "code", + "multilingual", + ] + | None + ) = None + """ + Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + """ + frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] + """ + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + """ + top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + """ + Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + """ + repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + length_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + """ + min_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + """ + Minimum probability compared to leading token to be considered + """ + + class ChunkChatResponse(BaseChatResponse): model_config = ConfigDict( populate_by_name=True, @@ -118,7 +322,11 @@ class CompletionResponseFormat(BaseModel): """ -class GenerationPresetSettings(BaseModel): +class DefaultChatSettings(BaseModel): + """ + Default settings for the chat session (also used by the agent) + """ + model_config = ConfigDict( populate_by_name=True, ) @@ -139,6 +347,34 @@ class GenerationPresetSettings(BaseModel): """ Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) """ + frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] + """ + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + """ + top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + """ + Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + """ + repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + length_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + """ + min_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] + """ + Minimum probability compared to leading token to be considered + """ class LogProbResponse(BaseModel): @@ -151,8 +387,14 @@ class LogProbResponse(BaseModel): """ -class MessageChatResponse(ChunkChatResponse): - pass +class MessageChatResponse(BaseChatResponse): + model_config = ConfigDict( + populate_by_name=True, + ) + choices: list[SingleChatOutput | MultipleChatOutput] + """ + The deltas generated by the model + """ class MultipleChatOutput(BaseChatOutput): @@ -166,28 +408,6 @@ class MultipleChatOutput(BaseChatOutput): messages: list[ChatMLMessage] -class OpenAISettings(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - - class SingleChatOutput(BaseChatOutput): """ The output returned by the model. Note that, depending on the model provider, they might return more than one message. @@ -204,29 +424,3 @@ class TokenLogProb(BaseTokenLogProb): populate_by_name=True, ) top_logprobs: list[BaseTokenLogProb] - - -class VLLMSettings(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - length_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - """ - temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - min_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] - """ - Minimum probability compared to leading token to be considered - """ diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index d56b333d8..6c8bf9ca4 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, RootModel +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field from .Tools import ChosenToolCall, Tool, ToolResponse diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index 87f5a5263..d4eb09e41 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -8,8 +8,7 @@ from pydantic import AwareDatetime, BaseModel, ConfigDict, Field -from .Chat import CompletionResponseFormat -from .Common import LogitBias +from .Chat import ChatSettings from .Entries import InputChatMLMessage from .Tools import FunctionDef @@ -167,209 +166,12 @@ class PromptStep(BaseWorkflowStep): """ The prompt to run """ - settings: Settings | SettingsModel | SettingsModel1 + settings: ChatSettings """ Settings for the prompt """ -class Settings(BaseModel): - """ - Settings for the prompt - """ - - model_config = ConfigDict( - populate_by_name=True, - ) - model: Annotated[ - str | None, - Field( - None, - pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", - ), - ] - """ - Identifier of the model to be used - """ - stream: bool = False - """ - Indicates if the server should stream the response as it's generated - """ - stop: Annotated[list[str] | None, Field(None, max_length=4, min_length=1)] - """ - Up to 4 sequences where the API will stop generating further tokens. - """ - seed: Annotated[int | None, Field(None, ge=-1, le=1000)] - """ - If specified, the system will make a best effort to sample deterministically for that particular seed value - """ - max_tokens: Annotated[int | None, Field(None, ge=1)] - """ - The maximum number of tokens to generate in the chat completion - """ - logit_bias: dict[str, LogitBias] | None = None - """ - Modify the likelihood of specified tokens appearing in the completion - """ - response_format: CompletionResponseFormat | None = None - """ - Response format (set to `json_object` to restrict output to JSON) - """ - agent: UUID | None = None - """ - Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - """ - preset: ( - Literal[ - "problem_solving", - "conversational", - "fun", - "prose", - "creative", - "business", - "deterministic", - "code", - "multilingual", - ] - | None - ) = None - """ - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - """ - - -class SettingsModel(BaseModel): - """ - Settings for the prompt - """ - - model_config = ConfigDict( - populate_by_name=True, - ) - model: Annotated[ - str | None, - Field( - None, - pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", - ), - ] - """ - Identifier of the model to be used - """ - stream: bool = False - """ - Indicates if the server should stream the response as it's generated - """ - stop: Annotated[list[str] | None, Field(None, max_length=4, min_length=1)] - """ - Up to 4 sequences where the API will stop generating further tokens. - """ - seed: Annotated[int | None, Field(None, ge=-1, le=1000)] - """ - If specified, the system will make a best effort to sample deterministically for that particular seed value - """ - max_tokens: Annotated[int | None, Field(None, ge=1)] - """ - The maximum number of tokens to generate in the chat completion - """ - logit_bias: dict[str, LogitBias] | None = None - """ - Modify the likelihood of specified tokens appearing in the completion - """ - response_format: CompletionResponseFormat | None = None - """ - Response format (set to `json_object` to restrict output to JSON) - """ - agent: UUID | None = None - """ - Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - """ - frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - - -class SettingsModel1(BaseModel): - """ - Settings for the prompt - """ - - model_config = ConfigDict( - populate_by_name=True, - ) - model: Annotated[ - str | None, - Field( - None, - pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", - ), - ] - """ - Identifier of the model to be used - """ - stream: bool = False - """ - Indicates if the server should stream the response as it's generated - """ - stop: Annotated[list[str] | None, Field(None, max_length=4, min_length=1)] - """ - Up to 4 sequences where the API will stop generating further tokens. - """ - seed: Annotated[int | None, Field(None, ge=-1, le=1000)] - """ - If specified, the system will make a best effort to sample deterministically for that particular seed value - """ - max_tokens: Annotated[int | None, Field(None, ge=1)] - """ - The maximum number of tokens to generate in the chat completion - """ - logit_bias: dict[str, LogitBias] | None = None - """ - Modify the likelihood of specified tokens appearing in the completion - """ - response_format: CompletionResponseFormat | None = None - """ - Response format (set to `json_object` to restrict output to JSON) - """ - agent: UUID | None = None - """ - Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - """ - repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - length_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - """ - temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - min_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] - """ - Minimum probability compared to leading token to be considered - """ - - class Task(BaseModel): """ Object describing a Task diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index 362384251..16a57ac31 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -165,3 +165,6 @@ class UpdateTaskRequest(_UpdateTaskRequest): class ListResponse(BaseModel, Generic[DataT]): items: list[DataT] + + +ChatResponse = ChunkChatResponse | MessageChatResponse diff --git a/agents-api/agents_api/common/protocol/sessions.py b/agents-api/agents_api/common/protocol/sessions.py index 6973d61ad..66d837e50 100644 --- a/agents-api/agents_api/common/protocol/sessions.py +++ b/agents-api/agents_api/common/protocol/sessions.py @@ -3,19 +3,17 @@ It includes definitions for session settings and session data models. """ -from typing import Optional +from uuid import UUID from pydantic import BaseModel from ...autogen.openapi_model import ( Agent, - Entry, - GenerationPresetSettings, - OpenAISettings, + BaseEntry, + ChatSettings, Session, Tool, User, - VLLMSettings, ) from .agents import AgentDefaultSettings @@ -37,7 +35,12 @@ class SessionData(BaseModel): session: Session agents: list[Agent] users: list[User] = [] - settings: Optional[GenerationPresetSettings | OpenAISettings | VLLMSettings] = None + settings: ChatSettings | None = None + + +class Toolset(BaseModel): + agent_id: UUID + tools: list[Tool] class ChatContext(SessionData): @@ -45,5 +48,5 @@ class ChatContext(SessionData): Represents the data associated with a context, including for agents, and users. """ - entries: list[Entry] - tools: list[Tool] + entries: list[BaseEntry] + toolsets: list[Toolset] diff --git a/agents-api/agents_api/models/docs/get_doc.py b/agents-api/agents_api/models/docs/get_doc.py index 4a06333c5..6c612fef7 100644 --- a/agents-api/agents_api/models/docs/get_doc.py +++ b/agents-api/agents_api/models/docs/get_doc.py @@ -20,8 +20,9 @@ @rewrap_exceptions( { lambda e: isinstance(e, AssertionError) - and "Expected one result" - in repr(e): partialclass(HTTPException, status_code=404), + and "Expected one result" in repr(e): partialclass( + HTTPException, status_code=404 + ), QueryException: partialclass(HTTPException, status_code=400), ValidationError: partialclass(HTTPException, status_code=400), TypeError: partialclass(HTTPException, status_code=400), diff --git a/agents-api/agents_api/models/session/prepare_chat_context.py b/agents-api/agents_api/models/session/prepare_chat_context.py index ccd49a36a..c59c09d0d 100644 --- a/agents-api/agents_api/models/session/prepare_chat_context.py +++ b/agents-api/agents_api/models/session/prepare_chat_context.py @@ -8,9 +8,9 @@ from ...autogen.openapi_model import make_session from ...common.protocol.sessions import ChatContext from ..entry.list_entries import list_entries -from ..tools.list_tools import list_tools from ..utils import ( cozo_query, + fix_uuid_if_present, make_cozo_json_query, partialclass, rewrap_exceptions, @@ -38,27 +38,27 @@ users=[u["id"] for u in d["users"]], **d["session"], ), + "toolsets": [ + {**ts, "tools": [*map(fix_uuid_if_present, ts["tools"])]} + for ts in d["toolsets"] + ], }, ) -@cozo_query +@cozo_query(debug=True) @beartype def prepare_chat_context( *, developer_id: UUID, - agent_id: UUID, session_id: UUID, ) -> tuple[list[str], dict]: """ Executes a complex query to retrieve memory context based on session ID. """ - session_data_query, sd_vars = prepare_session_data.__wrapped__( + [*_, session_data_query], sd_vars = prepare_session_data.__wrapped__( developer_id=developer_id, session_id=session_id ) - # Remove the outer curly braces - session_data_query = session_data_query.strip()[1:-1] - session_data_fields = ("session", "agents", "users") session_data_query += """ @@ -69,31 +69,49 @@ def prepare_chat_context( } """ - tools_query, t_vars = list_tools.__wrapped__( - developer_id=developer_id, agent_id=agent_id - ) + toolsets_query = """ + input[session_id] <- [[to_uuid($session_id)]] - # Remove the outer curly braces - tools_query = tools_query.strip()[1:-1] + tools_by_agent[agent_id, collect(tool)] := + input[session_id], + *session_lookup{ + session_id, + participant_id: agent_id, + participant_type: "agent", + }, - tools_fields = ("name", "type", "spec") + *tools { agent_id, tool_id, name, type, spec, updated_at, created_at }, + tool = { + "id": tool_id, + "name": name, + "type": type, + "spec": spec, + "updated_at": updated_at, + "created_at": created_at, + } - tools_query += f""" - :create _tools {{ - {', '.join(tools_fields)} - }} + agent_toolsets[collect(toolset)] := + tools_by_agent[agent_id, tools], + toolset = { + "agent_id": agent_id, + "tools": tools, + } + + ?[toolsets] := + agent_toolsets[toolsets] + + :create _toolsets_json { + toolsets: [Json], + } """ - entries_query, e_vars = list_entries.__wrapped__( + [*_, entries_query], e_vars = list_entries.__wrapped__( developer_id=developer_id, session_id=session_id, allowed_sources=["api_request", "api_response", "summarizer"], exclude_relations=["summary_of"], ) - # Remove the outer curly braces - entries_query = entries_query.strip()[1:-1] - entries_fields = ("source", "role", "name", "content", "token_count", "timestamp") entries_query += f""" @@ -103,17 +121,13 @@ def prepare_chat_context( """ combine_query = f""" - tools_json[collect(tool)] := - *_tools {{ {', '.join(tools_fields)} }}, - tool = {{ {make_cozo_json_query(tools_fields)} }} - entries_json[collect(entry)] := *_entries {{ {', '.join(entries_fields)} }}, entry = {{ {make_cozo_json_query(entries_fields)} }} - ?[{', '.join(session_data_fields)}, tools, entries] := + ?[{', '.join(session_data_fields)}, toolsets, entries] := *_session_data_json {{ {', '.join(session_data_fields)} }}, - tools_json[tools], + *_toolsets_json {{ toolsets }}, entries_json[entries] """ @@ -123,8 +137,8 @@ def prepare_chat_context( developer_id, "sessions", session_id=session_id ), session_data_query, - tools_query, entries_query, + toolsets_query, combine_query, ] @@ -133,7 +147,6 @@ def prepare_chat_context( { "session_id": str(session_id), **sd_vars, - **t_vars, **e_vars, }, ) diff --git a/agents-api/agents_api/routers/sessions/__init__.py b/agents-api/agents_api/routers/sessions/__init__.py index 8ed3d953d..bbf4014a5 100644 --- a/agents-api/agents_api/routers/sessions/__init__.py +++ b/agents-api/agents_api/routers/sessions/__init__.py @@ -1,5 +1,6 @@ # ruff: noqa: F401 +from .chat import chat from .create_or_update_session import create_or_update_session from .create_session import create_session from .delete_session import delete_session diff --git a/agents-api/agents_api/routers/sessions/chat.py b/agents-api/agents_api/routers/sessions/chat.py new file mode 100644 index 000000000..e03fceedc --- /dev/null +++ b/agents-api/agents_api/routers/sessions/chat.py @@ -0,0 +1,33 @@ +from typing import Annotated +from uuid import UUID + +from fastapi import Depends +from pydantic import UUID4 +from starlette.status import HTTP_201_CREATED + +from ...autogen.openapi_model import ( + ChatInput, + ChatResponse, +) +from ...dependencies.developer_id import get_developer_id +from ...models.session.prepare_chat_context import prepare_chat_context +from .router import router + + +@router.post( + "/sessions/{session_id}/chat", + status_code=HTTP_201_CREATED, + tags=["sessions", "chat"], +) +async def chat( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + session_id: UUID, + data: ChatInput, +) -> ChatResponse: + chat_context = prepare_chat_context( + developer_id=x_developer_id, + agent_id=data.agent_id, + session_id=session_id, + ) + + print(chat_context) diff --git a/agents-api/migrations/migrate_1723307805_add_lsh_index_to_docs.py b/agents-api/migrations/migrate_1723307805_add_lsh_index_to_docs.py index 1268fa5c4..01eaa8a60 100644 --- a/agents-api/migrations/migrate_1723307805_add_lsh_index_to_docs.py +++ b/agents-api/migrations/migrate_1723307805_add_lsh_index_to_docs.py @@ -1,4 +1,4 @@ -#/usr/bin/env python3 +# /usr/bin/env python3 MIGRATION_ID = "add_lsh_index_to_docs" CREATED_AT = 1723307805.007054 diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 38ffe14d1..c0cd64725 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -13,87 +13,87 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.2" +version = "3.10.3" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:95213b3d79c7e387144e9cb7b9d2809092d6ff2c044cb59033aedc612f38fb6d"}, - {file = "aiohttp-3.10.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1aa005f060aff7124cfadaa2493f00a4e28ed41b232add5869e129a2e395935a"}, - {file = "aiohttp-3.10.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eabe6bf4c199687592f5de4ccd383945f485779c7ffb62a9b9f1f8a3f9756df8"}, - {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96e010736fc16d21125c7e2dc5c350cd43c528b85085c04bf73a77be328fe944"}, - {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99f81f9c1529fd8e03be4a7bd7df32d14b4f856e90ef6e9cbad3415dbfa9166c"}, - {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d611d1a01c25277bcdea06879afbc11472e33ce842322496b211319aa95441bb"}, - {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e00191d38156e09e8c81ef3d75c0d70d4f209b8381e71622165f22ef7da6f101"}, - {file = "aiohttp-3.10.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74c091a5ded6cb81785de2d7a8ab703731f26de910dbe0f3934eabef4ae417cc"}, - {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:18186a80ec5a701816adbf1d779926e1069392cf18504528d6e52e14b5920525"}, - {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5a7ceb2a0d2280f23a02c64cd0afdc922079bb950400c3dd13a1ab2988428aac"}, - {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8bd7be6ff6c162a60cb8fce65ee879a684fbb63d5466aba3fa5b9288eb04aefa"}, - {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:fae962b62944eaebff4f4fddcf1a69de919e7b967136a318533d82d93c3c6bd1"}, - {file = "aiohttp-3.10.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a0fde16d284efcacbe15fb0c1013f0967b6c3e379649239d783868230bf1db42"}, - {file = "aiohttp-3.10.2-cp310-cp310-win32.whl", hash = "sha256:f81cd85a0e76ec7b8e2b6636fe02952d35befda4196b8c88f3cec5b4fb512839"}, - {file = "aiohttp-3.10.2-cp310-cp310-win_amd64.whl", hash = "sha256:54ba10eb5a3481c28282eb6afb5f709aedf53cf9c3a31875ffbdc9fc719ffd67"}, - {file = "aiohttp-3.10.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:87fab7f948e407444c2f57088286e00e2ed0003ceaf3d8f8cc0f60544ba61d91"}, - {file = "aiohttp-3.10.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ec6ad66ed660d46503243cbec7b2b3d8ddfa020f984209b3b8ef7d98ce69c3f2"}, - {file = "aiohttp-3.10.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a4be88807283bd96ae7b8e401abde4ca0bab597ba73b5e9a2d98f36d451e9aac"}, - {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01c98041f90927c2cbd72c22a164bb816fa3010a047d264969cf82e1d4bcf8d1"}, - {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54e36c67e1a9273ecafab18d6693da0fb5ac48fd48417e4548ac24a918c20998"}, - {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7de3ddb6f424af54535424082a1b5d1ae8caf8256ebd445be68c31c662354720"}, - {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dd9c7db94b4692b827ce51dcee597d61a0e4f4661162424faf65106775b40e7"}, - {file = "aiohttp-3.10.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e57e21e1167705f8482ca29cc5d02702208d8bf4aff58f766d94bcd6ead838cd"}, - {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a1a50e59b720060c29e2951fd9f13c01e1ea9492e5a527b92cfe04dd64453c16"}, - {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:686c87782481fda5ee6ba572d912a5c26d9f98cc5c243ebd03f95222af3f1b0f"}, - {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:dafb4abb257c0ed56dc36f4e928a7341b34b1379bd87e5a15ce5d883c2c90574"}, - {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:494a6f77560e02bd7d1ab579fdf8192390567fc96a603f21370f6e63690b7f3d"}, - {file = "aiohttp-3.10.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6fe8503b1b917508cc68bf44dae28823ac05e9f091021e0c41f806ebbb23f92f"}, - {file = "aiohttp-3.10.2-cp311-cp311-win32.whl", hash = "sha256:4ddb43d06ce786221c0dfd3c91b4892c318eaa36b903f7c4278e7e2fa0dd5102"}, - {file = "aiohttp-3.10.2-cp311-cp311-win_amd64.whl", hash = "sha256:ca2f5abcb0a9a47e56bac173c01e9f6c6e7f27534d91451c5f22e6a35a5a2093"}, - {file = "aiohttp-3.10.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:14eb6b17f6246959fb0b035d4f4ae52caa870c4edfb6170aad14c0de5bfbf478"}, - {file = "aiohttp-3.10.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:465e445ec348d4e4bd349edd8b22db75f025da9d7b6dc1369c48e7935b85581e"}, - {file = "aiohttp-3.10.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:341f8ece0276a828d95b70cd265d20e257f5132b46bf77d759d7f4e0443f2906"}, - {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c01fbb87b5426381cd9418b3ddcf4fc107e296fa2d3446c18ce6c76642f340a3"}, - {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2c474af073e1a6763e1c5522bbb2d85ff8318197e4c6c919b8d7886e16213345"}, - {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d9076810a5621236e29b2204e67a68e1fe317c8727ee4c9abbfbb1083b442c38"}, - {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8f515d6859e673940e08de3922b9c4a2249653b0ac181169313bd6e4b1978ac"}, - {file = "aiohttp-3.10.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:655e583afc639bef06f3b2446972c1726007a21003cd0ef57116a123e44601bc"}, - {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8da9449a575133828cc99985536552ea2dcd690e848f9d41b48d8853a149a959"}, - {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:19073d57d0feb1865d12361e2a1f5a49cb764bf81a4024a3b608ab521568093a"}, - {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c8e98e1845805f184d91fda6f9ab93d7c7b0dddf1c07e0255924bfdb151a8d05"}, - {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:377220a5efde6f9497c5b74649b8c261d3cce8a84cb661be2ed8099a2196400a"}, - {file = "aiohttp-3.10.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:92f7f4a4dc9cdb5980973a74d43cdbb16286dacf8d1896b6c3023b8ba8436f8e"}, - {file = "aiohttp-3.10.2-cp312-cp312-win32.whl", hash = "sha256:9bb2834a6f11d65374ce97d366d6311a9155ef92c4f0cee543b2155d06dc921f"}, - {file = "aiohttp-3.10.2-cp312-cp312-win_amd64.whl", hash = "sha256:518dc3cb37365255708283d1c1c54485bbacccd84f0a0fb87ed8917ba45eda5b"}, - {file = "aiohttp-3.10.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:7f98e70bbbf693086efe4b86d381efad8edac040b8ad02821453083d15ec315f"}, - {file = "aiohttp-3.10.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9f6f0b252a009e98fe84028a4ec48396a948e7a65b8be06ccfc6ef68cf1f614d"}, - {file = "aiohttp-3.10.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9360e3ffc7b23565600e729e8c639c3c50d5520e05fdf94aa2bd859eef12c407"}, - {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3988044d1635c7821dd44f0edfbe47e9875427464e59d548aece447f8c22800a"}, - {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:30a9d59da1543a6f1478c3436fd49ec59be3868bca561a33778b4391005e499d"}, - {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9f49bdb94809ac56e09a310a62f33e5f22973d6fd351aac72a39cd551e98194"}, - {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddfd2dca3f11c365d6857a07e7d12985afc59798458a2fdb2ffa4a0332a3fd43"}, - {file = "aiohttp-3.10.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:685c1508ec97b2cd3e120bfe309a4ff8e852e8a7460f1ef1de00c2c0ed01e33c"}, - {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:49904f38667c44c041a0b44c474b3ae36948d16a0398a8f8cd84e2bb3c42a069"}, - {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:352f3a4e5f11f3241a49b6a48bc5b935fabc35d1165fa0d87f3ca99c1fcca98b"}, - {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:fc61f39b534c5d5903490478a0dd349df397d2284a939aa3cbaa2fb7a19b8397"}, - {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:ad2274e707be37420d0b6c3d26a8115295fe9d8e6e530fa6a42487a8ca3ad052"}, - {file = "aiohttp-3.10.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c836bf3c7512100219fe1123743fd8dd9a2b50dd7cfb0c3bb10d041309acab4b"}, - {file = "aiohttp-3.10.2-cp38-cp38-win32.whl", hash = "sha256:53e8898adda402be03ff164b0878abe2d884e3ea03a4701e6ad55399d84b92dc"}, - {file = "aiohttp-3.10.2-cp38-cp38-win_amd64.whl", hash = "sha256:7cc8f65f5b22304693de05a245b6736b14cb5bc9c8a03da6e2ae9ef15f8b458f"}, - {file = "aiohttp-3.10.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9dfc906d656e14004c5bc672399c1cccc10db38df2b62a13fb2b6e165a81c316"}, - {file = "aiohttp-3.10.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:91b10208b222ddf655c3a3d5b727879d7163db12b634492df41a9182a76edaae"}, - {file = "aiohttp-3.10.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9fd16b5e1a7bdd14668cd6bde60a2a29b49147a535c74f50d8177d11b38433a7"}, - {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2bfdda4971bd79201f59adbad24ec2728875237e1c83bba5221284dbbf57bda"}, - {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:69d73f869cf29e8a373127fc378014e2b17bcfbe8d89134bc6fb06a2f67f3cb3"}, - {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:df59f8486507c421c0620a2c3dce81fbf1d54018dc20ff4fecdb2c106d6e6abc"}, - {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0df930015db36b460aa9badbf35eccbc383f00d52d4b6f3de2ccb57d064a6ade"}, - {file = "aiohttp-3.10.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:562b1153ab7f766ee6b8b357ec777a302770ad017cf18505d34f1c088fccc448"}, - {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:d984db6d855de58e0fde1ef908d48fe9a634cadb3cf715962722b4da1c40619d"}, - {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:14dc3fcb0d877911d775d511eb617a486a8c48afca0a887276e63db04d3ee920"}, - {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b52a27a5c97275e254704e1049f4b96a81e67d6205f52fa37a4777d55b0e98ef"}, - {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:cd33d9de8cfd006a0d0fe85f49b4183c57e91d18ffb7e9004ce855e81928f704"}, - {file = "aiohttp-3.10.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1238fc979160bc03a92fff9ad021375ff1c8799c6aacb0d8ea1b357ea40932bb"}, - {file = "aiohttp-3.10.2-cp39-cp39-win32.whl", hash = "sha256:e2f43d238eae4f0b04f58d4c0df4615697d4ca3e9f9b1963d49555a94f0f5a04"}, - {file = "aiohttp-3.10.2-cp39-cp39-win_amd64.whl", hash = "sha256:947847f07a8f81d7b39b2d0202fd73e61962ebe17ac2d8566f260679e467da7b"}, - {file = "aiohttp-3.10.2.tar.gz", hash = "sha256:4d1f694b5d6e459352e5e925a42e05bac66655bfde44d81c59992463d2897014"}, + {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc36cbdedf6f259371dbbbcaae5bb0e95b879bc501668ab6306af867577eb5db"}, + {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85466b5a695c2a7db13eb2c200af552d13e6a9313d7fa92e4ffe04a2c0ea74c1"}, + {file = "aiohttp-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71bb1d97bfe7e6726267cea169fdf5df7658831bb68ec02c9c6b9f3511e108bb"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baec1eb274f78b2de54471fc4c69ecbea4275965eab4b556ef7a7698dee18bf2"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13031e7ec1188274bad243255c328cc3019e36a5a907978501256000d57a7201"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2bbc55a964b8eecb341e492ae91c3bd0848324d313e1e71a27e3d96e6ee7e8e8"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8cc0564b286b625e673a2615ede60a1704d0cbbf1b24604e28c31ed37dc62aa"}, + {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f817a54059a4cfbc385a7f51696359c642088710e731e8df80d0607193ed2b73"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8542c9e5bcb2bd3115acdf5adc41cda394e7360916197805e7e32b93d821ef93"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:671efce3a4a0281060edf9a07a2f7e6230dca3a1cbc61d110eee7753d28405f7"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0974f3b5b0132edcec92c3306f858ad4356a63d26b18021d859c9927616ebf27"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:44bb159b55926b57812dca1b21c34528e800963ffe130d08b049b2d6b994ada7"}, + {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6ae9ae382d1c9617a91647575255ad55a48bfdde34cc2185dd558ce476bf16e9"}, + {file = "aiohttp-3.10.3-cp310-cp310-win32.whl", hash = "sha256:aed12a54d4e1ee647376fa541e1b7621505001f9f939debf51397b9329fd88b9"}, + {file = "aiohttp-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:b51aef59370baf7444de1572f7830f59ddbabd04e5292fa4218d02f085f8d299"}, + {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e021c4c778644e8cdc09487d65564265e6b149896a17d7c0f52e9a088cc44e1b"}, + {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24fade6dae446b183e2410a8628b80df9b7a42205c6bfc2eff783cbeedc224a2"}, + {file = "aiohttp-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bc8e9f15939dacb0e1f2d15f9c41b786051c10472c7a926f5771e99b49a5957f"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5a9ec959b5381271c8ec9310aae1713b2aec29efa32e232e5ef7dcca0df0279"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a5d0ea8a6467b15d53b00c4e8ea8811e47c3cc1bdbc62b1aceb3076403d551f"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9ed607dbbdd0d4d39b597e5bf6b0d40d844dfb0ac6a123ed79042ef08c1f87e"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e66d5b506832e56add66af88c288c1d5ba0c38b535a1a59e436b300b57b23e"}, + {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fda91ad797e4914cca0afa8b6cccd5d2b3569ccc88731be202f6adce39503189"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:61ccb867b2f2f53df6598eb2a93329b5eee0b00646ee79ea67d68844747a418e"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d881353264e6156f215b3cb778c9ac3184f5465c2ece5e6fce82e68946868ef"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b031ce229114825f49cec4434fa844ccb5225e266c3e146cb4bdd025a6da52f1"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5337cc742a03f9e3213b097abff8781f79de7190bbfaa987bd2b7ceb5bb0bdec"}, + {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab3361159fd3dcd0e48bbe804006d5cfb074b382666e6c064112056eb234f1a9"}, + {file = "aiohttp-3.10.3-cp311-cp311-win32.whl", hash = "sha256:05d66203a530209cbe40f102ebaac0b2214aba2a33c075d0bf825987c36f1f0b"}, + {file = "aiohttp-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:70b4a4984a70a2322b70e088d654528129783ac1ebbf7dd76627b3bd22db2f17"}, + {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:166de65e2e4e63357cfa8417cf952a519ac42f1654cb2d43ed76899e2319b1ee"}, + {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7084876352ba3833d5d214e02b32d794e3fd9cf21fdba99cff5acabeb90d9806"}, + {file = "aiohttp-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d98c604c93403288591d7d6d7d6cc8a63459168f8846aeffd5b3a7f3b3e5e09"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d73b073a25a0bb8bf014345374fe2d0f63681ab5da4c22f9d2025ca3e3ea54fc"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8da6b48c20ce78f5721068f383e0e113dde034e868f1b2f5ee7cb1e95f91db57"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a9dcdccf50284b1b0dc72bc57e5bbd3cc9bf019060dfa0668f63241ccc16aa7"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56fb94bae2be58f68d000d046172d8b8e6b1b571eb02ceee5535e9633dcd559c"}, + {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf75716377aad2c718cdf66451c5cf02042085d84522aec1f9246d3e4b8641a6"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c51ed03e19c885c8e91f574e4bbe7381793f56f93229731597e4a499ffef2a5"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b84857b66fa6510a163bb083c1199d1ee091a40163cfcbbd0642495fed096204"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c124b9206b1befe0491f48185fd30a0dd51b0f4e0e7e43ac1236066215aff272"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3461d9294941937f07bbbaa6227ba799bc71cc3b22c40222568dc1cca5118f68"}, + {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08bd0754d257b2db27d6bab208c74601df6f21bfe4cb2ec7b258ba691aac64b3"}, + {file = "aiohttp-3.10.3-cp312-cp312-win32.whl", hash = "sha256:7f9159ae530297f61a00116771e57516f89a3de6ba33f314402e41560872b50a"}, + {file = "aiohttp-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:e1128c5d3a466279cb23c4aa32a0f6cb0e7d2961e74e9e421f90e74f75ec1edf"}, + {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d1100e68e70eb72eadba2b932b185ebf0f28fd2f0dbfe576cfa9d9894ef49752"}, + {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a541414578ff47c0a9b0b8b77381ea86b0c8531ab37fc587572cb662ccd80b88"}, + {file = "aiohttp-3.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d5548444ef60bf4c7b19ace21f032fa42d822e516a6940d36579f7bfa8513f9c"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba2e838b5e6a8755ac8297275c9460e729dc1522b6454aee1766c6de6d56e5e"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48665433bb59144aaf502c324694bec25867eb6630fcd831f7a893ca473fcde4"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bac352fceed158620ce2d701ad39d4c1c76d114255a7c530e057e2b9f55bdf9f"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0f670502100cdc567188c49415bebba947eb3edaa2028e1a50dd81bd13363f"}, + {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b09f38a67679e32d380fe512189ccb0b25e15afc79b23fbd5b5e48e4fc8fd9"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:cd788602e239ace64f257d1c9d39898ca65525583f0fbf0988bcba19418fe93f"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:214277dcb07ab3875f17ee1c777d446dcce75bea85846849cc9d139ab8f5081f"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:32007fdcaab789689c2ecaaf4b71f8e37bf012a15cd02c0a9db8c4d0e7989fa8"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:123e5819bfe1b87204575515cf448ab3bf1489cdeb3b61012bde716cda5853e7"}, + {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:812121a201f0c02491a5db335a737b4113151926a79ae9ed1a9f41ea225c0e3f"}, + {file = "aiohttp-3.10.3-cp38-cp38-win32.whl", hash = "sha256:b97dc9a17a59f350c0caa453a3cb35671a2ffa3a29a6ef3568b523b9113d84e5"}, + {file = "aiohttp-3.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:3731a73ddc26969d65f90471c635abd4e1546a25299b687e654ea6d2fc052394"}, + {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38d91b98b4320ffe66efa56cb0f614a05af53b675ce1b8607cdb2ac826a8d58e"}, + {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9743fa34a10a36ddd448bba8a3adc2a66a1c575c3c2940301bacd6cc896c6bf1"}, + {file = "aiohttp-3.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7c126f532caf238031c19d169cfae3c6a59129452c990a6e84d6e7b198a001dc"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:926e68438f05703e500b06fe7148ef3013dd6f276de65c68558fa9974eeb59ad"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434b3ab75833accd0b931d11874e206e816f6e6626fd69f643d6a8269cd9166a"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d35235a44ec38109b811c3600d15d8383297a8fab8e3dec6147477ec8636712a"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59c489661edbd863edb30a8bd69ecb044bd381d1818022bc698ba1b6f80e5dd1"}, + {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50544fe498c81cb98912afabfc4e4d9d85e89f86238348e3712f7ca6a2f01dab"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:09bc79275737d4dc066e0ae2951866bb36d9c6b460cb7564f111cc0427f14844"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:af4dbec58e37f5afff4f91cdf235e8e4b0bd0127a2a4fd1040e2cad3369d2f06"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b22cae3c9dd55a6b4c48c63081d31c00fc11fa9db1a20c8a50ee38c1a29539d2"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ba562736d3fbfe9241dad46c1a8994478d4a0e50796d80e29d50cabe8fbfcc3f"}, + {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f25d6c4e82d7489be84f2b1c8212fafc021b3731abdb61a563c90e37cced3a21"}, + {file = "aiohttp-3.10.3-cp39-cp39-win32.whl", hash = "sha256:b69d832e5f5fa15b1b6b2c8eb6a9fd2c0ec1fd7729cb4322ed27771afc9fc2ac"}, + {file = "aiohttp-3.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:673bb6e3249dc8825df1105f6ef74e2eab779b7ff78e96c15cadb78b04a83752"}, + {file = "aiohttp-3.10.3.tar.gz", hash = "sha256:21650e7032cc2d31fc23d353d7123e771354f2a3d5b05a5647fc30fea214e696"}, ] [package.dependencies] @@ -2292,13 +2292,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.6" +version = "1.43.7" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.6-py3-none-any.whl", hash = "sha256:d1a8eebba7e22c785b413fe7908bcd30356ed652958bd55b4c8982ccede66933"}, - {file = "litellm-1.43.6.tar.gz", hash = "sha256:9a94e9c73d029c90de11c9cdbf633531ff0d89b87d7df8bd87ff43d7d3b7c205"}, + {file = "litellm-1.43.7-py3-none-any.whl", hash = "sha256:88d9d8dcb4579839106941f1ce59143ab926af986a2206cce4bcda1ae153a78c"}, + {file = "litellm-1.43.7.tar.gz", hash = "sha256:b6ef8db0c7555d590957c37b228584efc5e9154b925ab0fffb112be26f1ab5ab"}, ] [package.dependencies] diff --git a/agents-api/tests/test_docs_routes.py b/agents-api/tests/test_docs_routes.py index 3bd68cb0b..a2e699a47 100644 --- a/agents-api/tests/test_docs_routes.py +++ b/agents-api/tests/test_docs_routes.py @@ -1,6 +1,6 @@ from ward import test -from tests.fixtures import make_request, test_agent, test_user, test_doc, test_user_doc +from tests.fixtures import make_request, test_agent, test_doc, test_user, test_user_doc @test("route: create user doc") diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index 01c2559de..fe4fc0cb6 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -5,16 +5,19 @@ from agents_api.autogen.openapi_model import CreateOrUpdateSessionRequest, Session from agents_api.autogen.Sessions import CreateSessionRequest +from agents_api.common.protocol.sessions import ChatContext from agents_api.models.session.create_or_update_session import create_or_update_session from agents_api.models.session.create_session import create_session from agents_api.models.session.delete_session import delete_session from agents_api.models.session.get_session import get_session from agents_api.models.session.list_sessions import list_sessions +from agents_api.models.session.prepare_chat_context import prepare_chat_context from tests.fixtures import ( cozo_client, test_agent, test_developer_id, test_session, + test_tool, test_user, ) @@ -143,3 +146,27 @@ def _( assert result is not None assert isinstance(result, Session) assert result.id == session_id + + +@test("model: prepare chat context") +def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, + session=test_session, + tool=test_tool, + user=test_user, +): + try: + context = prepare_chat_context( + developer_id=developer_id, + session_id=session.id, + client=client, + ) + except Exception as e: + print(repr(e.__cause__)) + raise + + assert isinstance(context, ChatContext) + assert len(context.entries) > 0 + assert len(context.toolsets) > 0 diff --git a/fern/generators.yml b/fern/generators.yml index d072b43ca..123e0e957 100644 --- a/fern/generators.yml +++ b/fern/generators.yml @@ -11,8 +11,3 @@ groups: output: location: local-file-system path: ../sdks/python/julep/api - - name: fernapi/fern-postman - version: 0.1.1 - output: - location: local-file-system - path: ../sdks/postman diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index 0e6255648..0bee6519a 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -8,46 +8,36 @@ AgentToolsRouteListRequestSortBy, AgentToolsRouteListResponse, AgentsAgent, - AgentsAgentDefaultSettings, AgentsAgentInstructions, AgentsCreateAgentRequest, - AgentsCreateAgentRequestDefaultSettings, AgentsCreateAgentRequestInstructions, AgentsDocsSearchRouteSearchRequestBody, - AgentsPatchAgentRequestDefaultSettings, AgentsPatchAgentRequestInstructions, AgentsRouteListRequestDirection, AgentsRouteListRequestSortBy, AgentsRouteListResponse, AgentsUpdateAgentRequest, - AgentsUpdateAgentRequestDefaultSettings, AgentsUpdateAgentRequestInstructions, ChatBaseChatOutput, ChatBaseChatResponse, ChatBaseTokenLogProb, + ChatChatInputToolChoice, ChatChatOutputChunk, + ChatChatSettings, ChatChunkChatResponse, ChatCompetionUsage, ChatCompletionResponseFormat, ChatCompletionResponseFormatType, + ChatDefaultChatSettings, ChatFinishReason, ChatGenerationPreset, - ChatGenerationPresetSettings, ChatLogProbResponse, ChatMessageChatResponse, + ChatMessageChatResponseChoicesItem, ChatMultipleChatOutput, - ChatOpenAiSettings, - ChatRouteGenerateRequest, - ChatRouteGenerateRequestAgent, - ChatRouteGenerateRequestAgentToolChoice, - ChatRouteGenerateRequestFrequencyPenalty, - ChatRouteGenerateRequestFrequencyPenaltyToolChoice, - ChatRouteGenerateRequestPreset, - ChatRouteGenerateRequestPresetToolChoice, ChatRouteGenerateResponse, ChatSingleChatOutput, ChatTokenLogProb, - ChatVLlmSettings, CommonIdentifierSafeUnicode, CommonLimit, CommonLogitBias, @@ -161,10 +151,6 @@ TasksPatchTaskRequestMainItem_Yield, TasksPromptStep, TasksPromptStepPrompt, - TasksPromptStepSettings, - TasksPromptStepSettingsAgent, - TasksPromptStepSettingsFrequencyPenalty, - TasksPromptStepSettingsPreset, TasksRouteListRequestDirection, TasksRouteListRequestSortBy, TasksRouteListResponse, @@ -223,46 +209,36 @@ "AgentToolsRouteListRequestSortBy", "AgentToolsRouteListResponse", "AgentsAgent", - "AgentsAgentDefaultSettings", "AgentsAgentInstructions", "AgentsCreateAgentRequest", - "AgentsCreateAgentRequestDefaultSettings", "AgentsCreateAgentRequestInstructions", "AgentsDocsSearchRouteSearchRequestBody", - "AgentsPatchAgentRequestDefaultSettings", "AgentsPatchAgentRequestInstructions", "AgentsRouteListRequestDirection", "AgentsRouteListRequestSortBy", "AgentsRouteListResponse", "AgentsUpdateAgentRequest", - "AgentsUpdateAgentRequestDefaultSettings", "AgentsUpdateAgentRequestInstructions", "ChatBaseChatOutput", "ChatBaseChatResponse", "ChatBaseTokenLogProb", + "ChatChatInputToolChoice", "ChatChatOutputChunk", + "ChatChatSettings", "ChatChunkChatResponse", "ChatCompetionUsage", "ChatCompletionResponseFormat", "ChatCompletionResponseFormatType", + "ChatDefaultChatSettings", "ChatFinishReason", "ChatGenerationPreset", - "ChatGenerationPresetSettings", "ChatLogProbResponse", "ChatMessageChatResponse", + "ChatMessageChatResponseChoicesItem", "ChatMultipleChatOutput", - "ChatOpenAiSettings", - "ChatRouteGenerateRequest", - "ChatRouteGenerateRequestAgent", - "ChatRouteGenerateRequestAgentToolChoice", - "ChatRouteGenerateRequestFrequencyPenalty", - "ChatRouteGenerateRequestFrequencyPenaltyToolChoice", - "ChatRouteGenerateRequestPreset", - "ChatRouteGenerateRequestPresetToolChoice", "ChatRouteGenerateResponse", "ChatSingleChatOutput", "ChatTokenLogProb", - "ChatVLlmSettings", "CommonIdentifierSafeUnicode", "CommonLimit", "CommonLogitBias", @@ -377,10 +353,6 @@ "TasksPatchTaskRequestMainItem_Yield", "TasksPromptStep", "TasksPromptStepPrompt", - "TasksPromptStepSettings", - "TasksPromptStepSettingsAgent", - "TasksPromptStepSettingsFrequencyPenalty", - "TasksPromptStepSettingsPreset", "TasksRouteListRequestDirection", "TasksRouteListRequestSortBy", "TasksRouteListResponse", diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index f560e0bfe..86884cd7a 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -24,34 +24,29 @@ ) from .types.agent_tools_route_list_response import AgentToolsRouteListResponse from .types.agents_agent import AgentsAgent -from .types.agents_create_agent_request_default_settings import ( - AgentsCreateAgentRequestDefaultSettings, -) from .types.agents_create_agent_request_instructions import ( AgentsCreateAgentRequestInstructions, ) from .types.agents_docs_search_route_search_request_body import ( AgentsDocsSearchRouteSearchRequestBody, ) -from .types.agents_patch_agent_request_default_settings import ( - AgentsPatchAgentRequestDefaultSettings, -) from .types.agents_patch_agent_request_instructions import ( AgentsPatchAgentRequestInstructions, ) from .types.agents_route_list_request_direction import AgentsRouteListRequestDirection from .types.agents_route_list_request_sort_by import AgentsRouteListRequestSortBy from .types.agents_route_list_response import AgentsRouteListResponse -from .types.agents_update_agent_request_default_settings import ( - AgentsUpdateAgentRequestDefaultSettings, -) from .types.agents_update_agent_request_instructions import ( AgentsUpdateAgentRequestInstructions, ) -from .types.chat_route_generate_request import ChatRouteGenerateRequest +from .types.chat_chat_input_tool_choice import ChatChatInputToolChoice +from .types.chat_completion_response_format import ChatCompletionResponseFormat +from .types.chat_default_chat_settings import ChatDefaultChatSettings +from .types.chat_generation_preset import ChatGenerationPreset from .types.chat_route_generate_response import ChatRouteGenerateResponse from .types.common_identifier_safe_unicode import CommonIdentifierSafeUnicode from .types.common_limit import CommonLimit +from .types.common_logit_bias import CommonLogitBias from .types.common_offset import CommonOffset from .types.common_resource_created_response import CommonResourceCreatedResponse from .types.common_resource_deleted_response import CommonResourceDeletedResponse @@ -64,6 +59,7 @@ from .types.docs_embed_query_request import DocsEmbedQueryRequest from .types.docs_embed_query_response import DocsEmbedQueryResponse from .types.entries_history import EntriesHistory +from .types.entries_input_chat_ml_message import EntriesInputChatMlMessage from .types.execution_transitions_route_list_request_direction import ( ExecutionTransitionsRouteListRequestDirection, ) @@ -98,6 +94,7 @@ from .types.tasks_task_tool import TasksTaskTool from .types.tasks_update_task_request_main_item import TasksUpdateTaskRequestMainItem from .types.tools_function_def import ToolsFunctionDef +from .types.tools_function_tool import ToolsFunctionTool from .types.tools_tool_type import ToolsToolType from .types.user_docs_route_list_request_direction import ( UserDocsRouteListRequestDirection, @@ -269,9 +266,7 @@ def agents_route_create( model: str, instructions: AgentsCreateAgentRequestInstructions, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - default_settings: typing.Optional[ - AgentsCreateAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceCreatedResponse: """ @@ -293,7 +288,7 @@ def agents_route_create( metadata : typing.Optional[typing.Dict[str, typing.Any]] - default_settings : typing.Optional[AgentsCreateAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -394,9 +389,7 @@ def agents_route_create_or_update( model: str, instructions: AgentsUpdateAgentRequestInstructions, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - default_settings: typing.Optional[ - AgentsUpdateAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceUpdatedResponse: """ @@ -420,7 +413,7 @@ def agents_route_create_or_update( metadata : typing.Optional[typing.Dict[str, typing.Any]] - default_settings : typing.Optional[AgentsUpdateAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -478,9 +471,7 @@ def agents_route_update( model: str, instructions: AgentsUpdateAgentRequestInstructions, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - default_settings: typing.Optional[ - AgentsUpdateAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceUpdatedResponse: """ @@ -505,7 +496,7 @@ def agents_route_update( metadata : typing.Optional[typing.Dict[str, typing.Any]] - default_settings : typing.Optional[AgentsUpdateAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -607,9 +598,7 @@ def agents_route_patch( about: typing.Optional[str] = OMIT, model: typing.Optional[str] = OMIT, instructions: typing.Optional[AgentsPatchAgentRequestInstructions] = OMIT, - default_settings: typing.Optional[ - AgentsPatchAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceUpdatedResponse: """ @@ -634,7 +623,7 @@ def agents_route_patch( instructions : typing.Optional[AgentsPatchAgentRequestInstructions] Instructions for the agent - default_settings : typing.Optional[AgentsPatchAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -1418,9 +1407,7 @@ def agent_tools_route_create( model: str, instructions: AgentsCreateAgentRequestInstructions, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - default_settings: typing.Optional[ - AgentsCreateAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceCreatedResponse: """ @@ -1445,7 +1432,7 @@ def agent_tools_route_create( metadata : typing.Optional[typing.Dict[str, typing.Any]] - default_settings : typing.Optional[AgentsCreateAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -2677,7 +2664,28 @@ def chat_route_generate( self, id: CommonUuid, *, - request: ChatRouteGenerateRequest, + messages: typing.Sequence[EntriesInputChatMlMessage], + recall: bool, + remember: bool, + save: bool, + stream: bool, + tools: typing.Optional[typing.Sequence[ToolsFunctionTool]] = OMIT, + tool_choice: typing.Optional[ChatChatInputToolChoice] = OMIT, + model: typing.Optional[CommonIdentifierSafeUnicode] = OMIT, + stop: typing.Optional[typing.Sequence[str]] = OMIT, + seed: typing.Optional[int] = OMIT, + max_tokens: typing.Optional[int] = OMIT, + logit_bias: typing.Optional[typing.Dict[str, CommonLogitBias]] = OMIT, + response_format: typing.Optional[ChatCompletionResponseFormat] = OMIT, + agent: typing.Optional[CommonUuid] = OMIT, + preset: typing.Optional[ChatGenerationPreset] = OMIT, + frequency_penalty: typing.Optional[float] = OMIT, + presence_penalty: typing.Optional[float] = OMIT, + temperature: typing.Optional[float] = OMIT, + top_p: typing.Optional[float] = OMIT, + repetition_penalty: typing.Optional[float] = OMIT, + length_penalty: typing.Optional[float] = OMIT, + min_p: typing.Optional[float] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ChatRouteGenerateResponse: """ @@ -2688,7 +2696,71 @@ def chat_route_generate( id : CommonUuid The session ID - request : ChatRouteGenerateRequest + messages : typing.Sequence[EntriesInputChatMlMessage] + A list of new input messages comprising the conversation so far. + + recall : bool + Whether previous memories should be recalled or not (will be enabled in a future release) + + remember : bool + Whether this interaction should form new memories or not (will be enabled in a future release) + + save : bool + Whether this interaction should be stored in the session history or not + + stream : bool + Indicates if the server should stream the response as it's generated + + tools : typing.Optional[typing.Sequence[ToolsFunctionTool]] + (Advanced) List of tools that are provided in addition to agent's default set of tools. + + tool_choice : typing.Optional[ChatChatInputToolChoice] + Can be one of existing tools given to the agent earlier or the ones provided in this request. + + model : typing.Optional[CommonIdentifierSafeUnicode] + Identifier of the model to be used + + stop : typing.Optional[typing.Sequence[str]] + Up to 4 sequences where the API will stop generating further tokens. + + seed : typing.Optional[int] + If specified, the system will make a best effort to sample deterministically for that particular seed value + + max_tokens : typing.Optional[int] + The maximum number of tokens to generate in the chat completion + + logit_bias : typing.Optional[typing.Dict[str, CommonLogitBias]] + Modify the likelihood of specified tokens appearing in the completion + + response_format : typing.Optional[ChatCompletionResponseFormat] + Response format (set to `json_object` to restrict output to JSON) + + agent : typing.Optional[CommonUuid] + Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + + preset : typing.Optional[ChatGenerationPreset] + Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + + frequency_penalty : typing.Optional[float] + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + + presence_penalty : typing.Optional[float] + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + + temperature : typing.Optional[float] + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + + top_p : typing.Optional[float] + Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + + repetition_penalty : typing.Optional[float] + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + + length_penalty : typing.Optional[float] + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + + min_p : typing.Optional[float] + Minimum probability compared to leading token to be considered request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -2700,7 +2772,7 @@ def chat_route_generate( Examples -------- - from julep import ChatRouteGenerateRequestPreset, EntriesInputChatMlMessage + from julep import EntriesInputChatMlMessage from julep.client import JulepApi client = JulepApi( @@ -2709,24 +2781,45 @@ def chat_route_generate( ) client.chat_route_generate( id="id", - request=ChatRouteGenerateRequestPreset( - messages=[ - EntriesInputChatMlMessage( - role="user", - content="content", - ) - ], - recall=True, - remember=True, - save=True, - stream=True, - ), + messages=[ + EntriesInputChatMlMessage( + role="user", + content="content", + ) + ], + recall=True, + remember=True, + save=True, + stream=True, ) """ _response = self._client_wrapper.httpx_client.request( f"sessions/{jsonable_encoder(id)}/chat", method="POST", - json=request, + json={ + "messages": messages, + "tools": tools, + "tool_choice": tool_choice, + "recall": recall, + "remember": remember, + "save": save, + "model": model, + "stream": stream, + "stop": stop, + "seed": seed, + "max_tokens": max_tokens, + "logit_bias": logit_bias, + "response_format": response_format, + "agent": agent, + "preset": preset, + "frequency_penalty": frequency_penalty, + "presence_penalty": presence_penalty, + "temperature": temperature, + "top_p": top_p, + "repetition_penalty": repetition_penalty, + "length_penalty": length_penalty, + "min_p": min_p, + }, request_options=request_options, omit=OMIT, ) @@ -3777,9 +3870,7 @@ async def agents_route_create( model: str, instructions: AgentsCreateAgentRequestInstructions, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - default_settings: typing.Optional[ - AgentsCreateAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceCreatedResponse: """ @@ -3801,7 +3892,7 @@ async def agents_route_create( metadata : typing.Optional[typing.Dict[str, typing.Any]] - default_settings : typing.Optional[AgentsCreateAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -3918,9 +4009,7 @@ async def agents_route_create_or_update( model: str, instructions: AgentsUpdateAgentRequestInstructions, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - default_settings: typing.Optional[ - AgentsUpdateAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceUpdatedResponse: """ @@ -3944,7 +4033,7 @@ async def agents_route_create_or_update( metadata : typing.Optional[typing.Dict[str, typing.Any]] - default_settings : typing.Optional[AgentsUpdateAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -4010,9 +4099,7 @@ async def agents_route_update( model: str, instructions: AgentsUpdateAgentRequestInstructions, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - default_settings: typing.Optional[ - AgentsUpdateAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceUpdatedResponse: """ @@ -4037,7 +4124,7 @@ async def agents_route_update( metadata : typing.Optional[typing.Dict[str, typing.Any]] - default_settings : typing.Optional[AgentsUpdateAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -4155,9 +4242,7 @@ async def agents_route_patch( about: typing.Optional[str] = OMIT, model: typing.Optional[str] = OMIT, instructions: typing.Optional[AgentsPatchAgentRequestInstructions] = OMIT, - default_settings: typing.Optional[ - AgentsPatchAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceUpdatedResponse: """ @@ -4182,7 +4267,7 @@ async def agents_route_patch( instructions : typing.Optional[AgentsPatchAgentRequestInstructions] Instructions for the agent - default_settings : typing.Optional[AgentsPatchAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -5054,9 +5139,7 @@ async def agent_tools_route_create( model: str, instructions: AgentsCreateAgentRequestInstructions, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, - default_settings: typing.Optional[ - AgentsCreateAgentRequestDefaultSettings - ] = OMIT, + default_settings: typing.Optional[ChatDefaultChatSettings] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> CommonResourceCreatedResponse: """ @@ -5081,7 +5164,7 @@ async def agent_tools_route_create( metadata : typing.Optional[typing.Dict[str, typing.Any]] - default_settings : typing.Optional[AgentsCreateAgentRequestDefaultSettings] + default_settings : typing.Optional[ChatDefaultChatSettings] Default settings for all sessions created by this agent request_options : typing.Optional[RequestOptions] @@ -6465,7 +6548,28 @@ async def chat_route_generate( self, id: CommonUuid, *, - request: ChatRouteGenerateRequest, + messages: typing.Sequence[EntriesInputChatMlMessage], + recall: bool, + remember: bool, + save: bool, + stream: bool, + tools: typing.Optional[typing.Sequence[ToolsFunctionTool]] = OMIT, + tool_choice: typing.Optional[ChatChatInputToolChoice] = OMIT, + model: typing.Optional[CommonIdentifierSafeUnicode] = OMIT, + stop: typing.Optional[typing.Sequence[str]] = OMIT, + seed: typing.Optional[int] = OMIT, + max_tokens: typing.Optional[int] = OMIT, + logit_bias: typing.Optional[typing.Dict[str, CommonLogitBias]] = OMIT, + response_format: typing.Optional[ChatCompletionResponseFormat] = OMIT, + agent: typing.Optional[CommonUuid] = OMIT, + preset: typing.Optional[ChatGenerationPreset] = OMIT, + frequency_penalty: typing.Optional[float] = OMIT, + presence_penalty: typing.Optional[float] = OMIT, + temperature: typing.Optional[float] = OMIT, + top_p: typing.Optional[float] = OMIT, + repetition_penalty: typing.Optional[float] = OMIT, + length_penalty: typing.Optional[float] = OMIT, + min_p: typing.Optional[float] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ChatRouteGenerateResponse: """ @@ -6476,7 +6580,71 @@ async def chat_route_generate( id : CommonUuid The session ID - request : ChatRouteGenerateRequest + messages : typing.Sequence[EntriesInputChatMlMessage] + A list of new input messages comprising the conversation so far. + + recall : bool + Whether previous memories should be recalled or not (will be enabled in a future release) + + remember : bool + Whether this interaction should form new memories or not (will be enabled in a future release) + + save : bool + Whether this interaction should be stored in the session history or not + + stream : bool + Indicates if the server should stream the response as it's generated + + tools : typing.Optional[typing.Sequence[ToolsFunctionTool]] + (Advanced) List of tools that are provided in addition to agent's default set of tools. + + tool_choice : typing.Optional[ChatChatInputToolChoice] + Can be one of existing tools given to the agent earlier or the ones provided in this request. + + model : typing.Optional[CommonIdentifierSafeUnicode] + Identifier of the model to be used + + stop : typing.Optional[typing.Sequence[str]] + Up to 4 sequences where the API will stop generating further tokens. + + seed : typing.Optional[int] + If specified, the system will make a best effort to sample deterministically for that particular seed value + + max_tokens : typing.Optional[int] + The maximum number of tokens to generate in the chat completion + + logit_bias : typing.Optional[typing.Dict[str, CommonLogitBias]] + Modify the likelihood of specified tokens appearing in the completion + + response_format : typing.Optional[ChatCompletionResponseFormat] + Response format (set to `json_object` to restrict output to JSON) + + agent : typing.Optional[CommonUuid] + Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + + preset : typing.Optional[ChatGenerationPreset] + Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + + frequency_penalty : typing.Optional[float] + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + + presence_penalty : typing.Optional[float] + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + + temperature : typing.Optional[float] + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + + top_p : typing.Optional[float] + Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + + repetition_penalty : typing.Optional[float] + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + + length_penalty : typing.Optional[float] + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + + min_p : typing.Optional[float] + Minimum probability compared to leading token to be considered request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -6490,7 +6658,7 @@ async def chat_route_generate( -------- import asyncio - from julep import ChatRouteGenerateRequestPreset, EntriesInputChatMlMessage + from julep import EntriesInputChatMlMessage from julep.client import AsyncJulepApi client = AsyncJulepApi( @@ -6502,18 +6670,16 @@ async def chat_route_generate( async def main() -> None: await client.chat_route_generate( id="id", - request=ChatRouteGenerateRequestPreset( - messages=[ - EntriesInputChatMlMessage( - role="user", - content="content", - ) - ], - recall=True, - remember=True, - save=True, - stream=True, - ), + messages=[ + EntriesInputChatMlMessage( + role="user", + content="content", + ) + ], + recall=True, + remember=True, + save=True, + stream=True, ) @@ -6522,7 +6688,30 @@ async def main() -> None: _response = await self._client_wrapper.httpx_client.request( f"sessions/{jsonable_encoder(id)}/chat", method="POST", - json=request, + json={ + "messages": messages, + "tools": tools, + "tool_choice": tool_choice, + "recall": recall, + "remember": remember, + "save": save, + "model": model, + "stream": stream, + "stop": stop, + "seed": seed, + "max_tokens": max_tokens, + "logit_bias": logit_bias, + "response_format": response_format, + "agent": agent, + "preset": preset, + "frequency_penalty": frequency_penalty, + "presence_penalty": presence_penalty, + "temperature": temperature, + "top_p": top_p, + "repetition_penalty": repetition_penalty, + "length_penalty": length_penalty, + "min_p": min_p, + }, request_options=request_options, omit=OMIT, ) diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index 33e041f65..2f54bca4a 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -200,7 +200,7 @@ client.agents_route_create(
-**default_settings:** `typing.Optional[AgentsCreateAgentRequestDefaultSettings]` — Default settings for all sessions created by this agent +**default_settings:** `typing.Optional[ChatDefaultChatSettings]` — Default settings for all sessions created by this agent
@@ -394,7 +394,7 @@ client.agents_route_create_or_update(
-**default_settings:** `typing.Optional[AgentsUpdateAgentRequestDefaultSettings]` — Default settings for all sessions created by this agent +**default_settings:** `typing.Optional[ChatDefaultChatSettings]` — Default settings for all sessions created by this agent
@@ -517,7 +517,7 @@ client.agents_route_update(
-**default_settings:** `typing.Optional[AgentsUpdateAgentRequestDefaultSettings]` — Default settings for all sessions created by this agent +**default_settings:** `typing.Optional[ChatDefaultChatSettings]` — Default settings for all sessions created by this agent
@@ -707,7 +707,7 @@ client.agents_route_patch(
-**default_settings:** `typing.Optional[AgentsPatchAgentRequestDefaultSettings]` — Default settings for all sessions created by this agent +**default_settings:** `typing.Optional[ChatDefaultChatSettings]` — Default settings for all sessions created by this agent
@@ -1924,7 +1924,7 @@ client.agent_tools_route_create(
-**default_settings:** `typing.Optional[AgentsCreateAgentRequestDefaultSettings]` — Default settings for all sessions created by this agent +**default_settings:** `typing.Optional[ChatDefaultChatSettings]` — Default settings for all sessions created by this agent
@@ -3775,7 +3775,7 @@ Generate a response from the model
```python -from julep import ChatRouteGenerateRequestPreset, EntriesInputChatMlMessage +from julep import EntriesInputChatMlMessage from julep.client import JulepApi client = JulepApi( @@ -3784,18 +3784,16 @@ client = JulepApi( ) client.chat_route_generate( id="id", - request=ChatRouteGenerateRequestPreset( - messages=[ - EntriesInputChatMlMessage( - role="user", - content="content", - ) - ], - recall=True, - remember=True, - save=True, - stream=True, - ), + messages=[ + EntriesInputChatMlMessage( + role="user", + content="content", + ) + ], + recall=True, + remember=True, + save=True, + stream=True, ) ``` @@ -3820,7 +3818,175 @@ client.chat_route_generate(
-**request:** `ChatRouteGenerateRequest` +**messages:** `typing.Sequence[EntriesInputChatMlMessage]` — A list of new input messages comprising the conversation so far. + +
+
+ +
+
+ +**recall:** `bool` — Whether previous memories should be recalled or not (will be enabled in a future release) + +
+
+ +
+
+ +**remember:** `bool` — Whether this interaction should form new memories or not (will be enabled in a future release) + +
+
+ +
+
+ +**save:** `bool` — Whether this interaction should be stored in the session history or not + +
+
+ +
+
+ +**stream:** `bool` — Indicates if the server should stream the response as it's generated + +
+
+ +
+
+ +**tools:** `typing.Optional[typing.Sequence[ToolsFunctionTool]]` — (Advanced) List of tools that are provided in addition to agent's default set of tools. + +
+
+ +
+
+ +**tool_choice:** `typing.Optional[ChatChatInputToolChoice]` — Can be one of existing tools given to the agent earlier or the ones provided in this request. + +
+
+ +
+
+ +**model:** `typing.Optional[CommonIdentifierSafeUnicode]` — Identifier of the model to be used + +
+
+ +
+
+ +**stop:** `typing.Optional[typing.Sequence[str]]` — Up to 4 sequences where the API will stop generating further tokens. + +
+
+ +
+
+ +**seed:** `typing.Optional[int]` — If specified, the system will make a best effort to sample deterministically for that particular seed value + +
+
+ +
+
+ +**max_tokens:** `typing.Optional[int]` — The maximum number of tokens to generate in the chat completion + +
+
+ +
+
+ +**logit_bias:** `typing.Optional[typing.Dict[str, CommonLogitBias]]` — Modify the likelihood of specified tokens appearing in the completion + +
+
+ +
+
+ +**response_format:** `typing.Optional[ChatCompletionResponseFormat]` — Response format (set to `json_object` to restrict output to JSON) + +
+
+ +
+
+ +**agent:** `typing.Optional[CommonUuid]` — Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + +
+
+ +
+
+ +**preset:** `typing.Optional[ChatGenerationPreset]` — Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + +
+
+ +
+
+ +**frequency_penalty:** `typing.Optional[float]` — Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + +
+
+ +
+
+ +**presence_penalty:** `typing.Optional[float]` — Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + +
+
+ +
+
+ +**temperature:** `typing.Optional[float]` — What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + +
+
+ +
+
+ +**top_p:** `typing.Optional[float]` — Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + +
+
+ +
+
+ +**repetition_penalty:** `typing.Optional[float]` — Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + +
+
+ +
+
+ +**length_penalty:** `typing.Optional[float]` — Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + +
+
+ +
+
+ +**min_p:** `typing.Optional[float]` — Minimum probability compared to leading token to be considered
diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index fec55842f..f3df33631 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -9,66 +9,42 @@ from .agent_tools_route_list_request_sort_by import AgentToolsRouteListRequestSortBy from .agent_tools_route_list_response import AgentToolsRouteListResponse from .agents_agent import AgentsAgent -from .agents_agent_default_settings import AgentsAgentDefaultSettings from .agents_agent_instructions import AgentsAgentInstructions from .agents_create_agent_request import AgentsCreateAgentRequest -from .agents_create_agent_request_default_settings import ( - AgentsCreateAgentRequestDefaultSettings, -) from .agents_create_agent_request_instructions import ( AgentsCreateAgentRequestInstructions, ) from .agents_docs_search_route_search_request_body import ( AgentsDocsSearchRouteSearchRequestBody, ) -from .agents_patch_agent_request_default_settings import ( - AgentsPatchAgentRequestDefaultSettings, -) from .agents_patch_agent_request_instructions import AgentsPatchAgentRequestInstructions from .agents_route_list_request_direction import AgentsRouteListRequestDirection from .agents_route_list_request_sort_by import AgentsRouteListRequestSortBy from .agents_route_list_response import AgentsRouteListResponse from .agents_update_agent_request import AgentsUpdateAgentRequest -from .agents_update_agent_request_default_settings import ( - AgentsUpdateAgentRequestDefaultSettings, -) from .agents_update_agent_request_instructions import ( AgentsUpdateAgentRequestInstructions, ) from .chat_base_chat_output import ChatBaseChatOutput from .chat_base_chat_response import ChatBaseChatResponse from .chat_base_token_log_prob import ChatBaseTokenLogProb +from .chat_chat_input_tool_choice import ChatChatInputToolChoice from .chat_chat_output_chunk import ChatChatOutputChunk +from .chat_chat_settings import ChatChatSettings from .chat_chunk_chat_response import ChatChunkChatResponse from .chat_competion_usage import ChatCompetionUsage from .chat_completion_response_format import ChatCompletionResponseFormat from .chat_completion_response_format_type import ChatCompletionResponseFormatType +from .chat_default_chat_settings import ChatDefaultChatSettings from .chat_finish_reason import ChatFinishReason from .chat_generation_preset import ChatGenerationPreset -from .chat_generation_preset_settings import ChatGenerationPresetSettings from .chat_log_prob_response import ChatLogProbResponse from .chat_message_chat_response import ChatMessageChatResponse +from .chat_message_chat_response_choices_item import ChatMessageChatResponseChoicesItem from .chat_multiple_chat_output import ChatMultipleChatOutput -from .chat_open_ai_settings import ChatOpenAiSettings -from .chat_route_generate_request import ChatRouteGenerateRequest -from .chat_route_generate_request_agent import ChatRouteGenerateRequestAgent -from .chat_route_generate_request_agent_tool_choice import ( - ChatRouteGenerateRequestAgentToolChoice, -) -from .chat_route_generate_request_frequency_penalty import ( - ChatRouteGenerateRequestFrequencyPenalty, -) -from .chat_route_generate_request_frequency_penalty_tool_choice import ( - ChatRouteGenerateRequestFrequencyPenaltyToolChoice, -) -from .chat_route_generate_request_preset import ChatRouteGenerateRequestPreset -from .chat_route_generate_request_preset_tool_choice import ( - ChatRouteGenerateRequestPresetToolChoice, -) from .chat_route_generate_response import ChatRouteGenerateResponse from .chat_single_chat_output import ChatSingleChatOutput from .chat_token_log_prob import ChatTokenLogProb -from .chat_v_llm_settings import ChatVLlmSettings from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode from .common_limit import CommonLimit from .common_logit_bias import CommonLogitBias @@ -214,12 +190,6 @@ ) from .tasks_prompt_step import TasksPromptStep from .tasks_prompt_step_prompt import TasksPromptStepPrompt -from .tasks_prompt_step_settings import TasksPromptStepSettings -from .tasks_prompt_step_settings_agent import TasksPromptStepSettingsAgent -from .tasks_prompt_step_settings_frequency_penalty import ( - TasksPromptStepSettingsFrequencyPenalty, -) -from .tasks_prompt_step_settings_preset import TasksPromptStepSettingsPreset from .tasks_route_list_request_direction import TasksRouteListRequestDirection from .tasks_route_list_request_sort_by import TasksRouteListRequestSortBy from .tasks_route_list_response import TasksRouteListResponse @@ -279,46 +249,36 @@ "AgentToolsRouteListRequestSortBy", "AgentToolsRouteListResponse", "AgentsAgent", - "AgentsAgentDefaultSettings", "AgentsAgentInstructions", "AgentsCreateAgentRequest", - "AgentsCreateAgentRequestDefaultSettings", "AgentsCreateAgentRequestInstructions", "AgentsDocsSearchRouteSearchRequestBody", - "AgentsPatchAgentRequestDefaultSettings", "AgentsPatchAgentRequestInstructions", "AgentsRouteListRequestDirection", "AgentsRouteListRequestSortBy", "AgentsRouteListResponse", "AgentsUpdateAgentRequest", - "AgentsUpdateAgentRequestDefaultSettings", "AgentsUpdateAgentRequestInstructions", "ChatBaseChatOutput", "ChatBaseChatResponse", "ChatBaseTokenLogProb", + "ChatChatInputToolChoice", "ChatChatOutputChunk", + "ChatChatSettings", "ChatChunkChatResponse", "ChatCompetionUsage", "ChatCompletionResponseFormat", "ChatCompletionResponseFormatType", + "ChatDefaultChatSettings", "ChatFinishReason", "ChatGenerationPreset", - "ChatGenerationPresetSettings", "ChatLogProbResponse", "ChatMessageChatResponse", + "ChatMessageChatResponseChoicesItem", "ChatMultipleChatOutput", - "ChatOpenAiSettings", - "ChatRouteGenerateRequest", - "ChatRouteGenerateRequestAgent", - "ChatRouteGenerateRequestAgentToolChoice", - "ChatRouteGenerateRequestFrequencyPenalty", - "ChatRouteGenerateRequestFrequencyPenaltyToolChoice", - "ChatRouteGenerateRequestPreset", - "ChatRouteGenerateRequestPresetToolChoice", "ChatRouteGenerateResponse", "ChatSingleChatOutput", "ChatTokenLogProb", - "ChatVLlmSettings", "CommonIdentifierSafeUnicode", "CommonLimit", "CommonLogitBias", @@ -432,10 +392,6 @@ "TasksPatchTaskRequestMainItem_Yield", "TasksPromptStep", "TasksPromptStepPrompt", - "TasksPromptStepSettings", - "TasksPromptStepSettingsAgent", - "TasksPromptStepSettingsFrequencyPenalty", - "TasksPromptStepSettingsPreset", "TasksRouteListRequestDirection", "TasksRouteListRequestSortBy", "TasksRouteListResponse", diff --git a/sdks/python/julep/api/types/agents_agent.py b/sdks/python/julep/api/types/agents_agent.py index 04827f31f..01fce6908 100644 --- a/sdks/python/julep/api/types/agents_agent.py +++ b/sdks/python/julep/api/types/agents_agent.py @@ -5,8 +5,8 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .agents_agent_default_settings import AgentsAgentDefaultSettings from .agents_agent_instructions import AgentsAgentInstructions +from .chat_default_chat_settings import ChatDefaultChatSettings from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode from .common_uuid import CommonUuid @@ -44,7 +44,7 @@ class AgentsAgent(pydantic_v1.BaseModel): Instructions for the agent """ - default_settings: typing.Optional[AgentsAgentDefaultSettings] = pydantic_v1.Field( + default_settings: typing.Optional[ChatDefaultChatSettings] = pydantic_v1.Field( default=None ) """ diff --git a/sdks/python/julep/api/types/agents_agent_default_settings.py b/sdks/python/julep/api/types/agents_agent_default_settings.py deleted file mode 100644 index 71b82cfc2..000000000 --- a/sdks/python/julep/api/types/agents_agent_default_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .chat_generation_preset_settings import ChatGenerationPresetSettings -from .chat_open_ai_settings import ChatOpenAiSettings -from .chat_v_llm_settings import ChatVLlmSettings - -AgentsAgentDefaultSettings = typing.Union[ - ChatGenerationPresetSettings, ChatOpenAiSettings, ChatVLlmSettings -] diff --git a/sdks/python/julep/api/types/agents_create_agent_request.py b/sdks/python/julep/api/types/agents_create_agent_request.py index 2a545c6dc..d5a78d583 100644 --- a/sdks/python/julep/api/types/agents_create_agent_request.py +++ b/sdks/python/julep/api/types/agents_create_agent_request.py @@ -5,12 +5,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .agents_create_agent_request_default_settings import ( - AgentsCreateAgentRequestDefaultSettings, -) from .agents_create_agent_request_instructions import ( AgentsCreateAgentRequestInstructions, ) +from .chat_default_chat_settings import ChatDefaultChatSettings from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode @@ -40,8 +38,8 @@ class AgentsCreateAgentRequest(pydantic_v1.BaseModel): Instructions for the agent """ - default_settings: typing.Optional[AgentsCreateAgentRequestDefaultSettings] = ( - pydantic_v1.Field(default=None) + default_settings: typing.Optional[ChatDefaultChatSettings] = pydantic_v1.Field( + default=None ) """ Default settings for all sessions created by this agent diff --git a/sdks/python/julep/api/types/agents_create_agent_request_default_settings.py b/sdks/python/julep/api/types/agents_create_agent_request_default_settings.py deleted file mode 100644 index da2d239bf..000000000 --- a/sdks/python/julep/api/types/agents_create_agent_request_default_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .chat_generation_preset_settings import ChatGenerationPresetSettings -from .chat_open_ai_settings import ChatOpenAiSettings -from .chat_v_llm_settings import ChatVLlmSettings - -AgentsCreateAgentRequestDefaultSettings = typing.Union[ - ChatGenerationPresetSettings, ChatOpenAiSettings, ChatVLlmSettings -] diff --git a/sdks/python/julep/api/types/agents_patch_agent_request_default_settings.py b/sdks/python/julep/api/types/agents_patch_agent_request_default_settings.py deleted file mode 100644 index 2ce2b4fc3..000000000 --- a/sdks/python/julep/api/types/agents_patch_agent_request_default_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .chat_generation_preset_settings import ChatGenerationPresetSettings -from .chat_open_ai_settings import ChatOpenAiSettings -from .chat_v_llm_settings import ChatVLlmSettings - -AgentsPatchAgentRequestDefaultSettings = typing.Union[ - ChatGenerationPresetSettings, ChatOpenAiSettings, ChatVLlmSettings -] diff --git a/sdks/python/julep/api/types/agents_update_agent_request.py b/sdks/python/julep/api/types/agents_update_agent_request.py index 1b7a648a7..ccbcc48cf 100644 --- a/sdks/python/julep/api/types/agents_update_agent_request.py +++ b/sdks/python/julep/api/types/agents_update_agent_request.py @@ -5,12 +5,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .agents_update_agent_request_default_settings import ( - AgentsUpdateAgentRequestDefaultSettings, -) from .agents_update_agent_request_instructions import ( AgentsUpdateAgentRequestInstructions, ) +from .chat_default_chat_settings import ChatDefaultChatSettings from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode @@ -40,8 +38,8 @@ class AgentsUpdateAgentRequest(pydantic_v1.BaseModel): Instructions for the agent """ - default_settings: typing.Optional[AgentsUpdateAgentRequestDefaultSettings] = ( - pydantic_v1.Field(default=None) + default_settings: typing.Optional[ChatDefaultChatSettings] = pydantic_v1.Field( + default=None ) """ Default settings for all sessions created by this agent diff --git a/sdks/python/julep/api/types/agents_update_agent_request_default_settings.py b/sdks/python/julep/api/types/agents_update_agent_request_default_settings.py deleted file mode 100644 index dc6ff15ab..000000000 --- a/sdks/python/julep/api/types/agents_update_agent_request_default_settings.py +++ /dev/null @@ -1,11 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .chat_generation_preset_settings import ChatGenerationPresetSettings -from .chat_open_ai_settings import ChatOpenAiSettings -from .chat_v_llm_settings import ChatVLlmSettings - -AgentsUpdateAgentRequestDefaultSettings = typing.Union[ - ChatGenerationPresetSettings, ChatOpenAiSettings, ChatVLlmSettings -] diff --git a/sdks/python/julep/api/types/chat_route_generate_request_agent_tool_choice.py b/sdks/python/julep/api/types/chat_chat_input_tool_choice.py similarity index 79% rename from sdks/python/julep/api/types/chat_route_generate_request_agent_tool_choice.py rename to sdks/python/julep/api/types/chat_chat_input_tool_choice.py index 9e95f3e1d..e25d6980b 100644 --- a/sdks/python/julep/api/types/chat_route_generate_request_agent_tool_choice.py +++ b/sdks/python/julep/api/types/chat_chat_input_tool_choice.py @@ -4,6 +4,6 @@ from .tools_named_tool_choice import ToolsNamedToolChoice -ChatRouteGenerateRequestAgentToolChoice = typing.Union[ +ChatChatInputToolChoice = typing.Union[ typing.Literal["auto"], typing.Literal["none"], ToolsNamedToolChoice ] diff --git a/sdks/python/julep/api/types/tasks_prompt_step_settings_agent.py b/sdks/python/julep/api/types/chat_chat_settings.py similarity index 82% rename from sdks/python/julep/api/types/tasks_prompt_step_settings_agent.py rename to sdks/python/julep/api/types/chat_chat_settings.py index 4f05effd6..8b7df8f7c 100644 --- a/sdks/python/julep/api/types/tasks_prompt_step_settings_agent.py +++ b/sdks/python/julep/api/types/chat_chat_settings.py @@ -6,12 +6,13 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .chat_completion_response_format import ChatCompletionResponseFormat +from .chat_generation_preset import ChatGenerationPreset from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode from .common_logit_bias import CommonLogitBias from .common_uuid import CommonUuid -class TasksPromptStepSettingsAgent(pydantic_v1.BaseModel): +class ChatChatSettings(pydantic_v1.BaseModel): model: typing.Optional[CommonIdentifierSafeUnicode] = pydantic_v1.Field( default=None ) @@ -58,14 +59,19 @@ class TasksPromptStepSettingsAgent(pydantic_v1.BaseModel): Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) """ - repetition_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + preset: typing.Optional[ChatGenerationPreset] = pydantic_v1.Field(default=None) """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) """ - length_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + frequency_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + + presence_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ temperature: typing.Optional[float] = pydantic_v1.Field(default=None) @@ -78,6 +84,16 @@ class TasksPromptStepSettingsAgent(pydantic_v1.BaseModel): Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. """ + repetition_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + + length_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + """ + min_p: typing.Optional[float] = pydantic_v1.Field(default=None) """ Minimum probability compared to leading token to be considered diff --git a/sdks/python/julep/api/types/chat_v_llm_settings.py b/sdks/python/julep/api/types/chat_default_chat_settings.py similarity index 71% rename from sdks/python/julep/api/types/chat_v_llm_settings.py rename to sdks/python/julep/api/types/chat_default_chat_settings.py index 897c25b2f..1b1838a34 100644 --- a/sdks/python/julep/api/types/chat_v_llm_settings.py +++ b/sdks/python/julep/api/types/chat_default_chat_settings.py @@ -5,17 +5,27 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_generation_preset import ChatGenerationPreset -class ChatVLlmSettings(pydantic_v1.BaseModel): - repetition_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) +class ChatDefaultChatSettings(pydantic_v1.BaseModel): """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + Default settings for the chat session (also used by the agent) """ - length_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + preset: typing.Optional[ChatGenerationPreset] = pydantic_v1.Field(default=None) """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + """ + + frequency_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + + presence_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ temperature: typing.Optional[float] = pydantic_v1.Field(default=None) @@ -28,6 +38,16 @@ class ChatVLlmSettings(pydantic_v1.BaseModel): Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. """ + repetition_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + + length_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + """ + min_p: typing.Optional[float] = pydantic_v1.Field(default=None) """ Minimum probability compared to leading token to be considered diff --git a/sdks/python/julep/api/types/chat_generation_preset_settings.py b/sdks/python/julep/api/types/chat_generation_preset_settings.py deleted file mode 100644 index 7ce09ceff..000000000 --- a/sdks/python/julep/api/types/chat_generation_preset_settings.py +++ /dev/null @@ -1,46 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .chat_generation_preset import ChatGenerationPreset - - -class ChatGenerationPresetSettings(pydantic_v1.BaseModel): - preset: typing.Optional[ChatGenerationPreset] = pydantic_v1.Field(default=None) - """ - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - """ - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/chat_message_chat_response.py b/sdks/python/julep/api/types/chat_message_chat_response.py index fd8b40082..2a5a789d3 100644 --- a/sdks/python/julep/api/types/chat_message_chat_response.py +++ b/sdks/python/julep/api/types/chat_message_chat_response.py @@ -6,11 +6,11 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .chat_base_chat_response import ChatBaseChatResponse -from .chat_chat_output_chunk import ChatChatOutputChunk +from .chat_message_chat_response_choices_item import ChatMessageChatResponseChoicesItem class ChatMessageChatResponse(ChatBaseChatResponse): - choices: typing.List[ChatChatOutputChunk] = pydantic_v1.Field() + choices: typing.List[ChatMessageChatResponseChoicesItem] = pydantic_v1.Field() """ The deltas generated by the model """ diff --git a/sdks/python/julep/api/types/chat_message_chat_response_choices_item.py b/sdks/python/julep/api/types/chat_message_chat_response_choices_item.py new file mode 100644 index 000000000..c542c6335 --- /dev/null +++ b/sdks/python/julep/api/types/chat_message_chat_response_choices_item.py @@ -0,0 +1,10 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .chat_multiple_chat_output import ChatMultipleChatOutput +from .chat_single_chat_output import ChatSingleChatOutput + +ChatMessageChatResponseChoicesItem = typing.Union[ + ChatSingleChatOutput, ChatMultipleChatOutput +] diff --git a/sdks/python/julep/api/types/chat_open_ai_settings.py b/sdks/python/julep/api/types/chat_open_ai_settings.py deleted file mode 100644 index 0bb5a0799..000000000 --- a/sdks/python/julep/api/types/chat_open_ai_settings.py +++ /dev/null @@ -1,60 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 - - -class ChatOpenAiSettings(pydantic_v1.BaseModel): - frequency_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - presence_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - temperature: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - - top_p: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/chat_route_generate_request.py b/sdks/python/julep/api/types/chat_route_generate_request.py deleted file mode 100644 index 33c9ec62d..000000000 --- a/sdks/python/julep/api/types/chat_route_generate_request.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .chat_route_generate_request_agent import ChatRouteGenerateRequestAgent -from .chat_route_generate_request_frequency_penalty import ( - ChatRouteGenerateRequestFrequencyPenalty, -) -from .chat_route_generate_request_preset import ChatRouteGenerateRequestPreset - -ChatRouteGenerateRequest = typing.Union[ - ChatRouteGenerateRequestPreset, - ChatRouteGenerateRequestFrequencyPenalty, - ChatRouteGenerateRequestAgent, -] diff --git a/sdks/python/julep/api/types/chat_route_generate_request_agent.py b/sdks/python/julep/api/types/chat_route_generate_request_agent.py deleted file mode 100644 index 6ab50c264..000000000 --- a/sdks/python/julep/api/types/chat_route_generate_request_agent.py +++ /dev/null @@ -1,154 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .chat_completion_response_format import ChatCompletionResponseFormat -from .chat_route_generate_request_agent_tool_choice import ( - ChatRouteGenerateRequestAgentToolChoice, -) -from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode -from .common_logit_bias import CommonLogitBias -from .common_uuid import CommonUuid -from .entries_input_chat_ml_message import EntriesInputChatMlMessage -from .tools_function_tool import ToolsFunctionTool - - -class ChatRouteGenerateRequestAgent(pydantic_v1.BaseModel): - messages: typing.List[EntriesInputChatMlMessage] = pydantic_v1.Field() - """ - A list of new input messages comprising the conversation so far. - """ - - tools: typing.Optional[typing.List[ToolsFunctionTool]] = pydantic_v1.Field( - default=None - ) - """ - (Advanced) List of tools that are provided in addition to agent's default set of tools. - """ - - tool_choice: typing.Optional[ChatRouteGenerateRequestAgentToolChoice] = ( - pydantic_v1.Field(default=None) - ) - """ - Can be one of existing tools given to the agent earlier or the ones provided in this request. - """ - - recall: bool = pydantic_v1.Field() - """ - Whether previous memories should be recalled or not (will be enabled in a future release) - """ - - remember: bool = pydantic_v1.Field() - """ - Whether this interaction should form new memories or not (will be enabled in a future release) - """ - - save: bool = pydantic_v1.Field() - """ - Whether this interaction should be stored in the session history or not - """ - - model: typing.Optional[CommonIdentifierSafeUnicode] = pydantic_v1.Field( - default=None - ) - """ - Identifier of the model to be used - """ - - stream: bool = pydantic_v1.Field() - """ - Indicates if the server should stream the response as it's generated - """ - - stop: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - Up to 4 sequences where the API will stop generating further tokens. - """ - - seed: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - If specified, the system will make a best effort to sample deterministically for that particular seed value - """ - - max_tokens: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - The maximum number of tokens to generate in the chat completion - """ - - logit_bias: typing.Optional[typing.Dict[str, CommonLogitBias]] = pydantic_v1.Field( - default=None - ) - """ - Modify the likelihood of specified tokens appearing in the completion - """ - - response_format: typing.Optional[ChatCompletionResponseFormat] = pydantic_v1.Field( - default=None - ) - """ - Response format (set to `json_object` to restrict output to JSON) - """ - - agent: typing.Optional[CommonUuid] = pydantic_v1.Field(default=None) - """ - Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - """ - - repetition_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - length_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - """ - - temperature: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - - top_p: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - - min_p: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Minimum probability compared to leading token to be considered - """ - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/chat_route_generate_request_frequency_penalty.py b/sdks/python/julep/api/types/chat_route_generate_request_frequency_penalty.py deleted file mode 100644 index f62b323df..000000000 --- a/sdks/python/julep/api/types/chat_route_generate_request_frequency_penalty.py +++ /dev/null @@ -1,149 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .chat_completion_response_format import ChatCompletionResponseFormat -from .chat_route_generate_request_frequency_penalty_tool_choice import ( - ChatRouteGenerateRequestFrequencyPenaltyToolChoice, -) -from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode -from .common_logit_bias import CommonLogitBias -from .common_uuid import CommonUuid -from .entries_input_chat_ml_message import EntriesInputChatMlMessage -from .tools_function_tool import ToolsFunctionTool - - -class ChatRouteGenerateRequestFrequencyPenalty(pydantic_v1.BaseModel): - messages: typing.List[EntriesInputChatMlMessage] = pydantic_v1.Field() - """ - A list of new input messages comprising the conversation so far. - """ - - tools: typing.Optional[typing.List[ToolsFunctionTool]] = pydantic_v1.Field( - default=None - ) - """ - (Advanced) List of tools that are provided in addition to agent's default set of tools. - """ - - tool_choice: typing.Optional[ChatRouteGenerateRequestFrequencyPenaltyToolChoice] = ( - pydantic_v1.Field(default=None) - ) - """ - Can be one of existing tools given to the agent earlier or the ones provided in this request. - """ - - recall: bool = pydantic_v1.Field() - """ - Whether previous memories should be recalled or not (will be enabled in a future release) - """ - - remember: bool = pydantic_v1.Field() - """ - Whether this interaction should form new memories or not (will be enabled in a future release) - """ - - save: bool = pydantic_v1.Field() - """ - Whether this interaction should be stored in the session history or not - """ - - model: typing.Optional[CommonIdentifierSafeUnicode] = pydantic_v1.Field( - default=None - ) - """ - Identifier of the model to be used - """ - - stream: bool = pydantic_v1.Field() - """ - Indicates if the server should stream the response as it's generated - """ - - stop: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - Up to 4 sequences where the API will stop generating further tokens. - """ - - seed: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - If specified, the system will make a best effort to sample deterministically for that particular seed value - """ - - max_tokens: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - The maximum number of tokens to generate in the chat completion - """ - - logit_bias: typing.Optional[typing.Dict[str, CommonLogitBias]] = pydantic_v1.Field( - default=None - ) - """ - Modify the likelihood of specified tokens appearing in the completion - """ - - response_format: typing.Optional[ChatCompletionResponseFormat] = pydantic_v1.Field( - default=None - ) - """ - Response format (set to `json_object` to restrict output to JSON) - """ - - agent: typing.Optional[CommonUuid] = pydantic_v1.Field(default=None) - """ - Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - """ - - frequency_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - presence_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - temperature: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - - top_p: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/chat_route_generate_request_frequency_penalty_tool_choice.py b/sdks/python/julep/api/types/chat_route_generate_request_frequency_penalty_tool_choice.py deleted file mode 100644 index 73169ee30..000000000 --- a/sdks/python/julep/api/types/chat_route_generate_request_frequency_penalty_tool_choice.py +++ /dev/null @@ -1,9 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .tools_named_tool_choice import ToolsNamedToolChoice - -ChatRouteGenerateRequestFrequencyPenaltyToolChoice = typing.Union[ - typing.Literal["auto"], typing.Literal["none"], ToolsNamedToolChoice -] diff --git a/sdks/python/julep/api/types/chat_route_generate_request_preset.py b/sdks/python/julep/api/types/chat_route_generate_request_preset.py deleted file mode 100644 index b98ccd98d..000000000 --- a/sdks/python/julep/api/types/chat_route_generate_request_preset.py +++ /dev/null @@ -1,135 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .chat_completion_response_format import ChatCompletionResponseFormat -from .chat_generation_preset import ChatGenerationPreset -from .chat_route_generate_request_preset_tool_choice import ( - ChatRouteGenerateRequestPresetToolChoice, -) -from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode -from .common_logit_bias import CommonLogitBias -from .common_uuid import CommonUuid -from .entries_input_chat_ml_message import EntriesInputChatMlMessage -from .tools_function_tool import ToolsFunctionTool - - -class ChatRouteGenerateRequestPreset(pydantic_v1.BaseModel): - messages: typing.List[EntriesInputChatMlMessage] = pydantic_v1.Field() - """ - A list of new input messages comprising the conversation so far. - """ - - tools: typing.Optional[typing.List[ToolsFunctionTool]] = pydantic_v1.Field( - default=None - ) - """ - (Advanced) List of tools that are provided in addition to agent's default set of tools. - """ - - tool_choice: typing.Optional[ChatRouteGenerateRequestPresetToolChoice] = ( - pydantic_v1.Field(default=None) - ) - """ - Can be one of existing tools given to the agent earlier or the ones provided in this request. - """ - - recall: bool = pydantic_v1.Field() - """ - Whether previous memories should be recalled or not (will be enabled in a future release) - """ - - remember: bool = pydantic_v1.Field() - """ - Whether this interaction should form new memories or not (will be enabled in a future release) - """ - - save: bool = pydantic_v1.Field() - """ - Whether this interaction should be stored in the session history or not - """ - - model: typing.Optional[CommonIdentifierSafeUnicode] = pydantic_v1.Field( - default=None - ) - """ - Identifier of the model to be used - """ - - stream: bool = pydantic_v1.Field() - """ - Indicates if the server should stream the response as it's generated - """ - - stop: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - Up to 4 sequences where the API will stop generating further tokens. - """ - - seed: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - If specified, the system will make a best effort to sample deterministically for that particular seed value - """ - - max_tokens: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - The maximum number of tokens to generate in the chat completion - """ - - logit_bias: typing.Optional[typing.Dict[str, CommonLogitBias]] = pydantic_v1.Field( - default=None - ) - """ - Modify the likelihood of specified tokens appearing in the completion - """ - - response_format: typing.Optional[ChatCompletionResponseFormat] = pydantic_v1.Field( - default=None - ) - """ - Response format (set to `json_object` to restrict output to JSON) - """ - - agent: typing.Optional[CommonUuid] = pydantic_v1.Field(default=None) - """ - Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - """ - - preset: typing.Optional[ChatGenerationPreset] = pydantic_v1.Field(default=None) - """ - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - """ - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/chat_route_generate_request_preset_tool_choice.py b/sdks/python/julep/api/types/chat_route_generate_request_preset_tool_choice.py deleted file mode 100644 index 92d41ff7f..000000000 --- a/sdks/python/julep/api/types/chat_route_generate_request_preset_tool_choice.py +++ /dev/null @@ -1,9 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .tools_named_tool_choice import ToolsNamedToolChoice - -ChatRouteGenerateRequestPresetToolChoice = typing.Union[ - typing.Literal["auto"], typing.Literal["none"], ToolsNamedToolChoice -] diff --git a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py index f241c6fc7..ae9916c31 100644 --- a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py @@ -7,12 +7,12 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings from .common_py_expression import CommonPyExpression from .common_tool_ref import CommonToolRef from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen from .tasks_prompt_step_prompt import TasksPromptStepPrompt -from .tasks_prompt_step_settings import TasksPromptStepSettings from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo @@ -138,7 +138,7 @@ class Config: class TasksCreateTaskRequestMainItem_Prompt(pydantic_v1.BaseModel): prompt: TasksPromptStepPrompt - settings: TasksPromptStepSettings + settings: ChatChatSettings kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") def json(self, **kwargs: typing.Any) -> str: diff --git a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py index 999373c34..6532321d0 100644 --- a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py @@ -7,12 +7,12 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings from .common_py_expression import CommonPyExpression from .common_tool_ref import CommonToolRef from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen from .tasks_prompt_step_prompt import TasksPromptStepPrompt -from .tasks_prompt_step_settings import TasksPromptStepSettings from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo @@ -138,7 +138,7 @@ class Config: class TasksPatchTaskRequestMainItem_Prompt(pydantic_v1.BaseModel): prompt: TasksPromptStepPrompt - settings: TasksPromptStepSettings + settings: ChatChatSettings kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") def json(self, **kwargs: typing.Any) -> str: diff --git a/sdks/python/julep/api/types/tasks_prompt_step.py b/sdks/python/julep/api/types/tasks_prompt_step.py index 17b79d10e..998d97355 100644 --- a/sdks/python/julep/api/types/tasks_prompt_step.py +++ b/sdks/python/julep/api/types/tasks_prompt_step.py @@ -5,9 +5,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_prompt_step_prompt import TasksPromptStepPrompt -from .tasks_prompt_step_settings import TasksPromptStepSettings class TasksPromptStep(TasksBaseWorkflowStep): @@ -16,7 +16,7 @@ class TasksPromptStep(TasksBaseWorkflowStep): The prompt to run """ - settings: TasksPromptStepSettings = pydantic_v1.Field() + settings: ChatChatSettings = pydantic_v1.Field() """ Settings for the prompt """ diff --git a/sdks/python/julep/api/types/tasks_prompt_step_settings.py b/sdks/python/julep/api/types/tasks_prompt_step_settings.py deleted file mode 100644 index da86b2ad2..000000000 --- a/sdks/python/julep/api/types/tasks_prompt_step_settings.py +++ /dev/null @@ -1,15 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .tasks_prompt_step_settings_agent import TasksPromptStepSettingsAgent -from .tasks_prompt_step_settings_frequency_penalty import ( - TasksPromptStepSettingsFrequencyPenalty, -) -from .tasks_prompt_step_settings_preset import TasksPromptStepSettingsPreset - -TasksPromptStepSettings = typing.Union[ - TasksPromptStepSettingsPreset, - TasksPromptStepSettingsFrequencyPenalty, - TasksPromptStepSettingsAgent, -] diff --git a/sdks/python/julep/api/types/tasks_prompt_step_settings_frequency_penalty.py b/sdks/python/julep/api/types/tasks_prompt_step_settings_frequency_penalty.py deleted file mode 100644 index aaefaff25..000000000 --- a/sdks/python/julep/api/types/tasks_prompt_step_settings_frequency_penalty.py +++ /dev/null @@ -1,110 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .chat_completion_response_format import ChatCompletionResponseFormat -from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode -from .common_logit_bias import CommonLogitBias -from .common_uuid import CommonUuid - - -class TasksPromptStepSettingsFrequencyPenalty(pydantic_v1.BaseModel): - model: typing.Optional[CommonIdentifierSafeUnicode] = pydantic_v1.Field( - default=None - ) - """ - Identifier of the model to be used - """ - - stream: bool = pydantic_v1.Field() - """ - Indicates if the server should stream the response as it's generated - """ - - stop: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - Up to 4 sequences where the API will stop generating further tokens. - """ - - seed: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - If specified, the system will make a best effort to sample deterministically for that particular seed value - """ - - max_tokens: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - The maximum number of tokens to generate in the chat completion - """ - - logit_bias: typing.Optional[typing.Dict[str, CommonLogitBias]] = pydantic_v1.Field( - default=None - ) - """ - Modify the likelihood of specified tokens appearing in the completion - """ - - response_format: typing.Optional[ChatCompletionResponseFormat] = pydantic_v1.Field( - default=None - ) - """ - Response format (set to `json_object` to restrict output to JSON) - """ - - agent: typing.Optional[CommonUuid] = pydantic_v1.Field(default=None) - """ - Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - """ - - frequency_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - presence_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - temperature: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - - top_p: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_prompt_step_settings_preset.py b/sdks/python/julep/api/types/tasks_prompt_step_settings_preset.py deleted file mode 100644 index 699611201..000000000 --- a/sdks/python/julep/api/types/tasks_prompt_step_settings_preset.py +++ /dev/null @@ -1,96 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .chat_completion_response_format import ChatCompletionResponseFormat -from .chat_generation_preset import ChatGenerationPreset -from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode -from .common_logit_bias import CommonLogitBias -from .common_uuid import CommonUuid - - -class TasksPromptStepSettingsPreset(pydantic_v1.BaseModel): - model: typing.Optional[CommonIdentifierSafeUnicode] = pydantic_v1.Field( - default=None - ) - """ - Identifier of the model to be used - """ - - stream: bool = pydantic_v1.Field() - """ - Indicates if the server should stream the response as it's generated - """ - - stop: typing.Optional[typing.List[str]] = pydantic_v1.Field(default=None) - """ - Up to 4 sequences where the API will stop generating further tokens. - """ - - seed: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - If specified, the system will make a best effort to sample deterministically for that particular seed value - """ - - max_tokens: typing.Optional[int] = pydantic_v1.Field(default=None) - """ - The maximum number of tokens to generate in the chat completion - """ - - logit_bias: typing.Optional[typing.Dict[str, CommonLogitBias]] = pydantic_v1.Field( - default=None - ) - """ - Modify the likelihood of specified tokens appearing in the completion - """ - - response_format: typing.Optional[ChatCompletionResponseFormat] = pydantic_v1.Field( - default=None - ) - """ - Response format (set to `json_object` to restrict output to JSON) - """ - - agent: typing.Optional[CommonUuid] = pydantic_v1.Field(default=None) - """ - Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - """ - - preset: typing.Optional[ChatGenerationPreset] = pydantic_v1.Field(default=None) - """ - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - """ - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_task_main_item.py b/sdks/python/julep/api/types/tasks_task_main_item.py index 4294ceadb..41cda3d7d 100644 --- a/sdks/python/julep/api/types/tasks_task_main_item.py +++ b/sdks/python/julep/api/types/tasks_task_main_item.py @@ -7,12 +7,12 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings from .common_py_expression import CommonPyExpression from .common_tool_ref import CommonToolRef from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen from .tasks_prompt_step_prompt import TasksPromptStepPrompt -from .tasks_prompt_step_settings import TasksPromptStepSettings from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo @@ -138,7 +138,7 @@ class Config: class TasksTaskMainItem_Prompt(pydantic_v1.BaseModel): prompt: TasksPromptStepPrompt - settings: TasksPromptStepSettings + settings: ChatChatSettings kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") def json(self, **kwargs: typing.Any) -> str: diff --git a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py index 6eb5a72b5..730545283 100644 --- a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py @@ -7,12 +7,12 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings from .common_py_expression import CommonPyExpression from .common_tool_ref import CommonToolRef from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen from .tasks_prompt_step_prompt import TasksPromptStepPrompt -from .tasks_prompt_step_settings import TasksPromptStepSettings from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo @@ -138,7 +138,7 @@ class Config: class TasksUpdateTaskRequestMainItem_Prompt(pydantic_v1.BaseModel): prompt: TasksPromptStepPrompt - settings: TasksPromptStepSettings + settings: ChatChatSettings kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") def json(self, **kwargs: typing.Any) -> str: diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index 7cf1fe390..a9625977a 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -18,20 +18,20 @@ export type { Agents_UpdateAgentRequest } from "./models/Agents_UpdateAgentReque export type { Chat_BaseChatOutput } from "./models/Chat_BaseChatOutput"; export type { Chat_BaseChatResponse } from "./models/Chat_BaseChatResponse"; export type { Chat_BaseTokenLogProb } from "./models/Chat_BaseTokenLogProb"; +export type { Chat_ChatInput } from "./models/Chat_ChatInput"; export type { Chat_ChatOutputChunk } from "./models/Chat_ChatOutputChunk"; +export type { Chat_ChatSettings } from "./models/Chat_ChatSettings"; export type { Chat_ChunkChatResponse } from "./models/Chat_ChunkChatResponse"; export type { Chat_CompetionUsage } from "./models/Chat_CompetionUsage"; export type { Chat_CompletionResponseFormat } from "./models/Chat_CompletionResponseFormat"; +export type { Chat_DefaultChatSettings } from "./models/Chat_DefaultChatSettings"; export type { Chat_FinishReason } from "./models/Chat_FinishReason"; export type { Chat_GenerationPreset } from "./models/Chat_GenerationPreset"; -export type { Chat_GenerationPresetSettings } from "./models/Chat_GenerationPresetSettings"; export type { Chat_LogProbResponse } from "./models/Chat_LogProbResponse"; export type { Chat_MessageChatResponse } from "./models/Chat_MessageChatResponse"; export type { Chat_MultipleChatOutput } from "./models/Chat_MultipleChatOutput"; -export type { Chat_OpenAISettings } from "./models/Chat_OpenAISettings"; export type { Chat_SingleChatOutput } from "./models/Chat_SingleChatOutput"; export type { Chat_TokenLogProb } from "./models/Chat_TokenLogProb"; -export type { Chat_vLLMSettings } from "./models/Chat_vLLMSettings"; export type { Common_identifierSafeUnicode } from "./models/Common_identifierSafeUnicode"; export type { Common_limit } from "./models/Common_limit"; export type { Common_logit_bias } from "./models/Common_logit_bias"; @@ -132,20 +132,20 @@ export { $Agents_UpdateAgentRequest } from "./schemas/$Agents_UpdateAgentRequest export { $Chat_BaseChatOutput } from "./schemas/$Chat_BaseChatOutput"; export { $Chat_BaseChatResponse } from "./schemas/$Chat_BaseChatResponse"; export { $Chat_BaseTokenLogProb } from "./schemas/$Chat_BaseTokenLogProb"; +export { $Chat_ChatInput } from "./schemas/$Chat_ChatInput"; export { $Chat_ChatOutputChunk } from "./schemas/$Chat_ChatOutputChunk"; +export { $Chat_ChatSettings } from "./schemas/$Chat_ChatSettings"; export { $Chat_ChunkChatResponse } from "./schemas/$Chat_ChunkChatResponse"; export { $Chat_CompetionUsage } from "./schemas/$Chat_CompetionUsage"; export { $Chat_CompletionResponseFormat } from "./schemas/$Chat_CompletionResponseFormat"; +export { $Chat_DefaultChatSettings } from "./schemas/$Chat_DefaultChatSettings"; export { $Chat_FinishReason } from "./schemas/$Chat_FinishReason"; export { $Chat_GenerationPreset } from "./schemas/$Chat_GenerationPreset"; -export { $Chat_GenerationPresetSettings } from "./schemas/$Chat_GenerationPresetSettings"; export { $Chat_LogProbResponse } from "./schemas/$Chat_LogProbResponse"; export { $Chat_MessageChatResponse } from "./schemas/$Chat_MessageChatResponse"; export { $Chat_MultipleChatOutput } from "./schemas/$Chat_MultipleChatOutput"; -export { $Chat_OpenAISettings } from "./schemas/$Chat_OpenAISettings"; export { $Chat_SingleChatOutput } from "./schemas/$Chat_SingleChatOutput"; export { $Chat_TokenLogProb } from "./schemas/$Chat_TokenLogProb"; -export { $Chat_vLLMSettings } from "./schemas/$Chat_vLLMSettings"; export { $Common_identifierSafeUnicode } from "./schemas/$Common_identifierSafeUnicode"; export { $Common_limit } from "./schemas/$Common_limit"; export { $Common_logit_bias } from "./schemas/$Common_logit_bias"; diff --git a/sdks/ts/src/api/models/Agents_Agent.ts b/sdks/ts/src/api/models/Agents_Agent.ts index c92c84c33..bab63a6e2 100644 --- a/sdks/ts/src/api/models/Agents_Agent.ts +++ b/sdks/ts/src/api/models/Agents_Agent.ts @@ -2,9 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Chat_GenerationPresetSettings } from "./Chat_GenerationPresetSettings"; -import type { Chat_OpenAISettings } from "./Chat_OpenAISettings"; -import type { Chat_vLLMSettings } from "./Chat_vLLMSettings"; +import type { Chat_DefaultChatSettings } from "./Chat_DefaultChatSettings"; import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; import type { Common_uuid } from "./Common_uuid"; export type Agents_Agent = { @@ -37,8 +35,5 @@ export type Agents_Agent = { /** * Default settings for all sessions created by this agent */ - default_settings?: - | Chat_GenerationPresetSettings - | Chat_OpenAISettings - | Chat_vLLMSettings; + default_settings?: Chat_DefaultChatSettings; }; diff --git a/sdks/ts/src/api/models/Agents_CreateAgentRequest.ts b/sdks/ts/src/api/models/Agents_CreateAgentRequest.ts index c9c95127b..73c576816 100644 --- a/sdks/ts/src/api/models/Agents_CreateAgentRequest.ts +++ b/sdks/ts/src/api/models/Agents_CreateAgentRequest.ts @@ -2,9 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Chat_GenerationPresetSettings } from "./Chat_GenerationPresetSettings"; -import type { Chat_OpenAISettings } from "./Chat_OpenAISettings"; -import type { Chat_vLLMSettings } from "./Chat_vLLMSettings"; +import type { Chat_DefaultChatSettings } from "./Chat_DefaultChatSettings"; import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; /** * Payload for creating a agent (and associated documents) @@ -30,8 +28,5 @@ export type Agents_CreateAgentRequest = { /** * Default settings for all sessions created by this agent */ - default_settings?: - | Chat_GenerationPresetSettings - | Chat_OpenAISettings - | Chat_vLLMSettings; + default_settings?: Chat_DefaultChatSettings; }; diff --git a/sdks/ts/src/api/models/Agents_PatchAgentRequest.ts b/sdks/ts/src/api/models/Agents_PatchAgentRequest.ts index 52f60bb04..e4334103d 100644 --- a/sdks/ts/src/api/models/Agents_PatchAgentRequest.ts +++ b/sdks/ts/src/api/models/Agents_PatchAgentRequest.ts @@ -2,9 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Chat_GenerationPresetSettings } from "./Chat_GenerationPresetSettings"; -import type { Chat_OpenAISettings } from "./Chat_OpenAISettings"; -import type { Chat_vLLMSettings } from "./Chat_vLLMSettings"; +import type { Chat_DefaultChatSettings } from "./Chat_DefaultChatSettings"; import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; /** * Payload for patching a agent @@ -30,8 +28,5 @@ export type Agents_PatchAgentRequest = { /** * Default settings for all sessions created by this agent */ - default_settings?: - | Chat_GenerationPresetSettings - | Chat_OpenAISettings - | Chat_vLLMSettings; + default_settings?: Chat_DefaultChatSettings; }; diff --git a/sdks/ts/src/api/models/Agents_UpdateAgentRequest.ts b/sdks/ts/src/api/models/Agents_UpdateAgentRequest.ts index 1b5aa4c23..ead87abfe 100644 --- a/sdks/ts/src/api/models/Agents_UpdateAgentRequest.ts +++ b/sdks/ts/src/api/models/Agents_UpdateAgentRequest.ts @@ -2,9 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Chat_GenerationPresetSettings } from "./Chat_GenerationPresetSettings"; -import type { Chat_OpenAISettings } from "./Chat_OpenAISettings"; -import type { Chat_vLLMSettings } from "./Chat_vLLMSettings"; +import type { Chat_DefaultChatSettings } from "./Chat_DefaultChatSettings"; import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; /** * Payload for updating a agent @@ -30,8 +28,5 @@ export type Agents_UpdateAgentRequest = { /** * Default settings for all sessions created by this agent */ - default_settings?: - | Chat_GenerationPresetSettings - | Chat_OpenAISettings - | Chat_vLLMSettings; + default_settings?: Chat_DefaultChatSettings; }; diff --git a/sdks/ts/src/api/models/Chat_ChatInput.ts b/sdks/ts/src/api/models/Chat_ChatInput.ts new file mode 100644 index 000000000..68672573a --- /dev/null +++ b/sdks/ts/src/api/models/Chat_ChatInput.ts @@ -0,0 +1,102 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Chat_CompletionResponseFormat } from "./Chat_CompletionResponseFormat"; +import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; +import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; +import type { Common_logit_bias } from "./Common_logit_bias"; +import type { Common_uuid } from "./Common_uuid"; +import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; +import type { Tools_FunctionTool } from "./Tools_FunctionTool"; +import type { Tools_NamedToolChoice } from "./Tools_NamedToolChoice"; +export type Chat_ChatInput = { + /** + * A list of new input messages comprising the conversation so far. + */ + messages: Array; + /** + * (Advanced) List of tools that are provided in addition to agent's default set of tools. + */ + tools?: Array; + /** + * Can be one of existing tools given to the agent earlier or the ones provided in this request. + */ + tool_choice?: "auto" | "none" | Tools_NamedToolChoice; + /** + * Whether previous memories should be recalled or not (will be enabled in a future release) + */ + readonly recall: boolean; + /** + * Whether this interaction should form new memories or not (will be enabled in a future release) + */ + readonly remember: boolean; + /** + * Whether this interaction should be stored in the session history or not + */ + save: boolean; + /** + * Identifier of the model to be used + */ + model?: Common_identifierSafeUnicode; + /** + * Indicates if the server should stream the response as it's generated + */ + stream: boolean; + /** + * Up to 4 sequences where the API will stop generating further tokens. + */ + stop?: Array; + /** + * If specified, the system will make a best effort to sample deterministically for that particular seed value + */ + seed?: number; + /** + * The maximum number of tokens to generate in the chat completion + */ + max_tokens?: number; + /** + * Modify the likelihood of specified tokens appearing in the completion + */ + logit_bias?: Record; + /** + * Response format (set to `json_object` to restrict output to JSON) + */ + response_format?: Chat_CompletionResponseFormat; + /** + * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + */ + agent?: Common_uuid; + /** + * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + */ + preset?: Chat_GenerationPreset; + /** + * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + frequency_penalty?: number; + /** + * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + presence_penalty?: number; + /** + * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + */ + temperature?: number; + /** + * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + */ + top_p?: number; + /** + * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + repetition_penalty?: number; + /** + * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + */ + length_penalty?: number; + /** + * Minimum probability compared to leading token to be considered + */ + min_p?: number; +}; diff --git a/sdks/ts/src/api/models/Chat_ChatSettings.ts b/sdks/ts/src/api/models/Chat_ChatSettings.ts new file mode 100644 index 000000000..d19fa27d2 --- /dev/null +++ b/sdks/ts/src/api/models/Chat_ChatSettings.ts @@ -0,0 +1,75 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Chat_CompletionResponseFormat } from "./Chat_CompletionResponseFormat"; +import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; +import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; +import type { Common_logit_bias } from "./Common_logit_bias"; +import type { Common_uuid } from "./Common_uuid"; +export type Chat_ChatSettings = { + /** + * Identifier of the model to be used + */ + model?: Common_identifierSafeUnicode; + /** + * Indicates if the server should stream the response as it's generated + */ + stream: boolean; + /** + * Up to 4 sequences where the API will stop generating further tokens. + */ + stop?: Array; + /** + * If specified, the system will make a best effort to sample deterministically for that particular seed value + */ + seed?: number; + /** + * The maximum number of tokens to generate in the chat completion + */ + max_tokens?: number; + /** + * Modify the likelihood of specified tokens appearing in the completion + */ + logit_bias?: Record; + /** + * Response format (set to `json_object` to restrict output to JSON) + */ + response_format?: Chat_CompletionResponseFormat; + /** + * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + */ + agent?: Common_uuid; + /** + * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + */ + preset?: Chat_GenerationPreset; + /** + * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + frequency_penalty?: number; + /** + * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + presence_penalty?: number; + /** + * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + */ + temperature?: number; + /** + * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + */ + top_p?: number; + /** + * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + repetition_penalty?: number; + /** + * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + */ + length_penalty?: number; + /** + * Minimum probability compared to leading token to be considered + */ + min_p?: number; +}; diff --git a/sdks/ts/src/api/models/Chat_vLLMSettings.ts b/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts similarity index 59% rename from sdks/ts/src/api/models/Chat_vLLMSettings.ts rename to sdks/ts/src/api/models/Chat_DefaultChatSettings.ts index 8973da946..e1fb07bd1 100644 --- a/sdks/ts/src/api/models/Chat_vLLMSettings.ts +++ b/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts @@ -2,15 +2,23 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export type Chat_vLLMSettings = { +import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; +/** + * Default settings for the chat session (also used by the agent) + */ +export type Chat_DefaultChatSettings = { /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) */ - repetition_penalty?: number; + preset?: Chat_GenerationPreset; /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. */ - length_penalty?: number; + frequency_penalty?: number; + /** + * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + presence_penalty?: number; /** * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. */ @@ -19,6 +27,14 @@ export type Chat_vLLMSettings = { * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. */ top_p?: number; + /** + * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + repetition_penalty?: number; + /** + * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + */ + length_penalty?: number; /** * Minimum probability compared to leading token to be considered */ diff --git a/sdks/ts/src/api/models/Chat_GenerationPresetSettings.ts b/sdks/ts/src/api/models/Chat_GenerationPresetSettings.ts deleted file mode 100644 index 230124c0a..000000000 --- a/sdks/ts/src/api/models/Chat_GenerationPresetSettings.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; -export type Chat_GenerationPresetSettings = { - /** - * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - */ - preset?: Chat_GenerationPreset; -}; diff --git a/sdks/ts/src/api/models/Chat_MessageChatResponse.ts b/sdks/ts/src/api/models/Chat_MessageChatResponse.ts index 32efea493..4ed0e111f 100644 --- a/sdks/ts/src/api/models/Chat_MessageChatResponse.ts +++ b/sdks/ts/src/api/models/Chat_MessageChatResponse.ts @@ -3,10 +3,11 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_BaseChatResponse } from "./Chat_BaseChatResponse"; -import type { Chat_ChatOutputChunk } from "./Chat_ChatOutputChunk"; +import type { Chat_MultipleChatOutput } from "./Chat_MultipleChatOutput"; +import type { Chat_SingleChatOutput } from "./Chat_SingleChatOutput"; export type Chat_MessageChatResponse = Chat_BaseChatResponse & { /** * The deltas generated by the model */ - choices: Array; + choices: Array; }; diff --git a/sdks/ts/src/api/models/Chat_OpenAISettings.ts b/sdks/ts/src/api/models/Chat_OpenAISettings.ts deleted file mode 100644 index 2ab230a6f..000000000 --- a/sdks/ts/src/api/models/Chat_OpenAISettings.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type Chat_OpenAISettings = { - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - frequency_penalty?: number; - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - presence_penalty?: number; - /** - * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - */ - temperature?: number; - /** - * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - */ - top_p?: number; -}; diff --git a/sdks/ts/src/api/models/Tasks_PromptStep.ts b/sdks/ts/src/api/models/Tasks_PromptStep.ts index 58cb82462..39a7127ac 100644 --- a/sdks/ts/src/api/models/Tasks_PromptStep.ts +++ b/sdks/ts/src/api/models/Tasks_PromptStep.ts @@ -2,11 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Chat_CompletionResponseFormat } from "./Chat_CompletionResponseFormat"; -import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; -import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; -import type { Common_logit_bias } from "./Common_logit_bias"; -import type { Common_uuid } from "./Common_uuid"; +import type { Chat_ChatSettings } from "./Chat_ChatSettings"; import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; export type Tasks_PromptStep = Tasks_BaseWorkflowStep & { @@ -18,147 +14,5 @@ export type Tasks_PromptStep = Tasks_BaseWorkflowStep & { /** * Settings for the prompt */ - settings: - | { - /** - * Identifier of the model to be used - */ - model?: Common_identifierSafeUnicode; - /** - * Indicates if the server should stream the response as it's generated - */ - stream: boolean; - /** - * Up to 4 sequences where the API will stop generating further tokens. - */ - stop?: Array; - /** - * If specified, the system will make a best effort to sample deterministically for that particular seed value - */ - seed?: number; - /** - * The maximum number of tokens to generate in the chat completion - */ - max_tokens?: number; - /** - * Modify the likelihood of specified tokens appearing in the completion - */ - logit_bias?: Record; - /** - * Response format (set to `json_object` to restrict output to JSON) - */ - response_format?: Chat_CompletionResponseFormat; - /** - * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - */ - agent?: Common_uuid; - /** - * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - */ - preset?: Chat_GenerationPreset; - } - | { - /** - * Identifier of the model to be used - */ - model?: Common_identifierSafeUnicode; - /** - * Indicates if the server should stream the response as it's generated - */ - stream: boolean; - /** - * Up to 4 sequences where the API will stop generating further tokens. - */ - stop?: Array; - /** - * If specified, the system will make a best effort to sample deterministically for that particular seed value - */ - seed?: number; - /** - * The maximum number of tokens to generate in the chat completion - */ - max_tokens?: number; - /** - * Modify the likelihood of specified tokens appearing in the completion - */ - logit_bias?: Record; - /** - * Response format (set to `json_object` to restrict output to JSON) - */ - response_format?: Chat_CompletionResponseFormat; - /** - * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - */ - agent?: Common_uuid; - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - frequency_penalty?: number; - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - presence_penalty?: number; - /** - * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - */ - temperature?: number; - /** - * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - */ - top_p?: number; - } - | { - /** - * Identifier of the model to be used - */ - model?: Common_identifierSafeUnicode; - /** - * Indicates if the server should stream the response as it's generated - */ - stream: boolean; - /** - * Up to 4 sequences where the API will stop generating further tokens. - */ - stop?: Array; - /** - * If specified, the system will make a best effort to sample deterministically for that particular seed value - */ - seed?: number; - /** - * The maximum number of tokens to generate in the chat completion - */ - max_tokens?: number; - /** - * Modify the likelihood of specified tokens appearing in the completion - */ - logit_bias?: Record; - /** - * Response format (set to `json_object` to restrict output to JSON) - */ - response_format?: Chat_CompletionResponseFormat; - /** - * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - */ - agent?: Common_uuid; - /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - repetition_penalty?: number; - /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - */ - length_penalty?: number; - /** - * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - */ - temperature?: number; - /** - * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - */ - top_p?: number; - /** - * Minimum probability compared to leading token to be considered - */ - min_p?: number; - }; + settings: Chat_ChatSettings; }; diff --git a/sdks/ts/src/api/schemas/$Agents_Agent.ts b/sdks/ts/src/api/schemas/$Agents_Agent.ts index 72bb0e2b3..0aadbfb6d 100644 --- a/sdks/ts/src/api/schemas/$Agents_Agent.ts +++ b/sdks/ts/src/api/schemas/$Agents_Agent.ts @@ -71,17 +71,11 @@ export const $Agents_Agent = { isRequired: true, }, default_settings: { - type: "any-of", + type: "all-of", description: `Default settings for all sessions created by this agent`, contains: [ { - type: "Chat_GenerationPresetSettings", - }, - { - type: "Chat_OpenAISettings", - }, - { - type: "Chat_vLLMSettings", + type: "Chat_DefaultChatSettings", }, ], }, diff --git a/sdks/ts/src/api/schemas/$Agents_CreateAgentRequest.ts b/sdks/ts/src/api/schemas/$Agents_CreateAgentRequest.ts index 035b10261..3271b5264 100644 --- a/sdks/ts/src/api/schemas/$Agents_CreateAgentRequest.ts +++ b/sdks/ts/src/api/schemas/$Agents_CreateAgentRequest.ts @@ -48,17 +48,11 @@ export const $Agents_CreateAgentRequest = { isRequired: true, }, default_settings: { - type: "any-of", + type: "all-of", description: `Default settings for all sessions created by this agent`, contains: [ { - type: "Chat_GenerationPresetSettings", - }, - { - type: "Chat_OpenAISettings", - }, - { - type: "Chat_vLLMSettings", + type: "Chat_DefaultChatSettings", }, ], }, diff --git a/sdks/ts/src/api/schemas/$Agents_PatchAgentRequest.ts b/sdks/ts/src/api/schemas/$Agents_PatchAgentRequest.ts index 903376a1a..26a59a55f 100644 --- a/sdks/ts/src/api/schemas/$Agents_PatchAgentRequest.ts +++ b/sdks/ts/src/api/schemas/$Agents_PatchAgentRequest.ts @@ -44,17 +44,11 @@ export const $Agents_PatchAgentRequest = { ], }, default_settings: { - type: "any-of", + type: "all-of", description: `Default settings for all sessions created by this agent`, contains: [ { - type: "Chat_GenerationPresetSettings", - }, - { - type: "Chat_OpenAISettings", - }, - { - type: "Chat_vLLMSettings", + type: "Chat_DefaultChatSettings", }, ], }, diff --git a/sdks/ts/src/api/schemas/$Agents_UpdateAgentRequest.ts b/sdks/ts/src/api/schemas/$Agents_UpdateAgentRequest.ts index 6591bc329..c94cedc6e 100644 --- a/sdks/ts/src/api/schemas/$Agents_UpdateAgentRequest.ts +++ b/sdks/ts/src/api/schemas/$Agents_UpdateAgentRequest.ts @@ -48,17 +48,11 @@ export const $Agents_UpdateAgentRequest = { isRequired: true, }, default_settings: { - type: "any-of", + type: "all-of", description: `Default settings for all sessions created by this agent`, contains: [ { - type: "Chat_GenerationPresetSettings", - }, - { - type: "Chat_OpenAISettings", - }, - { - type: "Chat_vLLMSettings", + type: "Chat_DefaultChatSettings", }, ], }, diff --git a/sdks/ts/src/api/schemas/$Chat_ChatInput.ts b/sdks/ts/src/api/schemas/$Chat_ChatInput.ts new file mode 100644 index 000000000..e949a3657 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Chat_ChatInput.ts @@ -0,0 +1,160 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Chat_ChatInput = { + properties: { + messages: { + type: "array", + contains: { + type: "Entries_InputChatMLMessage", + }, + isRequired: true, + }, + tools: { + type: "array", + contains: { + type: "Tools_FunctionTool", + }, + }, + tool_choice: { + type: "any-of", + description: `Can be one of existing tools given to the agent earlier or the ones provided in this request.`, + contains: [ + { + type: "Enum", + }, + { + type: "Tools_NamedToolChoice", + }, + ], + }, + recall: { + type: "boolean", + description: `Whether previous memories should be recalled or not (will be enabled in a future release)`, + isReadOnly: true, + isRequired: true, + }, + remember: { + type: "boolean", + description: `Whether this interaction should form new memories or not (will be enabled in a future release)`, + isReadOnly: true, + isRequired: true, + }, + save: { + type: "boolean", + description: `Whether this interaction should be stored in the session history or not`, + isRequired: true, + }, + model: { + type: "all-of", + description: `Identifier of the model to be used`, + contains: [ + { + type: "Common_identifierSafeUnicode", + }, + ], + }, + stream: { + type: "boolean", + description: `Indicates if the server should stream the response as it's generated`, + isRequired: true, + }, + stop: { + type: "array", + contains: { + type: "string", + }, + }, + seed: { + type: "number", + description: `If specified, the system will make a best effort to sample deterministically for that particular seed value`, + format: "int16", + maximum: 1000, + minimum: -1, + }, + max_tokens: { + type: "number", + description: `The maximum number of tokens to generate in the chat completion`, + format: "uint32", + minimum: 1, + }, + logit_bias: { + type: "dictionary", + contains: { + type: "Common_logit_bias", + }, + }, + response_format: { + type: "all-of", + description: `Response format (set to \`json_object\` to restrict output to JSON)`, + contains: [ + { + type: "Chat_CompletionResponseFormat", + }, + ], + }, + agent: { + type: "all-of", + description: `Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)`, + contains: [ + { + type: "Common_uuid", + }, + ], + }, + preset: { + type: "all-of", + description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, + contains: [ + { + type: "Chat_GenerationPreset", + }, + ], + }, + frequency_penalty: { + type: "number", + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + minimum: -2, + }, + presence_penalty: { + type: "number", + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + minimum: -2, + }, + temperature: { + type: "number", + description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, + format: "float", + maximum: 5, + }, + top_p: { + type: "number", + description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, + format: "float", + maximum: 1, + }, + repetition_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + }, + length_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, + format: "float", + maximum: 2, + }, + min_p: { + type: "number", + description: `Minimum probability compared to leading token to be considered`, + format: "float", + maximum: 1, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Chat_ChatSettings.ts b/sdks/ts/src/api/schemas/$Chat_ChatSettings.ts new file mode 100644 index 000000000..c370979b9 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Chat_ChatSettings.ts @@ -0,0 +1,118 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Chat_ChatSettings = { + properties: { + model: { + type: "all-of", + description: `Identifier of the model to be used`, + contains: [ + { + type: "Common_identifierSafeUnicode", + }, + ], + }, + stream: { + type: "boolean", + description: `Indicates if the server should stream the response as it's generated`, + isRequired: true, + }, + stop: { + type: "array", + contains: { + type: "string", + }, + }, + seed: { + type: "number", + description: `If specified, the system will make a best effort to sample deterministically for that particular seed value`, + format: "int16", + maximum: 1000, + minimum: -1, + }, + max_tokens: { + type: "number", + description: `The maximum number of tokens to generate in the chat completion`, + format: "uint32", + minimum: 1, + }, + logit_bias: { + type: "dictionary", + contains: { + type: "Common_logit_bias", + }, + }, + response_format: { + type: "all-of", + description: `Response format (set to \`json_object\` to restrict output to JSON)`, + contains: [ + { + type: "Chat_CompletionResponseFormat", + }, + ], + }, + agent: { + type: "all-of", + description: `Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)`, + contains: [ + { + type: "Common_uuid", + }, + ], + }, + preset: { + type: "all-of", + description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, + contains: [ + { + type: "Chat_GenerationPreset", + }, + ], + }, + frequency_penalty: { + type: "number", + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + minimum: -2, + }, + presence_penalty: { + type: "number", + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + minimum: -2, + }, + temperature: { + type: "number", + description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, + format: "float", + maximum: 5, + }, + top_p: { + type: "number", + description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, + format: "float", + maximum: 1, + }, + repetition_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + }, + length_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, + format: "float", + maximum: 2, + }, + min_p: { + type: "number", + description: `Minimum probability compared to leading token to be considered`, + format: "float", + maximum: 1, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Chat_vLLMSettings.ts b/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts similarity index 60% rename from sdks/ts/src/api/schemas/$Chat_vLLMSettings.ts rename to sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts index 5006b6775..efef64d63 100644 --- a/sdks/ts/src/api/schemas/$Chat_vLLMSettings.ts +++ b/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts @@ -2,19 +2,31 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export const $Chat_vLLMSettings = { +export const $Chat_DefaultChatSettings = { + description: `Default settings for the chat session (also used by the agent)`, properties: { - repetition_penalty: { + preset: { + type: "all-of", + description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, + contains: [ + { + type: "Chat_GenerationPreset", + }, + ], + }, + frequency_penalty: { type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, format: "float", maximum: 2, + minimum: -2, }, - length_penalty: { + presence_penalty: { type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, format: "float", maximum: 2, + minimum: -2, }, temperature: { type: "number", @@ -28,6 +40,18 @@ export const $Chat_vLLMSettings = { format: "float", maximum: 1, }, + repetition_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + }, + length_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, + format: "float", + maximum: 2, + }, min_p: { type: "number", description: `Minimum probability compared to leading token to be considered`, diff --git a/sdks/ts/src/api/schemas/$Chat_GenerationPresetSettings.ts b/sdks/ts/src/api/schemas/$Chat_GenerationPresetSettings.ts deleted file mode 100644 index fd8b68db1..000000000 --- a/sdks/ts/src/api/schemas/$Chat_GenerationPresetSettings.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Chat_GenerationPresetSettings = { - properties: { - preset: { - type: "all-of", - description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, - contains: [ - { - type: "Chat_GenerationPreset", - }, - ], - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Chat_MessageChatResponse.ts b/sdks/ts/src/api/schemas/$Chat_MessageChatResponse.ts index e8a2a0161..8b80d8698 100644 --- a/sdks/ts/src/api/schemas/$Chat_MessageChatResponse.ts +++ b/sdks/ts/src/api/schemas/$Chat_MessageChatResponse.ts @@ -13,7 +13,15 @@ export const $Chat_MessageChatResponse = { choices: { type: "array", contains: { - type: "Chat_ChatOutputChunk", + type: "any-of", + contains: [ + { + type: "Chat_SingleChatOutput", + }, + { + type: "Chat_MultipleChatOutput", + }, + ], }, isRequired: true, }, diff --git a/sdks/ts/src/api/schemas/$Chat_OpenAISettings.ts b/sdks/ts/src/api/schemas/$Chat_OpenAISettings.ts deleted file mode 100644 index 61a12ed7e..000000000 --- a/sdks/ts/src/api/schemas/$Chat_OpenAISettings.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Chat_OpenAISettings = { - properties: { - frequency_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - presence_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - temperature: { - type: "number", - description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, - format: "float", - maximum: 5, - }, - top_p: { - type: "number", - description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, - format: "float", - maximum: 1, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts b/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts index 2abb023b2..3eb78c319 100644 --- a/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts @@ -31,256 +31,11 @@ export const $Tasks_PromptStep = { isRequired: true, }, settings: { - type: "any-of", + type: "all-of", description: `Settings for the prompt`, contains: [ { - properties: { - model: { - type: "all-of", - description: `Identifier of the model to be used`, - contains: [ - { - type: "Common_identifierSafeUnicode", - }, - ], - }, - stream: { - type: "boolean", - description: `Indicates if the server should stream the response as it's generated`, - isRequired: true, - }, - stop: { - type: "array", - contains: { - type: "string", - }, - }, - seed: { - type: "number", - description: `If specified, the system will make a best effort to sample deterministically for that particular seed value`, - format: "int16", - maximum: 1000, - minimum: -1, - }, - max_tokens: { - type: "number", - description: `The maximum number of tokens to generate in the chat completion`, - format: "uint32", - minimum: 1, - }, - logit_bias: { - type: "dictionary", - contains: { - type: "Common_logit_bias", - }, - }, - response_format: { - type: "all-of", - description: `Response format (set to \`json_object\` to restrict output to JSON)`, - contains: [ - { - type: "Chat_CompletionResponseFormat", - }, - ], - }, - agent: { - type: "all-of", - description: `Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)`, - contains: [ - { - type: "Common_uuid", - }, - ], - }, - preset: { - type: "all-of", - description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, - contains: [ - { - type: "Chat_GenerationPreset", - }, - ], - }, - }, - }, - { - properties: { - model: { - type: "all-of", - description: `Identifier of the model to be used`, - contains: [ - { - type: "Common_identifierSafeUnicode", - }, - ], - }, - stream: { - type: "boolean", - description: `Indicates if the server should stream the response as it's generated`, - isRequired: true, - }, - stop: { - type: "array", - contains: { - type: "string", - }, - }, - seed: { - type: "number", - description: `If specified, the system will make a best effort to sample deterministically for that particular seed value`, - format: "int16", - maximum: 1000, - minimum: -1, - }, - max_tokens: { - type: "number", - description: `The maximum number of tokens to generate in the chat completion`, - format: "uint32", - minimum: 1, - }, - logit_bias: { - type: "dictionary", - contains: { - type: "Common_logit_bias", - }, - }, - response_format: { - type: "all-of", - description: `Response format (set to \`json_object\` to restrict output to JSON)`, - contains: [ - { - type: "Chat_CompletionResponseFormat", - }, - ], - }, - agent: { - type: "all-of", - description: `Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)`, - contains: [ - { - type: "Common_uuid", - }, - ], - }, - frequency_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - presence_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - temperature: { - type: "number", - description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, - format: "float", - maximum: 5, - }, - top_p: { - type: "number", - description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, - format: "float", - maximum: 1, - }, - }, - }, - { - properties: { - model: { - type: "all-of", - description: `Identifier of the model to be used`, - contains: [ - { - type: "Common_identifierSafeUnicode", - }, - ], - }, - stream: { - type: "boolean", - description: `Indicates if the server should stream the response as it's generated`, - isRequired: true, - }, - stop: { - type: "array", - contains: { - type: "string", - }, - }, - seed: { - type: "number", - description: `If specified, the system will make a best effort to sample deterministically for that particular seed value`, - format: "int16", - maximum: 1000, - minimum: -1, - }, - max_tokens: { - type: "number", - description: `The maximum number of tokens to generate in the chat completion`, - format: "uint32", - minimum: 1, - }, - logit_bias: { - type: "dictionary", - contains: { - type: "Common_logit_bias", - }, - }, - response_format: { - type: "all-of", - description: `Response format (set to \`json_object\` to restrict output to JSON)`, - contains: [ - { - type: "Chat_CompletionResponseFormat", - }, - ], - }, - agent: { - type: "all-of", - description: `Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)`, - contains: [ - { - type: "Common_uuid", - }, - ], - }, - repetition_penalty: { - type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - }, - length_penalty: { - type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, - format: "float", - maximum: 2, - }, - temperature: { - type: "number", - description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, - format: "float", - maximum: 5, - }, - top_p: { - type: "number", - description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, - format: "float", - maximum: 1, - }, - min_p: { - type: "number", - description: `Minimum probability compared to leading token to be considered`, - format: "float", - maximum: 1, - }, - }, + type: "Chat_ChatSettings", }, ], isRequired: true, diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index b671d3fb5..b49a835ee 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -6,13 +6,10 @@ import type { Agents_Agent } from "../models/Agents_Agent"; import type { Agents_CreateAgentRequest } from "../models/Agents_CreateAgentRequest"; import type { Agents_PatchAgentRequest } from "../models/Agents_PatchAgentRequest"; import type { Agents_UpdateAgentRequest } from "../models/Agents_UpdateAgentRequest"; +import type { Chat_ChatInput } from "../models/Chat_ChatInput"; import type { Chat_ChunkChatResponse } from "../models/Chat_ChunkChatResponse"; -import type { Chat_CompletionResponseFormat } from "../models/Chat_CompletionResponseFormat"; -import type { Chat_GenerationPreset } from "../models/Chat_GenerationPreset"; import type { Chat_MessageChatResponse } from "../models/Chat_MessageChatResponse"; -import type { Common_identifierSafeUnicode } from "../models/Common_identifierSafeUnicode"; import type { Common_limit } from "../models/Common_limit"; -import type { Common_logit_bias } from "../models/Common_logit_bias"; import type { Common_offset } from "../models/Common_offset"; import type { Common_ResourceCreatedResponse } from "../models/Common_ResourceCreatedResponse"; import type { Common_ResourceDeletedResponse } from "../models/Common_ResourceDeletedResponse"; @@ -27,7 +24,6 @@ import type { Docs_HybridDocSearchRequest } from "../models/Docs_HybridDocSearch import type { Docs_TextOnlyDocSearchRequest } from "../models/Docs_TextOnlyDocSearchRequest"; import type { Docs_VectorDocSearchRequest } from "../models/Docs_VectorDocSearchRequest"; import type { Entries_History } from "../models/Entries_History"; -import type { Entries_InputChatMLMessage } from "../models/Entries_InputChatMLMessage"; import type { Executions_CreateExecutionRequest } from "../models/Executions_CreateExecutionRequest"; import type { Executions_Execution } from "../models/Executions_Execution"; import type { Executions_TaskTokenResumeExecutionRequest } from "../models/Executions_TaskTokenResumeExecutionRequest"; @@ -42,8 +38,6 @@ import type { Tasks_CreateTaskRequest } from "../models/Tasks_CreateTaskRequest" import type { Tasks_PatchTaskRequest } from "../models/Tasks_PatchTaskRequest"; import type { Tasks_Task } from "../models/Tasks_Task"; import type { Tasks_UpdateTaskRequest } from "../models/Tasks_UpdateTaskRequest"; -import type { Tools_FunctionTool } from "../models/Tools_FunctionTool"; -import type { Tools_NamedToolChoice } from "../models/Tools_NamedToolChoice"; import type { Tools_PatchToolRequest } from "../models/Tools_PatchToolRequest"; import type { Tools_Tool } from "../models/Tools_Tool"; import type { Tools_UpdateToolRequest } from "../models/Tools_UpdateToolRequest"; @@ -1122,221 +1116,7 @@ export class DefaultService { /** * Request to generate a response from the model */ - requestBody: - | { - /** - * A list of new input messages comprising the conversation so far. - */ - messages: Array; - /** - * (Advanced) List of tools that are provided in addition to agent's default set of tools. - */ - tools?: Array; - /** - * Can be one of existing tools given to the agent earlier or the ones provided in this request. - */ - tool_choice?: "auto" | "none" | Tools_NamedToolChoice; - /** - * Whether previous memories should be recalled or not (will be enabled in a future release) - */ - readonly recall: boolean; - /** - * Whether this interaction should form new memories or not (will be enabled in a future release) - */ - readonly remember: boolean; - /** - * Whether this interaction should be stored in the session history or not - */ - save: boolean; - /** - * Identifier of the model to be used - */ - model?: Common_identifierSafeUnicode; - /** - * Indicates if the server should stream the response as it's generated - */ - stream: boolean; - /** - * Up to 4 sequences where the API will stop generating further tokens. - */ - stop?: Array; - /** - * If specified, the system will make a best effort to sample deterministically for that particular seed value - */ - seed?: number; - /** - * The maximum number of tokens to generate in the chat completion - */ - max_tokens?: number; - /** - * Modify the likelihood of specified tokens appearing in the completion - */ - logit_bias?: Record; - /** - * Response format (set to `json_object` to restrict output to JSON) - */ - response_format?: Chat_CompletionResponseFormat; - /** - * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - */ - agent?: Common_uuid; - /** - * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - */ - preset?: Chat_GenerationPreset; - } - | { - /** - * A list of new input messages comprising the conversation so far. - */ - messages: Array; - /** - * (Advanced) List of tools that are provided in addition to agent's default set of tools. - */ - tools?: Array; - /** - * Can be one of existing tools given to the agent earlier or the ones provided in this request. - */ - tool_choice?: "auto" | "none" | Tools_NamedToolChoice; - /** - * Whether previous memories should be recalled or not (will be enabled in a future release) - */ - readonly recall: boolean; - /** - * Whether this interaction should form new memories or not (will be enabled in a future release) - */ - readonly remember: boolean; - /** - * Whether this interaction should be stored in the session history or not - */ - save: boolean; - /** - * Identifier of the model to be used - */ - model?: Common_identifierSafeUnicode; - /** - * Indicates if the server should stream the response as it's generated - */ - stream: boolean; - /** - * Up to 4 sequences where the API will stop generating further tokens. - */ - stop?: Array; - /** - * If specified, the system will make a best effort to sample deterministically for that particular seed value - */ - seed?: number; - /** - * The maximum number of tokens to generate in the chat completion - */ - max_tokens?: number; - /** - * Modify the likelihood of specified tokens appearing in the completion - */ - logit_bias?: Record; - /** - * Response format (set to `json_object` to restrict output to JSON) - */ - response_format?: Chat_CompletionResponseFormat; - /** - * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - */ - agent?: Common_uuid; - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - frequency_penalty?: number; - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - presence_penalty?: number; - /** - * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - */ - temperature?: number; - /** - * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - */ - top_p?: number; - } - | { - /** - * A list of new input messages comprising the conversation so far. - */ - messages: Array; - /** - * (Advanced) List of tools that are provided in addition to agent's default set of tools. - */ - tools?: Array; - /** - * Can be one of existing tools given to the agent earlier or the ones provided in this request. - */ - tool_choice?: "auto" | "none" | Tools_NamedToolChoice; - /** - * Whether previous memories should be recalled or not (will be enabled in a future release) - */ - readonly recall: boolean; - /** - * Whether this interaction should form new memories or not (will be enabled in a future release) - */ - readonly remember: boolean; - /** - * Whether this interaction should be stored in the session history or not - */ - save: boolean; - /** - * Identifier of the model to be used - */ - model?: Common_identifierSafeUnicode; - /** - * Indicates if the server should stream the response as it's generated - */ - stream: boolean; - /** - * Up to 4 sequences where the API will stop generating further tokens. - */ - stop?: Array; - /** - * If specified, the system will make a best effort to sample deterministically for that particular seed value - */ - seed?: number; - /** - * The maximum number of tokens to generate in the chat completion - */ - max_tokens?: number; - /** - * Modify the likelihood of specified tokens appearing in the completion - */ - logit_bias?: Record; - /** - * Response format (set to `json_object` to restrict output to JSON) - */ - response_format?: Chat_CompletionResponseFormat; - /** - * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - */ - agent?: Common_uuid; - /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - repetition_penalty?: number; - /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - */ - length_penalty?: number; - /** - * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - */ - temperature?: number; - /** - * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - */ - top_p?: number; - /** - * Minimum probability compared to leading token to be considered - */ - min_p?: number; - }; + requestBody: Chat_ChatInput; }): CancelablePromise { return this.httpRequest.request({ method: "POST", diff --git a/typespec/chat/models.tsp b/typespec/chat/models.tsp index a90ad47a5..320f3e489 100644 --- a/typespec/chat/models.tsp +++ b/typespec/chat/models.tsp @@ -133,16 +133,6 @@ model vLLMSettings { @maxValue(2) length_penalty?: float32; - /** What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. */ - @minValue(0) - @maxValue(5) - temperature?: float32; - - /** Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. */ - @minValue(0) - @maxValue(1) - top_p?: float32; - /** Minimum probability compared to leading token to be considered */ @minValue(0) @maxValue(1) @@ -150,15 +140,16 @@ model vLLMSettings { } /** Default settings for the chat session (also used by the agent) */ -alias DefaultChatSettings = GenerationPresetSettings | OpenAISettings | vLLMSettings; +model DefaultChatSettings { + ...GenerationPresetSettings; + ...OpenAISettings; + ...vLLMSettings; +} -alias ChatSettings = ( - CommonChatSettings & GenerationPresetSettings -) | ( - CommonChatSettings & OpenAISettings -) | ( - CommonChatSettings & vLLMSettings -); +model ChatSettings { + ...CommonChatSettings; + ...DefaultChatSettings; +} /** Usage statistics for the completion request */ model CompetionUsage { @@ -188,13 +179,11 @@ model ChatInputData { tool_choice?: ToolChoiceOption; } -alias ChatInput = ( - ChatInputData & MemoryAccessOptions & CommonChatSettings & GenerationPresetSettings -) | ( - ChatInputData & MemoryAccessOptions & CommonChatSettings & OpenAISettings -) | ( - ChatInputData & MemoryAccessOptions & CommonChatSettings & vLLMSettings -); +model ChatInput { + ...ChatInputData; + ...MemoryAccessOptions; + ...ChatSettings; +} model BaseTokenLogProb { token: string; @@ -267,7 +256,7 @@ model MessageChatResponse extends BaseChatResponse { @header contentType: json; /** The deltas generated by the model */ - choices: ChatOutputChunk[]; + choices: ChatOutput[]; } -alias ChatResponse = ChunkChatResponse | MessageChatResponse; \ No newline at end of file +alias ChatResponse = ChunkChatResponse | MessageChatResponse; From e4c7827a99d5c33ca049489b6ec582601e7061ce Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 10 Aug 2024 19:30:59 -0400 Subject: [PATCH 028/110] feat(agents-api): Make session.render_templates true by default Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Chat.py | 2 +- agents-api/agents_api/autogen/Entries.py | 2 +- agents-api/agents_api/autogen/Sessions.py | 8 ++++---- agents-api/agents_api/routers/sessions/chat.py | 3 +++ typespec/sessions/models.tsp | 2 +- 5 files changed, 10 insertions(+), 7 deletions(-) diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index f46c6289a..7b364f2b7 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel from .Common import LogitBias from .Docs import DocReference diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index 6c8bf9ca4..d56b333d8 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, RootModel from .Tools import ChosenToolCall, Tool, ToolResponse diff --git a/agents-api/agents_api/autogen/Sessions.py b/agents-api/agents_api/autogen/Sessions.py index 89f5f590d..28732aa7d 100644 --- a/agents-api/agents_api/autogen/Sessions.py +++ b/agents-api/agents_api/autogen/Sessions.py @@ -31,7 +31,7 @@ class CreateSessionRequest(BaseModel): """ A specific situation that sets the background for this session """ - render_templates: bool = False + render_templates: bool = True """ Render system and assistant message content as jinja templates """ @@ -58,7 +58,7 @@ class PatchSessionRequest(BaseModel): """ A specific situation that sets the background for this session """ - render_templates: bool = False + render_templates: bool = True """ Render system and assistant message content as jinja templates """ @@ -85,7 +85,7 @@ class Session(BaseModel): """ Summary (null at the beginning) - generated automatically after every interaction """ - render_templates: bool = False + render_templates: bool = True """ Render system and assistant message content as jinja templates """ @@ -148,7 +148,7 @@ class UpdateSessionRequest(BaseModel): """ A specific situation that sets the background for this session """ - render_templates: bool = False + render_templates: bool = True """ Render system and assistant message content as jinja templates """ diff --git a/agents-api/agents_api/routers/sessions/chat.py b/agents-api/agents_api/routers/sessions/chat.py index e03fceedc..3e355fbec 100644 --- a/agents-api/agents_api/routers/sessions/chat.py +++ b/agents-api/agents_api/routers/sessions/chat.py @@ -24,10 +24,13 @@ async def chat( session_id: UUID, data: ChatInput, ) -> ChatResponse: + # First get the chat context chat_context = prepare_chat_context( developer_id=x_developer_id, agent_id=data.agent_id, session_id=session_id, ) + # Then, use the chat context to chat + print(chat_context) diff --git a/typespec/sessions/models.tsp b/typespec/sessions/models.tsp index ed58f55c6..53ff574f6 100644 --- a/typespec/sessions/models.tsp +++ b/typespec/sessions/models.tsp @@ -54,7 +54,7 @@ model Session { summary: string | null = null; /** Render system and assistant message content as jinja templates */ - render_templates: boolean = false; + render_templates: boolean = true; /** Threshold value for the adaptive context functionality */ token_budget: uint16 | null = null; From 8428659d8f1874855787c9023441d32a13306427 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 10 Aug 2024 20:52:36 -0400 Subject: [PATCH 029/110] wip(agents-api): Implementing chat Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Chat.py | 2 +- agents-api/agents_api/autogen/Entries.py | 4 +- .../agents_api/common/protocol/sessions.py | 56 ++++++++++++++++++- .../agents_api/common/utils/template.py | 3 - .../models/session/prepare_chat_context.py | 30 +--------- .../agents_api/routers/sessions/chat.py | 38 ++++++++++++- agents-api/poetry.lock | 10 ++-- agents-api/tests/test_entry_queries.py | 40 +++++++++++++ agents-api/tests/test_session_queries.py | 15 ++--- .../python/julep/api/types/entries_history.py | 4 +- sdks/ts/src/api/models/Entries_History.ts | 4 +- sdks/ts/src/api/schemas/$Entries_History.ts | 2 +- typespec/entries/models.tsp | 2 +- 13 files changed, 152 insertions(+), 58 deletions(-) diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 7b364f2b7..f46c6289a 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field from .Common import LogitBias from .Docs import DocReference diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index d56b333d8..cfe3692f2 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, RootModel +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field from .Tools import ChosenToolCall, Tool, ToolResponse @@ -130,7 +130,7 @@ class History(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - entries: list[BaseEntry] + entries: list[Entry] relations: list[Relation] session_id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] diff --git a/agents-api/agents_api/common/protocol/sessions.py b/agents-api/agents_api/common/protocol/sessions.py index 66d837e50..c2376f8dd 100644 --- a/agents-api/agents_api/common/protocol/sessions.py +++ b/agents-api/agents_api/common/protocol/sessions.py @@ -9,7 +9,6 @@ from ...autogen.openapi_model import ( Agent, - BaseEntry, ChatSettings, Session, Tool, @@ -48,5 +47,58 @@ class ChatContext(SessionData): Represents the data associated with a context, including for agents, and users. """ - entries: list[BaseEntry] toolsets: list[Toolset] + + def get_active_agent(self) -> Agent: + """ + Get the active agent from the session data. + """ + requested_agent: UUID | None = self.settings.agent + + if requested_agent: + assert requested_agent in [agent.id for agent in self.agents], ( + f"Agent {requested_agent} not found in session agents: " + f"{[agent.id for agent in self.agents]}" + ) + + return next(agent for agent in self.agents if agent.id == requested_agent) + + return self.agents[0] + + def merge_settings(self, request_settings: ChatSettings): + active_agent = self.get_active_agent() + default_settings = active_agent.default_settings + + self.settings = ChatSettings( + **{ + **default_settings.model_dump(), + **request_settings.model_dump(exclude_unset=True), + } + ) + + def get_active_tools(self) -> list[Tool]: + """ + Get the active toolset from the session data. + """ + active_agent = self.get_active_agent() + active_toolset = next( + toolset for toolset in self.toolsets if toolset.agent_id == active_agent.id + ) + + return active_toolset.tools + + def get_chat_environment(self) -> dict[str, dict | list[dict]]: + """ + Get the chat environment from the session data. + """ + current_agent = self.get_active_agent() + tools = self.get_active_tools() + + return { + "session": self.session.model_dump(), + "agents": [agent.model_dump() for agent in self.agents], + "current_agent": current_agent.model_dump(), + "users": [user.model_dump() for user in self.users], + "settings": self.settings.model_dump(), + "tools": [tool.model_dump() for tool in tools], + } diff --git a/agents-api/agents_api/common/utils/template.py b/agents-api/agents_api/common/utils/template.py index 0a1a29c73..eabf6f307 100644 --- a/agents-api/agents_api/common/utils/template.py +++ b/agents-api/agents_api/common/utils/template.py @@ -88,6 +88,3 @@ async def render_template( elif isinstance(template_string, list): return await render_template_parts(template_string, variables, check) - - else: - raise ValueError("template_string should be str or list[dict]") diff --git a/agents-api/agents_api/models/session/prepare_chat_context.py b/agents-api/agents_api/models/session/prepare_chat_context.py index c59c09d0d..83e6c6f8b 100644 --- a/agents-api/agents_api/models/session/prepare_chat_context.py +++ b/agents-api/agents_api/models/session/prepare_chat_context.py @@ -7,11 +7,9 @@ from ...autogen.openapi_model import make_session from ...common.protocol.sessions import ChatContext -from ..entry.list_entries import list_entries from ..utils import ( cozo_query, fix_uuid_if_present, - make_cozo_json_query, partialclass, rewrap_exceptions, verify_developer_id_query, @@ -44,7 +42,7 @@ ], }, ) -@cozo_query(debug=True) +@cozo_query @beartype def prepare_chat_context( *, @@ -105,30 +103,10 @@ def prepare_chat_context( } """ - [*_, entries_query], e_vars = list_entries.__wrapped__( - developer_id=developer_id, - session_id=session_id, - allowed_sources=["api_request", "api_response", "summarizer"], - exclude_relations=["summary_of"], - ) - - entries_fields = ("source", "role", "name", "content", "token_count", "timestamp") - - entries_query += f""" - :create _entries {{ - {', '.join(entries_fields)} - }} - """ - combine_query = f""" - entries_json[collect(entry)] := - *_entries {{ {', '.join(entries_fields)} }}, - entry = {{ {make_cozo_json_query(entries_fields)} }} - - ?[{', '.join(session_data_fields)}, toolsets, entries] := + ?[{', '.join(session_data_fields)}, toolsets] := *_session_data_json {{ {', '.join(session_data_fields)} }}, - *_toolsets_json {{ toolsets }}, - entries_json[entries] + *_toolsets_json {{ toolsets }} """ queries = [ @@ -137,7 +115,6 @@ def prepare_chat_context( developer_id, "sessions", session_id=session_id ), session_data_query, - entries_query, toolsets_query, combine_query, ] @@ -147,6 +124,5 @@ def prepare_chat_context( { "session_id": str(session_id), **sd_vars, - **e_vars, }, ) diff --git a/agents-api/agents_api/routers/sessions/chat.py b/agents-api/agents_api/routers/sessions/chat.py index 3e355fbec..368fe2ea2 100644 --- a/agents-api/agents_api/routers/sessions/chat.py +++ b/agents-api/agents_api/routers/sessions/chat.py @@ -8,8 +8,11 @@ from ...autogen.openapi_model import ( ChatInput, ChatResponse, + History, ) +from ...common.utils.template import render_template from ...dependencies.developer_id import get_developer_id +from ...models.entry.get_history import get_history from ...models.session.prepare_chat_context import prepare_chat_context from .router import router @@ -31,6 +34,37 @@ async def chat( session_id=session_id, ) - # Then, use the chat context to chat + # Merge the settings and prepare environment + request_settings = data.settings + chat_context.merge_settings(request_settings) - print(chat_context) + env: dict = chat_context.get_chat_environment() + + # Get the session history + history: History = get_history( + developer_id=x_developer_id, + session_id=session_id, + allowed_sources=["api_request", "api_response", "tool_response", "summarizer"], + ) + + # Keep leaf nodes only + relations = history.relations + past_entries = [ + entry.model_dump() + for entry in history.entries + if entry.id not in {r.head for r in relations} + ] + + past_messages = render_template(past_entries, variables=env) + + messages = past_messages + [msg.model_dump() for msg in data.messages] + + # TODO: Implement the chat logic here + print(messages) + + # Get the response from the model + + # Save the input and the response to the session history + + # Return the response + raise NotImplementedError() diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index c0cd64725..d670766a4 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2206,18 +2206,18 @@ typing-extensions = ">=4.7" [[package]] name = "langchain-openai" -version = "0.1.20" +version = "0.1.21" description = "An integration package connecting OpenAI and LangChain" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_openai-0.1.20-py3-none-any.whl", hash = "sha256:232ebfe90b1898ef7cf181e364d45191edcf04bfc31b292ecaa1d2121942c28e"}, - {file = "langchain_openai-0.1.20.tar.gz", hash = "sha256:2c91e9f771541076b138e65dd4c5427b26957a2272406a7f4ee747d7896f9b35"}, + {file = "langchain_openai-0.1.21-py3-none-any.whl", hash = "sha256:44420f0c84859ae236a80c8ac8754a16d5b660c24377c27ba98308145d346352"}, + {file = "langchain_openai-0.1.21.tar.gz", hash = "sha256:2c65feaf12bb284eccf7bce35725fd06f3035fa751babad6aa84af2f99867f88"}, ] [package.dependencies] -langchain-core = ">=0.2.26,<0.3.0" -openai = ">=1.32.0,<2.0.0" +langchain-core = ">=0.2.29,<0.3.0" +openai = ">=1.40.0,<2.0.0" tiktoken = ">=0.7,<1" [[package]] diff --git a/agents-api/tests/test_entry_queries.py b/agents-api/tests/test_entry_queries.py index 1c6fdf38e..be8e6362e 100644 --- a/agents-api/tests/test_entry_queries.py +++ b/agents-api/tests/test_entry_queries.py @@ -10,6 +10,7 @@ from agents_api.autogen.openapi_model import CreateEntryRequest from agents_api.models.entry.create_entries import create_entries from agents_api.models.entry.delete_entries import delete_entries +from agents_api.models.entry.get_history import get_history from agents_api.models.entry.list_entries import list_entries from tests.fixtures import cozo_client, test_developer_id, test_session @@ -76,6 +77,45 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): assert len(result) == 1 +@test("model: get history") +def _(client=cozo_client, developer_id=test_developer_id, session=test_session): + """ + Tests the retrieval of entries from the database. + Verifies that entries matching specific criteria can be successfully retrieved. + """ + + test_entry = CreateEntryRequest( + session_id=session.id, + role="user", + source="api_request", + content="test entry content", + ) + + internal_entry = CreateEntryRequest( + session_id=session.id, + role="user", + content="test entry content", + source="internal", + ) + + create_entries( + developer_id=developer_id, + session_id=session.id, + data=[test_entry, internal_entry], + client=client, + ) + + result = get_history( + developer_id=developer_id, + session_id=session.id, + client=client, + ) + + # Asserts that only one entry is retrieved, matching the session_id. + assert len(result.entries) > 0 + assert result.entries[0].id + + @test("model: delete entries") def _(client=cozo_client, developer_id=test_developer_id, session=test_session): """ diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index fe4fc0cb6..763e62bed 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -157,16 +157,11 @@ def _( tool=test_tool, user=test_user, ): - try: - context = prepare_chat_context( - developer_id=developer_id, - session_id=session.id, - client=client, - ) - except Exception as e: - print(repr(e.__cause__)) - raise + context = prepare_chat_context( + developer_id=developer_id, + session_id=session.id, + client=client, + ) assert isinstance(context, ChatContext) - assert len(context.entries) > 0 assert len(context.toolsets) > 0 diff --git a/sdks/python/julep/api/types/entries_history.py b/sdks/python/julep/api/types/entries_history.py index 78c56e424..d3d124ffe 100644 --- a/sdks/python/julep/api/types/entries_history.py +++ b/sdks/python/julep/api/types/entries_history.py @@ -6,12 +6,12 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_uuid import CommonUuid -from .entries_base_entry import EntriesBaseEntry +from .entries_entry import EntriesEntry from .entries_relation import EntriesRelation class EntriesHistory(pydantic_v1.BaseModel): - entries: typing.List[EntriesBaseEntry] + entries: typing.List[EntriesEntry] relations: typing.List[EntriesRelation] session_id: CommonUuid created_at: dt.datetime = pydantic_v1.Field() diff --git a/sdks/ts/src/api/models/Entries_History.ts b/sdks/ts/src/api/models/Entries_History.ts index 6550afff3..784972435 100644 --- a/sdks/ts/src/api/models/Entries_History.ts +++ b/sdks/ts/src/api/models/Entries_History.ts @@ -3,10 +3,10 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_uuid } from "./Common_uuid"; -import type { Entries_BaseEntry } from "./Entries_BaseEntry"; +import type { Entries_Entry } from "./Entries_Entry"; import type { Entries_Relation } from "./Entries_Relation"; export type Entries_History = { - entries: Array; + entries: Array; relations: Array; readonly session_id: Common_uuid; /** diff --git a/sdks/ts/src/api/schemas/$Entries_History.ts b/sdks/ts/src/api/schemas/$Entries_History.ts index 208d12b46..c75c70678 100644 --- a/sdks/ts/src/api/schemas/$Entries_History.ts +++ b/sdks/ts/src/api/schemas/$Entries_History.ts @@ -7,7 +7,7 @@ export const $Entries_History = { entries: { type: "array", contains: { - type: "Entries_BaseEntry", + type: "Entries_Entry", }, isRequired: true, }, diff --git a/typespec/entries/models.tsp b/typespec/entries/models.tsp index 1a614d449..0d8e604d9 100644 --- a/typespec/entries/models.tsp +++ b/typespec/entries/models.tsp @@ -112,7 +112,7 @@ model Relation { } model History { - entries: BaseEntry[]; + entries: Entry[]; relations: Relation[]; session_id: Session.id; From a107221329abc63934cf646eca74043919f6f30b Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 11 Aug 2024 14:24:02 -0400 Subject: [PATCH 030/110] feat(agents-api,typespec): Re-org some of the endpoint models for proper class inheritance Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Agents.py | 34 ++ agents-api/agents_api/autogen/Chat.py | 303 ++++++++---------- agents-api/agents_api/autogen/Sessions.py | 34 ++ agents-api/agents_api/autogen/Tasks.py | 16 +- agents-api/agents_api/autogen/Tools.py | 22 ++ agents-api/agents_api/autogen/Users.py | 7 + agents-api/poetry.lock | 6 +- sdks/python/julep/api/__init__.py | 20 +- sdks/python/julep/api/client.py | 110 +++---- sdks/python/julep/api/reference.md | 44 +-- sdks/python/julep/api/types/__init__.py | 22 +- .../agents_create_or_update_agent_request.py | 75 +++++ .../julep/api/types/chat_chat_input_data.py | 62 ++++ ...py => chat_chat_input_data_tool_choice.py} | 2 +- .../julep/api/types/chat_chat_settings.py | 46 +-- .../api/types/chat_default_chat_settings.py | 25 +- .../julep/api/types/chat_open_ai_settings.py | 60 ++++ ...ssions_create_or_update_session_request.py | 78 +++++ .../python/julep/api/types/tasks_task_tool.py | 23 +- .../api/types/tools_create_tool_request.py | 62 ++++ .../users_create_or_update_user_request.py | 46 +++ ...equest.py => users_create_user_request.py} | 4 +- sdks/python/poetry.lock | 8 +- sdks/ts/src/api/index.ts | 16 +- .../Agents_CreateOrUpdateAgentRequest.ts | 32 ++ sdks/ts/src/api/models/Chat_ChatInput.ts | 42 +-- sdks/ts/src/api/models/Chat_ChatInputData.ts | 21 ++ sdks/ts/src/api/models/Chat_ChatSettings.ts | 36 +-- .../api/models/Chat_DefaultChatSettings.ts | 19 +- sdks/ts/src/api/models/Chat_OpenAISettings.ts | 22 ++ .../Sessions_CreateOrUpdateSessionRequest.ts | 38 +++ sdks/ts/src/api/models/Tasks_TaskTool.ts | 18 +- .../src/api/models/Tools_CreateToolRequest.ts | 24 ++ ....ts => Users_CreateOrUpdateUserRequest.ts} | 2 +- .../$Agents_CreateOrUpdateAgentRequest.ts | 71 ++++ sdks/ts/src/api/schemas/$Chat_ChatInput.ts | 283 ++++++++-------- .../ts/src/api/schemas/$Chat_ChatInputData.ts | 33 ++ sdks/ts/src/api/schemas/$Chat_ChatSettings.ts | 169 ++++------ .../api/schemas/$Chat_DefaultChatSettings.ts | 88 ++--- .../src/api/schemas/$Chat_OpenAISettings.ts | 34 ++ .../$Sessions_CreateOrUpdateSessionRequest.ts | 84 +++++ sdks/ts/src/api/schemas/$Tasks_TaskTool.ts | 48 +-- .../api/schemas/$Tools_CreateToolRequest.ts | 41 +++ ...ts => $Users_CreateOrUpdateUserRequest.ts} | 2 +- sdks/ts/src/api/services/DefaultService.ts | 2 +- typespec/agents/models.tsp | 3 +- typespec/chat/models.tsp | 9 +- typespec/sessions/models.tsp | 2 +- typespec/tasks/models.tsp | 4 +- typespec/users/models.tsp | 7 +- 50 files changed, 1438 insertions(+), 821 deletions(-) create mode 100644 sdks/python/julep/api/types/agents_create_or_update_agent_request.py create mode 100644 sdks/python/julep/api/types/chat_chat_input_data.py rename sdks/python/julep/api/types/{chat_chat_input_tool_choice.py => chat_chat_input_data_tool_choice.py} (82%) create mode 100644 sdks/python/julep/api/types/chat_open_ai_settings.py create mode 100644 sdks/python/julep/api/types/sessions_create_or_update_session_request.py create mode 100644 sdks/python/julep/api/types/tools_create_tool_request.py create mode 100644 sdks/python/julep/api/types/users_create_or_update_user_request.py rename sdks/python/julep/api/types/{users_update_user_request.py => users_create_user_request.py} (93%) create mode 100644 sdks/ts/src/api/models/Agents_CreateOrUpdateAgentRequest.ts create mode 100644 sdks/ts/src/api/models/Chat_ChatInputData.ts create mode 100644 sdks/ts/src/api/models/Chat_OpenAISettings.ts create mode 100644 sdks/ts/src/api/models/Sessions_CreateOrUpdateSessionRequest.ts create mode 100644 sdks/ts/src/api/models/Tools_CreateToolRequest.ts rename sdks/ts/src/api/models/{Users_CreateOrUpdateUserRequest_id.ts => Users_CreateOrUpdateUserRequest.ts} (74%) create mode 100644 sdks/ts/src/api/schemas/$Agents_CreateOrUpdateAgentRequest.ts create mode 100644 sdks/ts/src/api/schemas/$Chat_ChatInputData.ts create mode 100644 sdks/ts/src/api/schemas/$Chat_OpenAISettings.ts create mode 100644 sdks/ts/src/api/schemas/$Sessions_CreateOrUpdateSessionRequest.ts create mode 100644 sdks/ts/src/api/schemas/$Tools_CreateToolRequest.ts rename sdks/ts/src/api/schemas/{$Users_CreateOrUpdateUserRequest_id.ts => $Users_CreateOrUpdateUserRequest.ts} (75%) diff --git a/agents-api/agents_api/autogen/Agents.py b/agents-api/agents_api/autogen/Agents.py index 1bd2d23f7..b4acfb563 100644 --- a/agents-api/agents_api/autogen/Agents.py +++ b/agents-api/agents_api/autogen/Agents.py @@ -90,6 +90,40 @@ class CreateAgentRequest(BaseModel): """ +class CreateOrUpdateAgentRequest(CreateAgentRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID + metadata: dict[str, Any] | None = None + name: Annotated[ + str, + Field( + "", + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] + """ + Name of the agent + """ + about: str = "" + """ + About the agent + """ + model: str = "" + """ + Model name to use (gpt-4-turbo, gemini-nano etc) + """ + instructions: str | list[str] = "" + """ + Instructions for the agent + """ + default_settings: DefaultChatSettings | None = None + """ + Default settings for all sessions created by this agent + """ + + class PatchAgentRequest(BaseModel): """ Payload for patching a agent diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index f46c6289a..d00d73a24 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -64,7 +64,7 @@ class BaseTokenLogProb(BaseModel): bytes: Annotated[list[int] | None, Field(...)] -class ChatInput(BaseModel): +class ChatInputData(BaseModel): model_config = ConfigDict( populate_by_name=True, ) @@ -80,73 +80,99 @@ class ChatInput(BaseModel): """ Can be one of existing tools given to the agent earlier or the ones provided in this request. """ - recall: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] - """ - Whether previous memories should be recalled or not (will be enabled in a future release) - """ - remember: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] + + +class ChatOutputChunk(BaseChatOutput): """ - Whether this interaction should form new memories or not (will be enabled in a future release) + Streaming chat completion output """ - save: bool = True + + model_config = ConfigDict( + populate_by_name=True, + ) + delta: ChatMLMessage """ - Whether this interaction should be stored in the session history or not + The message generated by the model """ - model: Annotated[ - str | None, - Field( - None, - pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", - ), - ] + + +class ChunkChatResponse(BaseChatResponse): + model_config = ConfigDict( + populate_by_name=True, + ) + choices: list[ChatOutputChunk] """ - Identifier of the model to be used + The deltas generated by the model """ - stream: bool = False + + +class CompetionUsage(BaseModel): """ - Indicates if the server should stream the response as it's generated + Usage statistics for the completion request """ - stop: Annotated[list[str] | None, Field(None, max_length=4, min_length=1)] + + model_config = ConfigDict( + populate_by_name=True, + ) + completion_tokens: Annotated[int, Field(json_schema_extra={"readOnly": True})] """ - Up to 4 sequences where the API will stop generating further tokens. + Number of tokens in the generated completion """ - seed: Annotated[int | None, Field(None, ge=-1, le=1000)] + prompt_tokens: Annotated[int, Field(json_schema_extra={"readOnly": True})] """ - If specified, the system will make a best effort to sample deterministically for that particular seed value + Number of tokens in the prompt """ - max_tokens: Annotated[int | None, Field(None, ge=1)] + total_tokens: Annotated[int, Field(json_schema_extra={"readOnly": True})] """ - The maximum number of tokens to generate in the chat completion + Total number of tokens used in the request (prompt + completion) """ - logit_bias: dict[str, LogitBias] | None = None + + +class CompletionResponseFormat(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["text", "json_object"] = "text" """ - Modify the likelihood of specified tokens appearing in the completion + The format of the response """ - response_format: CompletionResponseFormat | None = None + + +class LogProbResponse(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + content: Annotated[list[TokenLogProb] | None, Field(...)] """ - Response format (set to `json_object` to restrict output to JSON) + The log probabilities of the tokens """ - agent: UUID | None = None + + +class MessageChatResponse(BaseChatResponse): + model_config = ConfigDict( + populate_by_name=True, + ) + choices: list[SingleChatOutput | MultipleChatOutput] """ - Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) + The deltas generated by the model """ - preset: ( - Literal[ - "problem_solving", - "conversational", - "fun", - "prose", - "creative", - "business", - "deterministic", - "code", - "multilingual", - ] - | None - ) = None + + +class MultipleChatOutput(BaseChatOutput): """ - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + The output returned by the model. Note that, depending on the model provider, they might return more than one message. """ + + model_config = ConfigDict( + populate_by_name=True, + ) + messages: list[ChatMLMessage] + + +class OpenAISettings(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] """ Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. @@ -163,38 +189,42 @@ class ChatInput(BaseModel): """ Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. """ - repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - length_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - """ - min_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] - """ - Minimum probability compared to leading token to be considered - """ -class ChatOutputChunk(BaseChatOutput): +class SingleChatOutput(BaseChatOutput): """ - Streaming chat completion output + The output returned by the model. Note that, depending on the model provider, they might return more than one message. """ model_config = ConfigDict( populate_by_name=True, ) - delta: ChatMLMessage - """ - The message generated by the model - """ + message: ChatMLMessage + + +class TokenLogProb(BaseTokenLogProb): + model_config = ConfigDict( + populate_by_name=True, + ) + top_logprobs: list[BaseTokenLogProb] -class ChatSettings(BaseModel): +class ChatInput(ChatInputData): model_config = ConfigDict( populate_by_name=True, ) + recall: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] + """ + Whether previous memories should be recalled or not (will be enabled in a future release) + """ + remember: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] + """ + Whether this interaction should form new memories or not (will be enabled in a future release) + """ + save: bool = True + """ + Whether this interaction should be stored in the session history or not + """ model: Annotated[ str | None, Field( @@ -250,22 +280,6 @@ class ChatSettings(BaseModel): """ Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) """ - frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. @@ -278,51 +292,25 @@ class ChatSettings(BaseModel): """ Minimum probability compared to leading token to be considered """ - - -class ChunkChatResponse(BaseChatResponse): - model_config = ConfigDict( - populate_by_name=True, - ) - choices: list[ChatOutputChunk] - """ - The deltas generated by the model - """ - - -class CompetionUsage(BaseModel): - """ - Usage statistics for the completion request - """ - - model_config = ConfigDict( - populate_by_name=True, - ) - completion_tokens: Annotated[int, Field(json_schema_extra={"readOnly": True})] + frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] """ - Number of tokens in the generated completion + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ - prompt_tokens: Annotated[int, Field(json_schema_extra={"readOnly": True})] + presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] """ - Number of tokens in the prompt + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. """ - total_tokens: Annotated[int, Field(json_schema_extra={"readOnly": True})] + temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] """ - Total number of tokens used in the request (prompt + completion) + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. """ - - -class CompletionResponseFormat(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - type: Literal["text", "json_object"] = "text" + top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] """ - The format of the response + Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. """ -class DefaultChatSettings(BaseModel): +class DefaultChatSettings(OpenAISettings): """ Default settings for the chat session (also used by the agent) """ @@ -347,22 +335,6 @@ class DefaultChatSettings(BaseModel): """ Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) """ - frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - presence_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - temperature: Annotated[float | None, Field(None, ge=0.0, le=5.0)] - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - top_p: Annotated[float | None, Field(None, ge=0.0, le=1.0)] - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. @@ -377,50 +349,45 @@ class DefaultChatSettings(BaseModel): """ -class LogProbResponse(BaseModel): +class ChatSettings(DefaultChatSettings): model_config = ConfigDict( populate_by_name=True, ) - content: Annotated[list[TokenLogProb] | None, Field(...)] + model: Annotated[ + str | None, + Field( + None, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", + ), + ] """ - The log probabilities of the tokens + Identifier of the model to be used """ - - -class MessageChatResponse(BaseChatResponse): - model_config = ConfigDict( - populate_by_name=True, - ) - choices: list[SingleChatOutput | MultipleChatOutput] + stream: bool = False """ - The deltas generated by the model + Indicates if the server should stream the response as it's generated """ - - -class MultipleChatOutput(BaseChatOutput): + stop: Annotated[list[str] | None, Field(None, max_length=4, min_length=1)] """ - The output returned by the model. Note that, depending on the model provider, they might return more than one message. + Up to 4 sequences where the API will stop generating further tokens. """ - - model_config = ConfigDict( - populate_by_name=True, - ) - messages: list[ChatMLMessage] - - -class SingleChatOutput(BaseChatOutput): + seed: Annotated[int | None, Field(None, ge=-1, le=1000)] """ - The output returned by the model. Note that, depending on the model provider, they might return more than one message. + If specified, the system will make a best effort to sample deterministically for that particular seed value + """ + max_tokens: Annotated[int | None, Field(None, ge=1)] + """ + The maximum number of tokens to generate in the chat completion + """ + logit_bias: dict[str, LogitBias] | None = None + """ + Modify the likelihood of specified tokens appearing in the completion + """ + response_format: CompletionResponseFormat | None = None + """ + Response format (set to `json_object` to restrict output to JSON) + """ + agent: UUID | None = None + """ + Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) """ - - model_config = ConfigDict( - populate_by_name=True, - ) - message: ChatMLMessage - - -class TokenLogProb(BaseTokenLogProb): - model_config = ConfigDict( - populate_by_name=True, - ) - top_logprobs: list[BaseTokenLogProb] diff --git a/agents-api/agents_api/autogen/Sessions.py b/agents-api/agents_api/autogen/Sessions.py index 28732aa7d..fdfe25c92 100644 --- a/agents-api/agents_api/autogen/Sessions.py +++ b/agents-api/agents_api/autogen/Sessions.py @@ -163,6 +163,40 @@ class UpdateSessionRequest(BaseModel): metadata: dict[str, Any] | None = None +class CreateOrUpdateSessionRequest(CreateSessionRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID + user: UUID | None = None + """ + User ID of user associated with this session + """ + users: list[UUID] | None = None + agent: UUID | None = None + """ + Agent ID of agent associated with this session + """ + agents: list[UUID] | None = None + situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' + """ + A specific situation that sets the background for this session + """ + render_templates: bool = True + """ + Render system and assistant message content as jinja templates + """ + token_budget: int | None = None + """ + Threshold value for the adaptive context functionality + """ + context_overflow: Literal["truncate", "adaptive"] | None = None + """ + Action to start on context window overflow + """ + metadata: dict[str, Any] | None = None + + class MultiAgentMultiUserSession(Session): model_config = ConfigDict( populate_by_name=True, diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index d4eb09e41..add03d675 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -10,7 +10,7 @@ from .Chat import ChatSettings from .Entries import InputChatMLMessage -from .Tools import FunctionDef +from .Tools import CreateToolRequest class BaseWorkflowStep(BaseModel): @@ -218,7 +218,7 @@ class Task(BaseModel): metadata: dict[str, Any] | None = None -class TaskTool(BaseModel): +class TaskTool(CreateToolRequest): model_config = ConfigDict( populate_by_name=True, ) @@ -226,18 +226,6 @@ class TaskTool(BaseModel): """ Read-only: Whether the tool was inherited or not. Only applies within tasks. """ - type: Literal["function", "integration", "system", "api_call"] - """ - Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - """ - name: Annotated[str, Field(pattern="^[^\\W0-9]\\w*$")] - """ - Name of the tool (must be unique for this agent and a valid python identifier string ) - """ - function: FunctionDef | None = None - integration: Any | None = None - system: Any | None = None - api_call: Any | None = None class ToolCallStep(BaseWorkflowStep): diff --git a/agents-api/agents_api/autogen/Tools.py b/agents-api/agents_api/autogen/Tools.py index 1f25759d5..6d7f60a6b 100644 --- a/agents-api/agents_api/autogen/Tools.py +++ b/agents-api/agents_api/autogen/Tools.py @@ -28,6 +28,28 @@ class ChosenToolCall(BaseModel): id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] +class CreateToolRequest(BaseModel): + """ + Payload for creating a tool + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + type: Literal["function", "integration", "system", "api_call"] + """ + Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) + """ + name: Annotated[str, Field(pattern="^[^\\W0-9]\\w*$")] + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + function: FunctionDef | None = None + integration: Any | None = None + system: Any | None = None + api_call: Any | None = None + + class FunctionCallOption(BaseModel): model_config = ConfigDict( populate_by_name=True, diff --git a/agents-api/agents_api/autogen/Users.py b/agents-api/agents_api/autogen/Users.py index 4e99f6923..409f5039b 100644 --- a/agents-api/agents_api/autogen/Users.py +++ b/agents-api/agents_api/autogen/Users.py @@ -112,3 +112,10 @@ class User(BaseModel): """ About the user """ + + +class CreateOrUpdateUserRequest(CreateUserRequest): + model_config = ConfigDict( + populate_by_name=True, + ) + id: UUID diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index d670766a4..8136f659e 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -5172,13 +5172,13 @@ files = [ [[package]] name = "zipp" -version = "3.19.2" +version = "3.20.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, - {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, + {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, + {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, ] [package.extras] diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index 0bee6519a..72a573a52 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -11,6 +11,7 @@ AgentsAgentInstructions, AgentsCreateAgentRequest, AgentsCreateAgentRequestInstructions, + AgentsCreateOrUpdateAgentRequest, AgentsDocsSearchRouteSearchRequestBody, AgentsPatchAgentRequestInstructions, AgentsRouteListRequestDirection, @@ -21,7 +22,8 @@ ChatBaseChatOutput, ChatBaseChatResponse, ChatBaseTokenLogProb, - ChatChatInputToolChoice, + ChatChatInputData, + ChatChatInputDataToolChoice, ChatChatOutputChunk, ChatChatSettings, ChatChunkChatResponse, @@ -35,6 +37,7 @@ ChatMessageChatResponse, ChatMessageChatResponseChoicesItem, ChatMultipleChatOutput, + ChatOpenAiSettings, ChatRouteGenerateResponse, ChatSingleChatOutput, ChatTokenLogProb, @@ -106,6 +109,7 @@ JobsJobState, JobsJobStatus, SessionsContextOverflowType, + SessionsCreateOrUpdateSessionRequest, SessionsCreateSessionRequest, SessionsMultiAgentMultiUserSession, SessionsMultiAgentNoUserSession, @@ -179,6 +183,7 @@ ToolsChosenFunctionCall, ToolsChosenToolCall, ToolsChosenToolCall_Function, + ToolsCreateToolRequest, ToolsFunctionCallOption, ToolsFunctionDef, ToolsFunctionTool, @@ -193,10 +198,11 @@ UserDocsRouteListRequestSortBy, UserDocsRouteListResponse, UserDocsSearchRouteSearchRequestBody, + UsersCreateOrUpdateUserRequest, + UsersCreateUserRequest, UsersRouteListRequestDirection, UsersRouteListRequestSortBy, UsersRouteListResponse, - UsersUpdateUserRequest, UsersUser, ) from .environment import JulepApiEnvironment @@ -212,6 +218,7 @@ "AgentsAgentInstructions", "AgentsCreateAgentRequest", "AgentsCreateAgentRequestInstructions", + "AgentsCreateOrUpdateAgentRequest", "AgentsDocsSearchRouteSearchRequestBody", "AgentsPatchAgentRequestInstructions", "AgentsRouteListRequestDirection", @@ -222,7 +229,8 @@ "ChatBaseChatOutput", "ChatBaseChatResponse", "ChatBaseTokenLogProb", - "ChatChatInputToolChoice", + "ChatChatInputData", + "ChatChatInputDataToolChoice", "ChatChatOutputChunk", "ChatChatSettings", "ChatChunkChatResponse", @@ -236,6 +244,7 @@ "ChatMessageChatResponse", "ChatMessageChatResponseChoicesItem", "ChatMultipleChatOutput", + "ChatOpenAiSettings", "ChatRouteGenerateResponse", "ChatSingleChatOutput", "ChatTokenLogProb", @@ -308,6 +317,7 @@ "JobsJobStatus", "JulepApiEnvironment", "SessionsContextOverflowType", + "SessionsCreateOrUpdateSessionRequest", "SessionsCreateSessionRequest", "SessionsMultiAgentMultiUserSession", "SessionsMultiAgentNoUserSession", @@ -381,6 +391,7 @@ "ToolsChosenFunctionCall", "ToolsChosenToolCall", "ToolsChosenToolCall_Function", + "ToolsCreateToolRequest", "ToolsFunctionCallOption", "ToolsFunctionDef", "ToolsFunctionTool", @@ -395,9 +406,10 @@ "UserDocsRouteListRequestSortBy", "UserDocsRouteListResponse", "UserDocsSearchRouteSearchRequestBody", + "UsersCreateOrUpdateUserRequest", + "UsersCreateUserRequest", "UsersRouteListRequestDirection", "UsersRouteListRequestSortBy", "UsersRouteListResponse", - "UsersUpdateUserRequest", "UsersUser", ] diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index 86884cd7a..dd0c4e99f 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -39,7 +39,7 @@ from .types.agents_update_agent_request_instructions import ( AgentsUpdateAgentRequestInstructions, ) -from .types.chat_chat_input_tool_choice import ChatChatInputToolChoice +from .types.chat_chat_input_data_tool_choice import ChatChatInputDataToolChoice from .types.chat_completion_response_format import ChatCompletionResponseFormat from .types.chat_default_chat_settings import ChatDefaultChatSettings from .types.chat_generation_preset import ChatGenerationPreset @@ -2664,13 +2664,11 @@ def chat_route_generate( self, id: CommonUuid, *, - messages: typing.Sequence[EntriesInputChatMlMessage], recall: bool, remember: bool, save: bool, stream: bool, - tools: typing.Optional[typing.Sequence[ToolsFunctionTool]] = OMIT, - tool_choice: typing.Optional[ChatChatInputToolChoice] = OMIT, + messages: typing.Sequence[EntriesInputChatMlMessage], model: typing.Optional[CommonIdentifierSafeUnicode] = OMIT, stop: typing.Optional[typing.Sequence[str]] = OMIT, seed: typing.Optional[int] = OMIT, @@ -2679,13 +2677,15 @@ def chat_route_generate( response_format: typing.Optional[ChatCompletionResponseFormat] = OMIT, agent: typing.Optional[CommonUuid] = OMIT, preset: typing.Optional[ChatGenerationPreset] = OMIT, + repetition_penalty: typing.Optional[float] = OMIT, + length_penalty: typing.Optional[float] = OMIT, + min_p: typing.Optional[float] = OMIT, frequency_penalty: typing.Optional[float] = OMIT, presence_penalty: typing.Optional[float] = OMIT, temperature: typing.Optional[float] = OMIT, top_p: typing.Optional[float] = OMIT, - repetition_penalty: typing.Optional[float] = OMIT, - length_penalty: typing.Optional[float] = OMIT, - min_p: typing.Optional[float] = OMIT, + tools: typing.Optional[typing.Sequence[ToolsFunctionTool]] = OMIT, + tool_choice: typing.Optional[ChatChatInputDataToolChoice] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ChatRouteGenerateResponse: """ @@ -2696,9 +2696,6 @@ def chat_route_generate( id : CommonUuid The session ID - messages : typing.Sequence[EntriesInputChatMlMessage] - A list of new input messages comprising the conversation so far. - recall : bool Whether previous memories should be recalled or not (will be enabled in a future release) @@ -2711,11 +2708,8 @@ def chat_route_generate( stream : bool Indicates if the server should stream the response as it's generated - tools : typing.Optional[typing.Sequence[ToolsFunctionTool]] - (Advanced) List of tools that are provided in addition to agent's default set of tools. - - tool_choice : typing.Optional[ChatChatInputToolChoice] - Can be one of existing tools given to the agent earlier or the ones provided in this request. + messages : typing.Sequence[EntriesInputChatMlMessage] + A list of new input messages comprising the conversation so far. model : typing.Optional[CommonIdentifierSafeUnicode] Identifier of the model to be used @@ -2741,6 +2735,15 @@ def chat_route_generate( preset : typing.Optional[ChatGenerationPreset] Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + repetition_penalty : typing.Optional[float] + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + + length_penalty : typing.Optional[float] + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + + min_p : typing.Optional[float] + Minimum probability compared to leading token to be considered + frequency_penalty : typing.Optional[float] Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. @@ -2753,14 +2756,11 @@ def chat_route_generate( top_p : typing.Optional[float] Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - repetition_penalty : typing.Optional[float] - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - - length_penalty : typing.Optional[float] - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + tools : typing.Optional[typing.Sequence[ToolsFunctionTool]] + (Advanced) List of tools that are provided in addition to agent's default set of tools. - min_p : typing.Optional[float] - Minimum probability compared to leading token to be considered + tool_choice : typing.Optional[ChatChatInputDataToolChoice] + Can be one of existing tools given to the agent earlier or the ones provided in this request. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -2797,9 +2797,6 @@ def chat_route_generate( f"sessions/{jsonable_encoder(id)}/chat", method="POST", json={ - "messages": messages, - "tools": tools, - "tool_choice": tool_choice, "recall": recall, "remember": remember, "save": save, @@ -2812,13 +2809,16 @@ def chat_route_generate( "response_format": response_format, "agent": agent, "preset": preset, + "repetition_penalty": repetition_penalty, + "length_penalty": length_penalty, + "min_p": min_p, "frequency_penalty": frequency_penalty, "presence_penalty": presence_penalty, "temperature": temperature, "top_p": top_p, - "repetition_penalty": repetition_penalty, - "length_penalty": length_penalty, - "min_p": min_p, + "messages": messages, + "tools": tools, + "tool_choice": tool_choice, }, request_options=request_options, omit=OMIT, @@ -6548,13 +6548,11 @@ async def chat_route_generate( self, id: CommonUuid, *, - messages: typing.Sequence[EntriesInputChatMlMessage], recall: bool, remember: bool, save: bool, stream: bool, - tools: typing.Optional[typing.Sequence[ToolsFunctionTool]] = OMIT, - tool_choice: typing.Optional[ChatChatInputToolChoice] = OMIT, + messages: typing.Sequence[EntriesInputChatMlMessage], model: typing.Optional[CommonIdentifierSafeUnicode] = OMIT, stop: typing.Optional[typing.Sequence[str]] = OMIT, seed: typing.Optional[int] = OMIT, @@ -6563,13 +6561,15 @@ async def chat_route_generate( response_format: typing.Optional[ChatCompletionResponseFormat] = OMIT, agent: typing.Optional[CommonUuid] = OMIT, preset: typing.Optional[ChatGenerationPreset] = OMIT, + repetition_penalty: typing.Optional[float] = OMIT, + length_penalty: typing.Optional[float] = OMIT, + min_p: typing.Optional[float] = OMIT, frequency_penalty: typing.Optional[float] = OMIT, presence_penalty: typing.Optional[float] = OMIT, temperature: typing.Optional[float] = OMIT, top_p: typing.Optional[float] = OMIT, - repetition_penalty: typing.Optional[float] = OMIT, - length_penalty: typing.Optional[float] = OMIT, - min_p: typing.Optional[float] = OMIT, + tools: typing.Optional[typing.Sequence[ToolsFunctionTool]] = OMIT, + tool_choice: typing.Optional[ChatChatInputDataToolChoice] = OMIT, request_options: typing.Optional[RequestOptions] = None, ) -> ChatRouteGenerateResponse: """ @@ -6580,9 +6580,6 @@ async def chat_route_generate( id : CommonUuid The session ID - messages : typing.Sequence[EntriesInputChatMlMessage] - A list of new input messages comprising the conversation so far. - recall : bool Whether previous memories should be recalled or not (will be enabled in a future release) @@ -6595,11 +6592,8 @@ async def chat_route_generate( stream : bool Indicates if the server should stream the response as it's generated - tools : typing.Optional[typing.Sequence[ToolsFunctionTool]] - (Advanced) List of tools that are provided in addition to agent's default set of tools. - - tool_choice : typing.Optional[ChatChatInputToolChoice] - Can be one of existing tools given to the agent earlier or the ones provided in this request. + messages : typing.Sequence[EntriesInputChatMlMessage] + A list of new input messages comprising the conversation so far. model : typing.Optional[CommonIdentifierSafeUnicode] Identifier of the model to be used @@ -6625,6 +6619,15 @@ async def chat_route_generate( preset : typing.Optional[ChatGenerationPreset] Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) + repetition_penalty : typing.Optional[float] + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + + length_penalty : typing.Optional[float] + Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + + min_p : typing.Optional[float] + Minimum probability compared to leading token to be considered + frequency_penalty : typing.Optional[float] Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. @@ -6637,14 +6640,11 @@ async def chat_route_generate( top_p : typing.Optional[float] Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - repetition_penalty : typing.Optional[float] - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - - length_penalty : typing.Optional[float] - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + tools : typing.Optional[typing.Sequence[ToolsFunctionTool]] + (Advanced) List of tools that are provided in addition to agent's default set of tools. - min_p : typing.Optional[float] - Minimum probability compared to leading token to be considered + tool_choice : typing.Optional[ChatChatInputDataToolChoice] + Can be one of existing tools given to the agent earlier or the ones provided in this request. request_options : typing.Optional[RequestOptions] Request-specific configuration. @@ -6689,9 +6689,6 @@ async def main() -> None: f"sessions/{jsonable_encoder(id)}/chat", method="POST", json={ - "messages": messages, - "tools": tools, - "tool_choice": tool_choice, "recall": recall, "remember": remember, "save": save, @@ -6704,13 +6701,16 @@ async def main() -> None: "response_format": response_format, "agent": agent, "preset": preset, + "repetition_penalty": repetition_penalty, + "length_penalty": length_penalty, + "min_p": min_p, "frequency_penalty": frequency_penalty, "presence_penalty": presence_penalty, "temperature": temperature, "top_p": top_p, - "repetition_penalty": repetition_penalty, - "length_penalty": length_penalty, - "min_p": min_p, + "messages": messages, + "tools": tools, + "tool_choice": tool_choice, }, request_options=request_options, omit=OMIT, diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index 2f54bca4a..191769362 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -3818,7 +3818,7 @@ client.chat_route_generate(
-**messages:** `typing.Sequence[EntriesInputChatMlMessage]` — A list of new input messages comprising the conversation so far. +**recall:** `bool` — Whether previous memories should be recalled or not (will be enabled in a future release)
@@ -3826,7 +3826,7 @@ client.chat_route_generate(
-**recall:** `bool` — Whether previous memories should be recalled or not (will be enabled in a future release) +**remember:** `bool` — Whether this interaction should form new memories or not (will be enabled in a future release)
@@ -3834,7 +3834,7 @@ client.chat_route_generate(
-**remember:** `bool` — Whether this interaction should form new memories or not (will be enabled in a future release) +**save:** `bool` — Whether this interaction should be stored in the session history or not
@@ -3842,7 +3842,7 @@ client.chat_route_generate(
-**save:** `bool` — Whether this interaction should be stored in the session history or not +**stream:** `bool` — Indicates if the server should stream the response as it's generated
@@ -3850,7 +3850,7 @@ client.chat_route_generate(
-**stream:** `bool` — Indicates if the server should stream the response as it's generated +**messages:** `typing.Sequence[EntriesInputChatMlMessage]` — A list of new input messages comprising the conversation so far.
@@ -3858,7 +3858,7 @@ client.chat_route_generate(
-**tools:** `typing.Optional[typing.Sequence[ToolsFunctionTool]]` — (Advanced) List of tools that are provided in addition to agent's default set of tools. +**model:** `typing.Optional[CommonIdentifierSafeUnicode]` — Identifier of the model to be used
@@ -3866,7 +3866,7 @@ client.chat_route_generate(
-**tool_choice:** `typing.Optional[ChatChatInputToolChoice]` — Can be one of existing tools given to the agent earlier or the ones provided in this request. +**stop:** `typing.Optional[typing.Sequence[str]]` — Up to 4 sequences where the API will stop generating further tokens.
@@ -3874,7 +3874,7 @@ client.chat_route_generate(
-**model:** `typing.Optional[CommonIdentifierSafeUnicode]` — Identifier of the model to be used +**seed:** `typing.Optional[int]` — If specified, the system will make a best effort to sample deterministically for that particular seed value
@@ -3882,7 +3882,7 @@ client.chat_route_generate(
-**stop:** `typing.Optional[typing.Sequence[str]]` — Up to 4 sequences where the API will stop generating further tokens. +**max_tokens:** `typing.Optional[int]` — The maximum number of tokens to generate in the chat completion
@@ -3890,7 +3890,7 @@ client.chat_route_generate(
-**seed:** `typing.Optional[int]` — If specified, the system will make a best effort to sample deterministically for that particular seed value +**logit_bias:** `typing.Optional[typing.Dict[str, CommonLogitBias]]` — Modify the likelihood of specified tokens appearing in the completion
@@ -3898,7 +3898,7 @@ client.chat_route_generate(
-**max_tokens:** `typing.Optional[int]` — The maximum number of tokens to generate in the chat completion +**response_format:** `typing.Optional[ChatCompletionResponseFormat]` — Response format (set to `json_object` to restrict output to JSON)
@@ -3906,7 +3906,7 @@ client.chat_route_generate(
-**logit_bias:** `typing.Optional[typing.Dict[str, CommonLogitBias]]` — Modify the likelihood of specified tokens appearing in the completion +**agent:** `typing.Optional[CommonUuid]` — Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)
@@ -3914,7 +3914,7 @@ client.chat_route_generate(
-**response_format:** `typing.Optional[ChatCompletionResponseFormat]` — Response format (set to `json_object` to restrict output to JSON) +**preset:** `typing.Optional[ChatGenerationPreset]` — Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)
@@ -3922,7 +3922,7 @@ client.chat_route_generate(
-**agent:** `typing.Optional[CommonUuid]` — Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) +**repetition_penalty:** `typing.Optional[float]` — Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
@@ -3930,7 +3930,7 @@ client.chat_route_generate(
-**preset:** `typing.Optional[ChatGenerationPreset]` — Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) +**length_penalty:** `typing.Optional[float]` — Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.
@@ -3938,7 +3938,7 @@ client.chat_route_generate(
-**frequency_penalty:** `typing.Optional[float]` — Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. +**min_p:** `typing.Optional[float]` — Minimum probability compared to leading token to be considered
@@ -3946,7 +3946,7 @@ client.chat_route_generate(
-**presence_penalty:** `typing.Optional[float]` — Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. +**frequency_penalty:** `typing.Optional[float]` — Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
@@ -3954,7 +3954,7 @@ client.chat_route_generate(
-**temperature:** `typing.Optional[float]` — What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. +**presence_penalty:** `typing.Optional[float]` — Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
@@ -3962,7 +3962,7 @@ client.chat_route_generate(
-**top_p:** `typing.Optional[float]` — Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. +**temperature:** `typing.Optional[float]` — What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.
@@ -3970,7 +3970,7 @@ client.chat_route_generate(
-**repetition_penalty:** `typing.Optional[float]` — Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. +**top_p:** `typing.Optional[float]` — Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.
@@ -3978,7 +3978,7 @@ client.chat_route_generate(
-**length_penalty:** `typing.Optional[float]` — Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. +**tools:** `typing.Optional[typing.Sequence[ToolsFunctionTool]]` — (Advanced) List of tools that are provided in addition to agent's default set of tools.
@@ -3986,7 +3986,7 @@ client.chat_route_generate(
-**min_p:** `typing.Optional[float]` — Minimum probability compared to leading token to be considered +**tool_choice:** `typing.Optional[ChatChatInputDataToolChoice]` — Can be one of existing tools given to the agent earlier or the ones provided in this request.
diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index f3df33631..640d4cd4e 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -14,6 +14,7 @@ from .agents_create_agent_request_instructions import ( AgentsCreateAgentRequestInstructions, ) +from .agents_create_or_update_agent_request import AgentsCreateOrUpdateAgentRequest from .agents_docs_search_route_search_request_body import ( AgentsDocsSearchRouteSearchRequestBody, ) @@ -28,7 +29,8 @@ from .chat_base_chat_output import ChatBaseChatOutput from .chat_base_chat_response import ChatBaseChatResponse from .chat_base_token_log_prob import ChatBaseTokenLogProb -from .chat_chat_input_tool_choice import ChatChatInputToolChoice +from .chat_chat_input_data import ChatChatInputData +from .chat_chat_input_data_tool_choice import ChatChatInputDataToolChoice from .chat_chat_output_chunk import ChatChatOutputChunk from .chat_chat_settings import ChatChatSettings from .chat_chunk_chat_response import ChatChunkChatResponse @@ -42,6 +44,7 @@ from .chat_message_chat_response import ChatMessageChatResponse from .chat_message_chat_response_choices_item import ChatMessageChatResponseChoicesItem from .chat_multiple_chat_output import ChatMultipleChatOutput +from .chat_open_ai_settings import ChatOpenAiSettings from .chat_route_generate_response import ChatRouteGenerateResponse from .chat_single_chat_output import ChatSingleChatOutput from .chat_token_log_prob import ChatTokenLogProb @@ -129,6 +132,9 @@ from .jobs_job_state import JobsJobState from .jobs_job_status import JobsJobStatus from .sessions_context_overflow_type import SessionsContextOverflowType +from .sessions_create_or_update_session_request import ( + SessionsCreateOrUpdateSessionRequest, +) from .sessions_create_session_request import SessionsCreateSessionRequest from .sessions_multi_agent_multi_user_session import SessionsMultiAgentMultiUserSession from .sessions_multi_agent_no_user_session import SessionsMultiAgentNoUserSession @@ -221,6 +227,7 @@ from .tasks_yield_step import TasksYieldStep from .tools_chosen_function_call import ToolsChosenFunctionCall from .tools_chosen_tool_call import ToolsChosenToolCall, ToolsChosenToolCall_Function +from .tools_create_tool_request import ToolsCreateToolRequest from .tools_function_call_option import ToolsFunctionCallOption from .tools_function_def import ToolsFunctionDef from .tools_function_tool import ToolsFunctionTool @@ -235,10 +242,11 @@ from .user_docs_search_route_search_request_body import ( UserDocsSearchRouteSearchRequestBody, ) +from .users_create_or_update_user_request import UsersCreateOrUpdateUserRequest +from .users_create_user_request import UsersCreateUserRequest from .users_route_list_request_direction import UsersRouteListRequestDirection from .users_route_list_request_sort_by import UsersRouteListRequestSortBy from .users_route_list_response import UsersRouteListResponse -from .users_update_user_request import UsersUpdateUserRequest from .users_user import UsersUser __all__ = [ @@ -252,6 +260,7 @@ "AgentsAgentInstructions", "AgentsCreateAgentRequest", "AgentsCreateAgentRequestInstructions", + "AgentsCreateOrUpdateAgentRequest", "AgentsDocsSearchRouteSearchRequestBody", "AgentsPatchAgentRequestInstructions", "AgentsRouteListRequestDirection", @@ -262,7 +271,8 @@ "ChatBaseChatOutput", "ChatBaseChatResponse", "ChatBaseTokenLogProb", - "ChatChatInputToolChoice", + "ChatChatInputData", + "ChatChatInputDataToolChoice", "ChatChatOutputChunk", "ChatChatSettings", "ChatChunkChatResponse", @@ -276,6 +286,7 @@ "ChatMessageChatResponse", "ChatMessageChatResponseChoicesItem", "ChatMultipleChatOutput", + "ChatOpenAiSettings", "ChatRouteGenerateResponse", "ChatSingleChatOutput", "ChatTokenLogProb", @@ -347,6 +358,7 @@ "JobsJobState", "JobsJobStatus", "SessionsContextOverflowType", + "SessionsCreateOrUpdateSessionRequest", "SessionsCreateSessionRequest", "SessionsMultiAgentMultiUserSession", "SessionsMultiAgentNoUserSession", @@ -420,6 +432,7 @@ "ToolsChosenFunctionCall", "ToolsChosenToolCall", "ToolsChosenToolCall_Function", + "ToolsCreateToolRequest", "ToolsFunctionCallOption", "ToolsFunctionDef", "ToolsFunctionTool", @@ -434,9 +447,10 @@ "UserDocsRouteListRequestSortBy", "UserDocsRouteListResponse", "UserDocsSearchRouteSearchRequestBody", + "UsersCreateOrUpdateUserRequest", + "UsersCreateUserRequest", "UsersRouteListRequestDirection", "UsersRouteListRequestSortBy", "UsersRouteListResponse", - "UsersUpdateUserRequest", "UsersUser", ] diff --git a/sdks/python/julep/api/types/agents_create_or_update_agent_request.py b/sdks/python/julep/api/types/agents_create_or_update_agent_request.py new file mode 100644 index 000000000..fa92a7a9b --- /dev/null +++ b/sdks/python/julep/api/types/agents_create_or_update_agent_request.py @@ -0,0 +1,75 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .agents_create_agent_request_instructions import ( + AgentsCreateAgentRequestInstructions, +) +from .chat_default_chat_settings import ChatDefaultChatSettings +from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode +from .common_uuid import CommonUuid + + +class AgentsCreateOrUpdateAgentRequest(pydantic_v1.BaseModel): + id: CommonUuid + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + default_settings: typing.Optional[ChatDefaultChatSettings] = pydantic_v1.Field( + default=None + ) + """ + Default settings for all sessions created by this agent + """ + + name: CommonIdentifierSafeUnicode = pydantic_v1.Field() + """ + Name of the agent + """ + + about: str = pydantic_v1.Field() + """ + About the agent + """ + + model: str = pydantic_v1.Field() + """ + Model name to use (gpt-4-turbo, gemini-nano etc) + """ + + instructions: AgentsCreateAgentRequestInstructions = pydantic_v1.Field() + """ + Instructions for the agent + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/chat_chat_input_data.py b/sdks/python/julep/api/types/chat_chat_input_data.py new file mode 100644 index 000000000..c7ab665b7 --- /dev/null +++ b/sdks/python/julep/api/types/chat_chat_input_data.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_input_data_tool_choice import ChatChatInputDataToolChoice +from .entries_input_chat_ml_message import EntriesInputChatMlMessage +from .tools_function_tool import ToolsFunctionTool + + +class ChatChatInputData(pydantic_v1.BaseModel): + messages: typing.List[EntriesInputChatMlMessage] = pydantic_v1.Field() + """ + A list of new input messages comprising the conversation so far. + """ + + tools: typing.Optional[typing.List[ToolsFunctionTool]] = pydantic_v1.Field( + default=None + ) + """ + (Advanced) List of tools that are provided in addition to agent's default set of tools. + """ + + tool_choice: typing.Optional[ChatChatInputDataToolChoice] = pydantic_v1.Field( + default=None + ) + """ + Can be one of existing tools given to the agent earlier or the ones provided in this request. + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/chat_chat_input_tool_choice.py b/sdks/python/julep/api/types/chat_chat_input_data_tool_choice.py similarity index 82% rename from sdks/python/julep/api/types/chat_chat_input_tool_choice.py rename to sdks/python/julep/api/types/chat_chat_input_data_tool_choice.py index e25d6980b..4f0f065e0 100644 --- a/sdks/python/julep/api/types/chat_chat_input_tool_choice.py +++ b/sdks/python/julep/api/types/chat_chat_input_data_tool_choice.py @@ -4,6 +4,6 @@ from .tools_named_tool_choice import ToolsNamedToolChoice -ChatChatInputToolChoice = typing.Union[ +ChatChatInputDataToolChoice = typing.Union[ typing.Literal["auto"], typing.Literal["none"], ToolsNamedToolChoice ] diff --git a/sdks/python/julep/api/types/chat_chat_settings.py b/sdks/python/julep/api/types/chat_chat_settings.py index 8b7df8f7c..01a10a9fc 100644 --- a/sdks/python/julep/api/types/chat_chat_settings.py +++ b/sdks/python/julep/api/types/chat_chat_settings.py @@ -6,13 +6,13 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .chat_completion_response_format import ChatCompletionResponseFormat -from .chat_generation_preset import ChatGenerationPreset +from .chat_default_chat_settings import ChatDefaultChatSettings from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode from .common_logit_bias import CommonLogitBias from .common_uuid import CommonUuid -class ChatChatSettings(pydantic_v1.BaseModel): +class ChatChatSettings(ChatDefaultChatSettings): model: typing.Optional[CommonIdentifierSafeUnicode] = pydantic_v1.Field( default=None ) @@ -59,46 +59,6 @@ class ChatChatSettings(pydantic_v1.BaseModel): Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) """ - preset: typing.Optional[ChatGenerationPreset] = pydantic_v1.Field(default=None) - """ - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - """ - - frequency_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - presence_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - temperature: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - - top_p: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - - repetition_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - length_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - """ - - min_p: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Minimum probability compared to leading token to be considered - """ - def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { "by_alias": True, @@ -127,5 +87,7 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True + allow_population_by_field_name = True + populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/chat_default_chat_settings.py b/sdks/python/julep/api/types/chat_default_chat_settings.py index 1b1838a34..a3972e99f 100644 --- a/sdks/python/julep/api/types/chat_default_chat_settings.py +++ b/sdks/python/julep/api/types/chat_default_chat_settings.py @@ -6,9 +6,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .chat_generation_preset import ChatGenerationPreset +from .chat_open_ai_settings import ChatOpenAiSettings -class ChatDefaultChatSettings(pydantic_v1.BaseModel): +class ChatDefaultChatSettings(ChatOpenAiSettings): """ Default settings for the chat session (also used by the agent) """ @@ -18,26 +19,6 @@ class ChatDefaultChatSettings(pydantic_v1.BaseModel): Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) """ - frequency_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - presence_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - """ - - temperature: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - """ - - top_p: typing.Optional[float] = pydantic_v1.Field(default=None) - """ - Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - """ - repetition_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. @@ -81,5 +62,7 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True + allow_population_by_field_name = True + populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/chat_open_ai_settings.py b/sdks/python/julep/api/types/chat_open_ai_settings.py new file mode 100644 index 000000000..0bb5a0799 --- /dev/null +++ b/sdks/python/julep/api/types/chat_open_ai_settings.py @@ -0,0 +1,60 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class ChatOpenAiSettings(pydantic_v1.BaseModel): + frequency_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + + presence_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + """ + + temperature: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + """ + + top_p: typing.Optional[float] = pydantic_v1.Field(default=None) + """ + Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/sessions_create_or_update_session_request.py b/sdks/python/julep/api/types/sessions_create_or_update_session_request.py new file mode 100644 index 000000000..b5dc6bc40 --- /dev/null +++ b/sdks/python/julep/api/types/sessions_create_or_update_session_request.py @@ -0,0 +1,78 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_uuid import CommonUuid +from .sessions_context_overflow_type import SessionsContextOverflowType + + +class SessionsCreateOrUpdateSessionRequest(pydantic_v1.BaseModel): + id: CommonUuid + user: typing.Optional[CommonUuid] = pydantic_v1.Field(default=None) + """ + User ID of user associated with this session + """ + + users: typing.Optional[typing.List[CommonUuid]] = None + agent: typing.Optional[CommonUuid] = pydantic_v1.Field(default=None) + """ + Agent ID of agent associated with this session + """ + + agents: typing.Optional[typing.List[CommonUuid]] = None + token_budget: typing.Optional[int] = pydantic_v1.Field(default=None) + """ + Threshold value for the adaptive context functionality + """ + + context_overflow: typing.Optional[SessionsContextOverflowType] = pydantic_v1.Field( + default=None + ) + """ + Action to start on context window overflow + """ + + metadata: typing.Optional[typing.Dict[str, typing.Any]] = None + situation: str = pydantic_v1.Field() + """ + A specific situation that sets the background for this session + """ + + render_templates: bool = pydantic_v1.Field() + """ + Render system and assistant message content as jinja templates + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_task_tool.py b/sdks/python/julep/api/types/tasks_task_tool.py index 56a891988..c5638b4ea 100644 --- a/sdks/python/julep/api/types/tasks_task_tool.py +++ b/sdks/python/julep/api/types/tasks_task_tool.py @@ -5,32 +5,15 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .common_valid_python_identifier import CommonValidPythonIdentifier -from .tools_function_def import ToolsFunctionDef -from .tools_tool_type import ToolsToolType +from .tools_create_tool_request import ToolsCreateToolRequest -class TasksTaskTool(pydantic_v1.BaseModel): +class TasksTaskTool(ToolsCreateToolRequest): inherited: typing.Optional[bool] = pydantic_v1.Field(default=None) """ Read-only: Whether the tool was inherited or not. Only applies within tasks. """ - type: ToolsToolType = pydantic_v1.Field() - """ - Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - """ - - name: CommonValidPythonIdentifier = pydantic_v1.Field() - """ - Name of the tool (must be unique for this agent and a valid python identifier string ) - """ - - function: typing.Optional[ToolsFunctionDef] = None - integration: typing.Optional[typing.Any] = None - system: typing.Optional[typing.Any] = None - api_call: typing.Optional[typing.Any] = None - def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { "by_alias": True, @@ -59,5 +42,7 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True + allow_population_by_field_name = True + populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tools_create_tool_request.py b/sdks/python/julep/api/types/tools_create_tool_request.py new file mode 100644 index 000000000..cb54b8434 --- /dev/null +++ b/sdks/python/julep/api/types/tools_create_tool_request.py @@ -0,0 +1,62 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_valid_python_identifier import CommonValidPythonIdentifier +from .tools_function_def import ToolsFunctionDef +from .tools_tool_type import ToolsToolType + + +class ToolsCreateToolRequest(pydantic_v1.BaseModel): + """ + Payload for creating a tool + """ + + type: ToolsToolType = pydantic_v1.Field() + """ + Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) + """ + + name: CommonValidPythonIdentifier = pydantic_v1.Field() + """ + Name of the tool (must be unique for this agent and a valid python identifier string ) + """ + + function: typing.Optional[ToolsFunctionDef] = None + integration: typing.Optional[typing.Any] = None + system: typing.Optional[typing.Any] = None + api_call: typing.Optional[typing.Any] = None + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/users_create_or_update_user_request.py b/sdks/python/julep/api/types/users_create_or_update_user_request.py new file mode 100644 index 000000000..18e694bd6 --- /dev/null +++ b/sdks/python/julep/api/types/users_create_or_update_user_request.py @@ -0,0 +1,46 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_uuid import CommonUuid +from .users_create_user_request import UsersCreateUserRequest + + +class UsersCreateOrUpdateUserRequest(UsersCreateUserRequest): + id: CommonUuid + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/users_update_user_request.py b/sdks/python/julep/api/types/users_create_user_request.py similarity index 93% rename from sdks/python/julep/api/types/users_update_user_request.py rename to sdks/python/julep/api/types/users_create_user_request.py index f747a2093..a9abbb461 100644 --- a/sdks/python/julep/api/types/users_update_user_request.py +++ b/sdks/python/julep/api/types/users_create_user_request.py @@ -8,9 +8,9 @@ from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode -class UsersUpdateUserRequest(pydantic_v1.BaseModel): +class UsersCreateUserRequest(pydantic_v1.BaseModel): """ - Payload for updating a user + Payload for creating a user (and associated documents) """ metadata: typing.Optional[typing.Dict[str, typing.Any]] = None diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 8db25188c..bef7da8bf 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. [[package]] name = "annotated-types" @@ -3262,13 +3262,13 @@ test = ["websockets"] [[package]] name = "zipp" -version = "3.19.2" +version = "3.20.0" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.19.2-py3-none-any.whl", hash = "sha256:f091755f667055f2d02b32c53771a7a6c8b47e1fdbc4b72a8b9072b3eef8015c"}, - {file = "zipp-3.19.2.tar.gz", hash = "sha256:bf1dcf6450f873a13e952a29504887c89e6de7506209e5b1bcc3460135d4de19"}, + {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, + {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, ] [package.extras] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index a9625977a..0e213abee 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -12,6 +12,7 @@ export type { OpenAPIConfig } from "./core/OpenAPI"; export type { Agents_Agent } from "./models/Agents_Agent"; export type { Agents_CreateAgentRequest } from "./models/Agents_CreateAgentRequest"; +export type { Agents_CreateOrUpdateAgentRequest } from "./models/Agents_CreateOrUpdateAgentRequest"; export type { Agents_CreateOrUpdateAgentRequest_id } from "./models/Agents_CreateOrUpdateAgentRequest_id"; export type { Agents_PatchAgentRequest } from "./models/Agents_PatchAgentRequest"; export type { Agents_UpdateAgentRequest } from "./models/Agents_UpdateAgentRequest"; @@ -19,6 +20,7 @@ export type { Chat_BaseChatOutput } from "./models/Chat_BaseChatOutput"; export type { Chat_BaseChatResponse } from "./models/Chat_BaseChatResponse"; export type { Chat_BaseTokenLogProb } from "./models/Chat_BaseTokenLogProb"; export type { Chat_ChatInput } from "./models/Chat_ChatInput"; +export type { Chat_ChatInputData } from "./models/Chat_ChatInputData"; export type { Chat_ChatOutputChunk } from "./models/Chat_ChatOutputChunk"; export type { Chat_ChatSettings } from "./models/Chat_ChatSettings"; export type { Chat_ChunkChatResponse } from "./models/Chat_ChunkChatResponse"; @@ -30,6 +32,7 @@ export type { Chat_GenerationPreset } from "./models/Chat_GenerationPreset"; export type { Chat_LogProbResponse } from "./models/Chat_LogProbResponse"; export type { Chat_MessageChatResponse } from "./models/Chat_MessageChatResponse"; export type { Chat_MultipleChatOutput } from "./models/Chat_MultipleChatOutput"; +export type { Chat_OpenAISettings } from "./models/Chat_OpenAISettings"; export type { Chat_SingleChatOutput } from "./models/Chat_SingleChatOutput"; export type { Chat_TokenLogProb } from "./models/Chat_TokenLogProb"; export type { Common_identifierSafeUnicode } from "./models/Common_identifierSafeUnicode"; @@ -81,6 +84,7 @@ export type { Executions_UpdateExecutionRequest } from "./models/Executions_Upda export type { Jobs_JobState } from "./models/Jobs_JobState"; export type { Jobs_JobStatus } from "./models/Jobs_JobStatus"; export type { Sessions_ContextOverflowType } from "./models/Sessions_ContextOverflowType"; +export type { Sessions_CreateOrUpdateSessionRequest } from "./models/Sessions_CreateOrUpdateSessionRequest"; export type { Sessions_CreateOrUpdateSessionRequest_id } from "./models/Sessions_CreateOrUpdateSessionRequest_id"; export type { Sessions_CreateSessionRequest } from "./models/Sessions_CreateSessionRequest"; export type { Sessions_MultiAgentMultiUserSession } from "./models/Sessions_MultiAgentMultiUserSession"; @@ -108,6 +112,7 @@ export type { Tasks_WaitForInputStep } from "./models/Tasks_WaitForInputStep"; export type { Tasks_YieldStep } from "./models/Tasks_YieldStep"; export type { Tools_ChosenFunctionCall } from "./models/Tools_ChosenFunctionCall"; export type { Tools_ChosenToolCall } from "./models/Tools_ChosenToolCall"; +export type { Tools_CreateToolRequest } from "./models/Tools_CreateToolRequest"; export type { Tools_FunctionCallOption } from "./models/Tools_FunctionCallOption"; export type { Tools_FunctionDef } from "./models/Tools_FunctionDef"; export type { Tools_FunctionTool } from "./models/Tools_FunctionTool"; @@ -118,7 +123,8 @@ export type { Tools_Tool } from "./models/Tools_Tool"; export type { Tools_ToolResponse } from "./models/Tools_ToolResponse"; export type { Tools_ToolType } from "./models/Tools_ToolType"; export type { Tools_UpdateToolRequest } from "./models/Tools_UpdateToolRequest"; -export type { Users_CreateOrUpdateUserRequest_id } from "./models/Users_CreateOrUpdateUserRequest_id"; +export type { Users_CreateOrUpdateUserRequest } from "./models/Users_CreateOrUpdateUserRequest"; +export type { Users_CreateOrUpdateUserRequest } from "./models/Users_CreateOrUpdateUserRequest"; export type { Users_CreateUserRequest } from "./models/Users_CreateUserRequest"; export type { Users_PatchUserRequest } from "./models/Users_PatchUserRequest"; export type { Users_UpdateUserRequest } from "./models/Users_UpdateUserRequest"; @@ -126,6 +132,7 @@ export type { Users_User } from "./models/Users_User"; export { $Agents_Agent } from "./schemas/$Agents_Agent"; export { $Agents_CreateAgentRequest } from "./schemas/$Agents_CreateAgentRequest"; +export { $Agents_CreateOrUpdateAgentRequest } from "./schemas/$Agents_CreateOrUpdateAgentRequest"; export { $Agents_CreateOrUpdateAgentRequest_id } from "./schemas/$Agents_CreateOrUpdateAgentRequest_id"; export { $Agents_PatchAgentRequest } from "./schemas/$Agents_PatchAgentRequest"; export { $Agents_UpdateAgentRequest } from "./schemas/$Agents_UpdateAgentRequest"; @@ -133,6 +140,7 @@ export { $Chat_BaseChatOutput } from "./schemas/$Chat_BaseChatOutput"; export { $Chat_BaseChatResponse } from "./schemas/$Chat_BaseChatResponse"; export { $Chat_BaseTokenLogProb } from "./schemas/$Chat_BaseTokenLogProb"; export { $Chat_ChatInput } from "./schemas/$Chat_ChatInput"; +export { $Chat_ChatInputData } from "./schemas/$Chat_ChatInputData"; export { $Chat_ChatOutputChunk } from "./schemas/$Chat_ChatOutputChunk"; export { $Chat_ChatSettings } from "./schemas/$Chat_ChatSettings"; export { $Chat_ChunkChatResponse } from "./schemas/$Chat_ChunkChatResponse"; @@ -144,6 +152,7 @@ export { $Chat_GenerationPreset } from "./schemas/$Chat_GenerationPreset"; export { $Chat_LogProbResponse } from "./schemas/$Chat_LogProbResponse"; export { $Chat_MessageChatResponse } from "./schemas/$Chat_MessageChatResponse"; export { $Chat_MultipleChatOutput } from "./schemas/$Chat_MultipleChatOutput"; +export { $Chat_OpenAISettings } from "./schemas/$Chat_OpenAISettings"; export { $Chat_SingleChatOutput } from "./schemas/$Chat_SingleChatOutput"; export { $Chat_TokenLogProb } from "./schemas/$Chat_TokenLogProb"; export { $Common_identifierSafeUnicode } from "./schemas/$Common_identifierSafeUnicode"; @@ -195,6 +204,7 @@ export { $Executions_UpdateExecutionRequest } from "./schemas/$Executions_Update export { $Jobs_JobState } from "./schemas/$Jobs_JobState"; export { $Jobs_JobStatus } from "./schemas/$Jobs_JobStatus"; export { $Sessions_ContextOverflowType } from "./schemas/$Sessions_ContextOverflowType"; +export { $Sessions_CreateOrUpdateSessionRequest } from "./schemas/$Sessions_CreateOrUpdateSessionRequest"; export { $Sessions_CreateOrUpdateSessionRequest_id } from "./schemas/$Sessions_CreateOrUpdateSessionRequest_id"; export { $Sessions_CreateSessionRequest } from "./schemas/$Sessions_CreateSessionRequest"; export { $Sessions_MultiAgentMultiUserSession } from "./schemas/$Sessions_MultiAgentMultiUserSession"; @@ -222,6 +232,7 @@ export { $Tasks_WaitForInputStep } from "./schemas/$Tasks_WaitForInputStep"; export { $Tasks_YieldStep } from "./schemas/$Tasks_YieldStep"; export { $Tools_ChosenFunctionCall } from "./schemas/$Tools_ChosenFunctionCall"; export { $Tools_ChosenToolCall } from "./schemas/$Tools_ChosenToolCall"; +export { $Tools_CreateToolRequest } from "./schemas/$Tools_CreateToolRequest"; export { $Tools_FunctionCallOption } from "./schemas/$Tools_FunctionCallOption"; export { $Tools_FunctionDef } from "./schemas/$Tools_FunctionDef"; export { $Tools_FunctionTool } from "./schemas/$Tools_FunctionTool"; @@ -232,7 +243,8 @@ export { $Tools_Tool } from "./schemas/$Tools_Tool"; export { $Tools_ToolResponse } from "./schemas/$Tools_ToolResponse"; export { $Tools_ToolType } from "./schemas/$Tools_ToolType"; export { $Tools_UpdateToolRequest } from "./schemas/$Tools_UpdateToolRequest"; -export { $Users_CreateOrUpdateUserRequest_id } from "./schemas/$Users_CreateOrUpdateUserRequest_id"; +export { $Users_CreateOrUpdateUserRequest } from "./schemas/$Users_CreateOrUpdateUserRequest"; +export { $Users_CreateOrUpdateUserRequest } from "./schemas/$Users_CreateOrUpdateUserRequest"; export { $Users_CreateUserRequest } from "./schemas/$Users_CreateUserRequest"; export { $Users_PatchUserRequest } from "./schemas/$Users_PatchUserRequest"; export { $Users_UpdateUserRequest } from "./schemas/$Users_UpdateUserRequest"; diff --git a/sdks/ts/src/api/models/Agents_CreateOrUpdateAgentRequest.ts b/sdks/ts/src/api/models/Agents_CreateOrUpdateAgentRequest.ts new file mode 100644 index 000000000..19892b153 --- /dev/null +++ b/sdks/ts/src/api/models/Agents_CreateOrUpdateAgentRequest.ts @@ -0,0 +1,32 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Agents_CreateAgentRequest } from "./Agents_CreateAgentRequest"; +import type { Chat_DefaultChatSettings } from "./Chat_DefaultChatSettings"; +import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; +import type { Common_uuid } from "./Common_uuid"; +export type Agents_CreateOrUpdateAgentRequest = Agents_CreateAgentRequest & { + id: Common_uuid; + metadata?: Record; + /** + * Name of the agent + */ + name: Common_identifierSafeUnicode; + /** + * About the agent + */ + about: string; + /** + * Model name to use (gpt-4-turbo, gemini-nano etc) + */ + model: string; + /** + * Instructions for the agent + */ + instructions: string | Array; + /** + * Default settings for all sessions created by this agent + */ + default_settings?: Chat_DefaultChatSettings; +}; diff --git a/sdks/ts/src/api/models/Chat_ChatInput.ts b/sdks/ts/src/api/models/Chat_ChatInput.ts index 68672573a..d9b037889 100644 --- a/sdks/ts/src/api/models/Chat_ChatInput.ts +++ b/sdks/ts/src/api/models/Chat_ChatInput.ts @@ -2,27 +2,13 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Chat_ChatInputData } from "./Chat_ChatInputData"; import type { Chat_CompletionResponseFormat } from "./Chat_CompletionResponseFormat"; import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; import type { Common_logit_bias } from "./Common_logit_bias"; import type { Common_uuid } from "./Common_uuid"; -import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; -import type { Tools_FunctionTool } from "./Tools_FunctionTool"; -import type { Tools_NamedToolChoice } from "./Tools_NamedToolChoice"; -export type Chat_ChatInput = { - /** - * A list of new input messages comprising the conversation so far. - */ - messages: Array; - /** - * (Advanced) List of tools that are provided in addition to agent's default set of tools. - */ - tools?: Array; - /** - * Can be one of existing tools given to the agent earlier or the ones provided in this request. - */ - tool_choice?: "auto" | "none" | Tools_NamedToolChoice; +export type Chat_ChatInput = Chat_ChatInputData & { /** * Whether previous memories should be recalled or not (will be enabled in a future release) */ @@ -71,6 +57,18 @@ export type Chat_ChatInput = { * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) */ preset?: Chat_GenerationPreset; + /** + * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + repetition_penalty?: number; + /** + * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. + */ + length_penalty?: number; + /** + * Minimum probability compared to leading token to be considered + */ + min_p?: number; /** * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. */ @@ -87,16 +85,4 @@ export type Chat_ChatInput = { * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. */ top_p?: number; - /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - repetition_penalty?: number; - /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - */ - length_penalty?: number; - /** - * Minimum probability compared to leading token to be considered - */ - min_p?: number; }; diff --git a/sdks/ts/src/api/models/Chat_ChatInputData.ts b/sdks/ts/src/api/models/Chat_ChatInputData.ts new file mode 100644 index 000000000..fb11ddec2 --- /dev/null +++ b/sdks/ts/src/api/models/Chat_ChatInputData.ts @@ -0,0 +1,21 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; +import type { Tools_FunctionTool } from "./Tools_FunctionTool"; +import type { Tools_NamedToolChoice } from "./Tools_NamedToolChoice"; +export type Chat_ChatInputData = { + /** + * A list of new input messages comprising the conversation so far. + */ + messages: Array; + /** + * (Advanced) List of tools that are provided in addition to agent's default set of tools. + */ + tools?: Array; + /** + * Can be one of existing tools given to the agent earlier or the ones provided in this request. + */ + tool_choice?: "auto" | "none" | Tools_NamedToolChoice; +}; diff --git a/sdks/ts/src/api/models/Chat_ChatSettings.ts b/sdks/ts/src/api/models/Chat_ChatSettings.ts index d19fa27d2..0dd2f61cc 100644 --- a/sdks/ts/src/api/models/Chat_ChatSettings.ts +++ b/sdks/ts/src/api/models/Chat_ChatSettings.ts @@ -3,11 +3,11 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_CompletionResponseFormat } from "./Chat_CompletionResponseFormat"; -import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; +import type { Chat_DefaultChatSettings } from "./Chat_DefaultChatSettings"; import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; import type { Common_logit_bias } from "./Common_logit_bias"; import type { Common_uuid } from "./Common_uuid"; -export type Chat_ChatSettings = { +export type Chat_ChatSettings = Chat_DefaultChatSettings & { /** * Identifier of the model to be used */ @@ -40,36 +40,4 @@ export type Chat_ChatSettings = { * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) */ agent?: Common_uuid; - /** - * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - */ - preset?: Chat_GenerationPreset; - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - frequency_penalty?: number; - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - presence_penalty?: number; - /** - * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - */ - temperature?: number; - /** - * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - */ - top_p?: number; - /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - repetition_penalty?: number; - /** - * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated. - */ - length_penalty?: number; - /** - * Minimum probability compared to leading token to be considered - */ - min_p?: number; }; diff --git a/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts b/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts index e1fb07bd1..fc82c8956 100644 --- a/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts +++ b/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts @@ -3,30 +3,15 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; +import type { Chat_OpenAISettings } from "./Chat_OpenAISettings"; /** * Default settings for the chat session (also used by the agent) */ -export type Chat_DefaultChatSettings = { +export type Chat_DefaultChatSettings = Chat_OpenAISettings & { /** * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) */ preset?: Chat_GenerationPreset; - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - frequency_penalty?: number; - /** - * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. - */ - presence_penalty?: number; - /** - * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. - */ - temperature?: number; - /** - * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. - */ - top_p?: number; /** * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. */ diff --git a/sdks/ts/src/api/models/Chat_OpenAISettings.ts b/sdks/ts/src/api/models/Chat_OpenAISettings.ts new file mode 100644 index 000000000..2ab230a6f --- /dev/null +++ b/sdks/ts/src/api/models/Chat_OpenAISettings.ts @@ -0,0 +1,22 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type Chat_OpenAISettings = { + /** + * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + frequency_penalty?: number; + /** + * Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + */ + presence_penalty?: number; + /** + * What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + */ + temperature?: number; + /** + * Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both. + */ + top_p?: number; +}; diff --git a/sdks/ts/src/api/models/Sessions_CreateOrUpdateSessionRequest.ts b/sdks/ts/src/api/models/Sessions_CreateOrUpdateSessionRequest.ts new file mode 100644 index 000000000..44ebd67e4 --- /dev/null +++ b/sdks/ts/src/api/models/Sessions_CreateOrUpdateSessionRequest.ts @@ -0,0 +1,38 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_uuid } from "./Common_uuid"; +import type { Sessions_ContextOverflowType } from "./Sessions_ContextOverflowType"; +import type { Sessions_CreateSessionRequest } from "./Sessions_CreateSessionRequest"; +export type Sessions_CreateOrUpdateSessionRequest = + Sessions_CreateSessionRequest & { + id: Common_uuid; + /** + * User ID of user associated with this session + */ + user?: Common_uuid; + users?: Array; + /** + * Agent ID of agent associated with this session + */ + agent?: Common_uuid; + agents?: Array; + /** + * A specific situation that sets the background for this session + */ + situation: string; + /** + * Render system and assistant message content as jinja templates + */ + render_templates: boolean; + /** + * Threshold value for the adaptive context functionality + */ + token_budget: number | null; + /** + * Action to start on context window overflow + */ + context_overflow: Sessions_ContextOverflowType | null; + metadata?: Record; + }; diff --git a/sdks/ts/src/api/models/Tasks_TaskTool.ts b/sdks/ts/src/api/models/Tasks_TaskTool.ts index 815d7f61b..8f53afa9e 100644 --- a/sdks/ts/src/api/models/Tasks_TaskTool.ts +++ b/sdks/ts/src/api/models/Tasks_TaskTool.ts @@ -2,24 +2,10 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Common_validPythonIdentifier } from "./Common_validPythonIdentifier"; -import type { Tools_FunctionDef } from "./Tools_FunctionDef"; -import type { Tools_ToolType } from "./Tools_ToolType"; -export type Tasks_TaskTool = { +import type { Tools_CreateToolRequest } from "./Tools_CreateToolRequest"; +export type Tasks_TaskTool = Tools_CreateToolRequest & { /** * Read-only: Whether the tool was inherited or not. Only applies within tasks. */ readonly inherited?: boolean; - /** - * Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) - */ - type: Tools_ToolType; - /** - * Name of the tool (must be unique for this agent and a valid python identifier string ) - */ - name: Common_validPythonIdentifier; - function?: Tools_FunctionDef; - integration?: any; - system?: any; - api_call?: any; }; diff --git a/sdks/ts/src/api/models/Tools_CreateToolRequest.ts b/sdks/ts/src/api/models/Tools_CreateToolRequest.ts new file mode 100644 index 000000000..51024282c --- /dev/null +++ b/sdks/ts/src/api/models/Tools_CreateToolRequest.ts @@ -0,0 +1,24 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_validPythonIdentifier } from "./Common_validPythonIdentifier"; +import type { Tools_FunctionDef } from "./Tools_FunctionDef"; +import type { Tools_ToolType } from "./Tools_ToolType"; +/** + * Payload for creating a tool + */ +export type Tools_CreateToolRequest = { + /** + * Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) + */ + type: Tools_ToolType; + /** + * Name of the tool (must be unique for this agent and a valid python identifier string ) + */ + name: Common_validPythonIdentifier; + function?: Tools_FunctionDef; + integration?: any; + system?: any; + api_call?: any; +}; diff --git a/sdks/ts/src/api/models/Users_CreateOrUpdateUserRequest_id.ts b/sdks/ts/src/api/models/Users_CreateOrUpdateUserRequest.ts similarity index 74% rename from sdks/ts/src/api/models/Users_CreateOrUpdateUserRequest_id.ts rename to sdks/ts/src/api/models/Users_CreateOrUpdateUserRequest.ts index 7338d9bed..8257ad304 100644 --- a/sdks/ts/src/api/models/Users_CreateOrUpdateUserRequest_id.ts +++ b/sdks/ts/src/api/models/Users_CreateOrUpdateUserRequest.ts @@ -3,4 +3,4 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_uuid } from "./Common_uuid"; -export type Users_CreateOrUpdateUserRequest_id = Common_uuid; +export type Users_CreateOrUpdateUserRequest = Common_uuid; diff --git a/sdks/ts/src/api/schemas/$Agents_CreateOrUpdateAgentRequest.ts b/sdks/ts/src/api/schemas/$Agents_CreateOrUpdateAgentRequest.ts new file mode 100644 index 000000000..3c30c3cf3 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Agents_CreateOrUpdateAgentRequest.ts @@ -0,0 +1,71 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Agents_CreateOrUpdateAgentRequest = { + type: "all-of", + contains: [ + { + type: "Agents_CreateAgentRequest", + }, + { + properties: { + id: { + type: "Common_uuid", + isRequired: true, + }, + metadata: { + type: "dictionary", + contains: { + properties: {}, + }, + }, + name: { + type: "all-of", + description: `Name of the agent`, + contains: [ + { + type: "Common_identifierSafeUnicode", + }, + ], + isRequired: true, + }, + about: { + type: "string", + description: `About the agent`, + isRequired: true, + }, + model: { + type: "string", + description: `Model name to use (gpt-4-turbo, gemini-nano etc)`, + isRequired: true, + }, + instructions: { + type: "any-of", + description: `Instructions for the agent`, + contains: [ + { + type: "string", + }, + { + type: "array", + contains: { + type: "string", + }, + }, + ], + isRequired: true, + }, + default_settings: { + type: "all-of", + description: `Default settings for all sessions created by this agent`, + contains: [ + { + type: "Chat_DefaultChatSettings", + }, + ], + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Chat_ChatInput.ts b/sdks/ts/src/api/schemas/$Chat_ChatInput.ts index e949a3657..e74f1e0a5 100644 --- a/sdks/ts/src/api/schemas/$Chat_ChatInput.ts +++ b/sdks/ts/src/api/schemas/$Chat_ChatInput.ts @@ -3,158 +3,141 @@ /* tslint:disable */ /* eslint-disable */ export const $Chat_ChatInput = { - properties: { - messages: { - type: "array", - contains: { - type: "Entries_InputChatMLMessage", - }, - isRequired: true, - }, - tools: { - type: "array", - contains: { - type: "Tools_FunctionTool", - }, - }, - tool_choice: { - type: "any-of", - description: `Can be one of existing tools given to the agent earlier or the ones provided in this request.`, - contains: [ - { - type: "Enum", + type: "all-of", + contains: [ + { + type: "Chat_ChatInputData", + }, + { + properties: { + recall: { + type: "boolean", + description: `Whether previous memories should be recalled or not (will be enabled in a future release)`, + isReadOnly: true, + isRequired: true, }, - { - type: "Tools_NamedToolChoice", + remember: { + type: "boolean", + description: `Whether this interaction should form new memories or not (will be enabled in a future release)`, + isReadOnly: true, + isRequired: true, + }, + save: { + type: "boolean", + description: `Whether this interaction should be stored in the session history or not`, + isRequired: true, + }, + model: { + type: "all-of", + description: `Identifier of the model to be used`, + contains: [ + { + type: "Common_identifierSafeUnicode", + }, + ], + }, + stream: { + type: "boolean", + description: `Indicates if the server should stream the response as it's generated`, + isRequired: true, + }, + stop: { + type: "array", + contains: { + type: "string", + }, + }, + seed: { + type: "number", + description: `If specified, the system will make a best effort to sample deterministically for that particular seed value`, + format: "int16", + maximum: 1000, + minimum: -1, + }, + max_tokens: { + type: "number", + description: `The maximum number of tokens to generate in the chat completion`, + format: "uint32", + minimum: 1, + }, + logit_bias: { + type: "dictionary", + contains: { + type: "Common_logit_bias", + }, + }, + response_format: { + type: "all-of", + description: `Response format (set to \`json_object\` to restrict output to JSON)`, + contains: [ + { + type: "Chat_CompletionResponseFormat", + }, + ], + }, + agent: { + type: "all-of", + description: `Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)`, + contains: [ + { + type: "Common_uuid", + }, + ], + }, + preset: { + type: "all-of", + description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, + contains: [ + { + type: "Chat_GenerationPreset", + }, + ], + }, + repetition_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + }, + length_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, + format: "float", + maximum: 2, + }, + min_p: { + type: "number", + description: `Minimum probability compared to leading token to be considered`, + format: "float", + maximum: 1, + }, + frequency_penalty: { + type: "number", + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + minimum: -2, + }, + presence_penalty: { + type: "number", + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + minimum: -2, + }, + temperature: { + type: "number", + description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, + format: "float", + maximum: 5, + }, + top_p: { + type: "number", + description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, + format: "float", + maximum: 1, }, - ], - }, - recall: { - type: "boolean", - description: `Whether previous memories should be recalled or not (will be enabled in a future release)`, - isReadOnly: true, - isRequired: true, - }, - remember: { - type: "boolean", - description: `Whether this interaction should form new memories or not (will be enabled in a future release)`, - isReadOnly: true, - isRequired: true, - }, - save: { - type: "boolean", - description: `Whether this interaction should be stored in the session history or not`, - isRequired: true, - }, - model: { - type: "all-of", - description: `Identifier of the model to be used`, - contains: [ - { - type: "Common_identifierSafeUnicode", - }, - ], - }, - stream: { - type: "boolean", - description: `Indicates if the server should stream the response as it's generated`, - isRequired: true, - }, - stop: { - type: "array", - contains: { - type: "string", - }, - }, - seed: { - type: "number", - description: `If specified, the system will make a best effort to sample deterministically for that particular seed value`, - format: "int16", - maximum: 1000, - minimum: -1, - }, - max_tokens: { - type: "number", - description: `The maximum number of tokens to generate in the chat completion`, - format: "uint32", - minimum: 1, - }, - logit_bias: { - type: "dictionary", - contains: { - type: "Common_logit_bias", }, }, - response_format: { - type: "all-of", - description: `Response format (set to \`json_object\` to restrict output to JSON)`, - contains: [ - { - type: "Chat_CompletionResponseFormat", - }, - ], - }, - agent: { - type: "all-of", - description: `Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)`, - contains: [ - { - type: "Common_uuid", - }, - ], - }, - preset: { - type: "all-of", - description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, - contains: [ - { - type: "Chat_GenerationPreset", - }, - ], - }, - frequency_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - presence_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - temperature: { - type: "number", - description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, - format: "float", - maximum: 5, - }, - top_p: { - type: "number", - description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, - format: "float", - maximum: 1, - }, - repetition_penalty: { - type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - }, - length_penalty: { - type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, - format: "float", - maximum: 2, - }, - min_p: { - type: "number", - description: `Minimum probability compared to leading token to be considered`, - format: "float", - maximum: 1, - }, - }, + ], } as const; diff --git a/sdks/ts/src/api/schemas/$Chat_ChatInputData.ts b/sdks/ts/src/api/schemas/$Chat_ChatInputData.ts new file mode 100644 index 000000000..b5ed3b199 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Chat_ChatInputData.ts @@ -0,0 +1,33 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Chat_ChatInputData = { + properties: { + messages: { + type: "array", + contains: { + type: "Entries_InputChatMLMessage", + }, + isRequired: true, + }, + tools: { + type: "array", + contains: { + type: "Tools_FunctionTool", + }, + }, + tool_choice: { + type: "any-of", + description: `Can be one of existing tools given to the agent earlier or the ones provided in this request.`, + contains: [ + { + type: "Enum", + }, + { + type: "Tools_NamedToolChoice", + }, + ], + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Chat_ChatSettings.ts b/sdks/ts/src/api/schemas/$Chat_ChatSettings.ts index c370979b9..3c123c30c 100644 --- a/sdks/ts/src/api/schemas/$Chat_ChatSettings.ts +++ b/sdks/ts/src/api/schemas/$Chat_ChatSettings.ts @@ -3,116 +3,71 @@ /* tslint:disable */ /* eslint-disable */ export const $Chat_ChatSettings = { - properties: { - model: { - type: "all-of", - description: `Identifier of the model to be used`, - contains: [ - { - type: "Common_identifierSafeUnicode", + type: "all-of", + contains: [ + { + type: "Chat_DefaultChatSettings", + }, + { + properties: { + model: { + type: "all-of", + description: `Identifier of the model to be used`, + contains: [ + { + type: "Common_identifierSafeUnicode", + }, + ], }, - ], - }, - stream: { - type: "boolean", - description: `Indicates if the server should stream the response as it's generated`, - isRequired: true, - }, - stop: { - type: "array", - contains: { - type: "string", - }, - }, - seed: { - type: "number", - description: `If specified, the system will make a best effort to sample deterministically for that particular seed value`, - format: "int16", - maximum: 1000, - minimum: -1, - }, - max_tokens: { - type: "number", - description: `The maximum number of tokens to generate in the chat completion`, - format: "uint32", - minimum: 1, - }, - logit_bias: { - type: "dictionary", - contains: { - type: "Common_logit_bias", - }, - }, - response_format: { - type: "all-of", - description: `Response format (set to \`json_object\` to restrict output to JSON)`, - contains: [ - { - type: "Chat_CompletionResponseFormat", + stream: { + type: "boolean", + description: `Indicates if the server should stream the response as it's generated`, + isRequired: true, }, - ], - }, - agent: { - type: "all-of", - description: `Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)`, - contains: [ - { - type: "Common_uuid", + stop: { + type: "array", + contains: { + type: "string", + }, }, - ], - }, - preset: { - type: "all-of", - description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, - contains: [ - { - type: "Chat_GenerationPreset", + seed: { + type: "number", + description: `If specified, the system will make a best effort to sample deterministically for that particular seed value`, + format: "int16", + maximum: 1000, + minimum: -1, }, - ], - }, - frequency_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - presence_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - temperature: { - type: "number", - description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, - format: "float", - maximum: 5, - }, - top_p: { - type: "number", - description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, - format: "float", - maximum: 1, - }, - repetition_penalty: { - type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - }, - length_penalty: { - type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, - format: "float", - maximum: 2, - }, - min_p: { - type: "number", - description: `Minimum probability compared to leading token to be considered`, - format: "float", - maximum: 1, + max_tokens: { + type: "number", + description: `The maximum number of tokens to generate in the chat completion`, + format: "uint32", + minimum: 1, + }, + logit_bias: { + type: "dictionary", + contains: { + type: "Common_logit_bias", + }, + }, + response_format: { + type: "all-of", + description: `Response format (set to \`json_object\` to restrict output to JSON)`, + contains: [ + { + type: "Chat_CompletionResponseFormat", + }, + ], + }, + agent: { + type: "all-of", + description: `Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions)`, + contains: [ + { + type: "Common_uuid", + }, + ], + }, + }, }, - }, + ], } as const; diff --git a/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts b/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts index efef64d63..253273536 100644 --- a/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts +++ b/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts @@ -3,60 +3,42 @@ /* tslint:disable */ /* eslint-disable */ export const $Chat_DefaultChatSettings = { + type: "all-of", description: `Default settings for the chat session (also used by the agent)`, - properties: { - preset: { - type: "all-of", - description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, - contains: [ - { - type: "Chat_GenerationPreset", + contains: [ + { + type: "Chat_OpenAISettings", + }, + { + properties: { + preset: { + type: "all-of", + description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, + contains: [ + { + type: "Chat_GenerationPreset", + }, + ], }, - ], - }, - frequency_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - presence_penalty: { - type: "number", - description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - minimum: -2, - }, - temperature: { - type: "number", - description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, - format: "float", - maximum: 5, - }, - top_p: { - type: "number", - description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, - format: "float", - maximum: 1, - }, - repetition_penalty: { - type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, - format: "float", - maximum: 2, - }, - length_penalty: { - type: "number", - description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, - format: "float", - maximum: 2, - }, - min_p: { - type: "number", - description: `Minimum probability compared to leading token to be considered`, - format: "float", - maximum: 1, + repetition_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + }, + length_penalty: { + type: "number", + description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize number of tokens generated.`, + format: "float", + maximum: 2, + }, + min_p: { + type: "number", + description: `Minimum probability compared to leading token to be considered`, + format: "float", + maximum: 1, + }, + }, }, - }, + ], } as const; diff --git a/sdks/ts/src/api/schemas/$Chat_OpenAISettings.ts b/sdks/ts/src/api/schemas/$Chat_OpenAISettings.ts new file mode 100644 index 000000000..61a12ed7e --- /dev/null +++ b/sdks/ts/src/api/schemas/$Chat_OpenAISettings.ts @@ -0,0 +1,34 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Chat_OpenAISettings = { + properties: { + frequency_penalty: { + type: "number", + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + minimum: -2, + }, + presence_penalty: { + type: "number", + description: `Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, + format: "float", + maximum: 2, + minimum: -2, + }, + temperature: { + type: "number", + description: `What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.`, + format: "float", + maximum: 5, + }, + top_p: { + type: "number", + description: `Defaults to 1 An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. We generally recommend altering this or temperature but not both.`, + format: "float", + maximum: 1, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Sessions_CreateOrUpdateSessionRequest.ts b/sdks/ts/src/api/schemas/$Sessions_CreateOrUpdateSessionRequest.ts new file mode 100644 index 000000000..16720dd71 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Sessions_CreateOrUpdateSessionRequest.ts @@ -0,0 +1,84 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Sessions_CreateOrUpdateSessionRequest = { + type: "all-of", + contains: [ + { + type: "Sessions_CreateSessionRequest", + }, + { + properties: { + id: { + type: "Common_uuid", + isRequired: true, + }, + user: { + type: "all-of", + description: `User ID of user associated with this session`, + contains: [ + { + type: "Common_uuid", + }, + ], + }, + users: { + type: "array", + contains: { + type: "Common_uuid", + }, + }, + agent: { + type: "all-of", + description: `Agent ID of agent associated with this session`, + contains: [ + { + type: "Common_uuid", + }, + ], + }, + agents: { + type: "array", + contains: { + type: "Common_uuid", + }, + }, + situation: { + type: "string", + description: `A specific situation that sets the background for this session`, + isRequired: true, + }, + render_templates: { + type: "boolean", + description: `Render system and assistant message content as jinja templates`, + isRequired: true, + }, + token_budget: { + type: "number", + description: `Threshold value for the adaptive context functionality`, + isRequired: true, + isNullable: true, + format: "uint16", + }, + context_overflow: { + type: "one-of", + description: `Action to start on context window overflow`, + contains: [ + { + type: "Sessions_ContextOverflowType", + }, + ], + isRequired: true, + isNullable: true, + }, + metadata: { + type: "dictionary", + contains: { + properties: {}, + }, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_TaskTool.ts b/sdks/ts/src/api/schemas/$Tasks_TaskTool.ts index f13fe0486..f329deb7c 100644 --- a/sdks/ts/src/api/schemas/$Tasks_TaskTool.ts +++ b/sdks/ts/src/api/schemas/$Tasks_TaskTool.ts @@ -3,43 +3,19 @@ /* tslint:disable */ /* eslint-disable */ export const $Tasks_TaskTool = { - properties: { - inherited: { - type: "boolean", - description: `Read-only: Whether the tool was inherited or not. Only applies within tasks.`, - isReadOnly: true, + type: "all-of", + contains: [ + { + type: "Tools_CreateToolRequest", }, - type: { - type: "all-of", - description: `Whether this tool is a \`function\`, \`api_call\`, \`system\` etc. (Only \`function\` tool supported right now)`, - contains: [ - { - type: "Tools_ToolType", + { + properties: { + inherited: { + type: "boolean", + description: `Read-only: Whether the tool was inherited or not. Only applies within tasks.`, + isReadOnly: true, }, - ], - isRequired: true, + }, }, - name: { - type: "all-of", - description: `Name of the tool (must be unique for this agent and a valid python identifier string )`, - contains: [ - { - type: "Common_validPythonIdentifier", - }, - ], - isRequired: true, - }, - function: { - type: "Tools_FunctionDef", - }, - integration: { - properties: {}, - }, - system: { - properties: {}, - }, - api_call: { - properties: {}, - }, - }, + ], } as const; diff --git a/sdks/ts/src/api/schemas/$Tools_CreateToolRequest.ts b/sdks/ts/src/api/schemas/$Tools_CreateToolRequest.ts new file mode 100644 index 000000000..bea3b4740 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tools_CreateToolRequest.ts @@ -0,0 +1,41 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tools_CreateToolRequest = { + description: `Payload for creating a tool`, + properties: { + type: { + type: "all-of", + description: `Whether this tool is a \`function\`, \`api_call\`, \`system\` etc. (Only \`function\` tool supported right now)`, + contains: [ + { + type: "Tools_ToolType", + }, + ], + isRequired: true, + }, + name: { + type: "all-of", + description: `Name of the tool (must be unique for this agent and a valid python identifier string )`, + contains: [ + { + type: "Common_validPythonIdentifier", + }, + ], + isRequired: true, + }, + function: { + type: "Tools_FunctionDef", + }, + integration: { + properties: {}, + }, + system: { + properties: {}, + }, + api_call: { + properties: {}, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Users_CreateOrUpdateUserRequest_id.ts b/sdks/ts/src/api/schemas/$Users_CreateOrUpdateUserRequest.ts similarity index 75% rename from sdks/ts/src/api/schemas/$Users_CreateOrUpdateUserRequest_id.ts rename to sdks/ts/src/api/schemas/$Users_CreateOrUpdateUserRequest.ts index 47f504e4c..3edb83c22 100644 --- a/sdks/ts/src/api/schemas/$Users_CreateOrUpdateUserRequest_id.ts +++ b/sdks/ts/src/api/schemas/$Users_CreateOrUpdateUserRequest.ts @@ -2,6 +2,6 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export const $Users_CreateOrUpdateUserRequest_id = { +export const $Users_CreateOrUpdateUserRequest = { type: "Common_uuid", } as const; diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index b49a835ee..70cb5c0aa 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -1324,7 +1324,7 @@ export class DefaultService { requestBody, }: { id: Common_uuid; - requestBody: Users_UpdateUserRequest; + requestBody: Users_CreateUserRequest; }): CancelablePromise { return this.httpRequest.request({ method: "POST", diff --git a/typespec/agents/models.tsp b/typespec/agents/models.tsp index 3d8642d2e..0e5e17b62 100644 --- a/typespec/agents/models.tsp +++ b/typespec/agents/models.tsp @@ -46,11 +46,12 @@ model UpdateAgentRequest { model PatchAgentRequest is UpdateAgentRequest {} /** Payload for creating a agent (and associated documents) */ +@withVisibility("create") model CreateAgentRequest { ...UpdateAgentRequest; } -model CreateOrUpdateAgentRequest { +model CreateOrUpdateAgentRequest extends CreateAgentRequest { @path id: uuid; ...UpdateAgentRequest; diff --git a/typespec/chat/models.tsp b/typespec/chat/models.tsp index 320f3e489..513e86a39 100644 --- a/typespec/chat/models.tsp +++ b/typespec/chat/models.tsp @@ -140,15 +140,13 @@ model vLLMSettings { } /** Default settings for the chat session (also used by the agent) */ -model DefaultChatSettings { +model DefaultChatSettings extends OpenAISettings { ...GenerationPresetSettings; - ...OpenAISettings; ...vLLMSettings; } -model ChatSettings { +model ChatSettings extends DefaultChatSettings { ...CommonChatSettings; - ...DefaultChatSettings; } /** Usage statistics for the completion request */ @@ -179,8 +177,7 @@ model ChatInputData { tool_choice?: ToolChoiceOption; } -model ChatInput { - ...ChatInputData; +model ChatInput extends ChatInputData { ...MemoryAccessOptions; ...ChatSettings; } diff --git a/typespec/sessions/models.tsp b/typespec/sessions/models.tsp index 53ff574f6..27c1179d9 100644 --- a/typespec/sessions/models.tsp +++ b/typespec/sessions/models.tsp @@ -150,7 +150,7 @@ model CreateSessionRequest { } @withVisibility("create", "update") -model CreateOrUpdateSessionRequest { +model CreateOrUpdateSessionRequest extends CreateSessionRequest { @path id: uuid; diff --git a/typespec/tasks/models.tsp b/typespec/tasks/models.tsp index ad29e73d5..7cde91759 100644 --- a/typespec/tasks/models.tsp +++ b/typespec/tasks/models.tsp @@ -126,12 +126,10 @@ model Workflow { steps: WorkflowStep[]; } -model TaskTool { +model TaskTool extends CreateToolRequest{ /** Read-only: Whether the tool was inherited or not. Only applies within tasks. */ @visibility("read") inherited?: boolean = false; - - ...CreateToolRequest; } /** Object describing a Task */ diff --git a/typespec/users/models.tsp b/typespec/users/models.tsp index 3bcd691ae..de7baadef 100644 --- a/typespec/users/models.tsp +++ b/typespec/users/models.tsp @@ -35,12 +35,9 @@ model UpdateUserRequest { model PatchUserRequest is UpdateUserRequest {} /** Payload for creating a user (and associated documents) */ -model CreateUserRequest { - ...UpdateUserRequest; -} +model CreateUserRequest is UpdateUserRequest {} -model CreateOrUpdateUserRequest { +model CreateOrUpdateUserRequest extends CreateUserRequest { @path id: uuid; - ...UpdateUserRequest; } \ No newline at end of file From b813353ed1853d360f8a78d6bb1837426d7111d5 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 11 Aug 2024 17:42:14 -0400 Subject: [PATCH 031/110] feat(agents-api): Add litellm client Signed-off-by: Diwank Tomer --- .env.example | 35 ++++++++----- agents-api/agents_api/clients/embed.py | 6 +-- agents-api/agents_api/clients/litellm.py | 8 +++ agents-api/agents_api/clients/model.py | 10 ---- .../agents_api/clients/worker/worker.py | 4 +- .../agents_api/common/protocol/sessions.py | 8 ++- agents-api/agents_api/env.py | 52 ++++++++++--------- .../models/docs/search_docs_hybrid.py | 3 -- agents-api/agents_api/models/utils.py | 11 ++-- 9 files changed, 77 insertions(+), 60 deletions(-) create mode 100644 agents-api/agents_api/clients/litellm.py delete mode 100644 agents-api/agents_api/clients/model.py diff --git a/.env.example b/.env.example index ce2d956a4..ce8220e01 100644 --- a/.env.example +++ b/.env.example @@ -6,25 +6,18 @@ COZO_HOST=http://memory-store:9070 COZO_PORT=9070 COZO_ROCKSDB_DIR=cozo.db DTYPE=float16 -EMBEDDING_SERVICE_URL=http://text-embeddings-inference/embed +EMBEDDING_SERVICE_BASE=http://text-embeddings-inference +EMBEDDING_SERVICE_URL=${EMBEDDING_SERVICE_BASE}/embed GATEWAY_PORT=80 GPU_MEMORY_UTILIZATION=0.90 -HF_TOKEN="" -HUGGING_FACE_HUB_TOKEN="" +HF_TOKEN= +HUGGING_FACE_HUB_TOKEN= JWT_SHARED_KEY= MAX_MODEL_LEN=8192 MAX_NUM_SEQS=1 MNT_DIR=/data -MODEL_API_KEY=myauthkey -MODEL_API_KEY_HEADER_NAME=Authorization -MODEL_API_URL=http://model-serving:8000 -MODEL_INFERENCE_URL=http://model-serving:8000/v1 -MODEL_ID=BAAI/bge-m3 - -# MODEL_NAME="OpenPipe/Hermes-2-Theta-Llama-3-8B-32k" -MODEL_NAME="julep-ai/Hermes-2-Theta-Llama-3-8B" SKIP_CHECK_DEVELOPER_HEADERS=true SUMMARIZATION_TOKENS_THRESHOLD=2048 @@ -40,4 +33,22 @@ WORKER_URL=temporal:7233 AGENTS_API_DEBUG=false OPENAI_API_KEY= -ANTHROPIC_API_KEY= \ No newline at end of file +ANTHROPIC_API_KEY= +GROQ_API_KEY= +CLOUDFLARE_API_KEY= +CLOUDFLARE_ACCOUNT_ID= +NVIDIA_NIM_API_KEY= +GITHUB_API_KEY= +VOYAGE_API_KEY= +GOOGLE_APPLICATION_CREDENTIALS= + +LITELLM_URL=http://litellm:4000 +POSTGRES_DB=litellm +POSTGRES_USER=llmproxy +POSTGRES_PASSWORD= +LITELLM_DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@litellm-db:5432/${POSTGRES_DB} +LITELLM_MASTER_KEY= +LITELLM_REDIS_HOST=litellm-redis +LITELLM_REDIS_PORT=6379 +LITELLM_REDIS_PASSWORD= +REDIS_ARGS="--requirepass ${LITELLM_REDIS_PASSWORD}" \ No newline at end of file diff --git a/agents-api/agents_api/clients/embed.py b/agents-api/agents_api/clients/embed.py index 1176585a8..b9412f485 100644 --- a/agents-api/agents_api/clients/embed.py +++ b/agents-api/agents_api/clients/embed.py @@ -1,12 +1,12 @@ import httpx -from ..env import embedding_model_id, embedding_service_url, truncate_embed_text +from ..env import embedding_model_id, embedding_service_base, truncate_embed_text async def embed( inputs: list[str], join_inputs=False, - embedding_service_url: str = embedding_service_url, + embedding_service_url: str = embedding_service_base + "/embed", embedding_model_name: str = embedding_model_id, ) -> list[list[float]]: async with httpx.AsyncClient(timeout=30) as client: @@ -17,7 +17,7 @@ async def embed( }, json={ "inputs": "\n\n".join(inputs) if join_inputs else inputs, - "normalize": True, + # # FIXME: We should control the truncation ourselves and truncate before sending "truncate": truncate_embed_text, "model_id": embedding_model_name, diff --git a/agents-api/agents_api/clients/litellm.py b/agents-api/agents_api/clients/litellm.py new file mode 100644 index 000000000..61aa53327 --- /dev/null +++ b/agents-api/agents_api/clients/litellm.py @@ -0,0 +1,8 @@ +from functools import partial +from litellm import acompletion as _acompletion + +from ..env import litellm_master_key, litellm_url + +__all__ = ["acompletion"] + +acompletion = partial(_acompletion, api_base=litellm_url, api_key=litellm_master_key) \ No newline at end of file diff --git a/agents-api/agents_api/clients/model.py b/agents-api/agents_api/clients/model.py deleted file mode 100644 index 648cc8d57..000000000 --- a/agents-api/agents_api/clients/model.py +++ /dev/null @@ -1,10 +0,0 @@ -from openai import AsyncOpenAI - -from ..env import model_api_key, model_inference_url, openai_api_key - -openai_client = AsyncOpenAI(api_key=openai_api_key) - -julep_client = AsyncOpenAI( - base_url=model_inference_url, - api_key=model_api_key, -) diff --git a/agents-api/agents_api/clients/worker/worker.py b/agents-api/agents_api/clients/worker/worker.py index d7bcacc2b..1deb8d1c3 100644 --- a/agents-api/agents_api/clients/worker/worker.py +++ b/agents-api/agents_api/clients/worker/worker.py @@ -1,6 +1,6 @@ import httpx -from agents_api.env import worker_url +from agents_api.env import temporal_worker_url from .types import ( MemoryManagementTask, @@ -16,7 +16,7 @@ async def add_summarization_task(data: MemoryManagementTaskArgs): ) await client.post( - f"{worker_url}/task", + f"{temporal_worker_url}/task", headers={"Content-Type": "json"}, data=data.model_dump_json(), ) diff --git a/agents-api/agents_api/common/protocol/sessions.py b/agents-api/agents_api/common/protocol/sessions.py index c2376f8dd..ea52da481 100644 --- a/agents-api/agents_api/common/protocol/sessions.py +++ b/agents-api/agents_api/common/protocol/sessions.py @@ -9,6 +9,7 @@ from ...autogen.openapi_model import ( Agent, + ChatInput, ChatSettings, Session, Tool, @@ -65,17 +66,20 @@ def get_active_agent(self) -> Agent: return self.agents[0] - def merge_settings(self, request_settings: ChatSettings): + def merge_settings(self, chat_input: ChatInput) -> ChatSettings: + request_settings = ChatSettings.model_validate(chat_input) active_agent = self.get_active_agent() default_settings = active_agent.default_settings - self.settings = ChatSettings( + self.settings = settings = ChatSettings( **{ **default_settings.model_dump(), **request_settings.model_dump(exclude_unset=True), } ) + return settings + def get_active_tools(self) -> list[Tool]: """ Get the active toolset from the session data. diff --git a/agents-api/agents_api/env.py b/agents-api/agents_api/env.py index 6ba8f0b90..227103f6d 100644 --- a/agents-api/agents_api/env.py +++ b/agents-api/agents_api/env.py @@ -11,27 +11,24 @@ # Initialize the Env object for environment variable parsing. env = Env() -# Debug mode + +# Debug +# ----- debug: bool = env.bool("AGENTS_API_DEBUG", default=False) +sentry_dsn: str = env.str("SENTRY_DSN", default=None) -# Base URL for the COZO service. Defaults to the local development URL if not specified. + +# Cozo +# ---- cozo_host: str = env.str("COZO_HOST", default="http://127.0.0.1:9070") cozo_auth: str = env.str("COZO_AUTH_TOKEN", default=None) -model_api_key: str = env.str("MODEL_API_KEY", default=None) -model_inference_url: str = env.str("MODEL_INFERENCE_URL", default=None) -openai_api_key: str = env.str("OPENAI_API_KEY", default="") summarization_model_name: str = env.str( "SUMMARIZATION_MODEL_NAME", default="gpt-4-turbo" ) -worker_url: str = env.str("WORKER_URL", default=None) - -sentry_dsn: str = env.str("SENTRY_DSN", default=None) -# Temporal -temporal_endpoint = env.str("TEMPORAL_ENDPOINT", default="localhost:7233") -temporal_task_queue = env.str("TEMPORAL_TASK_QUEUE", default="memory-task-queue") -# auth +# Auth +# ---- _random_generated_key = "".join(str(random.randint(0, 9)) for _ in range(32)) api_key: str = env.str("AGENTS_API_KEY", _random_generated_key) @@ -43,45 +40,52 @@ "SKIP_CHECK_DEVELOPER_HEADERS", default=False ) -embedding_service_url: str = env.str( - "EMBEDDING_SERVICE_URL", default="http://0.0.0.0:8083/embed" -) +# Litellm API +# ----------- +litellm_url: str = env.str("LITELLM_URL", default="http://0.0.0.0:4000") +litellm_master_key: str = env.str("LITELLM_MASTER_KEY", default="") -embedding_model_id: str = env.str("EMBEDDING_MODEL_ID", default="BAAI/bge-m3") +# Embedding service +# ----------------- +embedding_service_base: str = env.str( + "EMBEDDING_SERVICE_BASE", default="http://0.0.0.0:8082" +) +embedding_model_id: str = env.str( + "EMBEDDING_MODEL_ID", default="Alibaba-NLP/gte-large-en-v1.5" +) truncate_embed_text: bool = env.bool("TRUNCATE_EMBED_TEXT", default=False) + # Temporal +# -------- temporal_worker_url: str = env.str("TEMPORAL_WORKER_URL", default="localhost:7233") temporal_namespace: str = env.str("TEMPORAL_NAMESPACE", default="default") temporal_client_cert: str = env.str("TEMPORAL_CLIENT_CERT", default=None) temporal_private_key: str = env.str("TEMPORAL_PRIVATE_KEY", default=None) +temporal_endpoint = env.str("TEMPORAL_ENDPOINT", default="localhost:7233") +temporal_task_queue = env.str("TEMPORAL_TASK_QUEUE", default="memory-task-queue") -# Consolidate environment variables into a dictionary for easy access and debugging. + +# Consolidate environment variables environment = dict( debug=debug, cozo_host=cozo_host, cozo_auth=cozo_auth, - worker_url=worker_url, sentry_dsn=sentry_dsn, temporal_endpoint=temporal_endpoint, temporal_task_queue=temporal_task_queue, api_key=api_key, api_key_header_name=api_key_header_name, skip_check_developer_headers=skip_check_developer_headers, - embedding_service_url=embedding_service_url, + embedding_service_base=embedding_service_base, truncate_embed_text=truncate_embed_text, temporal_worker_url=temporal_worker_url, temporal_namespace=temporal_namespace, - openai_api_key=openai_api_key, - docs_embedding_service_url=embedding_service_url, embedding_model_id=embedding_model_id, ) -if openai_api_key == "": - print("OpenAI API key not found. OpenAI API will not be enabled.") - if debug: # Print the loaded environment variables for debugging purposes. print("Environment variables:") diff --git a/agents-api/agents_api/models/docs/search_docs_hybrid.py b/agents-api/agents_api/models/docs/search_docs_hybrid.py index 82f550ac9..52ae76277 100644 --- a/agents-api/agents_api/models/docs/search_docs_hybrid.py +++ b/agents-api/agents_api/models/docs/search_docs_hybrid.py @@ -100,7 +100,6 @@ def search_docs_hybrid( alpha: float = 0.7, # Weight of the embedding search results (this is a good default) embed_search_options: dict = {}, text_search_options: dict = {}, - **kwargs, ) -> list[DocReference]: # TODO: We should probably parallelize these queries text_results = search_docs_by_text( @@ -110,7 +109,6 @@ def search_docs_hybrid( query=query, k=2 * k, **text_search_options, - **kwargs, ) embedding_results = search_docs_by_embedding( @@ -120,7 +118,6 @@ def search_docs_hybrid( query_embedding=query_embedding, k=2 * k, **embed_search_options, - **kwargs, ) return dbsf_fuse(text_results, embedding_results, alpha)[:k] diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index 126d797a3..73c3f3e0b 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -11,6 +11,7 @@ P = ParamSpec("P") T = TypeVar("T") +ModelT = TypeVar("ModelT", bound=BaseModel) def fix_uuid( @@ -175,13 +176,13 @@ def wrapper(*args: P.args, client=None, **kwargs: P.kwargs) -> pd.DataFrame: def wrap_in_class( - cls: Type[BaseModel] | Callable[..., BaseModel], + cls: Type[ModelT] | Callable[..., ModelT], one: bool = False, transform: Callable[[dict], dict] | None = None, ): def decorator(func: Callable[P, pd.DataFrame]): @wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs): + def wrapper(*args: P.args, **kwargs: P.kwargs) -> ModelT | list[ModelT]: df = func(*args, **kwargs) # Convert df to list of dicts @@ -192,9 +193,11 @@ def wrapper(*args: P.args, **kwargs: P.kwargs): if one: assert len(data) >= 1, "Expected one result, got none" - return cls(**transform(data[0])) + obj: ModelT = cls(**transform(data[0])) + return obj - return [cls(**item) for item in map(transform, data)] + objs: list[ModelT] = [cls(**item) for item in map(transform, data)] + return objs # Set the wrapped function as an attribute of the wrapper, # forwards the __wrapped__ attribute if it exists. From 7043cdddeb2734b84574d29334a7581f0a06e3ed Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 11 Aug 2024 17:42:47 -0400 Subject: [PATCH 032/110] feat(model-serving): Remove model-serving container Signed-off-by: Diwank Tomer --- .github/workflows/lint-and-format.yml | 2 +- .github/workflows/push-to-hub.yml | 1 - .../agents_api/activities/embed_docs.py | 12 +- .../agents_api/activities/summarization.py | 13 +- agents-api/agents_api/clients/litellm.py | 13 +- .../agents_api/common/protocol/sessions.py | 1 + .../agents_api/embed_models_registry.py | 161 - agents-api/agents_api/model_registry.py | 155 +- agents-api/agents_api/rec_sum/generate.py | 12 +- docker-compose.yml | 1 - gateway/docker-compose.yml | 3 - model-serving/.gitignore | 163 - model-serving/.tool-versions | 1 - model-serving/Dockerfile | 12 - model-serving/README.md | 43 - .../artifacts/function_classifier.bin | Bin 2552804 -> 0 bytes model-serving/artifacts/nous-llama-fix.ipynb | 278 - model-serving/docker-compose.yml | 45 - model-serving/modal_mixtral.py | 125 - model-serving/modal_production.py | 120 - model-serving/modal_staging.py | 121 - model-serving/model_api/__init__.py | 0 model-serving/model_api/__main__.py | 18 - model-serving/model_api/chat_template.jinja | 159 - .../model_api/conversion/__init__.py | 0 .../model_api/conversion/conversions.py | 233 - .../model_api/conversion/datatypes.py | 19 - .../model_api/conversion/exceptions.py | 17 - .../model_api/conversion/test_conversions.py | 813 --- .../model_api/dependencies/__init__.py | 0 model-serving/model_api/dependencies/auth.py | 19 - .../model_api/dependencies/developer.py | 41 - .../model_api/dependencies/exceptions.py | 2 - model-serving/model_api/env.py | 36 - .../model_api/function_classifier.py | 50 - model-serving/model_api/logger.py | 9 - model-serving/model_api/logits_processors.py | 55 - model-serving/model_api/metrics.py | 121 - model-serving/model_api/protocol.py | 479 -- model-serving/model_api/tokens.py | 17 - model-serving/model_api/utils.py | 101 - model-serving/model_api/web.py | 1022 --- model-serving/poetry.lock | 6270 ----------------- model-serving/pyproject.toml | 64 - model-serving/test_chat_template.py | 727 -- model-serving/tests/__init__.py | 0 model-serving/tests/fixtures.py | 44 - model-serving/tests/test_chat_completions.py | 691 -- model-serving/tests/test_completions.py | 263 - model-serving/update_tokenizer_template.py | 34 - 50 files changed, 22 insertions(+), 12564 deletions(-) delete mode 100644 agents-api/agents_api/embed_models_registry.py delete mode 100644 model-serving/.gitignore delete mode 100644 model-serving/.tool-versions delete mode 100644 model-serving/Dockerfile delete mode 100644 model-serving/README.md delete mode 100644 model-serving/artifacts/function_classifier.bin delete mode 100644 model-serving/artifacts/nous-llama-fix.ipynb delete mode 100644 model-serving/docker-compose.yml delete mode 100644 model-serving/modal_mixtral.py delete mode 100644 model-serving/modal_production.py delete mode 100644 model-serving/modal_staging.py delete mode 100644 model-serving/model_api/__init__.py delete mode 100644 model-serving/model_api/__main__.py delete mode 100644 model-serving/model_api/chat_template.jinja delete mode 100644 model-serving/model_api/conversion/__init__.py delete mode 100644 model-serving/model_api/conversion/conversions.py delete mode 100644 model-serving/model_api/conversion/datatypes.py delete mode 100644 model-serving/model_api/conversion/exceptions.py delete mode 100644 model-serving/model_api/conversion/test_conversions.py delete mode 100644 model-serving/model_api/dependencies/__init__.py delete mode 100644 model-serving/model_api/dependencies/auth.py delete mode 100644 model-serving/model_api/dependencies/developer.py delete mode 100644 model-serving/model_api/dependencies/exceptions.py delete mode 100644 model-serving/model_api/env.py delete mode 100644 model-serving/model_api/function_classifier.py delete mode 100644 model-serving/model_api/logger.py delete mode 100644 model-serving/model_api/logits_processors.py delete mode 100644 model-serving/model_api/metrics.py delete mode 100644 model-serving/model_api/protocol.py delete mode 100644 model-serving/model_api/tokens.py delete mode 100644 model-serving/model_api/utils.py delete mode 100644 model-serving/model_api/web.py delete mode 100644 model-serving/poetry.lock delete mode 100644 model-serving/pyproject.toml delete mode 100644 model-serving/test_chat_template.py delete mode 100644 model-serving/tests/__init__.py delete mode 100644 model-serving/tests/fixtures.py delete mode 100644 model-serving/tests/test_chat_completions.py delete mode 100644 model-serving/tests/test_completions.py delete mode 100644 model-serving/update_tokenizer_template.py diff --git a/.github/workflows/lint-and-format.yml b/.github/workflows/lint-and-format.yml index 88ae16211..a58ec8737 100644 --- a/.github/workflows/lint-and-format.yml +++ b/.github/workflows/lint-and-format.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: - directory: [agents-api, model-serving, sdks/python] + directory: [agents-api, sdks/python] steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/push-to-hub.yml b/.github/workflows/push-to-hub.yml index 80e76601c..9e8b6a428 100644 --- a/.github/workflows/push-to-hub.yml +++ b/.github/workflows/push-to-hub.yml @@ -131,7 +131,6 @@ jobs: service-directory: - gateway - memory-store - # - model-serving steps: - uses: actions/checkout@v4 diff --git a/agents-api/agents_api/activities/embed_docs.py b/agents-api/agents_api/activities/embed_docs.py index e2316d59e..b486c3af1 100644 --- a/agents-api/agents_api/activities/embed_docs.py +++ b/agents-api/agents_api/activities/embed_docs.py @@ -1,11 +1,8 @@ from pydantic import UUID4 from temporalio import activity -from agents_api.embed_models_registry import EmbeddingModel -from agents_api.env import embedding_model_id -from agents_api.models.docs.embed_docs import ( - embed_docs_snippets_query, -) +from agents_api.clients.embed import embed +from agents_api.models.docs.embed_snippets import embed_snippets as embed_snippets_query snippet_embed_instruction = "Encode this passage for retrieval: " @@ -13,8 +10,7 @@ @activity.defn async def embed_docs(doc_id: UUID4, title: str, content: list[str]) -> None: indices, snippets = list(zip(*enumerate(content))) - model = EmbeddingModel.from_model_name(embedding_model_id) - embeddings = await model.embed( + embeddings = await embed( [ { "instruction": snippet_embed_instruction, @@ -24,7 +20,7 @@ async def embed_docs(doc_id: UUID4, title: str, content: list[str]) -> None: ] ) - embed_docs_snippets_query( + embed_snippets_query( doc_id=doc_id, snippet_indices=indices, embeddings=embeddings, diff --git a/agents-api/agents_api/activities/summarization.py b/agents-api/agents_api/activities/summarization.py index 2e61ab9e4..4d2b37f8c 100644 --- a/agents-api/agents_api/activities/summarization.py +++ b/agents-api/agents_api/activities/summarization.py @@ -6,7 +6,6 @@ from uuid import UUID import pandas as pd -from litellm import acompletion from temporalio import activity from agents_api.common.protocol.entries import Entry @@ -19,8 +18,8 @@ from agents_api.rec_sum.summarize import summarize_messages from agents_api.rec_sum.trim import trim_messages -from ..env import model_api_key, model_inference_url, summarization_model_name -from ..model_registry import LOCAL_MODELS +from ..clients.litellm import acompletion +from ..env import summarization_model_name # TODO: remove stubs @@ -149,12 +148,6 @@ async def run_prompt( parser: Callable[[str], str] = lambda x: x, **kwargs, ) -> str: - api_base = None - api_key = None - if model in LOCAL_MODELS: - api_base = model_inference_url - api_key = model_api_key - model = f"openai/{model}" prompt = make_prompt(dialog, previous_memories, **kwargs) response = await acompletion( model=model, @@ -168,8 +161,6 @@ async def run_prompt( temperature=temperature, stop=["<", "<|"], stream=False, - api_base=api_base, - api_key=api_key, ) content = response.choices[0].message.content diff --git a/agents-api/agents_api/clients/litellm.py b/agents-api/agents_api/clients/litellm.py index 61aa53327..d1ae7018c 100644 --- a/agents-api/agents_api/clients/litellm.py +++ b/agents-api/agents_api/clients/litellm.py @@ -1,8 +1,17 @@ -from functools import partial +from functools import wraps + from litellm import acompletion as _acompletion from ..env import litellm_master_key, litellm_url __all__ = ["acompletion"] -acompletion = partial(_acompletion, api_base=litellm_url, api_key=litellm_master_key) \ No newline at end of file + +@wraps(_acompletion) +async def acompletion(*, model: str, **kwargs): + return await _acompletion( + model=f"openai/{model}", # This is here because litellm proxy expects this format + **kwargs, + api_base=litellm_url, + api_key=litellm_master_key, + ) diff --git a/agents-api/agents_api/common/protocol/sessions.py b/agents-api/agents_api/common/protocol/sessions.py index ea52da481..73c71b365 100644 --- a/agents-api/agents_api/common/protocol/sessions.py +++ b/agents-api/agents_api/common/protocol/sessions.py @@ -73,6 +73,7 @@ def merge_settings(self, chat_input: ChatInput) -> ChatSettings: self.settings = settings = ChatSettings( **{ + "model": active_agent.model, **default_settings.model_dump(), **request_settings.model_dump(exclude_unset=True), } diff --git a/agents-api/agents_api/embed_models_registry.py b/agents-api/agents_api/embed_models_registry.py deleted file mode 100644 index fe125fefb..000000000 --- a/agents-api/agents_api/embed_models_registry.py +++ /dev/null @@ -1,161 +0,0 @@ -from dataclasses import dataclass -from typing import Any, TypedDict - -import numpy as np -import tiktoken -from tokenizers import Tokenizer - -from agents_api.clients.embed import embed -from agents_api.clients.model import openai_client -from agents_api.env import embedding_service_url -from agents_api.exceptions import ( - ModelNotSupportedError, - PromptTooBigError, - UnknownTokenizerError, -) - - -def normalize_l2(x): - x = np.array(x) - if x.ndim == 1: - norm = np.linalg.norm(x) - if norm == 0: - return x - return x / norm - else: - norm = np.linalg.norm(x, 2, axis=1, keepdims=True) - return np.where(norm == 0, x, x / norm) - - -class EmbeddingInput(TypedDict): - instruction: str | None - text: str - - -@dataclass -class EmbeddingModel: - embedding_service_url: str | None - embedding_provider: str - embedding_model_name: str - original_embedding_dimensions: int - output_embedding_dimensions: int - context_window: int - tokenizer: Any - - @classmethod - def from_model_name(cls, model_name: str): - try: - return _embedding_model_registry[model_name] - except KeyError: - raise ModelNotSupportedError(model_name) - - def _token_count(self, text: str) -> int: - tokenize = getattr(self.tokenizer, "tokenize", None) - if tokenize: - return len(tokenize(text)) - - encode = getattr(self.tokenizer, "encode", None) - if encode: - return len(encode(text)) - - raise UnknownTokenizerError - - def preprocess(self, inputs: list[EmbeddingInput]) -> list[str]: - """Maybe use this function from embed() to truncate (if needed) or raise an error""" - result: list[str] = [] - - for i in inputs: - instruction = i.get("instruction", "") - sep = " " if len(instruction) else "" - result.append(f"{instruction}{sep}{i['text']}") - - token_count = self._token_count(" ".join(result)) - if token_count > self.context_window: - raise PromptTooBigError(token_count, self.context_window) - - return result - - async def embed( - self, inputs: list[EmbeddingInput] - ) -> list[np.ndarray | list[float]]: - input = self.preprocess(inputs) - embeddings: list[np.ndarray | list[float]] = [] - - if self.embedding_provider == "julep": - embeddings = await embed( - input, - embedding_service_url=self.embedding_service_url - or embedding_service_url, - embedding_model_name=self.embedding_model_name, - ) - elif self.embedding_provider == "openai": - embeddings = ( - await openai_client.embeddings.create( - input=input, model=self.embedding_model_name - ) - .data[0] - .embedding - ) - - return self.normalize(embeddings) - - def normalize( - self, embeddings: list[np.ndarray | list[float]] - ) -> list[np.ndarray | list[float]]: - return [ - ( - e - if len(e) <= self.original_embedding_dimensions - else normalize_l2(e[: self.original_embedding_dimensions]) - ) - for e in embeddings - ] - - -_embedding_model_registry = { - "text-embedding-3-small": EmbeddingModel( - embedding_service_url=None, - embedding_provider="openai", - embedding_model_name="text-embedding-3-small", - original_embedding_dimensions=1024, - output_embedding_dimensions=1024, - context_window=8192, - tokenizer=tiktoken.encoding_for_model("text-embedding-3-small"), - ), - "text-embedding-3-large": EmbeddingModel( - embedding_service_url=None, - embedding_provider="openai", - embedding_model_name="text-embedding-3-large", - original_embedding_dimensions=1024, - output_embedding_dimensions=1024, - context_window=8192, - tokenizer=tiktoken.encoding_for_model("text-embedding-3-large"), - ), - "Alibaba-NLP/gte-large-en-v1.5": EmbeddingModel( - embedding_service_url=embedding_service_url, - embedding_provider="julep", - embedding_model_name="Alibaba-NLP/gte-large-en-v1.5", - original_embedding_dimensions=1024, - output_embedding_dimensions=1024, - context_window=8192, - tokenizer=Tokenizer.from_pretrained("Alibaba-NLP/gte-large-en-v1.5"), - ), - "BAAI/bge-m3": EmbeddingModel( - embedding_service_url=embedding_service_url, - embedding_provider="julep", - embedding_model_name="BAAI/bge-m3", - original_embedding_dimensions=1024, - output_embedding_dimensions=1024, - context_window=8192, - tokenizer=Tokenizer.from_pretrained("BAAI/bge-m3"), - ), - "BAAI/llm-embedder": EmbeddingModel( - embedding_service_url=embedding_service_url, - embedding_provider="julep", - embedding_model_name="BAAI/llm-embedder", - original_embedding_dimensions=1024, - output_embedding_dimensions=1024, - context_window=8192, - tokenizer=Tokenizer.from_pretrained("BAAI/llm-embedder"), - ), -} diff --git a/agents-api/agents_api/model_registry.py b/agents-api/agents_api/model_registry.py index a7bf13e39..8b7bce4f0 100644 --- a/agents-api/agents_api/model_registry.py +++ b/agents-api/agents_api/model_registry.py @@ -2,20 +2,7 @@ Model Registry maintains a list of supported models and their configs. """ -import ast -import json -import xml.etree.ElementTree as ET -from typing import Dict, Literal, Optional - -import litellm -from litellm.utils import get_valid_models -from pydantic import BaseModel - -from agents_api.clients.worker.types import ChatML -from agents_api.common.exceptions.agents import ( - AgentModelNotValid, - MissingAgentModelAPIKeyError, -) +from typing import Dict GPT4_MODELS: Dict[str, int] = { # stable model names: @@ -118,143 +105,3 @@ } CHAT_MODELS = {**GPT4_MODELS, **TURBO_MODELS, **CLAUDE_MODELS} - -ALL_AVAILABLE_MODELS = litellm.model_list + list(LOCAL_MODELS.keys()) -VALID_MODELS = get_valid_models() + list(LOCAL_MODELS.keys()) - - -class FunctionCall(BaseModel): - arguments: dict - """ - The arguments to call the function with, as generated by the model in JSON - format. Note that the model does not always generate valid JSON, and may - hallucinate parameters not defined by your function schema. Validate the - arguments in your code before calling your function. - """ - - name: str - """The name of the function to call.""" - - -class FunctionDefinition(BaseModel): - name: str - description: Optional[str] = None - parameters: Optional[Dict[str, object]] = None - - -class FunctionSignature(BaseModel): - function: FunctionDefinition - type: Literal["function"] - - -class PromptSchema(BaseModel): - Role: str - Objective: str - Tools: str - Schema: str - Instructions: str - - -def validate_configuration(model: str): - """ - Validates the model specified in the request - """ - if model not in ALL_AVAILABLE_MODELS: - raise AgentModelNotValid(model, ALL_AVAILABLE_MODELS) - elif model not in VALID_MODELS: - raise MissingAgentModelAPIKeyError(model) - - -def load_context(init_context: list[ChatML], model: str): - """ - Converts the message history into a format supported by the model. - """ - if model in litellm.utils.get_valid_models(): - init_context = [ - { - "role": "assistant" if msg.role == "function_call" else msg.role, - "content": msg.content, - } - for msg in init_context - ] - elif model in LOCAL_MODELS: - init_context = [ - {"name": msg.name, "role": msg.role, "content": msg.content} - for msg in init_context - ] - else: - raise AgentModelNotValid(model, ALL_AVAILABLE_MODELS) - return init_context - - -def validate_and_extract_tool_calls(assistant_content): - validation_result = False - tool_calls = [] - error_message = None - - try: - # wrap content in root element - xml_root_element = f"{assistant_content}" - root = ET.fromstring(xml_root_element) - - # extract JSON data - for element in root.findall(".//tool_call"): - json_data = None - try: - if element.text is None: - continue - - json_text = element.text.strip() - - try: - # Prioritize json.loads for better error handling - json_data = json.loads(json_text) - except json.JSONDecodeError as json_err: - try: - # Fallback to ast.literal_eval if json.loads fails - json_data = ast.literal_eval(json_text) - except (SyntaxError, ValueError) as eval_err: - error_message = ( - f"JSON parsing failed with both json.loads and ast.literal_eval:\n" - f"- JSON Decode Error: {json_err}\n" - f"- Fallback Syntax/Value Error: {eval_err}\n" - f"- Problematic JSON text: {json_text}" - ) - continue - except BaseException as e: - error_message = f"Cannot strip text: {e}" - - if json_data is not None: - tool_calls.append(json_data) - validation_result = True - - except ET.ParseError as err: - error_message = f"XML Parse Error: {err}" - - # Return default values if no valid data is extracted - return validation_result, tool_calls, error_message - - -def get_extra_settings(settings): - extra_settings = ( - dict( - repetition_penalty=settings.repetition_penalty, - best_of=1, - top_k=1, - length_penalty=settings.length_penalty, - logit_bias=settings.logit_bias, - preset=settings.preset.name if settings.preset else None, - ) - if settings.model in LOCAL_MODELS - else {} - ) - - return extra_settings - - -# TODO: implement and use this to work with the response from different model formats -def parse_response(): - """ - method that converts the response from the provider back into the openai format - """ - pass diff --git a/agents-api/agents_api/rec_sum/generate.py b/agents-api/agents_api/rec_sum/generate.py index 5a8daa675..63bfa281f 100644 --- a/agents-api/agents_api/rec_sum/generate.py +++ b/agents-api/agents_api/rec_sum/generate.py @@ -1,8 +1,6 @@ -from litellm import acompletion from tenacity import retry, stop_after_attempt, wait_fixed -from agents_api.env import model_api_key, model_inference_url -from agents_api.model_registry import LOCAL_MODELS +from agents_api.clients.litellm import acompletion @retry(wait=wait_fixed(2), stop=stop_after_attempt(5)) @@ -11,16 +9,10 @@ async def generate( model: str = "gpt-4-turbo", **kwargs, ) -> dict: - base_url, api_key = None, None - if model in LOCAL_MODELS: - base_url, api_key = model_inference_url, model_api_key - model = f"openai/{model}" - result = await acompletion( model=model, messages=messages, - base_url=base_url, - api_key=api_key, + **kwargs, ) return result.choices[0].message.json() diff --git a/docker-compose.yml b/docker-compose.yml index 4af7c559e..6845bb838 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,7 +3,6 @@ version: "3" include: - ./memory-store/docker-compose.yml - - ./model-serving/docker-compose.yml - ./gateway/docker-compose.yml - ./agents-api/docker-compose.yml - ./llm-proxy/docker-compose.yml diff --git a/gateway/docker-compose.yml b/gateway/docker-compose.yml index 9f03ac098..8a6fa85d6 100644 --- a/gateway/docker-compose.yml +++ b/gateway/docker-compose.yml @@ -2,7 +2,6 @@ name: julep-gateway version: "3" include: - - ../model-serving/docker-compose.yml - ../agents-api/docker-compose.yml services: @@ -23,8 +22,6 @@ services: container_name: gateway depends_on: - model-serving: - condition: service_started agents-api: condition: service_started build: diff --git a/model-serving/.gitignore b/model-serving/.gitignore deleted file mode 100644 index 07c94e456..000000000 --- a/model-serving/.gitignore +++ /dev/null @@ -1,163 +0,0 @@ -# pickle files -notebooks/*.pickle - -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo -*.pot - -# Django stuff: -*.log -local_settings.py -db.sqlite3 -db.sqlite3-journal - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py - -# Environments -.env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ - -# Spyder project settings -.spyderproject -.spyproject - -# Rope project settings -.ropeproject - -# mkdocs documentation -/site - -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json - -# Pyre type checker -.pyre/ - -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ diff --git a/model-serving/.tool-versions b/model-serving/.tool-versions deleted file mode 100644 index 47cd22e3c..000000000 --- a/model-serving/.tool-versions +++ /dev/null @@ -1 +0,0 @@ -python 3.10.13 diff --git a/model-serving/Dockerfile b/model-serving/Dockerfile deleted file mode 100644 index b25c81eba..000000000 --- a/model-serving/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM vllm/vllm-openai:v0.5.0 as base - - -# Define the entrypoint -ENV MODEL_NAME julep-ai/Hermes-2-Theta-Llama-3-8B -ENV TP_SIZE 1 -ENV MAX_MODEL_LEN 8192 -ENV MAX_NUM_SEQS 1 -ENV GPU_MEMORY_UTILIZATION 0.95 -ENV DTYPE bfloat16 -ENV MODEL_API_KEY myauthkey -ENTRYPOINT python3 -m vllm.entrypoints.openai.api_server --model $MODEL_NAME --tensor-parallel-size $TP_SIZE --enforce-eager --gpu-memory-utilization $GPU_MEMORY_UTILIZATION --max-model-len $MAX_MODEL_LEN --max-num-seqs $MAX_NUM_SEQS --dtype $DTYPE --trust-remote-code --api_key=$MODEL_API_KEY diff --git a/model-serving/README.md b/model-serving/README.md deleted file mode 100644 index 7b76aaf6d..000000000 --- a/model-serving/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Samantha API -Samantha API server - -## Deploying to modal - -1. Login to modal using CLI. -2. Set up default env: `modal config set-environment staging` -3. `modal deploy --env staging modal_staging.py` - -## Install and run vllm service -```bash -$ cd services/vllm -$ poetry install -$ poetry shell -$ python samantha_api/web.py --model ehartford/samantha-33b --tensor-parallel-size 2 --host 127.0.0.1 --port 8000 --backlog 4096 - -$ python -m model_api --model julep-ai/samantha-1-turbo -``` - -## Set up skypilot to run service on A100 spot instances - -you can use this as a starting point: -https://github.com/julep-ai/samantha-monorepo/blob/main/infra/sky/vllm.yaml - -### Docs: -- quickstart: https://skypilot.readthedocs.io/en/latest/getting-started/quickstart.html -- spot jobs: https://skypilot.readthedocs.io/en/latest/examples/spot-jobs.html -- services: https://skypilot.readthedocs.io/en/latest/serving/sky-serve.html - -### Setup: -1. Authenticate gcloud cli. `gcloud auth login` and then `gcloud auth application-default login` -1. `pip install --upgrade skypilot nightly` -1. Run `sky check` to check that it detected the gcp credentials - -### Create service: -1. Edit the vllm.yaml file with setup instructions of our custom code -1. `sky serve up -n vllm-service vllm.yaml` to start service (no support for in-place update unfortunately) -1. `sky serve logs vllm-service 1` (1 is the ID of first replica, repeat for every replica) -1. `watch -n10 sky serve status` for live status of services - -### Notes: -1. Right now `sky serve up` does not support using environment variables for some reason so set them manually in the file itself (and remember to unset before committing to git) -1. Right now `sky serve` does not support updating a service -- which means if you change anything, you have to `sky serve down vllm-service` and then `sky serve up ...` again... diff --git a/model-serving/artifacts/function_classifier.bin b/model-serving/artifacts/function_classifier.bin deleted file mode 100644 index 268dd287930afcb436951ac937476cfcda94651a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2552804 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
    GU2w9VNPr9ai6rdPGv2s}k;}LEeo&ctL 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%0AkiJX!t-X+ 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

    0t`j=lAt2w2Z0h<1s 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

    D?N*f z$=|cz$MV}@OpgUUoSn}z3uNIn9#VO?(u~4O-FoTz`1Y4jD=L#z=|Gqz=q9M2(lAE$*y>)x@|IBFIean2>P!bJ@e()T!i^9HQwTNc3TLr1EAn?_bYO% zBUt*xo?xy?BPwdi@MH#ClBUud{+$tEksnXMz(OAO=m2nV?yA0FOUu~a?+6}nkI^CBaa%T)GF?Fmq|$S2&0648 zfIy>OP0asMR>JmvEEd5c+f;2Yx3!tfikT94e$e}Ix5m=njrAiMZ69;mvZ)wN(8|8? z^cde##!K{v5Q)E_4(>I<><7;hj~OZ-ln>_YX0xM3^-9Iv7rMBk79-f;e|~i&`8l8CZ()K?pSLSN*&Q#+W(La2r`NZ;UzWMEdu=&9wLX1&>`uJIM8b@b8fCs|%|~<+G!VKEV@jUh#I}z(|A#6shizt7ey^ai{t8GHlf0@Sk5& zn2ET*NOLVllvTQVCh_rRO-xAZomYTbOG*ilPr0rT5lFX!hf|GDxiGice2yWp>rQm& z_A)q*nFDl2^>=kRfsQ#PUL7cKF`T~dDJOMR5j>Z6t9^ZLg)xq}pGk9`7arayn#}HT z;o7go3ePOZ+Yz!YN_Dz0-O;=|WvOEIvXW`q;yWk_8l8)ZO|@>c=#K@?E@p$@p<~sg zUIf?3cdKoibquXCWS`l3@=axgKHQ^%%fdzerfYRpR}C?%Woss zop#*nmH|m`*Ax2}Q{v;$Pb!n-Q`5uQEww!1IJH;hQFPbLP0*xBR(}v`#^`n4O<92O zvuw5M!=7yB)5xai$v+|V+aB;w17Nv_-HfM0OW1ykmswyYu>1P>p!r*vN&Ws5nfMF* zpgw!T-`)V4PqeN(I&pNJOujocj{mGPy_{G=rTW8tpfiyC1VVt|{cS3Y^KP;2phxh$ zQUexu{d|>~EUSF&3pEzyucU4lt`=qN!o=CbPZ^wPa7DqsryN` z3{dP{k$I3A0DOWfA09UcKtuxp`iz_RIK#YzUBzL?O}FnuLxZ(rnRtqMN+hgj);L15 zn6ivvJi+JvqV8pA3nWH01+4xM%{Z>U(9gAIg|?Iy|Eh1Ar{1u1R6!w0q^J4#!u

    x5&J~t|2R~aM8_v1+E({qQ*|J&JBEUR#l zcTXPe-a6_XSY3%-DSeoi?)M)>F=32%vF-Q%m=kczU#wr z;rFeA5QxdrUF8`k*RS%M`Kxf2eMdMS@C-pL_m+Zc=9eO>KF?YL=gQaq&4hN-r(Z=C zQc#F@wBj?@b~D&L-5lQZ*YB&X=f)w$a7j9Y{(zTO#{=ZAuDEEXU&_t{*nV{4Nwwh& z1yd&Kn2ZE{=Q^c@d`#ib1{dx0M7^$-pq0RvDf$!ueNKS5@Z@IV#2$BN9LrwtR=&0(aT}~l+w@TRG&4@)?@1Bg#|v)=hRjbO{F`Q7;0xc?0HXIQT7dCwzZ77jWR_mT-_ z%4ChVY;2UaY_H+iL7IKyk&`UB=P!8knr*9EKO#u>Uhsk(r5PjR*zt&Kzm$ho9!zL9 z(%UXkFLiLgnh7KX{(@s(dxsB9;fxG_EGdIt(HXpe9TwZWg?@g&cN~VM!h)@VOnc%_H93X1_ z)zyc>xZN-j-~r@!H{!#qQ6XY>0^|xm==#_ z%1WKDrKaGR0r87HT;Ju>hDcux9v( z6s|tK2;OF0mxo^c0+~Lt%J?v~J|{QDC-Se(uzYTk-j9y||Ki ze_PyYTzf5w(U+u8eySO$G*wI1?p4Y63R*?(B`SdyMG ztgli0+OL`D&%Jst&UTw~VMPhcC;F4f^*Y{6v2uuJWad++p;hr*+w**RS9Omt#q?M` z(uVXXI`adYacd#!bpQ=326kX3++q34Es~z=z;TCH4$emL9m(t8AL)mb+hwI*;qSJ~ zGh143x!(`%1G=W1Xpo4Um)roplZSmewY&|-UvS5Q<8ZXWH+EIY z;@`!Q0CW(wZMtl^Y3{+k7GhO%g**B z?mWl&4&}chPzKntb(3fZxR?9SS-sBrXVOz@Z?f$k zO47SH?U_ki=U8SJMJy7ZvE70p+5`4D~#_1i9=pKL{p2}W5*Kxbpu92K*g)N57un9{b5PLa?1z1s*>Sw-+9yC)o z2Zbo{14{gFzt-tM2j4)FPp;hurzxLy4Dvu#RN*A>hF?B1C^CmSf* zxznc^%{~N5ta=aglz5M!@h1nxu+zKy1kZW%y;mXyW2QT3+o^oQ<~BLpWZ1K%!mXT# zE?3!US~|fqSZg}hD{d*;CdP%4=_sYLd-Z??kC?+u&;Xe@_#=fkq)z0&!z7UrkC*5s zxv&h|8bagvZiIv=+_{y+=JO+tL-5;c+2xrAnT*^h^2&Qa`Q+iXFTjoWhqfrg1w({H z&yXIytzr#5_Ec?2I&Mo zSXJi}J=JK7fV+ks%l-mEqq<31;f!QdN+m`fzBZpdgAu2aC&LlCMTISyhk*y{=(x+u?h{mG2o zq)m1~!2Ut!Lr>O}xFI~VlIpU@uZm>8QWsAcy%}V1pz9rC*qBNWZ|1=)8isYjs3CdH z7kekN!^UM2{h@2WS`TfO##l>D3_g@q#(fI@Wz84cX6b9WJDd9>++F^|QOlx!arZYL z`<{r{#Q~X^)Y0%?Bh`kzwasMoc)XNg7el*uhktzlS{*3T2vOy(uis8SSB85r~XVo^R{V zZoVY;M?qV0eariUlYNn+ti+C8ntwm6{ThDz^-1`wZ32glUQt9X5y`^Y+V;1XpWx#y z_1JAh&>B_21lWZRO3k`991ysf%9t*et%b( zQOcZ!X*MMhiCeO!IQv5!t?pTUo!Or=)jW$tI@qiA%hP=VP_;x7)NAOLIh)$0yYQF) zV)U~gm<)|(QZO%(<~rFNKG95p-K55~SBu@L(R{i$ldD(d~j&bR&Q`;Z!{#(qLgWN#XixB$5V8GV^ z_|ihOe{Th6N~fMg+E=kK-|urQVi&I@()s7L&aTy2-`y7cdKuZXFu<{S1rrL)42vp% z+!y60_1^Qx2gBu0bEek5k|3XL@47p+t!4HD9+!0v75Mc~_=|_-yA8V(!`u^D^(?fRgJq{`Sh~z{g##8-unH2U-K>hv2KWduV6H=DRyc9 zD3frv%~MTw^bJQ*R3k{Bj`^-G8c}(czud3dJ9zGR=U~(yx+7IDBACW#0X|-C%wl?O z-aaUrU7-&lk)Da1J-p+aShNQQB#LDGR7G+lAqXB`wQ5_bymiQA<;Y-yXEk!dqI4H;dfhqK3WvV%4Y!tPt7R(Yx$#4bbf*ao+Fr8*M=ab0J?siYnCP< zRzABx@ch14bWV5oK&oRU@GJmtGz2jQaJ)GGM_ny<;mCP{m#SE;r`s(A4bqjMGkk~# zcNyHh8(cD{v^HZeIN^+Gc)y#Zsy6HbT94!AJ21T~HwHY60Paujh$i-9FV+y0kxJYX{r&@q)TvIOuyQ8u?H}CV{US z-@l-&h?0B0bkgk8S=EKNo_lFK+bOGHAAchT`5IX({^l3Z?d zgzYUrv*f_okzH9CZpjI3y|4*9S-otmxsL}^3v&#rDX{5z9JM}x(DClUWq0C9`2=iD zjL!enea>~5u|p$JMHIoZ{baz=16ZxKEMTPK+N55rx!8oaxq!|hj#D(=;fl+AlVmj;so%#3eR0Qrm$fCz|rs4#52ZJ;Yhfq zhxPvJH?vAF@s|xP9$B6AbPfIAO#ZZQN-y=&<%Qx?(db_$%$iaIUeSDE@ex&b{&LP` zJFBs47gZyCXqFlu9HHM%2V?_)y(K;S#&(Hi+u;p~QN#DAe8Z`9v(U)T_5VOpNe;2m(^QhQJk#u`Lw+7GcIVvr<6rs@(e82GD^MB`HAUCr_Uxg(IS@z9_qwE%uk7Ip(jkH9Zhu@I zSaF^KZMSrns-tT(ek``yN~o+Nrf{hu`hna!7Z6MU zT4oxGLN-OQYF4~H2h&N7er0~$-O&m1Cgt&72?>8c_2$pR?Y|D$e{Nd}vqlOE@jhmd z^hra(iH~QqS#4Ba$!&B1+Kb>~@qN{ZcSy`#UW0^u3zj^iAiGO z@wbPg<&O^G&$jaAtoUUkvpg0AFok~$|Bs)4Z`EC|U%K=0*8D1Yce}BKQI@1LJRU6s z#6(ChKXL!<*RO9Y^Hx8pLkl`dL3ceiBrs|ekx3vY zoS7@GpWw@vTdy?kNJ=*@LV4XJ_yj&|ghf9~fE2yQeyv;OuyP0~?o}m85mrtKV6&Jq^ADxe$K~z)`#cf3b3-DY4 zAWq&nWY0scA31{uocnBl)=j^}k5M9TG9Oev!-r3e91AlbhhgH z&>5~UQjk@?g!P>aD_dXY(~J4O1czv^J7^Zn$Drw(93J1T;1zf1PXhfRQIpg8>x1e} z8CkDJK0p)NNYsl3jMJ+CXEnf^O&Xs?OE~#+9eBWP5S8lnEnl3LebMi&?=5Ga@%QHU zzl2Vay8Gv3Ay?d{vK@8NVf%XpCx_dLQD?{}<%H00t-%LpYvSG0cGJFRvh>OJhwWyo zt8$8pn4M6cbd|#V!3Je7KU5+gs27SpE!s7y_qzUmIO03(#U+A6B*|9&jroTj zXm}Hos8aJeV8u+uo~x*B%h?KsC#UJ9qn?xyA`wHe)v&SC=6dk+xn`u>Fw=Sd|2Bo| z2}K1Qd4W#1CCl5Io%e!Gu9a(UaAwFbxp&ncw2z>BjsFPa<1$MddtII9om zZ&Uvst_8K*gZ@_+QuULLHv;~Zkq;oR&Z2m6KLcv1;dHS+x8ZvJ6>v?X(+ zoGS+Lm3Vjz8+9h=Kr&g4;evTt8vY#f!UsLfmFUG{7@cspo(6C&x3=Y(2U+YYjvtA1 zk;--nyV2cTELSy~bw?g5)*h(}v^ z*IuQSw%>x=v>8hR_zw#DoO7=nrBO)m$q_cYU^2v`k9lX)fql}sQ8bX?-TwaL`Fo}s zjf6Z=QkNK3jqpe(574aj*A*`txWg(T1aJSD9p@DHW}+1oYa^eU9>OS_Ur8gVQFLWM z*rxTP&oCuTq5l0(?Zg1*7}HDq(f8q&)!zH<&oE9hnopo8-16+U?gc)T&#SsH<3SqW{duT_1 zIVTld8pai~qbO$Qu#hA_Z~hlwA2gRjBA=QbvpWq6RqpS{=Th@t>dJr8PGgj*MsGqP z|9UR8-(_eW!^b;sJZ_@i$ID0sq+_ZnEI=yh3|JZf>Yvm1wd=%5+mR%yFRiq6*4|D= zL6ar8qIx@#03<)6;mKY6VPo$Q&Tu;JSYRz>_?Qiz1;I=pv=t`fKh6=Pye{wtvkXDr zvlhYV?hTSp5)lYSZheaOWmTdQs28egU=~6S?P0OF=0SyH;7@i_KR2p1AFf?g_xvCe zPa*wDmHo5YrM>pzu|E966>|pe)-pVi`M({KzUMnRZ<7;=S57tC}a}YEBQc!r_l$OmQdqBY3wU)?^YCK+O{5R;v zB^}k8IDUN|zoh)b-8viL6=AA;b+CG;4mbk=mcMbSKO4p!s`0`x2`U`=cXCorK%WLl z;0b_UdO(1l0I!Z^G@mzxYj<{)QIK*&vs^!f@syQMMbpsj&0`Yy`P}N!7-V%#j4b;6 zX;PdGfdFq3?FGL=0MC2XZL8<9rdtIkzyl^G4lN%|4~alW)yV7LA6HMJg>O|;`0dbZ z^Hi0EJuR6#kWVVBBDmnPH$(&Z5UgL&xr=W|4!gRQM&d3iZX4LM(T!P7F*Au|zx7$< z(cB}Aze?r3#Wrl?!wO^vjX6fk|QgLjr}ttA^Fg{S1zS!GF04N|}>t9(}n<1Nr0(a@!as^sbK;SLD~ zlTZzdDvf?uA(YULk0kNm&@SP&Va^pkoztivA((Qyt$8V0;(1W%T{YYI&Bp3=oaJTuI zajm7sPEPy3m*6SC{?p`H&0}0)?4imh1U^3kq5=LP$Xh;M>D3zUf+2R5KlQGO=fx!@Oe2h~R6i1T z7LlD{n2d`mD|5m}ai*Ht*>$iWHvaSYd(1wdPl{g)q209r2W!ka{}}o(k|`wZz#T?a znZIrRy;Jqf8Q|AcKXQceeI($F0&x8FQfuoxZj&LN&cJ4uQ{U7UkH}{ued-!|q70`DwvLo)lHOSk-xQCFh)c-;r)D7G*TLNBEz1|Ry`9IlKrh~V?3f^AxD zE>3VoYh=-N(&r6NJ}6WClR1&o%OyJmg-P~ze)?-~jY-h)rP|$NbBp{9VI?RR`XAMP z0)-~6p3jsMwkeA_qZs2zjPcdCPk%d*$rwU<6ur0uy^Mt*69ATEjxXAn$z8N|f#4;( zq{>gwsbF3}J5X5_!{JaI^>~0Yd45sv_i`rW*%}_R2}oh+RwILP-9+`f2#(*#UpPoLu<2Xw8gg zcaS}niTO@-Agk4z3Z6#u7x&U|C}L-=xT0Wj{70v59ckl5uj+sayL0G;!S-bq2)`tlGJm6w?zt^W@D-oV+QT+&3{fJg? z3Pj*P<a* z7d#sR&J2Lz?TjqtO2sUl?FSFIm*>clBR;-9vR5aCLEkEeOs7_t*XGvDD({zp#>k?x zPA}R7O>v?Ql~(j}kDPlmo7n{IK$Y(ptl}6!hOm}xx9HmWTU&D3e1_v}3a&I{2X~LU z!`uS;q>`*_FCse|W<#J>SK{7fm2<1Tc?3_jcJxA(*@*EYZ&Dr&uZopYUQ>KR|3k~U z$niXqWr<8+ZL?2#i!*g`)`YZ$DM>|P$e${lM7jey%pP-d52f9??o#VAR_+SZeUe1^ zHp4t_F5oW!NWXtS>bP+++wM^$(mP{&cCJz@VM5zTq=64znt&e`Y?i#TbN5`&)h8xC zFiq>GW@S9Pfn0n|bqPP1=a@nOtf8B6rLxVHF5GJek%2gN_-YgM-RY)?a9sZ2`X=l;^zJ;g1C-+pI~ zzG|CPITO|{BwqLR&fCoFZ!xQ3+-ymFxCfrUW6dKTerij`GH#%{g|Ps(y5onS zyiQ~D@V9rEHGo7F5x0W{1h4`4Veb8Y@fvR4&$ffg%am#jt?P^z)#wlwYm-*M?@9op zl@^bu`IoWs=S1*;^M+f!i`%Y`lf7G{(}ZR9VqQ4ZxJ@Je?xSWapcQF`B@{*M5I#ZXGdsPCWj3;KP1a@UjYEM&H&oZrj;IE&^8) zqJ658U!fjihESFA5Ar|1o4k7D&%D5&H>&q@N!#DMw8v}U#TwxBbuY{I;%n^u7JI^NK}n;(7eqy)E#ytgWA*f4q4^~CLU67=)#b`t zB>Tq6-u+$N^^?J!8OQkLGhdh6(xEn!DdOV!Yl?BX7E_murPfhqm3XB->*9=l1D#P0 zOgR$G<7V(}QPS{a(H`!iQA|Xz)o}BYmyUxp5vw(5jxQ(fvJ;)XSc%Y>dx6|{CUp# zjTP0gTE*xW6pk8?9Dz?@V%Q4M>E6l>A5^(|5s?(G&hDc3)#xgNKTCBt`fE%m7~{rI z&$pZXK83dT$?hqt>BZV2fi1+e1Hh`6_KAblT>m>oqJQ$E%5>aqI4}fARMW#em29}* z4#Bj0AFc7WWlb4#^VGaN|9sP*v9jrtqyY1dcJPPG)CB{Vqr^`D#NhwR^EcNxZ4)Do?bxj|5+`fOf!B=RE)^Z{L<}SeeiAUAA8K zzJ7gRkk(za2J%g{AMi7x$+ZK#MMuxJ{I-^x_v3{CxFhw3!&380(F`6Q|HDzIC>|!x zmY?%5UH0M77Nei+*BVb<@wVPiI)W5#;7}ESLVX8z3 z2GDU0U>0O=p8D$Or6g86hr0|M!`s$&&XGndrc~CMU4yQ%qkgo_CbJ$cQxlh@W=-i9 zjF!EX7`6QrMPzuyBmCs=Z3-@&D`sa)>qvUthJ@(kt_WZfq5D&#DBi1ryC){aPRtOa z-WzYSQZp7C1OngJBa&AnMx%8!D<$9lJp8H}t|ZUMBhn$4&wC-?Uw`U#j4{GoTAI5v z^<$u~HY;s&cwzm*uDMN0K_Lzj`7~%F5@5-86u=wdLC-uPx2l5Kk3ev1R?4(C2LjN_ z;S$_t7mPA(AOK#LRdETNDR+`HHHy0@6gy(Bg=O_M<_C_O@5C_rwgr#Q086?t7sKFS z5Mg61|I9SqwEQl+OMySB7cRXFje7~3j^1b9 zLSN&&*u;dRS*>0dc$Ifs?R{?mr?m_q-{rkW+T0IxLq{aCM!kAPtQa14l+r#Hg+!U` zZI;2Tlf~V7zS?%X?W}A3d`?=mX2HRE>1cI-iGFLLDiSC`s)GQcZ^iB1d6F|-7f7;l zUwnAP>6iBC7ql@=FP%k1^N2M(W3NtVXg{Ekoh`W#yyGX{)=jp}V(OsZQdxCWX0>`M z5bVtBt?#aPY+&i^B*FVt+_#gFY7{z3RVMk=^l0;3&B+%`rr|!rh0~c)TV3j2#o%j= zDTk%J?%<;iFk|Ce&B2+s2_Bg$09##N_MxV}fq4O+sq$HLV$bTS!H2*paghVYai(o{ zf(PuO6E9i2pwS=oBX2Svls3@(ShE%v`d(x5WT6yy+&{DDyUTnjD5|hUQLnp6Xakg& zDx#xGlUDE1o^^d*b-u;UdOS!9`X(WoD^)`ods*on8~Nt!y8G;z1Fk-+I=NfbgruT3 zvhdh%RQbZT%__KhCJXh%yo>MrU@w&rJO%=cxAEKZ=vF1$?&rZnAn^9rFMT_t6rGgh zgLoCLHw5Sd(C)T(o~5vuZ9hz~u$BE=^`6{QZVZ;TrMh2@_ifIO%i;40i}<2x4_4ko z8(_Sm+8ytO>kBar0BBR8F|B$N*Y1zl=q*kwPU%*U8UcyogBBW1XZ4H}zUqz+DP(7Z zxdhK*fOSnb`D~O+H69z~pQlRajhVMLCiA8Ag*pCaf^YsvD2tz|bak8*>Oq-G*miPF za($aP=IEcu|MJJSe$DXlgO2m*?5b8wQfNRp<0`8@&s>f?lGd4(4!Lla&sWDN?yp4F z$`a`~Li-H{zo7u{#I9S6-;}X@Vi*aS9dQ3`7FcP-CUK~C@Pq!R0YUI|XtB?*LoK2T zS$bh9xpl{jZF|A-lPD64l*h#<<6X4v!}xq+L}lRN{q3XCDp~Dd)_hbz+MH-Up0{<> zwCmY*$P~tLx+!4Q9T!CtfYF_q@J*A`#;+ooTBiNBy6fadcQ0 zm0feVaWk3VeR;K5NB?m)`i#eNY0l4IPWY*iy13PNOYaTSzFuI$WVN5mk>1TbDlhT- z!_x0YgCqN1K{1kajZO1uK?X(w?C2Bz*vg5U6&xbD+W0ad=)}rIMn_s6oBXL+!PxQJ zy*8bWMtiA7Zwk!&N8z;`I^WFZVjK564HV?phQr|b|Ru#9RrcAzNz(0ud^PgjGBbZ{MT?e(F9#^Cc-SD7m?UQy{KwP&?@VJ`foOF@S#~oIG*PnO@=;B!x>_9`N}2GS zWS>(Kdgv140PLG?Lg=^S0eKO?k~lYG{eBtjykiTAv8RICrZ#GkXih(geq;fwX%isG ze1L^(1T)r{=CJdJg9NX~`f~@*R~$i}eI@ZgdyE(O<^ecf`&Bf~nmbd$j?)vD53(89 z{0bT%i6;O&SivV}JFhG_c{VPSyV!dxNpJbKz@=u(cQFI-xWjjNRSawOW&`AQu-y1_ zHg`eI8WL)n!GVz5X2Bb{dtdTJ;s!?d{Cd@MIRmOmL`fr9Tg?o zy&s&xnG8V;0LoA3T92&eO!{RIJpcIe%45|T%x#d{DZbonpq5ar|J{te?${F3s7|*DF=@G&0g3fI85Raw@SJH zxV;!TL8Eg;<`UCsGR2v4ue(ps5V(yLf661@&#Ls8*vm8)h0;hIF89+Hu0#95_e`=RQ2B(q&sJ{NcU)H%CP0)KwA@4{OTxyZW+ z?}~sVo1Cac(*=9k1>YPWPbgsJ5WEloi=AIfpFN)B!MHVH{QKW##j>Nx_rm$*dlojM zrFVV?N|4nKqA!;nXW04h@j`7{b-KSG4M{XQoud&6lGY8KluRms^t=z>@swC7ng|BPc zdR{`CJMfVt{(Ih9rt~g~%I86+wd2x=JAGE-Y(ZTlc!oZu%Z+!2GK*x%k=Z?(y?i+0 z6kiURz3znWAuab6PG^hyFqJci1IMw0c&M-WQX@@wWZAU2{0&V3N^pmZ*1Q z0y;z9q&%F{Y@g4_3*h(L&w?AiWqDDuFV3oaqMXq@{WKr1X0bTx=7>aQEtHSqgD_7e znnx^lWNxcT_ABLVk~|`r`Za&F_4Oo2W`?ZsZ}p`t`}?j*{CWx0G#zO4Erk)vl3Uqg zQ`+jx8R5Y`39e`=* zL^Q|iAlSi)-ODw>;1_8qwtNTMwpYH_+z;KR5+hMIJSI$=W@?nD?rVla8yDEV}DT)~1YY(Vfh|4m2# zDkF1O3keBYC_KJ%eBQXE+uL8J@u-~r_V;Jw4!`{20hAo#)V%kvZxQLTFD#s%UOLXk zls@~oUT*n1`JL!{7YVsl1iMqPD7_Ql)2|^(*5T~cRf3l!PfwnFo<9$4Ypv)kfd^;A z{+!k8GjVD{en16VFQo);aM60^+XJ|Y2>2OSWa2Ln!j%f!;O};TC(mjx?^wynyCvlN z-YZAn$G9e-w^SprsDGZiSGU{s4UFQ;J@3}P&>hV9Gu>q28Bb!yd{m0#<8{3nT3#8R z#F$Y;_9rFwk6znfNBXr%ozrq+ag_8Df-$v>0lw28rXD~+}xQ>SJUT(LK+)x%Or zg_2^D@sAL8cKwdvO;C+L@oAAIdS%~??j3%hMFh@8NTbDL?I}in;j9#tOz>I+uO9l@ zX%#y5lh|jZF^y#>W!w%>~G&W~4_3tj)j{_j8L(_bGwelUvPnvVO5=S-jF!Q}6gB`KEcy_eF><6~#pFS1UIxv+n1Ex&%4iRX}F!uzhO0Z+Zpg5<~+#X$49GqA6U3-AR z4Q9$n2|O&V;og8pS4+S4r&CopEgLJXy%B3HJrf;+rrGTV>Tu zP}q@GK?)C~Q7pl=1MuwDUR*SZvoxtfLMgTn`gm{DMbwKb92EhaOZ#!YI=44F8|2#0 zTX*A@n?6xYy@o_s{;T7(=J%I~C~aBy&nDLkyZa#(TzAr(tK~vlvn%XLWG+3$oy(73 z(mQ}Q=}XjS9Y|fUfPxrWjx`0$kF;u*Y+6nORDo14P zS^bR@xU+LCT}OU>)PLrpc%%hVCB?vD6^Ce66#=hKb$Yht9Iiewi-jF(Ny~lE(iqtW z^Uv7TjvIQC+I#S0W$69~@54|dNHdhft0OQ%k` zvrx}=o2QA)eHhN<^WSk^XFYl`YGWFIPutizU#{!C?8I0?-BI--Y}2IGdpq&7-TZ~z z9>Ef8Ah@akqBZdWO|yI)8j=i+rC=|WiXh( zade&cvhCJSFH=x&s86~|VH_I>;f4Z)H}`E^tWrqu$Px`Gu%GC6DC?5LSVDiN@nH5c z2yg}i{7xxBK2r-AkjU8rgW(yklOBbSV^AWOKK}AA=eS|?r=&tx2J=!bTy0~pW2+vw znQ-ueB%53vtj6H6W(a`MjiU0Ehp!PlK`jZhe(Eakv9LWH3-SNq-XP3Rx%4g$EH_xc z^n&aQ;q=qOSGWr!7#`jvn^VEAkyR)I;-T{C*h@st_YH?M9!yW%J@$AZTQ7G>wuW_6 zKB@O!jQ0FTCgJlN#H*J}+Bd(v%THpaLV8p?_(A)PfY6!%r8X;Sr$A$2JhB`FO;b^2 z_$@dBJ;GknT+KC?W4m%-<2MB0>ZQfZRV`kfOJ?xm(0@3pKDXWxp6)og5WRs+(j+=q zL^Oe8AU2y1ZNqnOEoABX6p8-r*tX&E_8H75NjkFt?Z+qxq7AU?@E$AMU@=RdB7&DU z>`9TUMj+YUKs^5T7^>bgX84q3(hgktHu>(<2`a5(@MuW&KYTV~Gj^A@J#v>lh78$Y`w5_7S%Rwq^EhR>Nofyrpmo>5X8|s#XG3)-8vw!^T z3K!hgl278}%`Y~{opiE|&GNwt5?lKd$2a_tPh>Wb zHz|*r8^`A!N=YE}!nL2@tgCzd{G$H1Gk%zVa(L$#*oG2Sv&1Uzyv*o)uYVecDhe-^U zU!*#NODg`~qeJ-P?T@7?-^6nwkx(kqpAXcY;0*@tE-g%pL^9=xv>M}u$ya^(+N<8T zoecwX;?RRX(f;L6cE}s`>>h|en=e{w9&h?1mucuB!3X;(iHMa@eTe7Kph;df)!b^R zJ&8CeLdWyx!w4i#F*AuY=Up`#@^LD4)>$b2)OA$5DCTZ=iLm_FQyLvbwTpuIt0R*} z_@0Oh$zYbqOSC(_AZj`w8vu+uP^34`i`#AIMPeN6vtzgZxkTm%KYq#oNi#NEE4jt< zS3|$WsvS9@+n2FuEfM&`K=!QOJn$c9W)+g@n#HchEFuA&zSU@MFUnzjfXFC16SZs7 z>gnA`F`Wxr)}SnKYL4KwtM#dCkzCK<@s{Fecr|S%1Tg{#d-Ltv?0MzvJb>k3t16e= zs))EoZhRmS{q4~#DYf~`+NJ#6K3ZGt71nhB4@-CJFL*DQyKj+Z#4~uP`wvHHR}01#C~Z=$Kyi>BbonNSMv+_j0eLA*xBkB>pV3y(&elTB|3pgeKOBDQv#@A=D{rwhr0I9qF z>^W-|>|z5v>8{t5U40^U^ZR?VnUPC-EWXHKHYwGqj9B>o<;PWgJm-|$y*Z|#viH{4 zO+Mb?{b@gbf6pG)*Cp~b>?)&(3{nsGc$BmVmR_af9IX8mh4BZTouE9gryc6m)H}!0EN^MyYiGcNQa` z;F;H__5Zw6j0(t`l!w7c(T8XE&hpFmG^*6o_IL<0OIACG3talfs?6f!z5i@@Si9>s z2Jcpu>H?bk-<{Qlx){2xoqL2@^KfQ?R3!Ltzr${A`^Hc{|1@0i>dpA6VzjE=pCO8i z0p`<7;IAb>eV1n@Z%!w%>u!1E`zYmVQ@vAVsI4S_!4}={6xagbMak$*a$q)-v@s@j zIF6Nk@Vxc^WA4l2atgY~pR5U?2r0@^cBv#P^AHLNNkS+pTTw)z6hgALkf>Bzl+wOX znHDW%sVpg5CHpSB@H=Lndd%p=bV{4bLPyMIWuRD7olkJcK8ar z{uyC!DfkX+Jb!x~W7p5PT?1vyEqcNk6_g^WUJBYTxvjb)fUh+%t-9s%ZP{u3k;%Lz z*LCi;aPKf1KHT2j^D8$TxQFe;G?Y&^82jbma8g$-09$|2t?X1lo#~ZTUvNg(J{m0oTohfy zF2p|;I1f9_n44!`#I^%F{w*FgWJr_34V(n+KvgMR@chRDr>&9Yg`s=6cBm$Wa;SJ& z{Nq6wV!yV9a(WAlPr1MH=YL#F}f;e0q zXk}Ldrgh-U-8C+S&rD(4VG}{`R}y3*_ay>30~}SQ%y_g4k~n}5aNp=_el(Mmlhf|p zJvJoOD4-J6L#0qzTC0k*dab_IKCzBH$7*w%NPaimd!B206^o~me=P)R7Wbkb4)^Ad z)3zJ#<|y?!hfYw9AO@dbesl5WM~C=uPvf8K^*mIG4~r!gF!O#!m|Y9r&9THrTPmF` zrwK{vs}c8TYGn-CAP)EUe@aVQa{RLW6S-^*V%C3i6x_^APh1~%9x)<#i>}YZJJMQJ z%lPzC`Perw;aDkJMCSB7$htx7Sv?nsvyFV?t@J*P)g$yLiBukUeZBSMC0xP-A&<#} zSgjuO;k}pdIX-YAr$^{VUXS_cr~l|p6bdK*BnH$)&2F6@zwGIehSem3ke3wP^BwE` z4)(1=N4kp9Cv*B~Bc$H~zEz{YX3^yomhaXRBfy86aW_RAA!$0Ou6B-|`49~RII2J7cM@6Pl-^aoAlH)XM;Qc8;r*wfxytrfmtrpD zl_(gwEMnT8i`1^2(v+VY-?(G+N_>0}L4nqJTYaOz0Q6@hcrZN?k`pk~j}Q01qt&B6I2=ZMCW7fzukX55&eM%$ zCY7-Jk%VdT_TwHGJK;63IgO?sICH?TNv2ozi*gBgEFHu4#-9Xkt66uA7^~H|VZE4E z&w99!AYxk3Ebei0XnizG<$VZT#1sD_E!!0QY#RA1DWH1Qn{ji4R*qYazYnH(w?N

    xpG7A;Jdmv#J56>X6(pf+)0w3nd)* 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 z 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
    FH*l6FdHS`M6(jewUyBrgi8K5q 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', '')" - ] - }, - "execution_count": 89, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "old_token, new_token" - ] - }, - { - "cell_type": "code", - "execution_count": 79, - "metadata": {}, - "outputs": [ - { - "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "8fbb2345fcca46c5b4f8cdbf7fac4901", - "version_major": 2, - "version_minor": 0 - }, - "text/plain": [ - "Fetching 12 files: 0%| | 0/12 [00:00') | list --%} - -{%- set input_messages_roles = - messages | map(attribute='role') | list --%} - -{%- set idx_first_assistant = - input_messages_roles.index('assistant') - if 'assistant' in input_messages_roles - else -1 --%} - -{%- if not messages -%} - {{ raise_exception('No messages') if 'raise_exception' is filter else 1 / 0 }} -{%- endif -%} - -{%- set ns = namespace(messages=messages) -%} - -{%- set first_message = ns.messages[0] -%} -{%- set has_situation = - first_message.role == 'system' - and first_message.name == 'situation' - or not first_message.name --%} - -{%- set default_situation = { - 'role': 'system', - 'name': 'situation', - 'content': 'You are a helpful AI Assistant.' -} -%} - -{%- if not has_situation -%} - {%- set ns.messages = [default_situation] + ns.messages -%} -{%- endif -%} - -{#- Validation -#} -{#- ---------- -#} - -{%- for message in ns.messages -%} - {%- set role = message.role | lower -%} - {%- set name = message.name | default('situation' if role == 'system' else '') -%} - {%- set is_situation = role == 'system' and name == 'situation' -%} - - {#- Validate role -#} - {%- if role not in allowed_roles -%} - {{ raise_exception('Invalid role: ' + role) if 'raise_exception' is filter else 1 / 0 }} - {%- endif -%} - - {#- Validate system name -#} - {%- if role == 'system' and name not in allowed_system_names -%} - {{ raise_exception('Invalid name for role=system: ' + name) if 'raise_exception' is filter else 1 / 0 }} - {%- endif -%} - - {#- Validate situation tag can only be in first message -#} - {%- if not loop.first and is_situation -%} - {{ raise_exception('Situation can only appear as the very first message') if 'raise_exception' is filter else 1 / 0 }} - {%- endif -%} - - {#- Validate continue=True only in last message -#} - {%- if not loop.last and message.continue -%} - {{ raise_exception('continue=True can only appear in the last message') if 'raise_exception' is filter else 1 / 0 }} - {%- endif -%} - -{%- endfor -%} - -{#- Render -#} -{#- ------ -#} - -{#- - Add bos token in the beginning; - as tokenizer.apply_chat_template does not do that automatically. - https://github.com/huggingface/transformers/blob/main/src/transformers/tokenization_utils_base.py#L1753 --#} -{{ bos_token }} - -{%- for message in ns.messages -%} - {%- set role = message.role | lower -%} - {%- set name = message.name | default('situation' if role == 'system' else '') -%} - - {#- Process content -#} - {%- set content_ns = namespace(value=message.content) -%} - - {#- Convert functions to json if needed -#} - {%- if role == 'system' and name == 'functions' and content is not string -%} - {%- set content_ns.value = - 'Available functions:\n\n' - + (content_ns.value | map('tojson', indent=4) | join('\n')) - -%} - {%- endif -%} - - {#- Escape content -#} - {%- for escape_token in escape_tokens -%} - {#- Replace '<|im_start|>' with '< |im_start|>' and so on -#} - {%- set content_ns.value = content_ns.value | replace( - escape_token, - escape_token.replace(escape_token[0], escape_token[0]+' ', 1) - ) -%} - {%- endfor -%} - - {#- Strip trailing single space -#} - {%- if (content_ns.value | length) > 1 - and content_ns.value[-1] == ' ' - and content_ns.value[-2] != ' ' - -%} - {%- set content_ns.value = content_ns.value[:-1] -%} - {%- endif -%} - -{{ newline_unless_first() }}<|im_start|> - {%- if role == 'system' -%}{{ name }} - {%- elif role == 'user' -%}person{{ ' (' + name + ')' if name else '' }} - {%- elif role == 'assistant' -%}me - {#- Only add name for assistant in the very first example -#} - {%- if name and loop.index0 == idx_first_assistant -%}{{ ' (' + name + ')' }}{%- endif -%} - {%- elif role == 'function_call' -%}function_call - {%- endif -%} - -{{ '\n' + content_ns.value }} - - {%- if not loop.last -%} -<|im_end|> - {%- elif not message.continue -%} -<|im_end|> - {%- if add_generation_prompt -%} -{{ '\n<|im_start|>' }} - {%- endif -%} - {%- endif -%} - -{%- endfor -%} diff --git a/model-serving/model_api/conversion/__init__.py b/model-serving/model_api/conversion/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/model-serving/model_api/conversion/conversions.py b/model-serving/model_api/conversion/conversions.py deleted file mode 100644 index 27fc8e1f8..000000000 --- a/model-serving/model_api/conversion/conversions.py +++ /dev/null @@ -1,233 +0,0 @@ -from io import StringIO -import re - -from .datatypes import ChatML, ChatMLMessage -from .exceptions import ( - InvalidPromptException, - InvalidFunctionName, - InvalidMessageFormat, -) -from ..protocol import RequestFunctionCall, FunctionCall, FunctionDef - - -me_regex = re.compile(r"(?Pme)(\s+\((?P.+)\)|$)") -person_regex = re.compile(r"(?Pperson)(\s+\((?P.+)\)|$)") - - -def parse_message(message: str) -> ChatMLMessage: - parts = message.split("\n", 1) - - tag = "" - content = message - if len(parts) > 1: - tag = parts[0].strip() - content = parts[1].lstrip() - - if tag in ("situation", "information", "thought"): - return ChatMLMessage(role="system", name=tag, content=content) - - if tag == "function_call": - return ChatMLMessage(role="assistant", content=None, function_call=content) - - assistant = me_regex.match(tag) - if assistant: - return ChatMLMessage(role="assistant", name=assistant["name"], content=content) - - person = person_regex.match(tag) - if person: - return ChatMLMessage(role="user", name=person["name"], content=content) - - return ChatMLMessage(role="assistant", content=message) - - -def message_role_to_prefix(message: ChatMLMessage) -> str | None: - match (message.model_dump()): - case {"role": "user", **rest}: - name = rest.get("name") - return f"person ({name})" if name else "person" - - case {"role": "assistant", **rest}: - name = rest.get("name") - return f"me ({name})" if name else "me" - - case {"role": "function_call", **rest}: - return "function_call" - - case {"role": "system", "name": "functions", **rest}: - return "functions" - - case {"role": "system", "name": "thought", **rest}: - return "thought" - - case {"role": "system", "name": "information", **rest}: - return "information" - - case {"role": "system", "name": "situation", **rest}: - return "situation" - - # If empty tag, then assume role="situation" - case {"role": "system", **rest}: - name = rest.get("name") - return name.lower() if name else "situation" - - case msg: - raise InvalidMessageFormat(msg) - - -def _check_last_message(message: ChatMLMessage): - match (message.model_dump()): - case ( - {"role": "system", "name": "thought", **_rest} - | {"role": "assistant", **_rest} - | {"role": "system", "name": "functions", **_rest} - | {"role": "function_call", **_rest} - ): - return True - - return False - - -def _validate_message(message: ChatMLMessage, continue_: bool, is_last: bool): - msg_role = message.role - if not msg_role: - raise InvalidPromptException("'role' can not be null") - - if not message.content and not (is_last and continue_): - raise InvalidPromptException("'content' can not be null") - - #### "functions" is only valid as a system name - allowed_roles = {"system", "user", "assistant", "function_call"} - if msg_role not in allowed_roles: - raise InvalidPromptException(f"role must be one of {allowed_roles}") - - allowed_system_names = { - "situation", - "thought", - "information", - "functions", - "instruction", - None, - } - - if msg_role == "system" and message.name not in allowed_system_names: - raise InvalidPromptException( - f"name for role 'system' must be one of {allowed_system_names}" - ) - - if is_last and continue_ and not _check_last_message(message): - raise InvalidPromptException( - "last message with continue=True can not have this format" - ) - - if not is_last and continue_: - raise InvalidPromptException( - "only last message can have 'continue' equal to True" - ) - - -def _validate_functions( - functions: list[FunctionDef], function_call: FunctionCall -) -> list[FunctionDef]: - for f in functions: - if f.name.strip() == function_call.name.strip(): - return [f] - - raise InvalidFunctionName(function_call.name) - - -def to_prompt( - messages: ChatML, - bos: str = "<|im_start|>", - eos: str = "<|im_end|>", - functions: list[FunctionDef] | None = None, - function_call: RequestFunctionCall | None = None, -) -> str: - # Input format: - # [ - # {"role": "system", "name": "situation", "content": "I am talking to Diwank"}, - # {"role": "assistant", "name": "Samantha", "content": "Hey Diwank"}, - # {"role": "user", "name": "Diwank", "content": "Hey!"}, - # ] - - # Output format: - # - # <|section|>situation - # I am talking to Diwank<|endsection|> - # <|section|>me (Samantha) - # Hey Diwank<|endsection|> - # <|section|>person (Diwank) - # Hey<|endsection|> - # <|section|>me (Samantha)\n - - if functions: - if function_call not in ("auto", "none", None): - formatted_functions: str = "\n".join( - [ - f.model_dump_json(indent=4) - for f in _validate_functions(functions, function_call) - ] - ) - - functions_msg = ChatMLMessage( - role="system", - name="functions", - content=f"Available functions:\n\n{formatted_functions}", - ) - messages.insert(1, functions_msg) - if messages[-1].continue_: - raise InvalidPromptException( - "Conflicting instructions, " - "please remove the last instruction with 'continue' " - "flag set to 'true' or set the flag to 'false'. " - "You can either remove `functions` and/or `function_call` parameters." - ) - - # Get function name (could be a string or an object) - if isinstance(function_call, FunctionCall): - function_name = function_call.name - else: - function_name = function_call - - messages.append( - ChatMLMessage( - role="function_call", - continue_=True, - content=f'{{"name": "{function_name}",', - ) - ) - - elif function_call in ("auto", None): - formatted_functions: str = "\n".join( - [f.model_dump_json(indent=4) for f in functions] - ) - - messages.insert( - 1, - ChatMLMessage( - role="system", - name="functions", - content=f"Available functions:\n\n{formatted_functions}", - ), - ) - - prompt = StringIO() - add_extra_message = False - - for idx, message in enumerate(messages): - continue_ = message.continue_ - is_last = idx == len(messages) - 1 - - _validate_message(message, continue_, is_last) - if is_last and not continue_: - add_extra_message = True - - end_tag = "" if is_last and continue_ else f"{eos}\n" - content = f"{bos}{message_role_to_prefix(message)}\n{(message.content or '').strip()}{end_tag}" - prompt.write(content) - - if add_extra_message: - content = bos if functions and function_call in ("auto", None) else f"{bos}me\n" - - prompt.write(content) - - return prompt.getvalue() diff --git a/model-serving/model_api/conversion/datatypes.py b/model-serving/model_api/conversion/datatypes.py deleted file mode 100644 index a3d2a40e1..000000000 --- a/model-serving/model_api/conversion/datatypes.py +++ /dev/null @@ -1,19 +0,0 @@ -from typing import Literal - -from pydantic import BaseModel, Field, ConfigDict - - -ValidRole = Literal["assistant", "system", "user", "function_call"] - - -class ChatMLMessage(BaseModel): - model_config = ConfigDict(populate_by_name=True) - - name: str | None = None - role: ValidRole | None = None - content: str | None = None - continue_: bool | None = Field(default=None, alias="continue") - function_call: str | None = None - - -ChatML = list[ChatMLMessage] diff --git a/model-serving/model_api/conversion/exceptions.py b/model-serving/model_api/conversion/exceptions.py deleted file mode 100644 index 983c39a19..000000000 --- a/model-serving/model_api/conversion/exceptions.py +++ /dev/null @@ -1,17 +0,0 @@ -class BaseException(Exception): - pass - - -class InvalidPromptException(BaseException): - def __init__(self, msg: str): - super().__init__(f"Invalid prompt format: {msg}") - - -class InvalidFunctionName(BaseException): - def __init__(self, msg: str): - super().__init__(f"Invalid function name: {msg}") - - -class InvalidMessageFormat(BaseException): - def __init__(self, msg: str): - super().__init__(f"Invalid message format: {msg}") diff --git a/model-serving/model_api/conversion/test_conversions.py b/model-serving/model_api/conversion/test_conversions.py deleted file mode 100644 index 71437faa1..000000000 --- a/model-serving/model_api/conversion/test_conversions.py +++ /dev/null @@ -1,813 +0,0 @@ -import pytest -from .conversions import to_prompt -from .exceptions import InvalidFunctionName, InvalidPromptException -from .datatypes import ChatMLMessage -from ..protocol import FunctionDef, FunctionCall - - -def test_function_call_none_last_not_continue(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ] - functions = [] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call="none", - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>me -""" - ) - - -def test_function_call_auto_functions_not_passed(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ] - functions = [] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call="auto", - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>me -""" - ) - - -def test_function_call_none_functions_not_passed(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ] - functions = [] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call=None, - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>me -""" - ) - - -def test_function_call_auto_functions_passed(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ] - functions = [ - FunctionDef( - **{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ) - ] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call="auto", - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>functions -Available functions: - -{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of" - } - }, - "required": [ - "word" - ] - } -}<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>""" - ) - - -def test_function_call_none_functions_passed(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ] - functions = [ - FunctionDef( - **{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ) - ] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call=None, - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>functions -Available functions: - -{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of" - } - }, - "required": [ - "word" - ] - } -}<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>""" - ) - - -def test_function_call_none_last_continue(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ChatMLMessage(**{"role": "assistant", "name": "Samantha", "continue": True}), - ] - functions = [] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call="none", - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>me (Samantha) -""" - ) - - -def test_function_call_none_last_continue_function_call(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ChatMLMessage(**{"role": "function_call", "content": "{}", "continue": True}), - ] - functions = [] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call="none", - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>function_call -{}""" - ) - - -def test_function_call_auto_last_not_continue(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ] - functions = [ - FunctionDef( - **{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ), - FunctionDef( - **{ - "name": "other_func", - "description": "Logic", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ), - ] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call="auto", - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>functions -Available functions: - -{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of" - } - }, - "required": [ - "word" - ] - } -} -{ - "name": "other_func", - "description": "Logic", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of" - } - }, - "required": [ - "word" - ] - } -}<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>""" - ) - - -def test_function_call_auto_last_continue(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ChatMLMessage(**{"role": "assistant", "name": "Samantha", "continue": True}), - ] - functions = [ - FunctionDef( - **{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ) - ] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call="auto", - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>functions -Available functions: - -{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of" - } - }, - "required": [ - "word" - ] - } -}<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>me (Samantha) -""" - ) - - -def test_function_call_auto_last_continue_function_call(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ChatMLMessage(**{"role": "function_call", "continue": True}), - ] - functions = [ - FunctionDef( - **{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ) - ] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call="auto", - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>functions -Available functions: - -{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of" - } - }, - "required": [ - "word" - ] - } -}<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>function_call -""" - ) - - -def test_function_call_func_name_last_not_continue(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ] - functions = [ - FunctionDef( - **{ - "name": "other_func", - "description": "Logic", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ), - FunctionDef( - **{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ), - ] - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call=FunctionCall(**{"name": "generate_anagram"}), - ) - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>functions -Available functions: - -{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of" - } - }, - "required": [ - "word" - ] - } -}<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>function_call -{"name": "generate_anagram",""" - ) - - -def test_function_call_func_name_last_not_continue_invalid_function_name(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ] - functions = [ - FunctionDef( - **{ - "name": "other_func", - "description": "Logic", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ), - FunctionDef( - **{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ), - ] - with pytest.raises(InvalidFunctionName) as e_info: - to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call=FunctionCall(**{"name": "unknown"}), - ) - assert e_info.value.args[0] == "Invalid function name: unknown" - - -def test_function_call_func_name_last_continue(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ChatMLMessage(**{"role": "assistant", "name": "Samantha", "continue": True}), - ] - functions = [ - FunctionDef( - **{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ) - ] - with pytest.raises(InvalidPromptException) as e_info: - to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call=FunctionCall(**{"name": "generate_anagram"}), - ) - assert e_info.value.args[0] == ( - "Invalid prompt format: Conflicting instructions, " - "please remove the last instruction with 'continue' " - "flag set to 'true' or set the flag to 'false'. " - "You can either remove `functions` and/or `function_call` parameters." - ) - - -def test_function_call_func_name_last_continue_function_call(): - messages = [ - ChatMLMessage( - **{"role": "system", "name": "situation", "content": "I am talking to John"} - ), - ChatMLMessage( - **{"role": "assistant", "name": "Samantha", "content": "Hey John"} - ), - ChatMLMessage(**{"role": "user", "name": "John", "content": "Hey!"}), - ChatMLMessage( - **{ - "role": "function_call", - "content": '{"name": "generate_anagram", ', - "continue": True, - } - ), - ] - functions = [ - FunctionDef( - **{ - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ) - ] - with pytest.raises(InvalidPromptException) as e_info: - to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - functions=functions, - function_call=FunctionCall(**{"name": "generate_anagram"}), - ) - assert e_info.value.args[0] == ( - "Invalid prompt format: Conflicting instructions, " - "please remove the last instruction with 'continue' " - "flag set to 'true' or set the flag to 'false'. " - "You can either remove `functions` and/or `function_call` parameters." - ) - - -def test_information_message(): - messages = [ - ChatMLMessage( - **{ - "role": "system", - "name": "information", - "content": "I am talking to John", - } - ) - ] - - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - ) - - assert ( - prompt - == """<|im_start|>information -I am talking to John<|im_end|> -<|im_start|>me -""" - ) - - -def test_situation_name_is_none(): - messages = [ - ChatMLMessage( - **{ - "role": "system", - "content": "I am talking to John", - } - ) - ] - - prompt = to_prompt( - messages, - bos="<|im_start|>", - eos="<|im_end|>", - ) - - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me -""" - ) diff --git a/model-serving/model_api/dependencies/__init__.py b/model-serving/model_api/dependencies/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/model-serving/model_api/dependencies/auth.py b/model-serving/model_api/dependencies/auth.py deleted file mode 100644 index 5df7787ad..000000000 --- a/model-serving/model_api/dependencies/auth.py +++ /dev/null @@ -1,19 +0,0 @@ -from fastapi.security.api_key import APIKeyHeader -from fastapi import Security, HTTPException -from starlette.status import HTTP_403_FORBIDDEN - -from ..env import api_key, api_key_header_name - - -api_key_header = APIKeyHeader(name=api_key_header_name, auto_error=False) - - -async def get_api_key(user_api_key: str = Security(api_key_header)): - user_api_key = (user_api_key or "").replace("Bearer ", "").strip() - - if user_api_key != api_key: - raise HTTPException( - status_code=HTTP_403_FORBIDDEN, detail="Could not validate API KEY" - ) - else: - return user_api_key diff --git a/model-serving/model_api/dependencies/developer.py b/model-serving/model_api/dependencies/developer.py deleted file mode 100644 index 5edee7c35..000000000 --- a/model-serving/model_api/dependencies/developer.py +++ /dev/null @@ -1,41 +0,0 @@ -import uuid -from typing import Annotated -from fastapi import Header -from pydantic import validate_email -from pydantic_core import PydanticCustomError - -from ..env import skip_check_developer_headers -from .exceptions import InvalidHeaderFormat - - -async def get_developer_id(x_developer_id: Annotated[str | None, Header()] = None): - if skip_check_developer_headers: - return x_developer_id or uuid.UUID("00000000-0000-0000-0000-000000000000") - - if not x_developer_id: - raise InvalidHeaderFormat("X-Developer-Id header invalid") - - if isinstance(x_developer_id, str): - try: - x_developer_id = uuid.UUID(x_developer_id, version=4) - except ValueError: - raise InvalidHeaderFormat("X-Developer-Id must be a valid UUID") - - return x_developer_id - - -async def get_developer_email( - x_developer_email: Annotated[str | None, Header()] = None -): - if skip_check_developer_headers: - return x_developer_email or "unknown_user@mail.com" - - if not x_developer_email: - raise InvalidHeaderFormat("X-Developer-Email header invalid") - - try: - validate_email(x_developer_email) - except PydanticCustomError: - raise InvalidHeaderFormat("X-Developer-Email header invalid") - - return x_developer_email diff --git a/model-serving/model_api/dependencies/exceptions.py b/model-serving/model_api/dependencies/exceptions.py deleted file mode 100644 index 491868fa6..000000000 --- a/model-serving/model_api/dependencies/exceptions.py +++ /dev/null @@ -1,2 +0,0 @@ -class InvalidHeaderFormat(Exception): - pass diff --git a/model-serving/model_api/env.py b/model-serving/model_api/env.py deleted file mode 100644 index 7cbe420cf..000000000 --- a/model-serving/model_api/env.py +++ /dev/null @@ -1,36 +0,0 @@ -from pprint import pprint - -from environs import Env - - -env = Env() -env.read_env() - - -sentry_dsn: str = env.str("SENTRY_DSN", default="") -api_key: str = env.str("MODEL_API_KEY") -api_key_header_name: str = env.str("MODEL_API_KEY_HEADER_NAME", default="X-Auth-Key") -host: str = env.str("MODEL_API_HOST", default="0.0.0.0") -port: int = env.int("MODEL_API_PORT", default=8000) -backlog: int = env.int("MODEL_API_BACKLOG", default=2048) -skip_check_developer_headers: bool = env.bool( - "SKIP_CHECK_DEVELOPER_HEADERS", default=False -) -temperature_scaling_factor: float = env.float("TEMPERATURE_SCALING_FACTOR", default=1.0) -temperature_scaling_power: float = env.float("TEMPERATURE_SCALING_POWER", default=1.0) - -environment = dict( - sentry_dsn=sentry_dsn, - api_key=api_key, - api_key_header_name=api_key_header_name, - host=host, - port=port, - backlog=backlog, - skip_check_developer_headers=skip_check_developer_headers, - temperature_scaling_factor=temperature_scaling_factor, - temperature_scaling_power=temperature_scaling_power, -) - -print("Environment variables:") -pprint(environment) -print() diff --git a/model-serving/model_api/function_classifier.py b/model-serving/model_api/function_classifier.py deleted file mode 100644 index 66372ca33..000000000 --- a/model-serving/model_api/function_classifier.py +++ /dev/null @@ -1,50 +0,0 @@ -from functools import cache -import os -import pickle - -import torch - -from .tokens import tag_start_id_map - - -@cache -def load_function_classifier( - path: str = os.path.join( - os.path.dirname(__file__), "../artifacts/function_classifier.bin" - ) -): - """ - Load the classifier model from disk. - """ - - with open(path, "rb") as f: - return pickle.load(f) - - -def classify_function_call(logit_tensor: torch.Tensor) -> bool: - """ - Classify the input logit tensor as a function call or not. - """ - - # Load the classifier - classifier = load_function_classifier() - - # Get input - valid_tag_start_ids = list(tag_start_id_map.values()) - - # Only get the logits for the valid tag start ids - input = logit_tensor[valid_tag_start_ids] - - # Convert to numpy (bfloat16 is not supported by numpy) - input = input.cpu().to(dtype=torch.float16).numpy() - - # Reshape since the classifier expects a 2D array - # (1, -1) means 1 row and as many columns as needed - input = input.reshape(1, -1) - - # Get prediction - output = classifier.predict(input) - prediction = output[0] - prediction = bool(prediction) - - return prediction diff --git a/model-serving/model_api/logger.py b/model-serving/model_api/logger.py deleted file mode 100644 index 95260a3ef..000000000 --- a/model-serving/model_api/logger.py +++ /dev/null @@ -1,9 +0,0 @@ -import logging - - -logger = logging.getLogger(__name__) -h = logging.StreamHandler() -f = logging.Formatter("[%(asctime)s/%(levelname)s] - %(message)s") -h.setFormatter(f) -logger.addHandler(h) -logger.setLevel(logging.DEBUG) diff --git a/model-serving/model_api/logits_processors.py b/model-serving/model_api/logits_processors.py deleted file mode 100644 index 75391e7e2..000000000 --- a/model-serving/model_api/logits_processors.py +++ /dev/null @@ -1,55 +0,0 @@ -import torch - -from .function_classifier import classify_function_call -from .tokens import tag_start_id_map - -allowed_tag_start_token_id = list(tag_start_id_map.values()) - - -def drop_disallowed_start_tags( - previously_generated_tokens: list[int], - next_token_logits: torch.Tensor, -) -> torch.Tensor: - """ - Logits processor that sets the next token logits to -inf for all tokens that - do NOT correspond to allowed tag start tokens. - """ - - if len(previously_generated_tokens) > 0: - return next_token_logits - - next_token_logits_copy = next_token_logits.clone() - - # Creating a mask that is True for all elements except those at token indices of allowed - mask = torch.ones_like(next_token_logits_copy, dtype=torch.bool) - mask[allowed_tag_start_token_id] = False - - # Setting all except allowed to -inf - next_token_logits_copy[mask] = float("-inf") - - return next_token_logits_copy - - -def fix_function_call_prediction( - previously_generated_tokens: list[int], - next_token_logits: torch.Tensor, -) -> torch.Tensor: - """ - Logits processor that either allows or disallows the generation of function calls. - """ - - if len(previously_generated_tokens) > 0: - return next_token_logits - - next_token_logits_copy = next_token_logits.clone() - is_function_call = classify_function_call(next_token_logits_copy) - correct_tag_id = tag_start_id_map["function_call" if is_function_call else "me"] - - # Creating a mask that is True for all elements except the corrected tag - mask = torch.ones_like(next_token_logits_copy, dtype=torch.bool) - mask[correct_tag_id] = False - - # Setting all except allowed to negative inf - next_token_logits_copy[mask] = float("-inf") - - return next_token_logits_copy diff --git a/model-serving/model_api/metrics.py b/model-serving/model_api/metrics.py deleted file mode 100644 index 93b2d4eba..000000000 --- a/model-serving/model_api/metrics.py +++ /dev/null @@ -1,121 +0,0 @@ -import psutil -from aioprometheus import Gauge, Counter, MetricsMiddleware -from pynvml import ( - nvmlInit, - nvmlDeviceGetCount, - nvmlDeviceGetHandleByIndex, - nvmlDeviceGetMemoryInfo, - nvmlDeviceGetUtilizationRates, -) - -nvmlInit() - - -def _gpu_usage(): - res = [] - for index in range(nvmlDeviceGetCount()): - handle = nvmlDeviceGetHandleByIndex(index) - info = nvmlDeviceGetMemoryInfo(handle) - try: - util = nvmlDeviceGetUtilizationRates(handle) - util_gpu = util.gpu - util_memory = util.memory - except Exception: - util_gpu = 0 - util_memory = 0 - - res.append( - { - "mem_total": info.total, - "mem_free": info.free, - "mem_used": info.used, - "gpu_util_percents": util_gpu, - "gpu_memory_percents": util_memory, - } - ) - - return res - - -cpu_percent_metric = Gauge( - "cpu_percent_usage_info", - "CPU percent usage info", -) -mem_total_metric = Gauge( - "mem_total_info", - "Total memory info", -) -mem_free_metric = Gauge( - "mem_free_info", - "Free memory info", -) -mem_used_metric = Gauge( - "mem_used_info", - "Used memory info", -) -mem_percent_metric = Gauge( - "mem_percent_info", - "Memory used percentage info", -) - - -gpu_mem_total_metric = Gauge( - "gpu_mem_total", - "GPU memory total", -) -gpu_mem_free_metric = Gauge( - "gpu_mem_free", - "GPU memory free", -) -gpu_mem_used_metric = Gauge( - "gpu_mem_used", - "GPU memory used", -) -gpu_util_percents_metric = Gauge( - "gpu_util_percents", - "GPU utilization percents", -) -gpu_memory_percents_metric = Gauge( - "gpu_memory_percents", - "GPU utilization percents", -) - - -tokens_per_user_metric = Counter( - "total_tokens_per_user", - "Total tokens per user", -) - - -generation_time_metric = Gauge( - "model_response_generation_time", - "Model response generation time", -) - - -generated_tokens_per_second_metric = Gauge( - "generated_token_per_second", - "Generated tokens per second", -) - - -class MetricsMiddleware(MetricsMiddleware): - async def __call__(self, *args, **kwargs): - mem = psutil.virtual_memory() - - cpu_percent_metric.set({}, psutil.cpu_percent()) - mem_total_metric.set({}, mem.total) - mem_free_metric.set({}, mem.free) - mem_used_metric.set({}, mem.used) - mem_percent_metric.set({}, mem.percent) - - usage = _gpu_usage() - for idx, u in enumerate(usage): - idx = str(idx) - gpu_mem_total_metric.set({"gpu_index": idx}, u["mem_total"]) - gpu_mem_free_metric.set({"gpu_index": idx}, u["mem_free"]) - gpu_mem_used_metric.set({"gpu_index": idx}, u["mem_used"]) - gpu_util_percents_metric.set({"gpu_index": idx}, u["gpu_util_percents"]) - gpu_memory_percents_metric.set({"gpu_index": idx}, u["gpu_memory_percents"]) - - return await super().__call__(*args, **kwargs) diff --git a/model-serving/model_api/protocol.py b/model-serving/model_api/protocol.py deleted file mode 100644 index ba566abdd..000000000 --- a/model-serving/model_api/protocol.py +++ /dev/null @@ -1,479 +0,0 @@ -from enum import Enum -from typing import Literal, TypeAlias, Union, Optional -from pydantic import BaseModel, Field, ConfigDict, validator -from vllm.entrypoints.openai.protocol import ( - CompletionRequest, - ChatCompletionRequest, - ChatCompletionResponseChoice, - ChatCompletionResponseStreamChoice, - ChatCompletionStreamResponse, - ChatCompletionResponse, - ChatMessage, - DeltaMessage, -) -from vllm.sampling_params import LogitsProcessor, SamplingParams -from .conversion.datatypes import ChatML - - -DEFAULT_MAX_TOKENS = 4000 - - -class FunctionCall(BaseModel): - name: str - - -RequestFunctionCall: TypeAlias = Union[Literal["none", "auto"], FunctionCall] - - -class ToolCall(BaseModel): - id: str - type: Literal["function"] - function: str - - -class ChatMessage(ChatMessage): - name: str | None = None - function_call: str | None = None - tool_calls: list[ToolCall] | None = None - content: str | None = None - - -class DeltaMessage(DeltaMessage): - name: str | None = None - function_call: str | None = None - tool_calls: list[ToolCall] | None = None - - -class ChatCompletionResponseChoice(ChatCompletionResponseChoice): - message: ChatMessage - finish_reason: Literal["stop", "length", "function_call", "tool_calls"] | None = ( - None - ) - - -class ChatCompletionResponseStreamChoice(ChatCompletionResponseStreamChoice): - delta: DeltaMessage - - -class ChatCompletionStreamResponse(ChatCompletionStreamResponse): - choices: list[ChatCompletionResponseStreamChoice] - - -class ResponseFormat(BaseModel): - type_: str = Field(..., alias="type") - - -class FunctionParameters(BaseModel): - model_config = ConfigDict(extra="allow") - - -class FunctionDef(BaseModel): - name: str - description: str | None = None - parameters: FunctionParameters - - -class ToolType(Enum): - function = "function" - - -class NamedToolChoice(BaseModel): - type: ToolType - function: FunctionCall - - -ToolChoice: TypeAlias = Union[Literal["none", "auto"], NamedToolChoice] - - -class Type(Enum): - function = "function" - webhook = "webhook" - - -class Tool(BaseModel): - type: Type - function: FunctionDef - id: Optional[str] = None - - -class SamplingParams(SamplingParams): - _properties = [ - "n", - "best_of", - "presence_penalty", - "frequency_penalty", - "repetition_penalty", - "temperature", - "top_p", - "top_k", - "min_p", - "seed", - "use_beam_search", - "length_penalty", - "early_stopping", - "stop", - "stop_token_ids", - "include_stop_str_in_output", - "ignore_eos", - "max_tokens", - "logprobs", - "prompt_logprobs", - "skip_special_tokens", - "spaces_between_special_tokens", - "logits_processors", - ] - - def __init__( - self, - n: int = 1, - best_of: int | None = None, - presence_penalty: float = 0.0, - frequency_penalty: float = 0.01, # Custom - repetition_penalty: float = 1.0, - temperature: float = 0.0, # Custom - top_p: float = 0.99, # Custom - top_k: int = -1, - min_p: float = 0.01, # Custom - seed: int | None = None, - use_beam_search: bool = False, - length_penalty: float = 1.0, - early_stopping: bool | str = False, - stop: str | list[str] | None = None, - stop_token_ids: list[int] | None = None, - include_stop_str_in_output: bool = False, - ignore_eos: bool = False, - max_tokens: int | None = DEFAULT_MAX_TOKENS, # Custom - logprobs: int | None = None, - prompt_logprobs: int | None = None, - skip_special_tokens: bool = True, - spaces_between_special_tokens: bool = False, # Custom - logits_processors: list[LogitsProcessor] | None = None, - ) -> None: - super().__init__( - n=n, - best_of=best_of, - presence_penalty=presence_penalty, - frequency_penalty=frequency_penalty, - repetition_penalty=repetition_penalty, - temperature=temperature, - top_p=top_p, - top_k=top_k, - min_p=min_p, - seed=seed, - use_beam_search=use_beam_search, - length_penalty=length_penalty, - early_stopping=early_stopping, - stop=stop, - stop_token_ids=stop_token_ids, - include_stop_str_in_output=include_stop_str_in_output, - ignore_eos=ignore_eos, - max_tokens=max_tokens, - logprobs=logprobs, - prompt_logprobs=prompt_logprobs, - skip_special_tokens=skip_special_tokens, - spaces_between_special_tokens=spaces_between_special_tokens, - logits_processors=logits_processors, - ) - - def __eq__(self, other): - for p in self._properties: - if getattr(self, p) != getattr(other, p): - return False - - return True - - -class Preset(str, Enum): - problem_solving = "problem_solving" - conversational = "conversational" - fun = "fun" - prose = "prose" - creative = "creative" - business = "business" - deterministic = "deterministic" - code = "code" - multilingual = "multilingual" - - def get_settings(self): - return getattr(self, f"_get_settings_{self.name}", "_get_settings_default")() - - def _get_settings_problem_solving(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.0, - repetition_penalty=1.0, - temperature=0, - top_p=1.0, - min_p=0.0, - best_of=10, - top_k=-1, - use_beam_search=True, - length_penalty=1.0, - seed=None, - ) - - def _get_settings_conversational(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.0, - repetition_penalty=1.02, - temperature=0.7, - top_p=0.99, - min_p=0.01, - best_of=1, - top_k=-1, - use_beam_search=False, - length_penalty=1.0, - seed=None, - ) - - def _get_settings_fun(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.0, - repetition_penalty=1.05, - temperature=1.2, - top_p=1.0, - min_p=0.015, - best_of=2, - top_k=-1, - use_beam_search=False, - length_penalty=1.0, - seed=None, - ) - - def _get_settings_prose(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.0, - repetition_penalty=1.025, - temperature=0.9, - top_p=1.0, - min_p=0.02, - best_of=2, - top_k=50, - use_beam_search=False, - length_penalty=1.0, - seed=None, - ) - - def _get_settings_creative(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.0, - repetition_penalty=1.1, - temperature=1.2, - top_p=1.0, - min_p=0.02, - best_of=3, - top_k=10, - use_beam_search=False, - length_penalty=1.0, - seed=None, - ) - - def _get_settings_business(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.1, - repetition_penalty=1.1, - temperature=0.5, - top_p=0.98, - min_p=0.05, - best_of=2, - top_k=5, - use_beam_search=False, - length_penalty=1.0, - seed=1, - ) - - def _get_settings_deterministic(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.0, - repetition_penalty=1.0, - temperature=0.0, - top_p=1.0, - min_p=0.0, - best_of=1, - top_k=-1, - use_beam_search=False, - length_penalty=1.0, - seed=1, - ) - - def _get_settings_code(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.0, - repetition_penalty=1.0, - temperature=0, - top_p=1.0, - min_p=0.0, - best_of=3, - top_k=-1, - use_beam_search=True, - length_penalty=1.0, - seed=1, - ) - - def _get_settings_multilingual(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.0, - repetition_penalty=1.0, - temperature=None, - top_p=None, - min_p=None, - best_of=1, - top_k=-1, - use_beam_search=False, - length_penalty=1.0, - seed=None, - ) - - def _get_settings_default(self): - return dict( - n=1, - presence_penalty=0.0, - frequency_penalty=0.0, - repetition_penalty=1.0, - temperature=0.0, - top_p=0.99, - min_p=0.01, - best_of=1, - top_k=-1, - use_beam_search=False, - length_penalty=1.0, - seed=None, - ) - - -class ChatCompletionRequest(ChatCompletionRequest): - model_config = ConfigDict(extra="forbid", validate_assignment=True) - - functions: list[FunctionDef] | None = None - function_call: RequestFunctionCall | None = None - tools: list[Tool] | None = None - tool_choice: ToolChoice | None = None - response_format: ResponseFormat | None = None - messages: ChatML - - spaces_between_special_tokens: bool | None = False # Custom - max_tokens: int | None = DEFAULT_MAX_TOKENS # Custom - temperature: float | None = 0.0 # Custom - frequency_penalty: float | None = 0.01 # Custom - top_p: float | None = 0.99 # Custom - min_p: float | None = 0.01 # Custom - preset: Preset | None = None - - def to_sampling_params(self) -> SamplingParams: - settings = dict( - n=self.n or 1, - presence_penalty=self.presence_penalty or 0.0, - frequency_penalty=self.frequency_penalty or 0.0, - repetition_penalty=self.repetition_penalty or 1.0, - temperature=self.temperature or 0.0, - top_p=self.top_p or 0.99, - min_p=self.min_p or 0.01, - best_of=self.best_of, - top_k=self.top_k or -1, - use_beam_search=self.use_beam_search or False, - length_penalty=self.length_penalty or 1.0, - seed=self.seed, - ) - if self.preset is not None: - settings = self.preset.get_settings() - - echo_without_generation = self.echo and self.max_tokens == 0 - - if self.logit_bias is not None: - raise ValueError("logit_bias is not supported currently.") - - return SamplingParams( - stop=self.stop, - stop_token_ids=self.stop_token_ids, - max_tokens=( - (self.max_tokens or DEFAULT_MAX_TOKENS) - if not echo_without_generation - else 1 - ), - ignore_eos=self.ignore_eos or False, - skip_special_tokens=self.skip_special_tokens or True, - spaces_between_special_tokens=self.spaces_between_special_tokens or False, - include_stop_str_in_output=self.include_stop_str_in_output or False, - **settings, - ) - - @validator("max_tokens") - def set_max_tokens(cls, max_tokens): - return max_tokens if max_tokens is not None else DEFAULT_MAX_TOKENS - - @validator("stream") - def set_stream(cls, stream): - return stream or False - - -class CompletionRequest(CompletionRequest): - model_config = ConfigDict(extra="forbid") - - spaces_between_special_tokens: bool | None = False # Custom - max_tokens: int | None = DEFAULT_MAX_TOKENS # Custom - temperature: float | None = 0.0 # Custom - frequency_penalty: float | None = 0.01 # Custom - top_p: float | None = 0.99 # Custom - min_p: float | None = 0.01 # Custom - preset: Preset | None = None - - def to_sampling_params(self) -> SamplingParams: - echo_without_generation = self.echo and self.max_tokens == 0 - - if self.logit_bias is not None: - raise ValueError("logit_bias is not supported currently.") - - settings = dict( - n=self.n or 1, - presence_penalty=self.presence_penalty or 0.0, - frequency_penalty=self.frequency_penalty or 0.0, - repetition_penalty=self.repetition_penalty or 1.0, - temperature=self.temperature or 0.0, - top_p=self.top_p or 0.99, - min_p=self.min_p or 0.01, - best_of=self.best_of, - top_k=self.top_k or -1, - use_beam_search=self.use_beam_search or False, - length_penalty=self.length_penalty or 1.0, - seed=self.seed, - ) - if self.preset is not None: - settings = self.preset.get_settings() - - return SamplingParams( - stop=self.stop, - stop_token_ids=self.stop_token_ids, - ignore_eos=self.ignore_eos or False, - max_tokens=( - (self.max_tokens or DEFAULT_MAX_TOKENS) - if not echo_without_generation - else 1 - ), - logprobs=self.logprobs, - prompt_logprobs=self.logprobs if self.echo else None, - skip_special_tokens=self.skip_special_tokens or True, - spaces_between_special_tokens=self.spaces_between_special_tokens or False, - include_stop_str_in_output=self.include_stop_str_in_output or False, - **settings, - ) - - -class ChatCompletionResponse(ChatCompletionResponse): - choices: list[ChatCompletionResponseChoice] diff --git a/model-serving/model_api/tokens.py b/model-serving/model_api/tokens.py deleted file mode 100644 index 7dfcb95ab..000000000 --- a/model-serving/model_api/tokens.py +++ /dev/null @@ -1,17 +0,0 @@ -bos_token = "" -eos_token = "<|im_end|>" - -bos_token_id: int = 1 # -eos_token_id: int = 32000 # <|im_end|> - -tag_ids_map = { - "me": [528], - "function_call": [908, 28730, 2845], - "thought": [1654], - "situation": [4620], - "person": [1338], - "functions": [5572], - "information": [1871], -} - -tag_start_id_map = {tag: ids[0] for tag, ids in tag_ids_map.items()} diff --git a/model-serving/model_api/utils.py b/model-serving/model_api/utils.py deleted file mode 100644 index 1b495e538..000000000 --- a/model-serving/model_api/utils.py +++ /dev/null @@ -1,101 +0,0 @@ -import re -import string -import random -from typing import AsyncIterator, Any - -from interegular.patterns import _ParsePattern -from lmformatenforcer import CharacterLevelParser -from lmformatenforcer.integrations.vllm import ( - build_vllm_logits_processor, -) -from lmformatenforcer.integrations.transformers import ( - build_token_enforcer_tokenizer_data, -) -from lmformatenforcer import TokenEnforcerTokenizerData -from pydantic import BaseModel -from vllm import LLM -from vllm.outputs import RequestOutput - -from .protocol import SamplingParams -from .conversion.datatypes import ChatML - - -ListOrStrList = str | list[str] - -remove_last_space_re = re.compile(r"[^ ]+ {1}$") - - -def build_vllm_token_enforcer_tokenizer_data(tokenizer) -> TokenEnforcerTokenizerData: - # In some vLLM versions the tokenizer is wrapped in a TokenizerGroup - if tokenizer.__class__.__name__ == "TokenizerGroup": - tokenizer = tokenizer.tokenizer # noqa - return build_token_enforcer_tokenizer_data(tokenizer) - - -def vllm_with_character_level_parser( - engine: LLM, - tokenizer, - prompt: ListOrStrList, - sampling_params: SamplingParams, - request_id: str, - parser: CharacterLevelParser | None = None, -) -> AsyncIterator[RequestOutput]: - tokenizer_data = build_vllm_token_enforcer_tokenizer_data(tokenizer) - - if parser: - logits_processor = build_vllm_logits_processor(tokenizer_data, parser) - sampling_params.logits_processors = [logits_processor] - - return engine.generate(prompt, sampling_params, request_id) - - -class FunctionCallResult(BaseModel): - name: str - arguments: dict[str, Any] - - -def rescale_temperature( - temperature: float, - scaling_factor: float, - power: float = 1.0, -) -> float: - return (temperature**power) * scaling_factor - - -def validate_interegular_regex(pattern: str) -> bool: - try: - _ParsePattern(pattern).parse() - return True - except Exception: - return False - - -def random_tool_id(n: int = 8) -> str: - return "tool-" + "".join(random.choices(string.digits, k=n)) - - -def remove_last_space(prompt: str): - if remove_last_space_re.search(prompt): - return prompt[:-1] - - return prompt - - -def flatten(lst): - result = [] - for i in lst: - if isinstance(i, list): - result.extend(flatten(i)) - else: - result.append(i) - - return result - - -def escape_special_tokens(messages: ChatML, tokens: list[str]): - for m in messages: - if m.content is None: - continue - - for t in tokens: - m.content = m.content.replace(t, f"{t[0]} {t[1:]}") diff --git a/model-serving/model_api/web.py b/model-serving/model_api/web.py deleted file mode 100644 index 701eae3c9..000000000 --- a/model-serving/model_api/web.py +++ /dev/null @@ -1,1022 +0,0 @@ -import argparse -import asyncio -from contextlib import suppress -from http import HTTPStatus -import json -import logging -import time -from typing import AsyncGenerator, Annotated - -from aioprometheus.asgi.starlette import metrics -from fastapi.middleware.cors import CORSMiddleware -from fastapi import FastAPI, BackgroundTasks, Request, Depends -from fastapi.responses import Response, JSONResponse, StreamingResponse -from fastapi.exceptions import RequestValidationError -from jsonschema.exceptions import ValidationError -from pydantic import ValidationError as PydanticValidationError -from lmformatenforcer import JsonSchemaParser -from pydantic import UUID4 -import sentry_sdk - -# from vllm.engine.metrics import add_global_metrics_labels -from vllm.engine.arg_utils import AsyncEngineArgs -from vllm.engine.async_llm_engine import AsyncLLMEngine -from vllm.utils import random_uuid -from vllm.transformers_utils.tokenizer import get_tokenizer -from vllm.entrypoints.openai.protocol import ( - CompletionResponse, - CompletionResponseChoice, - CompletionResponseStreamChoice, - CompletionStreamResponse, - ErrorResponse, - LogProbs, - ModelCard, - ModelList, - ModelPermission, - UsageInfo, -) -from vllm.outputs import RequestOutput - -from .conversion.conversions import to_prompt, parse_message -from .conversion.datatypes import ChatMLMessage - -from .conversion.exceptions import ( - InvalidPromptException, - InvalidFunctionName, -) -from .logger import logger -from .env import ( - sentry_dsn, - temperature_scaling_factor, - temperature_scaling_power, -) - -from .metrics import ( - tokens_per_user_metric, - generation_time_metric, - generated_tokens_per_second_metric, - MetricsMiddleware, -) -from .dependencies.auth import get_api_key -from .dependencies.developer import get_developer_id, get_developer_email -from .dependencies.exceptions import InvalidHeaderFormat -from .utils import ( - vllm_with_character_level_parser, - FunctionCallResult, - rescale_temperature, - random_tool_id, - remove_last_space, - escape_special_tokens, - flatten, -) -from .protocol import ( - CompletionRequest, - ChatCompletionRequest, - ChatCompletionStreamResponse, - ChatCompletionResponseChoice, - ChatCompletionResponseStreamChoice, - ChatMessage, - DeltaMessage, - Type, - ToolCall, - NamedToolChoice, - FunctionCall, - ChatCompletionResponse, -) -from .logits_processors import drop_disallowed_start_tags, fix_function_call_prediction - - -DEFAULT_BOS = "<|im_start|>" -DEFAULT_EOS = "<|im_end|>" - - -engine = None -engine_model_config = None -tokenizer = None -served_model = None - - -model_settings = { - "julep-ai/samantha-1-turbo": { - "section_start_tag": "<|im_start|>", - "section_end_tag": "<|im_end|>", - } -} - - -if not sentry_dsn: - print("Sentry DSN not found. Sentry will not be enabled.") -else: - sentry_sdk.init( - dsn=sentry_dsn, - enable_tracing=True, - ) - - -class EndpointFilter(logging.Filter): - def __init__(self, endpoints: list[str], *args, **kwargs): - super().__init__(*args, **kwargs) - self._endpoints = endpoints - - def filter(self, record: logging.LogRecord) -> bool: - return all([record.getMessage().find(e) == -1 for e in self._endpoints]) - - -logging.getLogger("uvicorn.access").addFilter( - EndpointFilter(["/docs", "/status", "/metrics"]), -) - - -app = FastAPI(dependencies=[Depends(get_api_key)]) - - -TIMEOUT_KEEP_ALIVE = 30 # seconds. -AGENT_NAME = "Samantha" - - -# QUESTION: Can we have a detailed explanation of the logprobs creation process? -def create_logprobs( - token_ids: list[int], - id_logprobs: list[dict[int, float]], - initial_text_offset: int = 0, -) -> LogProbs: - """Create OpenAI-style logprobs.""" - logprobs = LogProbs() - last_token_len = 0 - for token_id, id_logprob in zip(token_ids, id_logprobs): - token = tokenizer.convert_ids_to_tokens(token_id) - logprobs.tokens.append(token) - logprobs.token_logprobs.append(id_logprob[token_id]) - if len(logprobs.text_offset) == 0: - logprobs.text_offset.append(initial_text_offset) - else: - logprobs.text_offset.append(logprobs.text_offset[-1] + last_token_len) - last_token_len = len(token) - - logprobs.top_logprobs.append( - {tokenizer.convert_ids_to_tokens(i): p for i, p in id_logprob.items()} - ) - return logprobs - - -# QUESTION: Please clarify how the maximum context length is determined for different model configurations. -async def check_length(request, prompt, model_config): - if hasattr(model_config.hf_config, "max_sequence_length"): - context_len = model_config.hf_config.max_sequence_length - elif hasattr(model_config.hf_config, "seq_length"): - context_len = model_config.hf_config.seq_length - elif hasattr(model_config.hf_config, "max_position_embeddings"): - context_len = model_config.hf_config.max_position_embeddings - elif hasattr(model_config.hf_config, "seq_length"): - context_len = model_config.hf_config.seq_length - else: - context_len = 2048 - - input_ids = tokenizer(prompt).input_ids - token_num = len(input_ids) - - if token_num + request.max_tokens > context_len: - return create_error_response( - HTTPStatus.BAD_REQUEST, - f"This model's maximum context length is {context_len} tokens. " - f"However, you requested {request.max_tokens + token_num} tokens " - f"({token_num} in the messages, " - f"{request.max_tokens} in the completion). " - f"Please reduce the length of the messages or completion.", - ) - else: - return None - - -@app.exception_handler(InvalidPromptException) -async def invalid_prompt_exception_handler( - request: Request, exc: InvalidPromptException -): - return JSONResponse( - status_code=400, - content={"error": {"message": str(exc), "code": "invalid prompt"}}, - ) - - -@app.exception_handler(json.decoder.JSONDecodeError) -async def json_decode_error_handler( - request: Request, exc: json.decoder.JSONDecodeError -): - return JSONResponse( - status_code=400, - content={"error": {"message": str(exc), "code": "invalid json input"}}, - ) - - -@app.exception_handler(InvalidFunctionName) -async def invalid_function_name_handler(request: Request, exc: InvalidFunctionName): - return JSONResponse( - status_code=400, - content={"error": {"message": str(exc), "code": "invalid function call"}}, - ) - - -@app.exception_handler(ValidationError) -async def validation_error_handler(request: Request, exc: ValidationError): - return JSONResponse( - status_code=400, - content={"error": {"message": str(exc), "code": "invalid functions parameter"}}, - ) - - -@app.exception_handler(PydanticValidationError) -async def pydantic_validation_error_handler( - request: Request, exc: PydanticValidationError -): - return JSONResponse( - status_code=400, - content={ - "error": {"message": str(exc), "code": "invalid request parameter(s)"} - }, - ) - - -@app.exception_handler(InvalidHeaderFormat) -async def invalid_dev_header_error_handler(request: Request, exc: InvalidHeaderFormat): - return JSONResponse( - status_code=400, - content={ - "error": { - "message": "The API key used has invalid metadata. Please contact support for fixing this issue", - "code": "invalid API key", - } - }, - ) - - -def create_error_response( - status_code: HTTPStatus, - message: str, -) -> JSONResponse: - return JSONResponse( - ErrorResponse( - message=message, - type="invalid_request_error", - code=status_code.value, - ).dict(), - status_code=status_code.value, - ) - - -@app.exception_handler(RequestValidationError) -async def validation_exception_handler(request, exc): # pylint: disable=unused-argument - return create_error_response(HTTPStatus.BAD_REQUEST, str(exc)) - - -async def check_model(request) -> JSONResponse | None: - if request.model == served_model: - return - ret = create_error_response( - HTTPStatus.NOT_FOUND, - f"The model `{request.model}` does not exist.", - ) - return ret - - -@app.get("/v1/models") -async def show_available_models(): - """Show available models. Right now we only have one model.""" - model_cards = [ - ModelCard( - id=served_model, - root=served_model, - permission=[ModelPermission()], - ) - ] - return ModelList(data=model_cards) - - -def _write_metrics( - total_gen_time: float, - total_tokens: float, - developer: UUID4 | None = None, - email: UUID4 | None = None, -): - developer = str(developer) - email = str(email) - generation_time_metric.set({"developer": developer, "email": email}, total_gen_time) - tokens_per_user_metric.add({"developer": developer, "email": email}, total_tokens) - generated_tokens_per_second_metric.set( - {"developer": developer, "email": email}, total_tokens / total_gen_time - ) - - -# QUESTION: Could the logic for handling unsupported features (echo, suffix, logit_bias) be simplified or modularized? -@app.post("/v1/completions") -async def completions( - raw_request: Request, - background_tasks: BackgroundTasks, - x_developer_id: Annotated[UUID4 | None, Depends(get_developer_id)] = None, - x_developer_email: Annotated[UUID4 | None, Depends(get_developer_email)] = None, -) -> Response: - """Completion API similar to OpenAI's API. - - See https://platform.openai.com/docs/api-reference/completions/create - for the API specification. This API mimics the OpenAI Completion API. - - NOTE: Currently we do not support the following features: - - echo (since the vLLM engine does not currently support - getting the logprobs of prompt tokens) - - suffix (the language models we currently support do not support - suffix) - - logit_bias (to be supported by vLLM engine) - """ - request = CompletionRequest(**await raw_request.json()) - logger.info(f"Received completion request: {request}") - - error_check_ret = await check_model(request) - if error_check_ret is not None: - return error_check_ret - - if request.echo: - # We do not support echo since the vLLM engine does not - # currently support getting the logprobs of prompt tokens. - return create_error_response( - HTTPStatus.BAD_REQUEST, "echo is not currently supported" - ) - - if request.suffix is not None: - # The language models we currently support do not support suffix. - return create_error_response( - HTTPStatus.BAD_REQUEST, "suffix is not currently supported" - ) - - if request.logit_bias is not None: - # TODO: support logit_bias in vLLM engine. - return create_error_response( - HTTPStatus.BAD_REQUEST, "logit_bias is not currently supported" - ) - - model_name = request.model - request_id = f"cmpl-{random_uuid()}" - if isinstance(request.prompt, list): - if len(request.prompt) == 0: - return create_error_response( - HTTPStatus.BAD_REQUEST, "please provide at least one prompt" - ) - if len(request.prompt) > 1: - return create_error_response( - HTTPStatus.BAD_REQUEST, - "multiple prompts in a batch is not currently supported", - ) - prompt = request.prompt[0] - else: - prompt = request.prompt - created_time = int(time.time()) - - try: - sampling_params = request.to_sampling_params() - except ValueError as e: - return create_error_response(HTTPStatus.BAD_REQUEST, str(e)) - - # Rescale the temperature - sampling_params.temperature = rescale_temperature( - sampling_params.temperature, - temperature_scaling_factor, - power=temperature_scaling_power, # Set it to lower than 1.0 to punish high temperatures more - ) - - prompt = remove_last_space(prompt) - - bos = model_settings.get(request.model, {}).get("section_start_tag", DEFAULT_BOS) - if prompt.endswith(bos): - if sampling_params.logits_processors is None: - sampling_params.logits_processors = [] - - sampling_params.logits_processors.append(drop_disallowed_start_tags) - - result_generator = engine.generate( - prompt, - sampling_params, - request_id, - ) - - # Similar to the OpenAI API, when n != best_of, we do not stream the - # results. In addition, we do not stream the results when use beam search. - stream = ( - request.stream - and (request.best_of is None or request.n == request.best_of) - and not request.use_beam_search - ) - - async def abort_request() -> None: - await engine.abort(request_id) - - def create_stream_response_json( - index: int, - text: str, - logprobs: LogProbs | None = None, - finish_reason: str | None = None, - ) -> str: - choice_data = CompletionResponseStreamChoice( - index=index, - text=text, - logprobs=logprobs, - finish_reason=finish_reason, - ) - response = CompletionStreamResponse( - id=request_id, - created=created_time, - model=model_name, - choices=[choice_data], - ) - response_json = response.json() - - return response_json - - async def completion_stream_generator() -> AsyncGenerator[str, None]: - previous_texts = [""] * request.n - previous_num_tokens = [0] * request.n - start = time.time() - async for res in result_generator: - res: RequestOutput - for output in res.outputs: - i = output.index - delta_text = output.text[len(previous_texts[i]) :] - if request.logprobs is not None: - logprobs = create_logprobs( - output.token_ids[previous_num_tokens[i] :], - output.logprobs[previous_num_tokens[i] :], - len(previous_texts[i]), - ) - else: - logprobs = None - previous_texts[i] = output.text - previous_num_tokens[i] = len(output.token_ids) - response_json = create_stream_response_json( - index=i, - text=delta_text, - logprobs=logprobs, - ) - yield f"data: {response_json}\n\n" - if output.finish_reason is not None: - logprobs = LogProbs() if request.logprobs is not None else None - response_json = create_stream_response_json( - index=i, - text="", - logprobs=logprobs, - finish_reason=output.finish_reason, - ) - yield f"data: {response_json}\n\n" - - total_gen_time = time.time() - start - total_tokens = sum(previous_num_tokens) - background_tasks.add_task( - _write_metrics, - total_gen_time, - total_tokens, - x_developer_id, - x_developer_email, - ) - - yield "data: [DONE]\n\n" - - # Streaming response - if stream: - background_tasks = BackgroundTasks() - # Abort the request if the client disconnects. - background_tasks.add_task(abort_request) - return StreamingResponse( - completion_stream_generator(), - media_type="text/event-stream", - background=background_tasks, - ) - - # Non-streaming response - final_res: RequestOutput = None - start = time.time() - async for res in result_generator: - if await raw_request.is_disconnected(): - # Abort the request if the client disconnects. - await abort_request() - return create_error_response(HTTPStatus.BAD_REQUEST, "Client disconnected") - final_res = res - - tokens_gen_time = time.time() - start - - assert final_res is not None - choices = [] - for output in final_res.outputs: - if request.logprobs is not None: - logprobs = create_logprobs(output.token_ids, output.logprobs) - else: - logprobs = None - choice_data = CompletionResponseChoice( - index=output.index, - text=output.text, - logprobs=logprobs, - finish_reason=output.finish_reason, - ) - choices.append(choice_data) - - num_prompt_tokens = len(final_res.prompt_token_ids) - num_generated_tokens = sum(len(output.token_ids) for output in final_res.outputs) - total_tokens = num_prompt_tokens + num_generated_tokens - - background_tasks.add_task( - _write_metrics, - tokens_gen_time, - total_tokens, - x_developer_id, - x_developer_email, - ) - - usage = UsageInfo( - prompt_tokens=num_prompt_tokens, - completion_tokens=num_generated_tokens, - total_tokens=total_tokens, - ) - - response = CompletionResponse( - id=request_id, - created=created_time, - model=model_name, - choices=choices, - usage=usage, - ) - - if request.stream: - # When user requests streaming but we don't stream, we still need to - # return a streaming response with a single event. - response_json = response.json() - - async def fake_stream_generator() -> AsyncGenerator[str, None]: - yield f"data: {response_json}\n\n" - yield "data: [DONE]\n\n" - - return StreamingResponse( - fake_stream_generator(), media_type="text/event-stream" - ) - - return response - - -# QUESTION: How does the chat completion process differ from the standard completion process, and why are certain features unsupported here? -@app.post("/v1/chat/completions") -async def chat_completions( - raw_request: Request, - background_tasks: BackgroundTasks, - x_developer_id: Annotated[UUID4 | None, Depends(get_developer_id)] = None, - x_developer_email: Annotated[UUID4 | None, Depends(get_developer_email)] = None, -) -> Response: - """Completion API similar to OpenAI's API. - - See https://platform.openai.com/docs/api-reference/chat/create - for the API specification. This API mimics the OpenAI ChatCompletion API. - - NOTE: Currently we do not support the following features: - - function_call (Users should implement this by themselves) - - logit_bias (to be supported by vLLM engine) - """ - request = ChatCompletionRequest(**await raw_request.json()) - logger.info(f"Received chat completion request: {request}") - - error_check_ret = await check_model(request) - if error_check_ret is not None: - return error_check_ret - - if request.logit_bias is not None: - # TODO: support logit_bias in vLLM engine. - return create_error_response( - HTTPStatus.BAD_REQUEST, - "logit_bias is not currently supported", - ) - - append_fcall_prefix = False - - if request.functions and request.tools: - raise InvalidPromptException("can not accept both 'functions' and 'tools'") - - if request.tools: - request.functions = [ - t.function for t in request.tools if t.type == Type.function - ] - - request.function_call = ( - request.tool_choice.function - if isinstance(request.tool_choice, NamedToolChoice) - else request.tool_choice - ) - - bos = model_settings.get(request.model, {}).get("section_start_tag", DEFAULT_BOS) - eos = model_settings.get(request.model, {}).get("section_end_tag", DEFAULT_EOS) - - if ( - request.messages - and request.messages[0].role != "system" - and request.messages[0].name not in (None, "situation") - ): - request.messages.insert( - 0, - ChatMLMessage( - name="situation", - role="system", - content="You are a helpful AI Assistant", - ), - ) - - escape_special_tokens( - request.messages, - flatten(engine.engine.tokenizer.tokenizer.special_tokens_map.values()), - ) - - prompt = remove_last_space( - to_prompt( - request.messages, - bos=bos, - eos=eos, - functions=request.functions, - function_call=request.function_call, - ) - ) - - if ( - request.functions - and request.function_call - and request.function_call not in ("none", "auto", None) - ): - with suppress(IndexError): - if prompt.split("\n")[-1].startswith('{"name":'): - append_fcall_prefix = True - - # prompt = await get_gen_prompt(request) - error_check_ret = await check_length(request, prompt, engine_model_config) - if error_check_ret is not None: - return error_check_ret - - model_name = request.model - request_id = f"cmpl-{random_uuid()}" - created_time = int(time.time()) - - try: - sampling_params = request.to_sampling_params() - except ValueError as e: - return create_error_response(HTTPStatus.BAD_REQUEST, str(e)) - - # Rescale the temperature - sampling_params.temperature = rescale_temperature( - sampling_params.temperature, - temperature_scaling_factor, - power=temperature_scaling_power, # Set it to lower than 1.0 to punish high temperatures more - ) - - if prompt.endswith(bos): - func_call_possible = ( - request.functions and request.function_call != "none" - ) or (request.tools and request.tool_choice != "none") - if sampling_params.logits_processors is None: - sampling_params.logits_processors = [] - - sampling_params.logits_processors.append( - fix_function_call_prediction - if func_call_possible - else drop_disallowed_start_tags - ) - - if ( - request.response_format is not None - and request.response_format.type_ == "json_object" - ): - result_generator = vllm_with_character_level_parser( - engine, - tokenizer, - prompt, - sampling_params, - request_id, - parser=JsonSchemaParser( - ( - FunctionCallResult.model_json_schema() - if request.function_call is not None - and request.function_call not in ("none", "auto") - else {} - ), - ), - ) - - else: - result_generator = engine.generate( - prompt, - sampling_params, - request_id, - ) - - async def abort_request() -> None: - await engine.abort(request_id) - - def create_stream_response_json( - index: int, - text: str, - role: str = "assistant", - name: str | None = None, - finish_reason: str | None = None, - is_function_call: bool | None = None, - is_tool_call: bool | None = None, - ) -> str: - choice_data = ChatCompletionResponseStreamChoice( - index=index, - delta=DeltaMessage( - role=role, - content=text if not (is_function_call or is_tool_call) else None, - name=name, - function_call=text if is_function_call else None, - tool_calls=( - [ - ToolCall( - id=random_tool_id(), - type="function", - function=text, - ) - ] - if is_tool_call - else None - ), - ), - finish_reason=finish_reason, - ) - response = ChatCompletionStreamResponse( - id=request_id, - created=created_time, - model=model_name, - choices=[choice_data], - ) - response_json = response.json() - - return response_json - - async def completion_stream_generator() -> AsyncGenerator[str, None]: - previous_texts = [""] * request.n - previous_num_tokens = [0] * request.n - start = time.time() - role = "assistant" - name = None - is_function_call = False - is_tool_call = False - async for res in result_generator: - res: RequestOutput - for idx, output in enumerate(res.outputs): - i = output.index - delta_text = output.text[len(previous_texts[i]) :] - if not idx: - if append_fcall_prefix: - delta_text = f"""function_call\n{delta_text}""" - - msg = parse_message(delta_text) - role = msg.role or "assistant" - name = msg.name - is_function_call = bool( - request.functions and msg.function_call and not request.tools - ) - is_tool_call = bool(request.tools and msg.function_call) - - for i in range(request.n): - choice_data = ChatCompletionResponseStreamChoice( - index=i, - delta=DeltaMessage(role=role), - finish_reason=None, - ) - chunk = ChatCompletionStreamResponse( - id=request_id, choices=[choice_data], model=model_name - ) - data = chunk.json(exclude_unset=True) - yield f"data: {data}\n\n" - - previous_texts[i] = output.text - previous_num_tokens[i] = len(output.token_ids) - response_json = create_stream_response_json( - index=i, - text=delta_text, - role=role, - name=name, - is_function_call=is_function_call, - is_tool_call=is_tool_call, - ) - yield f"data: {response_json}\n\n" - if output.finish_reason is not None: - finish_reason = output.finish_reason - if is_function_call: - finish_reason = "function_call" - if is_tool_call: - finish_reason = "tool_calls" - response_json = create_stream_response_json( - index=i, - text="", - role=role, - name=name, - finish_reason=finish_reason, - is_function_call=is_function_call, - is_tool_call=is_tool_call, - ) - yield f"data: {response_json}\n\n" - - total_gen_time = time.time() - start - total_tokens = sum(previous_num_tokens) - - background_tasks.add_task( - _write_metrics, - total_gen_time, - total_tokens, - x_developer_id, - x_developer_email, - ) - - yield "data: [DONE]\n\n" - - # Streaming response - if request.stream: - background_tasks = BackgroundTasks() - # Abort the request if the client disconnects. - background_tasks.add_task(abort_request) - return StreamingResponse( - completion_stream_generator(), - media_type="text/event-stream", - background=background_tasks, - ) - - # Non-streaming response - final_res: RequestOutput = None - start = time.time() - async for res in result_generator: - if await raw_request.is_disconnected(): - # Abort the request if the client disconnects. - await abort_request() - return create_error_response(HTTPStatus.BAD_REQUEST, "Client disconnected") - final_res = res - - tokens_gen_time = time.time() - start - - assert final_res is not None - choices = [] - for output in final_res.outputs: - msg = parse_message( - output.text - if not append_fcall_prefix - else f"""function_call\n{output.text}""" - ) - finish_reason = output.finish_reason - is_function_call = bool( - request.functions and msg.function_call and not request.tools - ) - is_tool_call = bool(request.tools and msg.function_call) - if is_function_call: - finish_reason = "function_call" - if is_tool_call: - finish_reason = "tool_calls" - - func_name = ( - request.function_call.name - if isinstance(request.function_call, FunctionCall) - else request.function_call or "" - ) - tool_func_name = ( - request.tool_choice.function - if isinstance(request.tool_choice, NamedToolChoice) - else request.tool_choice or "" - ) - choice_data = ChatCompletionResponseChoice( - index=output.index, - message=ChatMessage( - role=msg.role or "assistant", - name=msg.name, - content=( - None if is_function_call or is_tool_call else msg.content or "" - ), - function_call=( - f'{{"name": "{func_name}",{msg.function_call or ""}' - if is_function_call - else None - ), - tool_calls=( - [ - ToolCall( - id=random_tool_id(), - type="function", - function=f'{{"name": "{tool_func_name}",{msg.function_call or ""}', - ) - ] - if is_tool_call - else None - ), - ), - finish_reason=finish_reason, - ) - choices.append(choice_data) - - num_prompt_tokens = len(final_res.prompt_token_ids) - num_generated_tokens = sum(len(output.token_ids) for output in final_res.outputs) - total_tokens = num_prompt_tokens + num_generated_tokens - usage = UsageInfo( - prompt_tokens=num_prompt_tokens, - completion_tokens=num_generated_tokens, - total_tokens=total_tokens, - ) - - background_tasks.add_task( - _write_metrics, - tokens_gen_time, - total_tokens, - x_developer_id, - x_developer_email, - ) - - response = ChatCompletionResponse( - id=request_id, - created=created_time, - model=model_name, - choices=choices, - usage=usage, - ) - - if request.stream: - # When user requests streaming but we don't stream, we still need to - # return a streaming response with a single event. - response_json = response.json() - - async def fake_stream_generator() -> AsyncGenerator[str, None]: - yield f"data: {response_json}\n\n" - yield "data: [DONE]\n\n" - - return StreamingResponse( - fake_stream_generator(), - media_type="text/event-stream", - ) - - return response - - -@app.get("/status") -async def status(): - return {"status": "ok"} - - -@app.post("/me") -async def me(): - return {"status": "ok"} - - -# QUESTION: How does the MetricsMiddleware work, and what metrics are being excluded from tracking? -app.add_middleware( - MetricsMiddleware, - exclude_paths=["/metrics", "/docs", "/status"], -) - -app.add_route("/metrics", metrics) - - -# QUESTION: Please explain the CORS policy applied here and its implications for cross-origin requests. -app.add_middleware( - CORSMiddleware, - allow_credentials=True, - allow_origins="*", - allow_methods="*", - allow_headers="*", -) - - -# QUESTION: Can we have an explanation on how the app configuration is dynamically set based on command-line arguments? -def create_app(args=None): - global engine, engine_model_config, tokenizer, served_model - - parser = argparse.ArgumentParser( - description="vLLM OpenAI-Compatible RESTful API server." - ) - parser.add_argument("--host", type=str, default=None, help="host name") - parser.add_argument("--port", type=int, default=8000, help="port number") - parser.add_argument( - "--log-stats", type=bool, default=True, help="log stats metrics" - ) - parser.add_argument( - "--served-model-name", - type=str, - default=None, - help="The model name used in the API. If not " - "specified, the model name will be the same as " - "the huggingface name.", - ) - - parser = AsyncEngineArgs.add_cli_args(parser) - args = parser.parse_args(args=args) - - logger.info(f"args: {args}") - - if args.served_model_name is not None: - served_model = args.served_model_name - else: - served_model = args.model - - engine_args = AsyncEngineArgs.from_cli_args(args) - engine = AsyncLLMEngine.from_engine_args(engine_args) - engine_model_config = asyncio.run(engine.get_model_config()) - - # A separate tokenizer to map token IDs to strings. - tokenizer = get_tokenizer( - engine_args.tokenizer, - tokenizer_mode=engine_args.tokenizer_mode, - trust_remote_code=engine_args.trust_remote_code, - ) - - return app diff --git a/model-serving/poetry.lock b/model-serving/poetry.lock deleted file mode 100644 index da70c4133..000000000 --- a/model-serving/poetry.lock +++ /dev/null @@ -1,6270 +0,0 @@ -# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. - -[[package]] -name = "aiohttp" -version = "3.9.4" -description = "Async http client/server framework (asyncio)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "aiohttp-3.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:76d32588ef7e4a3f3adff1956a0ba96faabbdee58f2407c122dd45aa6e34f372"}, - {file = "aiohttp-3.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:56181093c10dbc6ceb8a29dfeea1e815e1dfdc020169203d87fd8d37616f73f9"}, - {file = "aiohttp-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c7a5b676d3c65e88b3aca41816bf72831898fcd73f0cbb2680e9d88e819d1e4d"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d1df528a85fb404899d4207a8d9934cfd6be626e30e5d3a5544a83dbae6d8a7e"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f595db1bceabd71c82e92df212dd9525a8a2c6947d39e3c994c4f27d2fe15b11"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c0b09d76e5a4caac3d27752027fbd43dc987b95f3748fad2b924a03fe8632ad"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:689eb4356649ec9535b3686200b231876fb4cab4aca54e3bece71d37f50c1d13"}, - {file = "aiohttp-3.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a3666cf4182efdb44d73602379a66f5fdfd5da0db5e4520f0ac0dcca644a3497"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b65b0f8747b013570eea2f75726046fa54fa8e0c5db60f3b98dd5d161052004a"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a1885d2470955f70dfdd33a02e1749613c5a9c5ab855f6db38e0b9389453dce7"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0593822dcdb9483d41f12041ff7c90d4d1033ec0e880bcfaf102919b715f47f1"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:47f6eb74e1ecb5e19a78f4a4228aa24df7fbab3b62d4a625d3f41194a08bd54f"}, - {file = "aiohttp-3.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c8b04a3dbd54de6ccb7604242fe3ad67f2f3ca558f2d33fe19d4b08d90701a89"}, - {file = "aiohttp-3.9.4-cp310-cp310-win32.whl", hash = "sha256:8a78dfb198a328bfb38e4308ca8167028920fb747ddcf086ce706fbdd23b2926"}, - {file = "aiohttp-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:e78da6b55275987cbc89141a1d8e75f5070e577c482dd48bd9123a76a96f0bbb"}, - {file = "aiohttp-3.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c111b3c69060d2bafc446917534150fd049e7aedd6cbf21ba526a5a97b4402a5"}, - {file = "aiohttp-3.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:efbdd51872cf170093998c87ccdf3cb5993add3559341a8e5708bcb311934c94"}, - {file = "aiohttp-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7bfdb41dc6e85d8535b00d73947548a748e9534e8e4fddd2638109ff3fb081df"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bd9d334412961125e9f68d5b73c1d0ab9ea3f74a58a475e6b119f5293eee7ba"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:35d78076736f4a668d57ade00c65d30a8ce28719d8a42471b2a06ccd1a2e3063"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:824dff4f9f4d0f59d0fa3577932ee9a20e09edec8a2f813e1d6b9f89ced8293f"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:52b8b4e06fc15519019e128abedaeb56412b106ab88b3c452188ca47a25c4093"}, - {file = "aiohttp-3.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eae569fb1e7559d4f3919965617bb39f9e753967fae55ce13454bec2d1c54f09"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:69b97aa5792428f321f72aeb2f118e56893371f27e0b7d05750bcad06fc42ca1"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4d79aad0ad4b980663316f26d9a492e8fab2af77c69c0f33780a56843ad2f89e"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:d6577140cd7db19e430661e4b2653680194ea8c22c994bc65b7a19d8ec834403"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:9860d455847cd98eb67897f5957b7cd69fbcb436dd3f06099230f16a66e66f79"}, - {file = "aiohttp-3.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:69ff36d3f8f5652994e08bd22f093e11cfd0444cea310f92e01b45a4e46b624e"}, - {file = "aiohttp-3.9.4-cp311-cp311-win32.whl", hash = "sha256:e27d3b5ed2c2013bce66ad67ee57cbf614288bda8cdf426c8d8fe548316f1b5f"}, - {file = "aiohttp-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d6a67e26daa686a6fbdb600a9af8619c80a332556245fa8e86c747d226ab1a1e"}, - {file = "aiohttp-3.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:c5ff8ff44825736a4065d8544b43b43ee4c6dd1530f3a08e6c0578a813b0aa35"}, - {file = "aiohttp-3.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d12a244627eba4e9dc52cbf924edef905ddd6cafc6513849b4876076a6f38b0e"}, - {file = "aiohttp-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dcad56c8d8348e7e468899d2fb3b309b9bc59d94e6db08710555f7436156097f"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7e69a7fd4b5ce419238388e55abd220336bd32212c673ceabc57ccf3d05b55"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4870cb049f10d7680c239b55428916d84158798eb8f353e74fa2c98980dcc0b"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2feaf1b7031ede1bc0880cec4b0776fd347259a723d625357bb4b82f62687b"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:939393e8c3f0a5bcd33ef7ace67680c318dc2ae406f15e381c0054dd658397de"}, - {file = "aiohttp-3.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7d2334e387b2adcc944680bebcf412743f2caf4eeebd550f67249c1c3696be04"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e0198ea897680e480845ec0ffc5a14e8b694e25b3f104f63676d55bf76a82f1a"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e40d2cd22914d67c84824045861a5bb0fb46586b15dfe4f046c7495bf08306b2"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:aba80e77c227f4234aa34a5ff2b6ff30c5d6a827a91d22ff6b999de9175d71bd"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:fb68dc73bc8ac322d2e392a59a9e396c4f35cb6fdbdd749e139d1d6c985f2527"}, - {file = "aiohttp-3.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f3460a92638dce7e47062cf088d6e7663adb135e936cb117be88d5e6c48c9d53"}, - {file = "aiohttp-3.9.4-cp312-cp312-win32.whl", hash = "sha256:32dc814ddbb254f6170bca198fe307920f6c1308a5492f049f7f63554b88ef36"}, - {file = "aiohttp-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:63f41a909d182d2b78fe3abef557fcc14da50c7852f70ae3be60e83ff64edba5"}, - {file = "aiohttp-3.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:c3770365675f6be220032f6609a8fbad994d6dcf3ef7dbcf295c7ee70884c9af"}, - {file = "aiohttp-3.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:305edae1dea368ce09bcb858cf5a63a064f3bff4767dec6fa60a0cc0e805a1d3"}, - {file = "aiohttp-3.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6f121900131d116e4a93b55ab0d12ad72573f967b100e49086e496a9b24523ea"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b71e614c1ae35c3d62a293b19eface83d5e4d194e3eb2fabb10059d33e6e8cbf"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:419f009fa4cfde4d16a7fc070d64f36d70a8d35a90d71aa27670bba2be4fd039"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7b39476ee69cfe64061fd77a73bf692c40021f8547cda617a3466530ef63f947"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b33f34c9c7decdb2ab99c74be6443942b730b56d9c5ee48fb7df2c86492f293c"}, - {file = "aiohttp-3.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c78700130ce2dcebb1a8103202ae795be2fa8c9351d0dd22338fe3dac74847d9"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:268ba22d917655d1259af2d5659072b7dc11b4e1dc2cb9662fdd867d75afc6a4"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:17e7c051f53a0d2ebf33013a9cbf020bb4e098c4bc5bce6f7b0c962108d97eab"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7be99f4abb008cb38e144f85f515598f4c2c8932bf11b65add0ff59c9c876d99"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:d58a54d6ff08d2547656356eea8572b224e6f9bbc0cf55fa9966bcaac4ddfb10"}, - {file = "aiohttp-3.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7673a76772bda15d0d10d1aa881b7911d0580c980dbd16e59d7ba1422b2d83cd"}, - {file = "aiohttp-3.9.4-cp38-cp38-win32.whl", hash = "sha256:e4370dda04dc8951012f30e1ce7956a0a226ac0714a7b6c389fb2f43f22a250e"}, - {file = "aiohttp-3.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:eb30c4510a691bb87081192a394fb661860e75ca3896c01c6d186febe7c88530"}, - {file = "aiohttp-3.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:84e90494db7df3be5e056f91412f9fa9e611fbe8ce4aaef70647297f5943b276"}, - {file = "aiohttp-3.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7d4845f8501ab28ebfdbeab980a50a273b415cf69e96e4e674d43d86a464df9d"}, - {file = "aiohttp-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69046cd9a2a17245c4ce3c1f1a4ff8c70c7701ef222fce3d1d8435f09042bba1"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b73a06bafc8dcc508420db43b4dd5850e41e69de99009d0351c4f3007960019"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:418bb0038dfafeac923823c2e63226179976c76f981a2aaad0ad5d51f2229bca"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:71a8f241456b6c2668374d5d28398f8e8cdae4cce568aaea54e0f39359cd928d"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:935c369bf8acc2dc26f6eeb5222768aa7c62917c3554f7215f2ead7386b33748"}, - {file = "aiohttp-3.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74e4e48c8752d14ecfb36d2ebb3d76d614320570e14de0a3aa7a726ff150a03c"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:916b0417aeddf2c8c61291238ce25286f391a6acb6f28005dd9ce282bd6311b6"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9b6787b6d0b3518b2ee4cbeadd24a507756ee703adbac1ab6dc7c4434b8c572a"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:221204dbda5ef350e8db6287937621cf75e85778b296c9c52260b522231940ed"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:10afd99b8251022ddf81eaed1d90f5a988e349ee7d779eb429fb07b670751e8c"}, - {file = "aiohttp-3.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2506d9f7a9b91033201be9ffe7d89c6a54150b0578803cce5cb84a943d075bc3"}, - {file = "aiohttp-3.9.4-cp39-cp39-win32.whl", hash = "sha256:e571fdd9efd65e86c6af2f332e0e95dad259bfe6beb5d15b3c3eca3a6eb5d87b"}, - {file = "aiohttp-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:7d29dd5319d20aa3b7749719ac9685fbd926f71ac8c77b2477272725f882072d"}, - {file = "aiohttp-3.9.4.tar.gz", hash = "sha256:6ff71ede6d9a5a58cfb7b6fffc83ab5d4a63138276c771ac91ceaaddf5459644"}, -] - -[package.dependencies] -aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} -attrs = ">=17.3.0" -frozenlist = ">=1.1.1" -multidict = ">=4.5,<7.0" -yarl = ">=1.0,<2.0" - -[package.extras] -speedups = ["Brotli", "aiodns", "brotlicffi"] - -[[package]] -name = "aioprometheus" -version = "23.12.0" -description = "A Prometheus Python client library for asyncio-based applications" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "aioprometheus-23.12.0-py3-none-any.whl", hash = "sha256:b1a77259131153ef820b494e76439b278434eaf2a5e25dc0b8bf3d835f455960"}, -] - -[package.dependencies] -orjson = "*" -quantile-python = ">=1.1" -starlette = {version = ">=0.14.2", optional = true, markers = "extra == \"starlette\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.3.2)"] -quart = ["quart (>=0.15.1)"] -starlette = ["starlette (>=0.14.2)"] - -[[package]] -name = "aiosignal" -version = "1.3.1" -description = "aiosignal: a list of registered asynchronous callbacks" -optional = false -python-versions = ">=3.7" -files = [ - {file = "aiosignal-1.3.1-py3-none-any.whl", hash = "sha256:f8376fb07dd1e86a584e4fcdec80b36b7f81aac666ebc724e2c090300dd83b17"}, - {file = "aiosignal-1.3.1.tar.gz", hash = "sha256:54cd96e15e1649b75d6c87526a6ff0b6c1b0dd3459f43d9ca11d48c339b68cfc"}, -] - -[package.dependencies] -frozenlist = ">=1.1.0" - -[[package]] -name = "aiostream" -version = "0.5.2" -description = "Generator-based operators for asynchronous iteration" -optional = false -python-versions = ">=3.8" -files = [ - {file = "aiostream-0.5.2-py3-none-any.whl", hash = "sha256:054660370be9d37f6fe3ece3851009240416bd082e469fd90cc8673d3818cf71"}, - {file = "aiostream-0.5.2.tar.gz", hash = "sha256:b71b519a2d66c38f0872403ab86417955b77352f08d9ad02ad46fc3926b389f4"}, -] - -[package.dependencies] -typing-extensions = "*" - -[[package]] -name = "annotated-types" -version = "0.6.0" -description = "Reusable constraint types to use with typing.Annotated" -optional = false -python-versions = ">=3.8" -files = [ - {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, - {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 = "appnope" -version = "0.1.4" -description = "Disable App Nap on macOS >= 10.9" -optional = false -python-versions = ">=3.6" -files = [ - {file = "appnope-0.1.4-py2.py3-none-any.whl", hash = "sha256:502575ee11cd7a28c0205f379b525beefebab9d161b7c964670864014ed7213c"}, - {file = "appnope-0.1.4.tar.gz", hash = "sha256:1de3860566df9caf38f01f86f65e0e13e379af54f9e4bee1e66b48f2efffd1ee"}, -] - -[[package]] -name = "argon2-cffi" -version = "23.1.0" -description = "Argon2 for Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "argon2_cffi-23.1.0-py3-none-any.whl", hash = "sha256:c670642b78ba29641818ab2e68bd4e6a78ba53b7eff7b4c3815ae16abf91c7ea"}, - {file = "argon2_cffi-23.1.0.tar.gz", hash = "sha256:879c3e79a2729ce768ebb7d36d4609e3a78a4ca2ec3a9f12286ca057e3d0db08"}, -] - -[package.dependencies] -argon2-cffi-bindings = "*" - -[package.extras] -dev = ["argon2-cffi[tests,typing]", "tox (>4)"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-copybutton", "sphinx-notfound-page"] -tests = ["hypothesis", "pytest"] -typing = ["mypy"] - -[[package]] -name = "argon2-cffi-bindings" -version = "21.2.0" -description = "Low-level CFFI bindings for Argon2" -optional = false -python-versions = ">=3.6" -files = [ - {file = "argon2-cffi-bindings-21.2.0.tar.gz", hash = "sha256:bb89ceffa6c791807d1305ceb77dbfacc5aa499891d2c55661c6459651fc39e3"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ccb949252cb2ab3a08c02024acb77cfb179492d5701c7cbdbfd776124d4d2367"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9524464572e12979364b7d600abf96181d3541da11e23ddf565a32e70bd4dc0d"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b746dba803a79238e925d9046a63aa26bf86ab2a2fe74ce6b009a1c3f5c8f2ae"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:58ed19212051f49a523abb1dbe954337dc82d947fb6e5a0da60f7c8471a8476c"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:bd46088725ef7f58b5a1ef7ca06647ebaf0eb4baff7d1d0d177c6cc8744abd86"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_i686.whl", hash = "sha256:8cd69c07dd875537a824deec19f978e0f2078fdda07fd5c42ac29668dda5f40f"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:f1152ac548bd5b8bcecfb0b0371f082037e47128653df2e8ba6e914d384f3c3e"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win32.whl", hash = "sha256:603ca0aba86b1349b147cab91ae970c63118a0f30444d4bc80355937c950c082"}, - {file = "argon2_cffi_bindings-21.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:b2ef1c30440dbbcba7a5dc3e319408b59676e2e039e2ae11a8775ecf482b192f"}, - {file = "argon2_cffi_bindings-21.2.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:e415e3f62c8d124ee16018e491a009937f8cf7ebf5eb430ffc5de21b900dad93"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3e385d1c39c520c08b53d63300c3ecc28622f076f4c2b0e6d7e796e9f6502194"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c3e3cc67fdb7d82c4718f19b4e7a87123caf8a93fde7e23cf66ac0337d3cb3f"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6a22ad9800121b71099d0fb0a65323810a15f2e292f2ba450810a7316e128ee5"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f9f8b450ed0547e3d473fdc8612083fd08dd2120d6ac8f73828df9b7d45bb351"}, - {file = "argon2_cffi_bindings-21.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:93f9bf70084f97245ba10ee36575f0c3f1e7d7724d67d8e5b08e61787c320ed7"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3b9ef65804859d335dc6b31582cad2c5166f0c3e7975f324d9ffaa34ee7e6583"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4966ef5848d820776f5f562a7d45fdd70c2f330c961d0d745b784034bd9f48d"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:20ef543a89dee4db46a1a6e206cd015360e5a75822f76df533845c3cbaf72670"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ed2937d286e2ad0cc79a7087d3c272832865f779430e0cc2b4f3718d3159b0cb"}, - {file = "argon2_cffi_bindings-21.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5e00316dabdaea0b2dd82d141cc66889ced0cdcbfa599e8b471cf22c620c329a"}, -] - -[package.dependencies] -cffi = ">=1.0.1" - -[package.extras] -dev = ["cogapp", "pre-commit", "pytest", "wheel"] -tests = ["pytest"] - -[[package]] -name = "arrow" -version = "1.3.0" -description = "Better dates & times for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, - {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, -] - -[package.dependencies] -python-dateutil = ">=2.7.0" -types-python-dateutil = ">=2.8.10" - -[package.extras] -doc = ["doc8", "sphinx (>=7.0.0)", "sphinx-autobuild", "sphinx-autodoc-typehints", "sphinx_rtd_theme (>=1.3.0)"] -test = ["dateparser (==1.*)", "pre-commit", "pytest", "pytest-cov", "pytest-mock", "pytz (==2021.1)", "simplejson (==3.*)"] - -[[package]] -name = "asttokens" -version = "2.4.1" -description = "Annotate AST trees with source code positions" -optional = false -python-versions = "*" -files = [ - {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, - {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, -] - -[package.dependencies] -six = ">=1.12.0" - -[package.extras] -astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] -test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] - -[[package]] -name = "async-lru" -version = "2.0.4" -description = "Simple LRU cache for asyncio" -optional = false -python-versions = ">=3.8" -files = [ - {file = "async-lru-2.0.4.tar.gz", hash = "sha256:b8a59a5df60805ff63220b2a0c5b5393da5521b113cd5465a44eb037d81a5627"}, - {file = "async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224"}, -] - -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - -[[package]] -name = "attrs" -version = "23.2.0" -description = "Classes Without Boilerplate" -optional = false -python-versions = ">=3.7" -files = [ - {file = "attrs-23.2.0-py3-none-any.whl", hash = "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"}, - {file = "attrs-23.2.0.tar.gz", hash = "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30"}, -] - -[package.extras] -cov = ["attrs[tests]", "coverage[toml] (>=5.3)"] -dev = ["attrs[tests]", "pre-commit"] -docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] -tests = ["attrs[tests-no-zope]", "zope-interface"] -tests-mypy = ["mypy (>=1.6)", "pytest-mypy-plugins"] -tests-no-zope = ["attrs[tests-mypy]", "cloudpickle", "hypothesis", "pympler", "pytest (>=4.3.0)", "pytest-xdist[psutil]"] - -[[package]] -name = "babel" -version = "2.14.0" -description = "Internationalization utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "Babel-2.14.0-py3-none-any.whl", hash = "sha256:efb1a25b7118e67ce3a259bed20545c29cb68be8ad2c784c83689981b7a57287"}, - {file = "Babel-2.14.0.tar.gz", hash = "sha256:6919867db036398ba21eb5c7a0f6b28ab8cbc3ae7a73a44ebe34ae74a4e7d363"}, -] - -[package.extras] -dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"] - -[[package]] -name = "beartype" -version = "0.18.2" -description = "Unbearably fast runtime type checking in pure Python." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "beartype-0.18.2-py3-none-any.whl", hash = "sha256:561aa7858e92289b952a6fc5faf15ea32f9519c07cdc0f4df7a01b59fc4bbeaf"}, - {file = "beartype-0.18.2.tar.gz", hash = "sha256:a6fbc0be9269889312388bfec6a9ddf41bf8fe31b68bcf9c8239db35cd38f411"}, -] - -[package.extras] -all = ["typing-extensions (>=3.10.0.0)"] -dev = ["autoapi (>=0.9.0)", "coverage (>=5.5)", "equinox", "mypy (>=0.800)", "numpy", "pandera", "pydata-sphinx-theme (<=0.7.2)", "pytest (>=4.0.0)", "sphinx", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)", "tox (>=3.20.1)", "typing-extensions (>=3.10.0.0)"] -doc-rtd = ["autoapi (>=0.9.0)", "pydata-sphinx-theme (<=0.7.2)", "sphinx (>=4.2.0,<6.0.0)", "sphinxext-opengraph (>=0.7.5)"] -test-tox = ["equinox", "mypy (>=0.800)", "numpy", "pandera", "pytest (>=4.0.0)", "sphinx", "typing-extensions (>=3.10.0.0)"] -test-tox-coverage = ["coverage (>=5.5)"] - -[[package]] -name = "beautifulsoup4" -version = "4.12.3" -description = "Screen-scraping library" -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"}, - {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"}, -] - -[package.dependencies] -soupsieve = ">1.2" - -[package.extras] -cchardet = ["cchardet"] -chardet = ["chardet"] -charset-normalizer = ["charset-normalizer"] -html5lib = ["html5lib"] -lxml = ["lxml"] - -[[package]] -name = "black" -version = "24.4.0" -description = "The uncompromising code formatter." -optional = false -python-versions = ">=3.8" -files = [ - {file = "black-24.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6ad001a9ddd9b8dfd1b434d566be39b1cd502802c8d38bbb1ba612afda2ef436"}, - {file = "black-24.4.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3a3a092b8b756c643fe45f4624dbd5a389f770a4ac294cf4d0fce6af86addaf"}, - {file = "black-24.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dae79397f367ac8d7adb6c779813328f6d690943f64b32983e896bcccd18cbad"}, - {file = "black-24.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:71d998b73c957444fb7c52096c3843875f4b6b47a54972598741fe9a7f737fcb"}, - {file = "black-24.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8e5537f456a22cf5cfcb2707803431d2feeb82ab3748ade280d6ccd0b40ed2e8"}, - {file = "black-24.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:64e60a7edd71fd542a10a9643bf369bfd2644de95ec71e86790b063aa02ff745"}, - {file = "black-24.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5cd5b4f76056cecce3e69b0d4c228326d2595f506797f40b9233424e2524c070"}, - {file = "black-24.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:64578cf99b6b46a6301bc28bdb89f9d6f9b592b1c5837818a177c98525dbe397"}, - {file = "black-24.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f95cece33329dc4aa3b0e1a771c41075812e46cf3d6e3f1dfe3d91ff09826ed2"}, - {file = "black-24.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4396ca365a4310beef84d446ca5016f671b10f07abdba3e4e4304218d2c71d33"}, - {file = "black-24.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:44d99dfdf37a2a00a6f7a8dcbd19edf361d056ee51093b2445de7ca09adac965"}, - {file = "black-24.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:21f9407063ec71c5580b8ad975653c66508d6a9f57bd008bb8691d273705adcd"}, - {file = "black-24.4.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:652e55bb722ca026299eb74e53880ee2315b181dfdd44dca98e43448620ddec1"}, - {file = "black-24.4.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:7f2966b9b2b3b7104fca9d75b2ee856fe3fdd7ed9e47c753a4bb1a675f2caab8"}, - {file = "black-24.4.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bb9ca06e556a09f7f7177bc7cb604e5ed2d2df1e9119e4f7d2f1f7071c32e5d"}, - {file = "black-24.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4e71cdebdc8efeb6deaf5f2deb28325f8614d48426bed118ecc2dcaefb9ebf3"}, - {file = "black-24.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6644f97a7ef6f401a150cca551a1ff97e03c25d8519ee0bbc9b0058772882665"}, - {file = "black-24.4.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:75a2d0b4f5eb81f7eebc31f788f9830a6ce10a68c91fbe0fade34fff7a2836e6"}, - {file = "black-24.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eb949f56a63c5e134dfdca12091e98ffb5fd446293ebae123d10fc1abad00b9e"}, - {file = "black-24.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:7852b05d02b5b9a8c893ab95863ef8986e4dda29af80bbbda94d7aee1abf8702"}, - {file = "black-24.4.0-py3-none-any.whl", hash = "sha256:74eb9b5420e26b42c00a3ff470dc0cd144b80a766128b1771d07643165e08d0e"}, - {file = "black-24.4.0.tar.gz", hash = "sha256:f07b69fda20578367eaebbd670ff8fc653ab181e1ff95d84497f9fa20e7d0641"}, -] - -[package.dependencies] -click = ">=8.0.0" -mypy-extensions = ">=0.4.3" -packaging = ">=22.0" -pathspec = ">=0.9.0" -platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} - -[package.extras] -colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.7.4)", "aiohttp (>=3.7.4,!=3.9.0)"] -jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] -uvloop = ["uvloop (>=0.15.2)"] - -[[package]] -name = "bleach" -version = "6.1.0" -description = "An easy safelist-based HTML-sanitizing tool." -optional = false -python-versions = ">=3.8" -files = [ - {file = "bleach-6.1.0-py3-none-any.whl", hash = "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6"}, - {file = "bleach-6.1.0.tar.gz", hash = "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe"}, -] - -[package.dependencies] -six = ">=1.9.0" -webencodings = "*" - -[package.extras] -css = ["tinycss2 (>=1.1.0,<1.3)"] - -[[package]] -name = "certifi" -version = "2024.2.2" -description = "Python package for providing Mozilla's CA Bundle." -optional = false -python-versions = ">=3.6" -files = [ - {file = "certifi-2024.2.2-py3-none-any.whl", hash = "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1"}, - {file = "certifi-2024.2.2.tar.gz", hash = "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f"}, -] - -[[package]] -name = "cffi" -version = "1.16.0" -description = "Foreign Function Interface for Python calling C code." -optional = false -python-versions = ">=3.8" -files = [ - {file = "cffi-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088"}, - {file = "cffi-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7"}, - {file = "cffi-1.16.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743"}, - {file = "cffi-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d"}, - {file = "cffi-1.16.0-cp310-cp310-win32.whl", hash = "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a"}, - {file = "cffi-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404"}, - {file = "cffi-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56"}, - {file = "cffi-1.16.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc"}, - {file = "cffi-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb"}, - {file = "cffi-1.16.0-cp311-cp311-win32.whl", hash = "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab"}, - {file = "cffi-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956"}, - {file = "cffi-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6"}, - {file = "cffi-1.16.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969"}, - {file = "cffi-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520"}, - {file = "cffi-1.16.0-cp312-cp312-win32.whl", hash = "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b"}, - {file = "cffi-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235"}, - {file = "cffi-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b"}, - {file = "cffi-1.16.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324"}, - {file = "cffi-1.16.0-cp38-cp38-win32.whl", hash = "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a"}, - {file = "cffi-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed"}, - {file = "cffi-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4"}, - {file = "cffi-1.16.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000"}, - {file = "cffi-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe"}, - {file = "cffi-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4"}, - {file = "cffi-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8"}, - {file = "cffi-1.16.0.tar.gz", hash = "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0"}, -] - -[package.dependencies] -pycparser = "*" - -[[package]] -name = "charset-normalizer" -version = "3.3.2" -description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, - {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, - {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, - {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, - {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, - {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, - {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, - {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, -] - -[[package]] -name = "click" -version = "8.1.7" -description = "Composable command line interface toolkit" -optional = false -python-versions = ">=3.7" -files = [ - {file = "click-8.1.7-py3-none-any.whl", hash = "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28"}, - {file = "click-8.1.7.tar.gz", hash = "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[[package]] -name = "cloudpickle" -version = "3.0.0" -description = "Pickler class to extend the standard pickle.Pickler functionality" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7"}, - {file = "cloudpickle-3.0.0.tar.gz", hash = "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882"}, -] - -[[package]] -name = "colorama" -version = "0.4.6" -description = "Cross-platform colored terminal text." -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" -files = [ - {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, - {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, -] - -[[package]] -name = "comm" -version = "0.2.2" -description = "Jupyter Python Comm implementation, for usage in ipykernel, xeus-python etc." -optional = false -python-versions = ">=3.8" -files = [ - {file = "comm-0.2.2-py3-none-any.whl", hash = "sha256:e6fb86cb70ff661ee8c9c14e7d36d6de3b4066f1441be4063df9c5009f0a64d3"}, - {file = "comm-0.2.2.tar.gz", hash = "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e"}, -] - -[package.dependencies] -traitlets = ">=4" - -[package.extras] -test = ["pytest"] - -[[package]] -name = "contourpy" -version = "1.2.1" -description = "Python library for calculating contours of 2D quadrilateral grids" -optional = false -python-versions = ">=3.9" -files = [ - {file = "contourpy-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040"}, - {file = "contourpy-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619"}, - {file = "contourpy-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8"}, - {file = "contourpy-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8"}, - {file = "contourpy-1.2.1-cp311-cp311-win32.whl", hash = "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec"}, - {file = "contourpy-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4"}, - {file = "contourpy-1.2.1-cp312-cp312-win32.whl", hash = "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f"}, - {file = "contourpy-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083"}, - {file = "contourpy-1.2.1-cp39-cp39-win32.whl", hash = "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba"}, - {file = "contourpy-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f"}, - {file = "contourpy-1.2.1.tar.gz", hash = "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c"}, -] - -[package.dependencies] -numpy = ">=1.20" - -[package.extras] -bokeh = ["bokeh", "selenium"] -docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] -test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] -test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] - -[[package]] -name = "cupy-cuda12x" -version = "12.1.0" -description = "CuPy: NumPy & SciPy for GPU" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cupy_cuda12x-12.1.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a3dbc76ce0f697a943061ddd2c47bc2138bc23ab56a020f1f5ff9141861b5245"}, - {file = "cupy_cuda12x-12.1.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:c432a0020f6d6e3399149e73128a9a581c29e4f996a4b63614811c47a82cf44e"}, - {file = "cupy_cuda12x-12.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a559f42db28ed10aea9642b9084dcb31860b46243714a464089daffe6c0a7e8f"}, - {file = "cupy_cuda12x-12.1.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:60a71296f8530a65e7fb693635f6d5963557789a34a42a7d8ca9f31b40c35920"}, - {file = "cupy_cuda12x-12.1.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:752a49c40654311d53a8953d8c16f7e216e10e8514599a476ea80c6f6b2b0163"}, - {file = "cupy_cuda12x-12.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:89a2f089cb04783dcfbca1c4e82338953fb933f1e6d838ec50713b9b8bd0a9c8"}, - {file = "cupy_cuda12x-12.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:d81dfdc7f6f47c392f24aa504e3b64732eb76a90b1e7ca31ad7265371be0ac42"}, - {file = "cupy_cuda12x-12.1.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:dc479d0397bb196a62c05322c0ff81a57af4dbbd020a7fbbb4b0843c35f61c27"}, - {file = "cupy_cuda12x-12.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:7ea86c085fca8e41579aced5a5fef45cc2dd90c270e030c32213cea2c471bb40"}, - {file = "cupy_cuda12x-12.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b52144ebbb3e1de1ca3da8c18b7c61066ac1f6d82e6252b7ea37ad11c66b5c3a"}, - {file = "cupy_cuda12x-12.1.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:1ecd027d279553a9e170c3724f9d1eb091dbf81b1eabbd2165add0da5d68a5bc"}, - {file = "cupy_cuda12x-12.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:259fccac3eeca4b9a04e1d2d32a1f79e74436d2b299a6b6bea7b84435c1dad0e"}, -] - -[package.dependencies] -fastrlock = ">=0.5" -numpy = ">=1.20,<1.27" - -[package.extras] -all = ["Cython (>=0.29.22,<3)", "optuna (>=2.0)", "scipy (>=1.6,<1.13)"] -stylecheck = ["autopep8 (==1.5.5)", "flake8 (==3.8.4)", "mypy (==0.950)", "pbr (==5.5.1)", "pycodestyle (==2.6.0)", "types-setuptools (==57.4.14)"] -test = ["hypothesis (>=6.37.2,<6.55.0)", "pytest (>=7.2)"] - -[[package]] -name = "cycler" -version = "0.12.1" -description = "Composable style cycles" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30"}, - {file = "cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c"}, -] - -[package.extras] -docs = ["ipython", "matplotlib", "numpydoc", "sphinx"] -tests = ["pytest", "pytest-cov", "pytest-xdist"] - -[[package]] -name = "datasets" -version = "2.18.0" -description = "HuggingFace community-driven open-source library of datasets" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "datasets-2.18.0-py3-none-any.whl", hash = "sha256:f1bbf0e2896917a914de01cbd37075b14deea3837af87ad0d9f697388ccaeb50"}, - {file = "datasets-2.18.0.tar.gz", hash = "sha256:cdf8b8c6abf7316377ba4f49f9589a4c74556d6b481afd0abd2284f3d69185cb"}, -] - -[package.dependencies] -aiohttp = "*" -dill = ">=0.3.0,<0.3.9" -filelock = "*" -fsspec = {version = ">=2023.1.0,<=2024.2.0", extras = ["http"]} -huggingface-hub = ">=0.19.4" -multiprocess = "*" -numpy = ">=1.17" -packaging = "*" -pandas = "*" -pyarrow = ">=12.0.0" -pyarrow-hotfix = "*" -pyyaml = ">=5.1" -requests = ">=2.19.0" -tqdm = ">=4.62.1" -xxhash = "*" - -[package.extras] -apache-beam = ["apache-beam (>=2.26.0)"] -audio = ["librosa", "soundfile (>=0.12.1)"] -benchmarks = ["tensorflow (==2.12.0)", "torch (==2.0.1)", "transformers (==4.30.1)"] -dev = ["Pillow (>=6.2.1)", "absl-py", "apache-beam (>=2.26.0)", "elasticsearch (<8.0.0)", "faiss-cpu (>=1.6.4)", "jax (>=0.3.14)", "jaxlib (>=0.3.14)", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "ruff (>=0.3.0)", "s3fs", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "sqlalchemy", "tensorflow (>=2.2.0,!=2.6.0,!=2.6.1)", "tensorflow (>=2.3,!=2.6.0,!=2.6.1)", "tensorflow-macos", "tiktoken", "torch", "torch (>=2.0.0)", "transformers", "typing-extensions (>=4.6.1)", "zstandard"] -docs = ["s3fs", "tensorflow (>=2.2.0,!=2.6.0,!=2.6.1)", "tensorflow-macos", "torch", "transformers"] -jax = ["jax (>=0.3.14)", "jaxlib (>=0.3.14)"] -metrics-tests = ["Werkzeug (>=1.0.1)", "accelerate", "bert-score (>=0.3.6)", "jiwer", "langdetect", "mauve-text", "nltk", "requests-file (>=1.5.1)", "rouge-score", "sacrebleu", "sacremoses", "scikit-learn", "scipy", "sentencepiece", "seqeval", "six (>=1.15.0,<1.16.0)", "spacy (>=3.0.0)", "texttable (>=1.6.3)", "tldextract", "tldextract (>=3.1.0)", "toml (>=0.10.1)", "typer (<0.5.0)"] -quality = ["ruff (>=0.3.0)"] -s3 = ["s3fs"] -tensorflow = ["tensorflow (>=2.2.0,!=2.6.0,!=2.6.1)", "tensorflow-macos"] -tensorflow-gpu = ["tensorflow-gpu (>=2.2.0,!=2.6.0,!=2.6.1)"] -tests = ["Pillow (>=6.2.1)", "absl-py", "apache-beam (>=2.26.0)", "elasticsearch (<8.0.0)", "faiss-cpu (>=1.6.4)", "jax (>=0.3.14)", "jaxlib (>=0.3.14)", "joblib (<1.3.0)", "joblibspark", "librosa", "lz4", "py7zr", "pyspark (>=3.4)", "pytest", "pytest-datadir", "pytest-xdist", "rarfile (>=4.0)", "s3fs (>=2021.11.1)", "soundfile (>=0.12.1)", "sqlalchemy", "tensorflow (>=2.3,!=2.6.0,!=2.6.1)", "tensorflow-macos", "tiktoken", "torch (>=2.0.0)", "transformers", "typing-extensions (>=4.6.1)", "zstandard"] -torch = ["torch"] -vision = ["Pillow (>=6.2.1)"] - -[[package]] -name = "debugpy" -version = "1.8.1" -description = "An implementation of the Debug Adapter Protocol for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, - {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, - {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, - {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, - {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, - {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, - {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, - {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, - {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, - {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, - {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, - {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, - {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, - {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, - {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, - {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, - {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, - {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, - {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, - {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, - {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, - {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, -] - -[[package]] -name = "decorator" -version = "5.1.1" -description = "Decorators for Humans" -optional = false -python-versions = ">=3.5" -files = [ - {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, - {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, -] - -[[package]] -name = "defusedxml" -version = "0.7.1" -description = "XML bomb protection for Python stdlib modules" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, - {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, -] - -[[package]] -name = "dill" -version = "0.3.8" -description = "serialize all of Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dill-0.3.8-py3-none-any.whl", hash = "sha256:c36ca9ffb54365bdd2f8eb3eff7d2a21237f8452b57ace88b1ac615b7e815bd7"}, - {file = "dill-0.3.8.tar.gz", hash = "sha256:3ebe3c479ad625c4553aca177444d89b486b1d84982eeacded644afc0cf797ca"}, -] - -[package.extras] -graph = ["objgraph (>=1.7.2)"] -profile = ["gprof2dot (>=2022.7.29)"] - -[[package]] -name = "diskcache" -version = "5.6.3" -description = "Disk Cache -- Disk and file backed persistent cache." -optional = false -python-versions = ">=3" -files = [ - {file = "diskcache-5.6.3-py3-none-any.whl", hash = "sha256:5e31b2d5fbad117cc363ebaf6b689474db18a1f6438bc82358b024abd4c2ca19"}, - {file = "diskcache-5.6.3.tar.gz", hash = "sha256:2c3a3fa2743d8535d832ec61c2054a1641f41775aa7c556758a109941e33e4fc"}, -] - -[[package]] -name = "distro" -version = "1.9.0" -description = "Distro - an OS platform information API" -optional = false -python-versions = ">=3.6" -files = [ - {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, - {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, -] - -[[package]] -name = "dnspython" -version = "2.6.1" -description = "DNS toolkit" -optional = false -python-versions = ">=3.8" -files = [ - {file = "dnspython-2.6.1-py3-none-any.whl", hash = "sha256:5ef3b9680161f6fa89daf8ad451b5f1a33b18ae8a1c6778cdf4b43f08c0a6e50"}, - {file = "dnspython-2.6.1.tar.gz", hash = "sha256:e8f0f9c23a7b7cb99ded64e6c3a6f3e701d78f50c55e002b839dea7225cff7cc"}, -] - -[package.extras] -dev = ["black (>=23.1.0)", "coverage (>=7.0)", "flake8 (>=7)", "mypy (>=1.8)", "pylint (>=3)", "pytest (>=7.4)", "pytest-cov (>=4.1.0)", "sphinx (>=7.2.0)", "twine (>=4.0.0)", "wheel (>=0.42.0)"] -dnssec = ["cryptography (>=41)"] -doh = ["h2 (>=4.1.0)", "httpcore (>=1.0.0)", "httpx (>=0.26.0)"] -doq = ["aioquic (>=0.9.25)"] -idna = ["idna (>=3.6)"] -trio = ["trio (>=0.23)"] -wmi = ["wmi (>=1.5.1)"] - -[[package]] -name = "email-validator" -version = "2.1.1" -description = "A robust email address syntax and deliverability validation library." -optional = false -python-versions = ">=3.8" -files = [ - {file = "email_validator-2.1.1-py3-none-any.whl", hash = "sha256:97d882d174e2a65732fb43bfce81a3a834cbc1bde8bf419e30ef5ea976370a05"}, - {file = "email_validator-2.1.1.tar.gz", hash = "sha256:200a70680ba08904be6d1eef729205cc0d687634399a5924d842533efb824b84"}, -] - -[package.dependencies] -dnspython = ">=2.0.0" -idna = ">=2.0.0" - -[[package]] -name = "environs" -version = "10.3.0" -description = "simplified environment variable parsing" -optional = false -python-versions = ">=3.8" -files = [ - {file = "environs-10.3.0-py3-none-any.whl", hash = "sha256:feeaf28f17fd0499f9cd7c0fcf408c6d82c308e69e335eb92d09322fc9ed8138"}, - {file = "environs-10.3.0.tar.gz", hash = "sha256:cc421ddb143fa30183568164755aa113a160e555cd19e97e664c478662032c24"}, -] - -[package.dependencies] -marshmallow = ">=3.0.0" -python-dotenv = "*" - -[package.extras] -dev = ["environs[lint,tests]", "tox"] -django = ["dj-database-url", "dj-email-url", "django-cache-url"] -lint = ["flake8 (==7.0.0)", "flake8-bugbear (==23.11.28)", "mypy (==1.8.0)", "pre-commit (>=3.6,<4.0)"] -tests = ["environs[django]", "pytest"] - -[[package]] -name = "exceptiongroup" -version = "1.2.0" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.0-py3-none-any.whl", hash = "sha256:4bfd3996ac73b41e9b9628b04e079f193850720ea5945fc96a08633c66912f14"}, - {file = "exceptiongroup-1.2.0.tar.gz", hash = "sha256:91f5c769735f051a4290d52edd0858999b57e5876e9f85937691bd4c9fa3ed68"}, -] - -[package.extras] -test = ["pytest (>=6)"] - -[[package]] -name = "executing" -version = "2.0.1" -description = "Get the currently executing AST node of a frame, and other information" -optional = false -python-versions = ">=3.5" -files = [ - {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, - {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, -] - -[package.extras] -tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] - -[[package]] -name = "fastapi" -version = "0.110.1" -description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fastapi-0.110.1-py3-none-any.whl", hash = "sha256:5df913203c482f820d31f48e635e022f8cbfe7350e4830ef05a3163925b1addc"}, - {file = "fastapi-0.110.1.tar.gz", hash = "sha256:6feac43ec359dfe4f45b2c18ec8c94edb8dc2dfc461d417d9e626590c071baad"}, -] - -[package.dependencies] -pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.38.0" -typing-extensions = ">=4.8.0" - -[package.extras] -all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] - -[[package]] -name = "fastjsonschema" -version = "2.19.1" -description = "Fastest Python implementation of JSON schema" -optional = false -python-versions = "*" -files = [ - {file = "fastjsonschema-2.19.1-py3-none-any.whl", hash = "sha256:3672b47bc94178c9f23dbb654bf47440155d4db9df5f7bc47643315f9c405cd0"}, - {file = "fastjsonschema-2.19.1.tar.gz", hash = "sha256:e3126a94bdc4623d3de4485f8d468a12f02a67921315ddc87836d6e456dc789d"}, -] - -[package.extras] -devel = ["colorama", "json-spec", "jsonschema", "pylint", "pytest", "pytest-benchmark", "pytest-cache", "validictory"] - -[[package]] -name = "fastrlock" -version = "0.8.2" -description = "Fast, re-entrant optimistic lock implemented in Cython" -optional = false -python-versions = "*" -files = [ - {file = "fastrlock-0.8.2-cp27-cp27m-macosx_10_15_x86_64.whl", hash = "sha256:94e348c72a1fd1f8191f25ea056448e4f5a87b8fbf005b39d290dcb0581a48cd"}, - {file = "fastrlock-0.8.2-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d5595903444c854b99c42122b87edfe8a37cd698a4eae32f4fd1d2a7b6c115d"}, - {file = "fastrlock-0.8.2-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:e4bbde174a0aff5f6eeba75cf8c4c5d2a316316bc21f03a0bddca0fc3659a6f3"}, - {file = "fastrlock-0.8.2-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7a2ccaf88ac0db153e84305d1ef0aa138cea82c6a88309066f6eaa3bc98636cd"}, - {file = "fastrlock-0.8.2-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:31a27a2edf482df72b91fe6c6438314d2c65290aa7becc55589d156c9b91f0da"}, - {file = "fastrlock-0.8.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:e9904b5b37c3e5bb4a245c56bc4b7e497da57ffb8528f4fc39af9dcb168ee2e1"}, - {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:43a241655e83e4603a152192cf022d5ca348c2f4e56dfb02e5c9c4c1a32f9cdb"}, - {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9121a894d74e65557e47e777060a495ab85f4b903e80dd73a3c940ba042920d7"}, - {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:11bbbbc526363955aeddb9eec4cee2a0012322b7b2f15b54f44454fcf4fd398a"}, - {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:27786c62a400e282756ae1b090bcd7cfa35f28270cff65a9e7b27a5327a32561"}, - {file = "fastrlock-0.8.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:08315bde19d0c2e6b06593d5a418be3dc8f9b1ee721afa96867b9853fceb45cf"}, - {file = "fastrlock-0.8.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:e8b49b5743ede51e0bcf6805741f39f5e0e0fd6a172ba460cb39e3097ba803bb"}, - {file = "fastrlock-0.8.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b443e73a4dfc7b6e0800ea4c13567b9694358e86f53bb2612a51c9e727cac67b"}, - {file = "fastrlock-0.8.2-cp310-cp310-win_amd64.whl", hash = "sha256:b3853ed4ce522598dc886160a7bab432a093051af85891fa2f5577c1dcac8ed6"}, - {file = "fastrlock-0.8.2-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:790fc19bccbd39426060047e53629f171a44745613bf360a045e9f9c8c4a2cea"}, - {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:dbdce852e6bb66e1b8c36679d482971d69d93acf1785657522e51b7de30c3356"}, - {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d47713ffe6d4a627fbf078be9836a95ac106b4a0543e3841572c91e292a5d885"}, - {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:ea96503b918fceaf40443182742b8964d47b65c5ebdea532893cb9479620000c"}, - {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:c6bffa978793bea5e1b00e677062e53a62255439339591b70e209fa1552d5ee0"}, - {file = "fastrlock-0.8.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:75c07726c8b1a52147fd7987d6baaa318c5dced1416c3f25593e40f56e10755b"}, - {file = "fastrlock-0.8.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:88f079335e9da631efa64486c8207564a7bcd0c00526bb9e842e9d5b7e50a6cc"}, - {file = "fastrlock-0.8.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4fb2e77ff04bc4beb71d63c8e064f052ce5a6ea1e001d528d4d7f4b37d736f2e"}, - {file = "fastrlock-0.8.2-cp311-cp311-win_amd64.whl", hash = "sha256:b4c9083ea89ab236b06e9ef2263971db3b4b507195fc7d5eecab95828dcae325"}, - {file = "fastrlock-0.8.2-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:98195866d3a9949915935d40a88e4f1c166e82e378f622c88025f2938624a90a"}, - {file = "fastrlock-0.8.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b22ea9bf5f9fad2b0077e944a7813f91593a4f61adf8faf734a70aed3f2b3a40"}, - {file = "fastrlock-0.8.2-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc1bf0ac8a194313cf6e645e300a8a379674ceed8e0b1e910a2de3e3c28989e"}, - {file = "fastrlock-0.8.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a3dcc876050b8f5cbc0ee84ef1e7f0c1dfe7c148f10098828bc4403683c33f10"}, - {file = "fastrlock-0.8.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:685e656048b59d8dfde8c601f188ad53a4d719eb97080cafc8696cda6d75865e"}, - {file = "fastrlock-0.8.2-cp312-cp312-win_amd64.whl", hash = "sha256:fb5363cf0fddd9b50525ddbf64a1e1b28ec4c6dfb28670a940cb1cf988a6786b"}, - {file = "fastrlock-0.8.2-cp35-cp35m-macosx_10_15_x86_64.whl", hash = "sha256:a74f5a92fa6e51c4f3c69b29c4662088b97be12f40652a21109605a175c81824"}, - {file = "fastrlock-0.8.2-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ccf39ad5702e33e4d335b48ef9d56e21619b529b7f7471b5211419f380329b62"}, - {file = "fastrlock-0.8.2-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:66f2662c640bb71a1016a031eea6eef9d25c2bcdf7ffd1d1ddc5a58f9a1ced04"}, - {file = "fastrlock-0.8.2-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:17734e2e5af4c07ddb0fb10bd484e062c22de3be6b67940b9cc6ec2f18fa61ba"}, - {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:ab91b0c36e95d42e1041a4907e3eefd06c482d53af3c7a77be7e214cc7cd4a63"}, - {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b32fdf874868326351a75b1e4c02f97e802147119ae44c52d3d9da193ec34f5b"}, - {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:2074548a335fcf7d19ebb18d9208da9e33b06f745754466a7e001d2b1c58dd19"}, - {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4fb04442b6d1e2b36c774919c6bcbe3339c61b337261d4bd57e27932589095af"}, - {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:1fed2f4797ad68e9982038423018cf08bec5f4ce9fed63a94a790773ed6a795c"}, - {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e380ec4e6d8b26e389713995a43cb7fe56baea2d25fe073d4998c4821a026211"}, - {file = "fastrlock-0.8.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:25945f962c7bd808415cfde3da624d4399d4ea71ed8918538375f16bceb79e1c"}, - {file = "fastrlock-0.8.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:2c1719ddc8218b01e82fb2e82e8451bd65076cb96d7bef4477194bbb4305a968"}, - {file = "fastrlock-0.8.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:5460c5ee6ced6d61ec8cd2324ebbe793a4960c4ffa2131ffff480e3b61c99ec5"}, - {file = "fastrlock-0.8.2-cp36-cp36m-win_amd64.whl", hash = "sha256:33145acbad8317584cd64588131c7e1e286beef6280c0009b4544c91fce171d2"}, - {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:59344c1d46b7dec97d3f22f1cc930fafe8980b3c5bc9c9765c56738a5f1559e4"}, - {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2a1c354f13f22b737621d914f3b4a8434ae69d3027a775e94b3e671756112f9"}, - {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:cf81e0278b645004388873e0a1f9e3bc4c9ab8c18e377b14ed1a544be4b18c9a"}, - {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1b15430b93d7eb3d56f6ff690d2ebecb79ed0e58248427717eba150a508d1cd7"}, - {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:067edb0a0805bf61e17a251d5046af59f6e9d2b8ad01222e0ef7a0b7937d5548"}, - {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eb31fe390f03f7ae886dcc374f1099ec88526631a4cb891d399b68181f154ff0"}, - {file = "fastrlock-0.8.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:643e1e65b4f5b284427e61a894d876d10459820e93aa1e724dfb415117be24e0"}, - {file = "fastrlock-0.8.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5dfb78dd600a12f23fc0c3ec58f81336229fdc74501ecf378d1ce5b3f2f313ea"}, - {file = "fastrlock-0.8.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8ca0fe21458457077e4cb2d81e1ebdb146a00b3e9e2db6180a773f7ea905032"}, - {file = "fastrlock-0.8.2-cp37-cp37m-win_amd64.whl", hash = "sha256:d918dfe473291e8bfd8e13223ea5cb9b317bd9f50c280923776c377f7c64b428"}, - {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:c393af77c659a38bffbca215c0bcc8629ba4299568308dd7e4ff65d62cabed39"}, - {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:73426f5eb2ecc10626c67cf86bd0af9e00d53e80e5c67d5ce8e18376d6abfa09"}, - {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:320fd55bafee3eb069cfb5d6491f811a912758387ef2193840e2663e80e16f48"}, - {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:8c1c91a68926421f5ccbc82c85f83bd3ba593b121a46a1b9a554b3f0dd67a4bf"}, - {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ad1bc61c7f6b0e58106aaab034916b6cb041757f708b07fbcdd9d6e1ac629225"}, - {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:87f4e01b042c84e6090dbc4fbe3415ddd69f6bc0130382323f9d3f1b8dd71b46"}, - {file = "fastrlock-0.8.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d34546ad2e4a480b94b6797bcc5a322b3c705c4c74c3e4e545c4a3841c1b2d59"}, - {file = "fastrlock-0.8.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ebb32d776b61acd49f859a1d16b9e3d84e7b46d0d92aebd58acd54dc38e96664"}, - {file = "fastrlock-0.8.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:30bdbe4662992348132d03996700e1cf910d141d629179b967b146a22942264e"}, - {file = "fastrlock-0.8.2-cp38-cp38-win_amd64.whl", hash = "sha256:07ed3c7b3867c05a3d6be4ced200c7767000f3431b9be6da66972822dd86e8be"}, - {file = "fastrlock-0.8.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:ddf5d247f686aec853ddcc9a1234bfcc6f57b0a0670d2ad82fc25d8ae7e6a15f"}, - {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7269bb3fc15587b0c191eecd95831d771a7d80f0c48929e560806b038ff3066c"}, - {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:adcb9e77aa132cc6c9de2ffe7cf880a20aa8cdba21d367d1da1a412f57bddd5d"}, - {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_24_i686.whl", hash = "sha256:a3b8b5d2935403f1b4b25ae324560e94b59593a38c0d2e7b6c9872126a9622ed"}, - {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2587cedbb36c7988e707d83f0f1175c1f882f362b5ebbee25d70218ea33d220d"}, - {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9af691a9861027181d4de07ed74f0aee12a9650ac60d0a07f4320bff84b5d95f"}, - {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99dd6652bd6f730beadf74ef769d38c6bbd8ee6d1c15c8d138ea680b0594387f"}, - {file = "fastrlock-0.8.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4d63b6596368dab9e0cc66bf047e7182a56f33b34db141816a4f21f5bf958228"}, - {file = "fastrlock-0.8.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ff75c90663d6e8996610d435e71487daa853871ad1770dd83dc0f2fc4997241e"}, - {file = "fastrlock-0.8.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e27c3cd27fbd25e5223c5c992b300cd4ee8f0a75c6f222ce65838138d853712c"}, - {file = "fastrlock-0.8.2-cp39-cp39-win_amd64.whl", hash = "sha256:dd961a32a7182c3891cdebca417fda67496d5d5de6ae636962254d22723bdf52"}, - {file = "fastrlock-0.8.2.tar.gz", hash = "sha256:644ec9215cf9c4df8028d8511379a15d9c1af3e16d80e47f1b6fdc6ba118356a"}, -] - -[[package]] -name = "filelock" -version = "3.13.4" -description = "A platform independent file lock." -optional = false -python-versions = ">=3.8" -files = [ - {file = "filelock-3.13.4-py3-none-any.whl", hash = "sha256:404e5e9253aa60ad457cae1be07c0f0ca90a63931200a47d9b6a6af84fd7b45f"}, - {file = "filelock-3.13.4.tar.gz", hash = "sha256:d13f466618bfde72bd2c18255e269f72542c6e70e7bac83a0232d6b1cc5c8cf4"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -testing = ["covdefaults (>=2.3)", "coverage (>=7.3.2)", "diff-cover (>=8.0.1)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)", "pytest-timeout (>=2.2)"] -typing = ["typing-extensions (>=4.8)"] - -[[package]] -name = "fire" -version = "0.5.0" -description = "A library for automatically generating command line interfaces." -optional = false -python-versions = "*" -files = [ - {file = "fire-0.5.0.tar.gz", hash = "sha256:a6b0d49e98c8963910021f92bba66f65ab440da2982b78eb1bbf95a0a34aacc6"}, -] - -[package.dependencies] -six = "*" -termcolor = "*" - -[[package]] -name = "fonttools" -version = "4.51.0" -description = "Tools to manipulate font files" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:84d7751f4468dd8cdd03ddada18b8b0857a5beec80bce9f435742abc9a851a74"}, - {file = "fonttools-4.51.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8b4850fa2ef2cfbc1d1f689bc159ef0f45d8d83298c1425838095bf53ef46308"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5b48a1121117047d82695d276c2af2ee3a24ffe0f502ed581acc2673ecf1037"}, - {file = "fonttools-4.51.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:180194c7fe60c989bb627d7ed5011f2bef1c4d36ecf3ec64daec8302f1ae0716"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:96a48e137c36be55e68845fc4284533bda2980f8d6f835e26bca79d7e2006438"}, - {file = "fonttools-4.51.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:806e7912c32a657fa39d2d6eb1d3012d35f841387c8fc6cf349ed70b7c340039"}, - {file = "fonttools-4.51.0-cp310-cp310-win32.whl", hash = "sha256:32b17504696f605e9e960647c5f64b35704782a502cc26a37b800b4d69ff3c77"}, - {file = "fonttools-4.51.0-cp310-cp310-win_amd64.whl", hash = "sha256:c7e91abdfae1b5c9e3a543f48ce96013f9a08c6c9668f1e6be0beabf0a569c1b"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a8feca65bab31479d795b0d16c9a9852902e3a3c0630678efb0b2b7941ea9c74"}, - {file = "fonttools-4.51.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ac27f436e8af7779f0bb4d5425aa3535270494d3bc5459ed27de3f03151e4c2"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0e19bd9e9964a09cd2433a4b100ca7f34e34731e0758e13ba9a1ed6e5468cc0f"}, - {file = "fonttools-4.51.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2b92381f37b39ba2fc98c3a45a9d6383bfc9916a87d66ccb6553f7bdd129097"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:5f6bc991d1610f5c3bbe997b0233cbc234b8e82fa99fc0b2932dc1ca5e5afec0"}, - {file = "fonttools-4.51.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9696fe9f3f0c32e9a321d5268208a7cc9205a52f99b89479d1b035ed54c923f1"}, - {file = "fonttools-4.51.0-cp311-cp311-win32.whl", hash = "sha256:3bee3f3bd9fa1d5ee616ccfd13b27ca605c2b4270e45715bd2883e9504735034"}, - {file = "fonttools-4.51.0-cp311-cp311-win_amd64.whl", hash = "sha256:0f08c901d3866a8905363619e3741c33f0a83a680d92a9f0e575985c2634fcc1"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:4060acc2bfa2d8e98117828a238889f13b6f69d59f4f2d5857eece5277b829ba"}, - {file = "fonttools-4.51.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1250e818b5f8a679ad79660855528120a8f0288f8f30ec88b83db51515411fcc"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76f1777d8b3386479ffb4a282e74318e730014d86ce60f016908d9801af9ca2a"}, - {file = "fonttools-4.51.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8b5ad456813d93b9c4b7ee55302208db2b45324315129d85275c01f5cb7e61a2"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:68b3fb7775a923be73e739f92f7e8a72725fd333eab24834041365d2278c3671"}, - {file = "fonttools-4.51.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8e2f1a4499e3b5ee82c19b5ee57f0294673125c65b0a1ff3764ea1f9db2f9ef5"}, - {file = "fonttools-4.51.0-cp312-cp312-win32.whl", hash = "sha256:278e50f6b003c6aed19bae2242b364e575bcb16304b53f2b64f6551b9c000e15"}, - {file = "fonttools-4.51.0-cp312-cp312-win_amd64.whl", hash = "sha256:b3c61423f22165541b9403ee39874dcae84cd57a9078b82e1dce8cb06b07fa2e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:1621ee57da887c17312acc4b0e7ac30d3a4fb0fec6174b2e3754a74c26bbed1e"}, - {file = "fonttools-4.51.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e9d9298be7a05bb4801f558522adbe2feea1b0b103d5294ebf24a92dd49b78e5"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee1af4be1c5afe4c96ca23badd368d8dc75f611887fb0c0dac9f71ee5d6f110e"}, - {file = "fonttools-4.51.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c18b49adc721a7d0b8dfe7c3130c89b8704baf599fb396396d07d4aa69b824a1"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de7c29bdbdd35811f14493ffd2534b88f0ce1b9065316433b22d63ca1cd21f14"}, - {file = "fonttools-4.51.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cadf4e12a608ef1d13e039864f484c8a968840afa0258b0b843a0556497ea9ed"}, - {file = "fonttools-4.51.0-cp38-cp38-win32.whl", hash = "sha256:aefa011207ed36cd280babfaa8510b8176f1a77261833e895a9d96e57e44802f"}, - {file = "fonttools-4.51.0-cp38-cp38-win_amd64.whl", hash = "sha256:865a58b6e60b0938874af0968cd0553bcd88e0b2cb6e588727117bd099eef836"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:60a3409c9112aec02d5fb546f557bca6efa773dcb32ac147c6baf5f742e6258b"}, - {file = "fonttools-4.51.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f7e89853d8bea103c8e3514b9f9dc86b5b4120afb4583b57eb10dfa5afbe0936"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56fc244f2585d6c00b9bcc59e6593e646cf095a96fe68d62cd4da53dd1287b55"}, - {file = "fonttools-4.51.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d145976194a5242fdd22df18a1b451481a88071feadf251221af110ca8f00ce"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c5b8cab0c137ca229433570151b5c1fc6af212680b58b15abd797dcdd9dd5051"}, - {file = "fonttools-4.51.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:54dcf21a2f2d06ded676e3c3f9f74b2bafded3a8ff12f0983160b13e9f2fb4a7"}, - {file = "fonttools-4.51.0-cp39-cp39-win32.whl", hash = "sha256:0118ef998a0699a96c7b28457f15546815015a2710a1b23a7bf6c1be60c01636"}, - {file = "fonttools-4.51.0-cp39-cp39-win_amd64.whl", hash = "sha256:599bdb75e220241cedc6faebfafedd7670335d2e29620d207dd0378a4e9ccc5a"}, - {file = "fonttools-4.51.0-py3-none-any.whl", hash = "sha256:15c94eeef6b095831067f72c825eb0e2d48bb4cea0647c1b05c981ecba2bf39f"}, - {file = "fonttools-4.51.0.tar.gz", hash = "sha256:dc0673361331566d7a663d7ce0f6fdcbfbdc1f59c6e3ed1165ad7202ca183c68"}, -] - -[package.extras] -all = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "fs (>=2.2.0,<3)", "lxml (>=4.0)", "lz4 (>=1.7.4.2)", "matplotlib", "munkres", "pycairo", "scipy", "skia-pathops (>=0.5.0)", "sympy", "uharfbuzz (>=0.23.0)", "unicodedata2 (>=15.1.0)", "xattr", "zopfli (>=0.1.4)"] -graphite = ["lz4 (>=1.7.4.2)"] -interpolatable = ["munkres", "pycairo", "scipy"] -lxml = ["lxml (>=4.0)"] -pathops = ["skia-pathops (>=0.5.0)"] -plot = ["matplotlib"] -repacker = ["uharfbuzz (>=0.23.0)"] -symfont = ["sympy"] -type1 = ["xattr"] -ufo = ["fs (>=2.2.0,<3)"] -unicode = ["unicodedata2 (>=15.1.0)"] -woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] - -[[package]] -name = "fqdn" -version = "1.5.1" -description = "Validates fully-qualified domain names against RFC 1123, so that they are acceptable to modern bowsers" -optional = false -python-versions = ">=2.7, !=3.0, !=3.1, !=3.2, !=3.3, !=3.4, <4" -files = [ - {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, - {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, -] - -[[package]] -name = "frozenlist" -version = "1.4.1" -description = "A list-like structure which implements collections.abc.MutableSequence" -optional = false -python-versions = ">=3.8" -files = [ - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f9aa1878d1083b276b0196f2dfbe00c9b7e752475ed3b682025ff20c1c1f51ac"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29acab3f66f0f24674b7dc4736477bcd4bc3ad4b896f5f45379a67bce8b96868"}, - {file = "frozenlist-1.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:74fb4bee6880b529a0c6560885fce4dc95936920f9f20f53d99a213f7bf66776"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:590344787a90ae57d62511dd7c736ed56b428f04cd8c161fcc5e7232c130c69a"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:068b63f23b17df8569b7fdca5517edef76171cf3897eb68beb01341131fbd2ad"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c849d495bf5154cd8da18a9eb15db127d4dba2968d88831aff6f0331ea9bd4c"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9750cc7fe1ae3b1611bb8cfc3f9ec11d532244235d75901fb6b8e42ce9229dfe"}, - {file = "frozenlist-1.4.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9b2de4cf0cdd5bd2dee4c4f63a653c61d2408055ab77b151c1957f221cabf2a"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0633c8d5337cb5c77acbccc6357ac49a1770b8c487e5b3505c57b949b4b82e98"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:27657df69e8801be6c3638054e202a135c7f299267f1a55ed3a598934f6c0d75"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:f9a3ea26252bd92f570600098783d1371354d89d5f6b7dfd87359d669f2109b5"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4f57dab5fe3407b6c0c1cc907ac98e8a189f9e418f3b6e54d65a718aaafe3950"}, - {file = "frozenlist-1.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e02a0e11cf6597299b9f3bbd3f93d79217cb90cfd1411aec33848b13f5c656cc"}, - {file = "frozenlist-1.4.1-cp310-cp310-win32.whl", hash = "sha256:a828c57f00f729620a442881cc60e57cfcec6842ba38e1b19fd3e47ac0ff8dc1"}, - {file = "frozenlist-1.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:f56e2333dda1fe0f909e7cc59f021eba0d2307bc6f012a1ccf2beca6ba362439"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a0cb6f11204443f27a1628b0e460f37fb30f624be6051d490fa7d7e26d4af3d0"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b46c8ae3a8f1f41a0d2ef350c0b6e65822d80772fe46b653ab6b6274f61d4a49"}, - {file = "frozenlist-1.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fde5bd59ab5357e3853313127f4d3565fc7dad314a74d7b5d43c22c6a5ed2ced"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:722e1124aec435320ae01ee3ac7bec11a5d47f25d0ed6328f2273d287bc3abb0"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2471c201b70d58a0f0c1f91261542a03d9a5e088ed3dc6c160d614c01649c106"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c757a9dd70d72b076d6f68efdbb9bc943665ae954dad2801b874c8c69e185068"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f146e0911cb2f1da549fc58fc7bcd2b836a44b79ef871980d605ec392ff6b0d2"}, - {file = "frozenlist-1.4.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9c515e7914626b2a2e1e311794b4c35720a0be87af52b79ff8e1429fc25f19"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c302220494f5c1ebeb0912ea782bcd5e2f8308037b3c7553fad0e48ebad6ad82"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:442acde1e068288a4ba7acfe05f5f343e19fac87bfc96d89eb886b0363e977ec"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:1b280e6507ea8a4fa0c0a7150b4e526a8d113989e28eaaef946cc77ffd7efc0a"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:fe1a06da377e3a1062ae5fe0926e12b84eceb8a50b350ddca72dc85015873f74"}, - {file = "frozenlist-1.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:db9e724bebd621d9beca794f2a4ff1d26eed5965b004a97f1f1685a173b869c2"}, - {file = "frozenlist-1.4.1-cp311-cp311-win32.whl", hash = "sha256:e774d53b1a477a67838a904131c4b0eef6b3d8a651f8b138b04f748fccfefe17"}, - {file = "frozenlist-1.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:fb3c2db03683b5767dedb5769b8a40ebb47d6f7f45b1b3e3b4b51ec8ad9d9825"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1979bc0aeb89b33b588c51c54ab0161791149f2461ea7c7c946d95d5f93b56ae"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cc7b01b3754ea68a62bd77ce6020afaffb44a590c2289089289363472d13aedb"}, - {file = "frozenlist-1.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c9c92be9fd329ac801cc420e08452b70e7aeab94ea4233a4804f0915c14eba9b"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c3894db91f5a489fc8fa6a9991820f368f0b3cbdb9cd8849547ccfab3392d86"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ba60bb19387e13597fb059f32cd4d59445d7b18b69a745b8f8e5db0346f33480"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aefbba5f69d42246543407ed2461db31006b0f76c4e32dfd6f42215a2c41d09"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780d3a35680ced9ce682fbcf4cb9c2bad3136eeff760ab33707b71db84664e3a"}, - {file = "frozenlist-1.4.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9acbb16f06fe7f52f441bb6f413ebae6c37baa6ef9edd49cdd567216da8600cd"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:23b701e65c7b36e4bf15546a89279bd4d8675faabc287d06bbcfac7d3c33e1e6"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:3e0153a805a98f5ada7e09826255ba99fb4f7524bb81bf6b47fb702666484ae1"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:dd9b1baec094d91bf36ec729445f7769d0d0cf6b64d04d86e45baf89e2b9059b"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:1a4471094e146b6790f61b98616ab8e44f72661879cc63fa1049d13ef711e71e"}, - {file = "frozenlist-1.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5667ed53d68d91920defdf4035d1cdaa3c3121dc0b113255124bcfada1cfa1b8"}, - {file = "frozenlist-1.4.1-cp312-cp312-win32.whl", hash = "sha256:beee944ae828747fd7cb216a70f120767fc9f4f00bacae8543c14a6831673f89"}, - {file = "frozenlist-1.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:64536573d0a2cb6e625cf309984e2d873979709f2cf22839bf2d61790b448ad5"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20b51fa3f588ff2fe658663db52a41a4f7aa6c04f6201449c6c7c476bd255c0d"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:410478a0c562d1a5bcc2f7ea448359fcb050ed48b3c6f6f4f18c313a9bdb1826"}, - {file = "frozenlist-1.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6321c9efe29975232da3bd0af0ad216800a47e93d763ce64f291917a381b8eb"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48f6a4533887e189dae092f1cf981f2e3885175f7a0f33c91fb5b7b682b6bab6"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6eb73fa5426ea69ee0e012fb59cdc76a15b1283d6e32e4f8dc4482ec67d1194d"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fbeb989b5cc29e8daf7f976b421c220f1b8c731cbf22b9130d8815418ea45887"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:32453c1de775c889eb4e22f1197fe3bdfe457d16476ea407472b9442e6295f7a"}, - {file = "frozenlist-1.4.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693945278a31f2086d9bf3df0fe8254bbeaef1fe71e1351c3bd730aa7d31c41b"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:1d0ce09d36d53bbbe566fe296965b23b961764c0bcf3ce2fa45f463745c04701"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3a670dc61eb0d0eb7080890c13de3066790f9049b47b0de04007090807c776b0"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:dca69045298ce5c11fd539682cff879cc1e664c245d1c64da929813e54241d11"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a06339f38e9ed3a64e4c4e43aec7f59084033647f908e4259d279a52d3757d09"}, - {file = "frozenlist-1.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b7f2f9f912dca3934c1baec2e4585a674ef16fe00218d833856408c48d5beee7"}, - {file = "frozenlist-1.4.1-cp38-cp38-win32.whl", hash = "sha256:e7004be74cbb7d9f34553a5ce5fb08be14fb33bc86f332fb71cbe5216362a497"}, - {file = "frozenlist-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:5a7d70357e7cee13f470c7883a063aae5fe209a493c57d86eb7f5a6f910fae09"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfa4a17e17ce9abf47a74ae02f32d014c5e9404b6d9ac7f729e01562bbee601e"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7e3ed87d4138356775346e6845cccbe66cd9e207f3cd11d2f0b9fd13681359d"}, - {file = "frozenlist-1.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c99169d4ff810155ca50b4da3b075cbde79752443117d89429595c2e8e37fed8"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edb678da49d9f72c9f6c609fbe41a5dfb9a9282f9e6a2253d5a91e0fc382d7c0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6db4667b187a6742b33afbbaf05a7bc551ffcf1ced0000a571aedbb4aa42fc7b"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55fdc093b5a3cb41d420884cdaf37a1e74c3c37a31f46e66286d9145d2063bd0"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82e8211d69a4f4bc360ea22cd6555f8e61a1bd211d1d5d39d3d228b48c83a897"}, - {file = "frozenlist-1.4.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89aa2c2eeb20957be2d950b85974b30a01a762f3308cd02bb15e1ad632e22dc7"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:9d3e0c25a2350080e9319724dede4f31f43a6c9779be48021a7f4ebde8b2d742"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7268252af60904bf52c26173cbadc3a071cece75f873705419c8681f24d3edea"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:0c250a29735d4f15321007fb02865f0e6b6a41a6b88f1f523ca1596ab5f50bd5"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:96ec70beabbd3b10e8bfe52616a13561e58fe84c0101dd031dc78f250d5128b9"}, - {file = "frozenlist-1.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:23b2d7679b73fe0e5a4560b672a39f98dfc6f60df63823b0a9970525325b95f6"}, - {file = "frozenlist-1.4.1-cp39-cp39-win32.whl", hash = "sha256:a7496bfe1da7fb1a4e1cc23bb67c58fab69311cc7d32b5a99c2007b4b2a0e932"}, - {file = "frozenlist-1.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:e6a20a581f9ce92d389a8c7d7c3dd47c81fd5d6e655c8dddf341e14aa48659d0"}, - {file = "frozenlist-1.4.1-py3-none-any.whl", hash = "sha256:04ced3e6a46b4cfffe20f9ae482818e34eba9b5fb0ce4056e4cc9b6e212d09b7"}, - {file = "frozenlist-1.4.1.tar.gz", hash = "sha256:c037a86e8513059a2613aaba4d817bb90b9d9b6b69aace3ce9c877e8c8ed402b"}, -] - -[[package]] -name = "fsspec" -version = "2024.2.0" -description = "File-system specification" -optional = false -python-versions = ">=3.8" -files = [ - {file = "fsspec-2024.2.0-py3-none-any.whl", hash = "sha256:817f969556fa5916bc682e02ca2045f96ff7f586d45110fcb76022063ad2c7d8"}, - {file = "fsspec-2024.2.0.tar.gz", hash = "sha256:b6ad1a679f760dda52b1168c859d01b7b80648ea6f7f7c7f5a8a91dc3f3ecb84"}, -] - -[package.dependencies] -aiohttp = {version = "<4.0.0a0 || >4.0.0a0,<4.0.0a1 || >4.0.0a1", optional = true, markers = "extra == \"http\""} - -[package.extras] -abfs = ["adlfs"] -adl = ["adlfs"] -arrow = ["pyarrow (>=1)"] -dask = ["dask", "distributed"] -devel = ["pytest", "pytest-cov"] -dropbox = ["dropbox", "dropboxdrivefs", "requests"] -full = ["adlfs", "aiohttp (!=4.0.0a0,!=4.0.0a1)", "dask", "distributed", "dropbox", "dropboxdrivefs", "fusepy", "gcsfs", "libarchive-c", "ocifs", "panel", "paramiko", "pyarrow (>=1)", "pygit2", "requests", "s3fs", "smbprotocol", "tqdm"] -fuse = ["fusepy"] -gcs = ["gcsfs"] -git = ["pygit2"] -github = ["requests"] -gs = ["gcsfs"] -gui = ["panel"] -hdfs = ["pyarrow (>=1)"] -http = ["aiohttp (!=4.0.0a0,!=4.0.0a1)"] -libarchive = ["libarchive-c"] -oci = ["ocifs"] -s3 = ["s3fs"] -sftp = ["paramiko"] -smb = ["smbprotocol"] -ssh = ["paramiko"] -tqdm = ["tqdm"] - -[[package]] -name = "grpclib" -version = "0.4.7" -description = "Pure-Python gRPC implementation for asyncio" -optional = false -python-versions = ">=3.7" -files = [ - {file = "grpclib-0.4.7.tar.gz", hash = "sha256:2988ef57c02b22b7a2e8e961792c41ccf97efc2ace91ae7a5b0de03c363823c3"}, -] - -[package.dependencies] -h2 = ">=3.1.0,<5" -multidict = "*" - -[package.extras] -protobuf = ["protobuf (>=3.20.0)"] - -[[package]] -name = "h11" -version = "0.14.0" -description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" -optional = false -python-versions = ">=3.7" -files = [ - {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, - {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, -] - -[[package]] -name = "h2" -version = "4.1.0" -description = "HTTP/2 State-Machine based protocol implementation" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "h2-4.1.0-py3-none-any.whl", hash = "sha256:03a46bcf682256c95b5fd9e9a99c1323584c3eec6440d379b9903d709476bc6d"}, - {file = "h2-4.1.0.tar.gz", hash = "sha256:a83aca08fbe7aacb79fec788c9c0bac936343560ed9ec18b82a13a12c28d2abb"}, -] - -[package.dependencies] -hpack = ">=4.0,<5" -hyperframe = ">=6.0,<7" - -[[package]] -name = "hpack" -version = "4.0.0" -description = "Pure-Python HPACK header compression" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "hpack-4.0.0-py3-none-any.whl", hash = "sha256:84a076fad3dc9a9f8063ccb8041ef100867b1878b25ef0ee63847a5d53818a6c"}, - {file = "hpack-4.0.0.tar.gz", hash = "sha256:fc41de0c63e687ebffde81187a948221294896f6bdc0ae2312708df339430095"}, -] - -[[package]] -name = "httpcore" -version = "1.0.5" -description = "A minimal low-level HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpcore-1.0.5-py3-none-any.whl", hash = "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5"}, - {file = "httpcore-1.0.5.tar.gz", hash = "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61"}, -] - -[package.dependencies] -certifi = "*" -h11 = ">=0.13,<0.15" - -[package.extras] -asyncio = ["anyio (>=4.0,<5.0)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] -trio = ["trio (>=0.22.0,<0.26.0)"] - -[[package]] -name = "httptools" -version = "0.6.1" -description = "A collection of framework independent HTTP protocol utils." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d2f6c3c4cb1948d912538217838f6e9960bc4a521d7f9b323b3da579cd14532f"}, - {file = "httptools-0.6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:00d5d4b68a717765b1fabfd9ca755bd12bf44105eeb806c03d1962acd9b8e563"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:639dc4f381a870c9ec860ce5c45921db50205a37cc3334e756269736ff0aac58"}, - {file = "httptools-0.6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e57997ac7fb7ee43140cc03664de5f268813a481dff6245e0075925adc6aa185"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0ac5a0ae3d9f4fe004318d64b8a854edd85ab76cffbf7ef5e32920faef62f142"}, - {file = "httptools-0.6.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3f30d3ce413088a98b9db71c60a6ada2001a08945cb42dd65a9a9fe228627658"}, - {file = "httptools-0.6.1-cp310-cp310-win_amd64.whl", hash = "sha256:1ed99a373e327f0107cb513b61820102ee4f3675656a37a50083eda05dc9541b"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7a7ea483c1a4485c71cb5f38be9db078f8b0e8b4c4dc0210f531cdd2ddac1ef1"}, - {file = "httptools-0.6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:85ed077c995e942b6f1b07583e4eb0a8d324d418954fc6af913d36db7c05a5a0"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b0bb634338334385351a1600a73e558ce619af390c2b38386206ac6a27fecfc"}, - {file = "httptools-0.6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d9ceb2c957320def533671fc9c715a80c47025139c8d1f3797477decbc6edd2"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4f0f8271c0a4db459f9dc807acd0eadd4839934a4b9b892f6f160e94da309837"}, - {file = "httptools-0.6.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6a4f5ccead6d18ec072ac0b84420e95d27c1cdf5c9f1bc8fbd8daf86bd94f43d"}, - {file = "httptools-0.6.1-cp311-cp311-win_amd64.whl", hash = "sha256:5cceac09f164bcba55c0500a18fe3c47df29b62353198e4f37bbcc5d591172c3"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:75c8022dca7935cba14741a42744eee13ba05db00b27a4b940f0d646bd4d56d0"}, - {file = "httptools-0.6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:48ed8129cd9a0d62cf4d1575fcf90fb37e3ff7d5654d3a5814eb3d55f36478c2"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f58e335a1402fb5a650e271e8c2d03cfa7cea46ae124649346d17bd30d59c90"}, - {file = "httptools-0.6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ad80d7176aa5788902f207a4e79885f0576134695dfb0fefc15b7a4648d503"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9bb68d3a085c2174c2477eb3ffe84ae9fb4fde8792edb7bcd09a1d8467e30a84"}, - {file = "httptools-0.6.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b512aa728bc02354e5ac086ce76c3ce635b62f5fbc32ab7082b5e582d27867bb"}, - {file = "httptools-0.6.1-cp312-cp312-win_amd64.whl", hash = "sha256:97662ce7fb196c785344d00d638fc9ad69e18ee4bfb4000b35a52efe5adcc949"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8e216a038d2d52ea13fdd9b9c9c7459fb80d78302b257828285eca1c773b99b3"}, - {file = "httptools-0.6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3e802e0b2378ade99cd666b5bffb8b2a7cc8f3d28988685dc300469ea8dd86cb"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bd3e488b447046e386a30f07af05f9b38d3d368d1f7b4d8f7e10af85393db97"}, - {file = "httptools-0.6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe467eb086d80217b7584e61313ebadc8d187a4d95bb62031b7bab4b205c3ba3"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3c3b214ce057c54675b00108ac42bacf2ab8f85c58e3f324a4e963bbc46424f4"}, - {file = "httptools-0.6.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8ae5b97f690badd2ca27cbf668494ee1b6d34cf1c464271ef7bfa9ca6b83ffaf"}, - {file = "httptools-0.6.1-cp38-cp38-win_amd64.whl", hash = "sha256:405784577ba6540fa7d6ff49e37daf104e04f4b4ff2d1ac0469eaa6a20fde084"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:95fb92dd3649f9cb139e9c56604cc2d7c7bf0fc2e7c8d7fbd58f96e35eddd2a3"}, - {file = "httptools-0.6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dcbab042cc3ef272adc11220517278519adf8f53fd3056d0e68f0a6f891ba94e"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cf2372e98406efb42e93bfe10f2948e467edfd792b015f1b4ecd897903d3e8d"}, - {file = "httptools-0.6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:678fcbae74477a17d103b7cae78b74800d795d702083867ce160fc202104d0da"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e0b281cf5a125c35f7f6722b65d8542d2e57331be573e9e88bc8b0115c4a7a81"}, - {file = "httptools-0.6.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:95658c342529bba4e1d3d2b1a874db16c7cca435e8827422154c9da76ac4e13a"}, - {file = "httptools-0.6.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ebaec1bf683e4bf5e9fbb49b8cc36da482033596a415b3e4ebab5a4c0d7ec5e"}, - {file = "httptools-0.6.1.tar.gz", hash = "sha256:c6e26c30455600b95d94b1b836085138e82f177351454ee841c148f93a9bad5a"}, -] - -[package.extras] -test = ["Cython (>=0.29.24,<0.30.0)"] - -[[package]] -name = "httpx" -version = "0.27.0" -description = "The next generation HTTP client." -optional = false -python-versions = ">=3.8" -files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, -] - -[package.dependencies] -anyio = "*" -certifi = "*" -httpcore = "==1.*" -idna = "*" -sniffio = "*" - -[package.extras] -brotli = ["brotli", "brotlicffi"] -cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] -http2 = ["h2 (>=3,<5)"] -socks = ["socksio (==1.*)"] - -[[package]] -name = "huggingface-hub" -version = "0.22.2" -description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "huggingface_hub-0.22.2-py3-none-any.whl", hash = "sha256:3429e25f38ccb834d310804a3b711e7e4953db5a9e420cc147a5e194ca90fd17"}, - {file = "huggingface_hub-0.22.2.tar.gz", hash = "sha256:32e9a9a6843c92f253ff9ca16b9985def4d80a93fb357af5353f770ef74a81be"}, -] - -[package.dependencies] -filelock = "*" -fsspec = ">=2023.5.0" -packaging = ">=20.9" -pyyaml = ">=5.1" -requests = "*" -tqdm = ">=4.42.1" -typing-extensions = ">=3.7.4.3" - -[package.extras] -all = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] -cli = ["InquirerPy (==0.3.4)"] -dev = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "mypy (==1.5.1)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "ruff (>=0.3.0)", "soundfile", "types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)", "urllib3 (<2.0)"] -fastai = ["fastai (>=2.4)", "fastcore (>=1.3.27)", "toml"] -hf-transfer = ["hf-transfer (>=0.1.4)"] -inference = ["aiohttp", "minijinja (>=1.0)"] -quality = ["mypy (==1.5.1)", "ruff (>=0.3.0)"] -tensorflow = ["graphviz", "pydot", "tensorflow"] -tensorflow-testing = ["keras (<3.0)", "tensorflow"] -testing = ["InquirerPy (==0.3.4)", "Jinja2", "Pillow", "aiohttp", "gradio", "jedi", "minijinja (>=1.0)", "numpy", "pytest", "pytest-asyncio", "pytest-cov", "pytest-env", "pytest-rerunfailures", "pytest-vcr", "pytest-xdist", "soundfile", "urllib3 (<2.0)"] -torch = ["safetensors", "torch"] -typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "types-tqdm", "types-urllib3", "typing-extensions (>=4.8.0)"] - -[[package]] -name = "hyperframe" -version = "6.0.1" -description = "HTTP/2 framing layer for Python" -optional = false -python-versions = ">=3.6.1" -files = [ - {file = "hyperframe-6.0.1-py3-none-any.whl", hash = "sha256:0ec6bafd80d8ad2195c4f03aacba3a8265e57bc4cff261e802bf39970ed02a15"}, - {file = "hyperframe-6.0.1.tar.gz", hash = "sha256:ae510046231dc8e9ecb1a6586f63d2347bf4c8905914aa84ba585ae85f28a914"}, -] - -[[package]] -name = "idna" -version = "3.7" -description = "Internationalized Domain Names in Applications (IDNA)" -optional = false -python-versions = ">=3.5" -files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, -] - -[[package]] -name = "imbalanced-learn" -version = "0.12.2" -description = "Toolbox for imbalanced dataset in machine learning." -optional = false -python-versions = "*" -files = [ - {file = "imbalanced-learn-0.12.2.tar.gz", hash = "sha256:a80c56cedcb07124f266be62d3a5d2ab5b5779909a7343fdf1b993479662f6c1"}, - {file = "imbalanced_learn-0.12.2-py3-none-any.whl", hash = "sha256:8523b3ee6c10c1d25f6bebe3faa73a0bca28d1fed55e0435b49af22802cdc259"}, -] - -[package.dependencies] -joblib = ">=1.1.1" -numpy = ">=1.17.3" -scikit-learn = ">=1.0.2" -scipy = ">=1.5.0" -threadpoolctl = ">=2.0.0" - -[package.extras] -docs = ["keras (>=2.4.3)", "matplotlib (>=3.1.2)", "memory-profiler (>=0.57.0)", "numpydoc (>=1.5.0)", "pandas (>=1.0.5)", "pydata-sphinx-theme (>=0.13.3)", "seaborn (>=0.9.0)", "sphinx (>=6.0.0)", "sphinx-copybutton (>=0.5.2)", "sphinx-gallery (>=0.13.0)", "sphinxcontrib-bibtex (>=2.4.1)", "tensorflow (>=2.4.3)"] -examples = ["keras (>=2.4.3)", "matplotlib (>=3.1.2)", "pandas (>=1.0.5)", "seaborn (>=0.9.0)", "tensorflow (>=2.4.3)"] -optional = ["keras (>=2.4.3)", "pandas (>=1.0.5)", "tensorflow (>=2.4.3)"] -tests = ["black (>=23.3.0)", "flake8 (>=3.8.2)", "keras (>=2.4.3)", "mypy (>=1.3.0)", "pandas (>=1.0.5)", "pytest (>=5.0.1)", "pytest-cov (>=2.9.0)", "tensorflow (>=2.4.3)"] - -[[package]] -name = "immutabledict" -version = "4.2.0" -description = "Immutable wrapper around dictionaries (a fork of frozendict)" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "immutabledict-4.2.0-py3-none-any.whl", hash = "sha256:d728b2c2410d698d95e6200237feb50a695584d20289ad3379a439aa3d90baba"}, - {file = "immutabledict-4.2.0.tar.gz", hash = "sha256:e003fd81aad2377a5a758bf7e1086cf3b70b63e9a5cc2f46bce8d0a2b4727c5f"}, -] - -[[package]] -name = "importlab" -version = "0.8.1" -description = "A library to calculate python dependency graphs." -optional = false -python-versions = ">=3.6.0" -files = [ - {file = "importlab-0.8.1-py2.py3-none-any.whl", hash = "sha256:124cfa00e8a34fefe8aac1a5e94f56c781b178c9eb61a1d3f60f7e03b77338d3"}, - {file = "importlab-0.8.1.tar.gz", hash = "sha256:b3893853b1f6eb027da509c3b40e6787e95dd66b4b66f1b3613aad77556e1465"}, -] - -[package.dependencies] -networkx = ">=2" - -[[package]] -name = "iniconfig" -version = "2.0.0" -description = "brain-dead simple config-ini parsing" -optional = false -python-versions = ">=3.7" -files = [ - {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, - {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, -] - -[[package]] -name = "interegular" -version = "0.3.3" -description = "a regex intersection checker" -optional = false -python-versions = ">=3.7" -files = [ - {file = "interegular-0.3.3-py37-none-any.whl", hash = "sha256:b0c07007d48c89d6d19f7204972d369b2a77222722e126b6aa63aa721dc3b19c"}, - {file = "interegular-0.3.3.tar.gz", hash = "sha256:d9b697b21b34884711399ba0f0376914b81899ce670032486d0d048344a76600"}, -] - -[[package]] -name = "ipykernel" -version = "6.29.4" -description = "IPython Kernel for Jupyter" -optional = false -python-versions = ">=3.8" -files = [ - {file = "ipykernel-6.29.4-py3-none-any.whl", hash = "sha256:1181e653d95c6808039c509ef8e67c4126b3b3af7781496c7cbfb5ed938a27da"}, - {file = "ipykernel-6.29.4.tar.gz", hash = "sha256:3d44070060f9475ac2092b760123fadf105d2e2493c24848b6691a7c4f42af5c"}, -] - -[package.dependencies] -appnope = {version = "*", markers = "platform_system == \"Darwin\""} -comm = ">=0.1.1" -debugpy = ">=1.6.5" -ipython = ">=7.23.1" -jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -matplotlib-inline = ">=0.1" -nest-asyncio = "*" -packaging = "*" -psutil = "*" -pyzmq = ">=24" -tornado = ">=6.1" -traitlets = ">=5.4.0" - -[package.extras] -cov = ["coverage[toml]", "curio", "matplotlib", "pytest-cov", "trio"] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "trio"] -pyqt5 = ["pyqt5"] -pyside6 = ["pyside6"] -test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.23.5)", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "ipympl" -version = "0.9.4" -description = "Matplotlib Jupyter Extension" -optional = false -python-versions = ">=3.9" -files = [ - {file = "ipympl-0.9.4-py3-none-any.whl", hash = "sha256:5b0c08c6f4f6ea655ba58239363457c10fb921557f5038c1a46db4457d6d6b0e"}, - {file = "ipympl-0.9.4.tar.gz", hash = "sha256:cfb53c5b4fcbcee6d18f095eecfc6c6c474303d5b744e72cc66e7a2804708907"}, -] - -[package.dependencies] -ipython = "<9" -ipython-genutils = "*" -ipywidgets = ">=7.6.0,<9" -matplotlib = ">=3.4.0,<4" -numpy = "*" -pillow = "*" -traitlets = "<6" - -[package.extras] -docs = ["myst-nb", "sphinx (>=1.5)", "sphinx-book-theme", "sphinx-copybutton", "sphinx-thebe", "sphinx-togglebutton"] - -[[package]] -name = "ipython" -version = "8.23.0" -description = "IPython: Productive Interactive Computing" -optional = false -python-versions = ">=3.10" -files = [ - {file = "ipython-8.23.0-py3-none-any.whl", hash = "sha256:07232af52a5ba146dc3372c7bf52a0f890a23edf38d77caef8d53f9cdc2584c1"}, - {file = "ipython-8.23.0.tar.gz", hash = "sha256:7468edaf4f6de3e1b912e57f66c241e6fd3c7099f2ec2136e239e142e800274d"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} -jedi = ">=0.16" -matplotlib-inline = "*" -pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} -prompt-toolkit = ">=3.0.41,<3.1.0" -pygments = ">=2.4.0" -stack-data = "*" -traitlets = ">=5.13.0" -typing-extensions = {version = "*", markers = "python_version < \"3.12\""} - -[package.extras] -all = ["ipython[black,doc,kernel,matplotlib,nbconvert,nbformat,notebook,parallel,qtconsole]", "ipython[test,test-extra]"] -black = ["black"] -doc = ["docrepr", "exceptiongroup", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "stack-data", "typing-extensions"] -kernel = ["ipykernel"] -matplotlib = ["matplotlib"] -nbconvert = ["nbconvert"] -nbformat = ["nbformat"] -notebook = ["ipywidgets", "notebook"] -parallel = ["ipyparallel"] -qtconsole = ["qtconsole"] -test = ["pickleshare", "pytest (<8)", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] - -[[package]] -name = "ipython-genutils" -version = "0.2.0" -description = "Vestigial utilities from IPython" -optional = false -python-versions = "*" -files = [ - {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"}, - {file = "ipython_genutils-0.2.0.tar.gz", hash = "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8"}, -] - -[[package]] -name = "ipywidgets" -version = "8.1.2" -description = "Jupyter interactive widgets" -optional = false -python-versions = ">=3.7" -files = [ - {file = "ipywidgets-8.1.2-py3-none-any.whl", hash = "sha256:bbe43850d79fb5e906b14801d6c01402857996864d1e5b6fa62dd2ee35559f60"}, - {file = "ipywidgets-8.1.2.tar.gz", hash = "sha256:d0b9b41e49bae926a866e613a39b0f0097745d2b9f1f3dd406641b4a57ec42c9"}, -] - -[package.dependencies] -comm = ">=0.1.3" -ipython = ">=6.1.0" -jupyterlab-widgets = ">=3.0.10,<3.1.0" -traitlets = ">=4.3.1" -widgetsnbextension = ">=4.0.10,<4.1.0" - -[package.extras] -test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] - -[[package]] -name = "isoduration" -version = "20.11.0" -description = "Operations with ISO 8601 durations" -optional = false -python-versions = ">=3.7" -files = [ - {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, - {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"}, -] - -[package.dependencies] -arrow = ">=0.15.0" - -[[package]] -name = "jedi" -version = "0.19.1" -description = "An autocompletion tool for Python that can be used for text editors." -optional = false -python-versions = ">=3.6" -files = [ - {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, - {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, -] - -[package.dependencies] -parso = ">=0.8.3,<0.9.0" - -[package.extras] -docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] - -[[package]] -name = "jinja2" -version = "3.1.3" -description = "A very fast and expressive template engine." -optional = false -python-versions = ">=3.7" -files = [ - {file = "Jinja2-3.1.3-py3-none-any.whl", hash = "sha256:7d6d50dd97d52cbc355597bd845fabfbac3f551e1f99619e39a35ce8c370b5fa"}, - {file = "Jinja2-3.1.3.tar.gz", hash = "sha256:ac8bd6544d4bb2c9792bf3a159e80bba8fda7f07e81bc3aed565432d5925ba90"}, -] - -[package.dependencies] -MarkupSafe = ">=2.0" - -[package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "joblib" -version = "1.4.0" -description = "Lightweight pipelining with Python functions" -optional = false -python-versions = ">=3.8" -files = [ - {file = "joblib-1.4.0-py3-none-any.whl", hash = "sha256:42942470d4062537be4d54c83511186da1fc14ba354961a2114da91efa9a4ed7"}, - {file = "joblib-1.4.0.tar.gz", hash = "sha256:1eb0dc091919cd384490de890cb5dfd538410a6d4b3b54eef09fb8c50b409b1c"}, -] - -[[package]] -name = "json5" -version = "0.9.25" -description = "A Python implementation of the JSON5 data format." -optional = false -python-versions = ">=3.8" -files = [ - {file = "json5-0.9.25-py3-none-any.whl", hash = "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f"}, - {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, -] - -[[package]] -name = "jsonpointer" -version = "2.4" -description = "Identify specific nodes in a JSON document (RFC 6901)" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" -files = [ - {file = "jsonpointer-2.4-py2.py3-none-any.whl", hash = "sha256:15d51bba20eea3165644553647711d150376234112651b4f1811022aecad7d7a"}, - {file = "jsonpointer-2.4.tar.gz", hash = "sha256:585cee82b70211fa9e6043b7bb89db6e1aa49524340dde8ad6b63206ea689d88"}, -] - -[[package]] -name = "jsonschema" -version = "4.21.1" -description = "An implementation of JSON Schema validation for Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jsonschema-4.21.1-py3-none-any.whl", hash = "sha256:7996507afae316306f9e2290407761157c6f78002dcf7419acb99822143d1c6f"}, - {file = "jsonschema-4.21.1.tar.gz", hash = "sha256:85727c00279f5fa6bedbe6238d2aa6403bedd8b4864ab11207d07df3cc1b2ee5"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -fqdn = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -idna = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -isoduration = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -jsonpointer = {version = ">1.13", optional = true, markers = "extra == \"format-nongpl\""} -jsonschema-specifications = ">=2023.03.6" -referencing = ">=0.28.4" -rfc3339-validator = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -rfc3986-validator = {version = ">0.1.0", optional = true, markers = "extra == \"format-nongpl\""} -rpds-py = ">=0.7.1" -uri-template = {version = "*", optional = true, markers = "extra == \"format-nongpl\""} -webcolors = {version = ">=1.11", optional = true, markers = "extra == \"format-nongpl\""} - -[package.extras] -format = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3987", "uri-template", "webcolors (>=1.11)"] -format-nongpl = ["fqdn", "idna", "isoduration", "jsonpointer (>1.13)", "rfc3339-validator", "rfc3986-validator (>0.1.0)", "uri-template", "webcolors (>=1.11)"] - -[[package]] -name = "jsonschema-specifications" -version = "2023.12.1" -description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jsonschema_specifications-2023.12.1-py3-none-any.whl", hash = "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c"}, - {file = "jsonschema_specifications-2023.12.1.tar.gz", hash = "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc"}, -] - -[package.dependencies] -referencing = ">=0.31.0" - -[[package]] -name = "julep" -version = "0.2.11" -description = "Julep is a platform for creating agents with long-term memory" -optional = false -python-versions = "<3.14,>=3.8" -files = [ - {file = "julep-0.2.11-py3-none-any.whl", hash = "sha256:31c742a8d7bb4e4ae8ded9642dc15e784e4fbd43f1f74386c2a99940014c3810"}, - {file = "julep-0.2.11.tar.gz", hash = "sha256:977eaeb1b2cd3d3192662ee76d544e8270bc8c82638efea066b203f8ec80374f"}, -] - -[package.dependencies] -beartype = ">=0.14.0,<1.0.0" -environs = ">=9.0.0,<11.0.0" -httpx = ">=0.20.0,<1.0.0" -openai = ">=1.0.1,<2.0.0" -pydantic = ">=2.0.1,<3.0.0" -typing-extensions = ">=4.0.0,<5.0.0" - -[[package]] -name = "jupyter-client" -version = "8.6.1" -description = "Jupyter protocol implementation and client libraries" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_client-8.6.1-py3-none-any.whl", hash = "sha256:3b7bd22f058434e3b9a7ea4b1500ed47de2713872288c0d511d19926f99b459f"}, - {file = "jupyter_client-8.6.1.tar.gz", hash = "sha256:e842515e2bab8e19186d89fdfea7abd15e39dd581f94e399f00e2af5a1652d3f"}, -] - -[package.dependencies] -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -python-dateutil = ">=2.8.2" -pyzmq = ">=23.0" -tornado = ">=6.2" -traitlets = ">=5.3" - -[package.extras] -docs = ["ipykernel", "myst-parser", "pydata-sphinx-theme", "sphinx (>=4)", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["coverage", "ipykernel (>=6.14)", "mypy", "paramiko", "pre-commit", "pytest", "pytest-cov", "pytest-jupyter[client] (>=0.4.1)", "pytest-timeout"] - -[[package]] -name = "jupyter-core" -version = "5.7.2" -description = "Jupyter core package. A base package on which Jupyter projects rely." -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_core-5.7.2-py3-none-any.whl", hash = "sha256:4f7315d2f6b4bcf2e3e7cb6e46772eba760ae459cd1f59d29eb57b0a01bd7409"}, - {file = "jupyter_core-5.7.2.tar.gz", hash = "sha256:aa5f8d32bbf6b431ac830496da7392035d6f61b4f54872f15c4bd2a9c3f536d9"}, -] - -[package.dependencies] -platformdirs = ">=2.5" -pywin32 = {version = ">=300", markers = "sys_platform == \"win32\" and platform_python_implementation != \"PyPy\""} -traitlets = ">=5.3" - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-spelling", "traitlets"] -test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout"] - -[[package]] -name = "jupyter-events" -version = "0.10.0" -description = "Jupyter Event System library" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_events-0.10.0-py3-none-any.whl", hash = "sha256:4b72130875e59d57716d327ea70d3ebc3af1944d3717e5a498b8a06c6c159960"}, - {file = "jupyter_events-0.10.0.tar.gz", hash = "sha256:670b8229d3cc882ec782144ed22e0d29e1c2d639263f92ca8383e66682845e22"}, -] - -[package.dependencies] -jsonschema = {version = ">=4.18.0", extras = ["format-nongpl"]} -python-json-logger = ">=2.0.4" -pyyaml = ">=5.3" -referencing = "*" -rfc3339-validator = "*" -rfc3986-validator = ">=0.1.1" -traitlets = ">=5.3" - -[package.extras] -cli = ["click", "rich"] -docs = ["jupyterlite-sphinx", "myst-parser", "pydata-sphinx-theme", "sphinxcontrib-spelling"] -test = ["click", "pre-commit", "pytest (>=7.0)", "pytest-asyncio (>=0.19.0)", "pytest-console-scripts", "rich"] - -[[package]] -name = "jupyter-lsp" -version = "2.2.5" -description = "Multi-Language Server WebSocket proxy for Jupyter Notebook/Lab server" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter-lsp-2.2.5.tar.gz", hash = "sha256:793147a05ad446f809fd53ef1cd19a9f5256fd0a2d6b7ce943a982cb4f545001"}, - {file = "jupyter_lsp-2.2.5-py3-none-any.whl", hash = "sha256:45fbddbd505f3fbfb0b6cb2f1bc5e15e83ab7c79cd6e89416b248cb3c00c11da"}, -] - -[package.dependencies] -jupyter-server = ">=1.1.2" - -[[package]] -name = "jupyter-server" -version = "2.14.0" -description = "The backend—i.e. core services, APIs, and REST endpoints—to Jupyter web applications." -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_server-2.14.0-py3-none-any.whl", hash = "sha256:fb6be52c713e80e004fac34b35a0990d6d36ba06fd0a2b2ed82b899143a64210"}, - {file = "jupyter_server-2.14.0.tar.gz", hash = "sha256:659154cea512083434fd7c93b7fe0897af7a2fd0b9dd4749282b42eaac4ae677"}, -] - -[package.dependencies] -anyio = ">=3.1.0" -argon2-cffi = ">=21.1" -jinja2 = ">=3.0.3" -jupyter-client = ">=7.4.4" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -jupyter-events = ">=0.9.0" -jupyter-server-terminals = ">=0.4.4" -nbconvert = ">=6.4.4" -nbformat = ">=5.3.0" -overrides = ">=5.0" -packaging = ">=22.0" -prometheus-client = ">=0.9" -pywinpty = {version = ">=2.0.1", markers = "os_name == \"nt\""} -pyzmq = ">=24" -send2trash = ">=1.8.2" -terminado = ">=0.8.3" -tornado = ">=6.2.0" -traitlets = ">=5.6.0" -websocket-client = ">=1.7" - -[package.extras] -docs = ["ipykernel", "jinja2", "jupyter-client", "jupyter-server", "myst-parser", "nbformat", "prometheus-client", "pydata-sphinx-theme", "send2trash", "sphinx-autodoc-typehints", "sphinxcontrib-github-alt", "sphinxcontrib-openapi (>=0.8.0)", "sphinxcontrib-spelling", "sphinxemoji", "tornado", "typing-extensions"] -test = ["flaky", "ipykernel", "pre-commit", "pytest (>=7.0,<9)", "pytest-console-scripts", "pytest-jupyter[server] (>=0.7)", "pytest-timeout", "requests"] - -[[package]] -name = "jupyter-server-terminals" -version = "0.5.3" -description = "A Jupyter Server Extension Providing Terminals." -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_server_terminals-0.5.3-py3-none-any.whl", hash = "sha256:41ee0d7dc0ebf2809c668e0fc726dfaf258fcd3e769568996ca731b6194ae9aa"}, - {file = "jupyter_server_terminals-0.5.3.tar.gz", hash = "sha256:5ae0295167220e9ace0edcfdb212afd2b01ee8d179fe6f23c899590e9b8a5269"}, -] - -[package.dependencies] -pywinpty = {version = ">=2.0.3", markers = "os_name == \"nt\""} -terminado = ">=0.8.3" - -[package.extras] -docs = ["jinja2", "jupyter-server", "mistune (<4.0)", "myst-parser", "nbformat", "packaging", "pydata-sphinx-theme", "sphinxcontrib-github-alt", "sphinxcontrib-openapi", "sphinxcontrib-spelling", "sphinxemoji", "tornado"] -test = ["jupyter-server (>=2.0.0)", "pytest (>=7.0)", "pytest-jupyter[server] (>=0.5.3)", "pytest-timeout"] - -[[package]] -name = "jupyterlab" -version = "4.1.6" -description = "JupyterLab computational environment" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyterlab-4.1.6-py3-none-any.whl", hash = "sha256:cf3e862bc10dbf4331e4eb37438634f813c238cfc62c71c640b3b3b2caa089a8"}, - {file = "jupyterlab-4.1.6.tar.gz", hash = "sha256:7935f36ba26eb615183a4f5c2bbca5791b5108ce2a00b5505f8cfd100d53648e"}, -] - -[package.dependencies] -async-lru = ">=1.0.0" -httpx = ">=0.25.0" -ipykernel = ">=6.5.0" -jinja2 = ">=3.0.3" -jupyter-core = "*" -jupyter-lsp = ">=2.0.0" -jupyter-server = ">=2.4.0,<3" -jupyterlab-server = ">=2.19.0,<3" -notebook-shim = ">=0.2" -packaging = "*" -tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} -tornado = ">=6.2.0" -traitlets = "*" - -[package.extras] -dev = ["build", "bump2version", "coverage", "hatch", "pre-commit", "pytest-cov", "ruff (==0.2.0)"] -docs = ["jsx-lexer", "myst-parser", "pydata-sphinx-theme (>=0.13.0)", "pytest", "pytest-check-links", "pytest-jupyter", "sphinx (>=1.8,<7.3.0)", "sphinx-copybutton"] -docs-screenshots = ["altair (==5.2.0)", "ipython (==8.16.1)", "ipywidgets (==8.1.1)", "jupyterlab-geojson (==3.4.0)", "jupyterlab-language-pack-zh-cn (==4.0.post6)", "matplotlib (==3.8.2)", "nbconvert (>=7.0.0)", "pandas (==2.2.0)", "scipy (==1.12.0)", "vega-datasets (==0.9.0)"] -test = ["coverage", "pytest (>=7.0)", "pytest-check-links (>=0.7)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter (>=0.5.3)", "pytest-timeout", "pytest-tornasync", "requests", "requests-cache", "virtualenv"] -upgrade-extension = ["copier (>=8.0,<9.0)", "jinja2-time (<0.3)", "pydantic (<2.0)", "pyyaml-include (<2.0)", "tomli-w (<2.0)"] - -[[package]] -name = "jupyterlab-pygments" -version = "0.3.0" -description = "Pygments theme using JupyterLab CSS variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyterlab_pygments-0.3.0-py3-none-any.whl", hash = "sha256:841a89020971da1d8693f1a99997aefc5dc424bb1b251fd6322462a1b8842780"}, - {file = "jupyterlab_pygments-0.3.0.tar.gz", hash = "sha256:721aca4d9029252b11cfa9d185e5b5af4d54772bb8072f9b7036f4170054d35d"}, -] - -[[package]] -name = "jupyterlab-server" -version = "2.26.0" -description = "A set of server components for JupyterLab and JupyterLab like applications." -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyterlab_server-2.26.0-py3-none-any.whl", hash = "sha256:54622cbd330526a385ee0c1fdccdff3a1e7219bf3e864a335284a1270a1973df"}, - {file = "jupyterlab_server-2.26.0.tar.gz", hash = "sha256:9b3ba91cf2837f7f124fca36d63f3ca80ace2bed4898a63dd47e6598c1ab006f"}, -] - -[package.dependencies] -babel = ">=2.10" -jinja2 = ">=3.0.3" -json5 = ">=0.9.0" -jsonschema = ">=4.18.0" -jupyter-server = ">=1.21,<3" -packaging = ">=21.3" -requests = ">=2.31" - -[package.extras] -docs = ["autodoc-traits", "jinja2 (<3.2.0)", "mistune (<4)", "myst-parser", "pydata-sphinx-theme", "sphinx", "sphinx-copybutton", "sphinxcontrib-openapi (>0.8)"] -openapi = ["openapi-core (>=0.18.0,<0.19.0)", "ruamel-yaml"] -test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-validator (>=0.6.0,<0.8.0)", "pytest (>=7.0,<8)", "pytest-console-scripts", "pytest-cov", "pytest-jupyter[server] (>=0.6.2)", "pytest-timeout", "requests-mock", "ruamel-yaml", "sphinxcontrib-spelling", "strict-rfc3339", "werkzeug"] - -[[package]] -name = "jupyterlab-widgets" -version = "3.0.10" -description = "Jupyter interactive widgets for JupyterLab" -optional = false -python-versions = ">=3.7" -files = [ - {file = "jupyterlab_widgets-3.0.10-py3-none-any.whl", hash = "sha256:dd61f3ae7a5a7f80299e14585ce6cf3d6925a96c9103c978eda293197730cb64"}, - {file = "jupyterlab_widgets-3.0.10.tar.gz", hash = "sha256:04f2ac04976727e4f9d0fa91cdc2f1ab860f965e504c29dbd6a65c882c9d04c0"}, -] - -[[package]] -name = "kiwisolver" -version = "1.4.5" -description = "A fast implementation of the Cassowary constraint solver" -optional = false -python-versions = ">=3.7" -files = [ - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:05703cf211d585109fcd72207a31bb170a0f22144d68298dc5e61b3c946518af"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:146d14bebb7f1dc4d5fbf74f8a6cb15ac42baadee8912eb84ac0b3b2a3dc6ac3"}, - {file = "kiwisolver-1.4.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6ef7afcd2d281494c0a9101d5c571970708ad911d028137cd558f02b851c08b4"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9eaa8b117dc8337728e834b9c6e2611f10c79e38f65157c4c38e9400286f5cb1"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec20916e7b4cbfb1f12380e46486ec4bcbaa91a9c448b97023fde0d5bbf9e4ff"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39b42c68602539407884cf70d6a480a469b93b81b7701378ba5e2328660c847a"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa12042de0171fad672b6c59df69106d20d5596e4f87b5e8f76df757a7c399aa"}, - {file = "kiwisolver-1.4.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a40773c71d7ccdd3798f6489aaac9eee213d566850a9533f8d26332d626b82c"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:19df6e621f6d8b4b9c4d45f40a66839294ff2bb235e64d2178f7522d9170ac5b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:83d78376d0d4fd884e2c114d0621624b73d2aba4e2788182d286309ebdeed770"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:e391b1f0a8a5a10ab3b9bb6afcfd74f2175f24f8975fb87ecae700d1503cdee0"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:852542f9481f4a62dbb5dd99e8ab7aedfeb8fb6342349a181d4036877410f525"}, - {file = "kiwisolver-1.4.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:59edc41b24031bc25108e210c0def6f6c2191210492a972d585a06ff246bb79b"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win32.whl", hash = "sha256:a6aa6315319a052b4ee378aa171959c898a6183f15c1e541821c5c59beaa0238"}, - {file = "kiwisolver-1.4.5-cp310-cp310-win_amd64.whl", hash = "sha256:d0ef46024e6a3d79c01ff13801cb19d0cad7fd859b15037aec74315540acc276"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:11863aa14a51fd6ec28688d76f1735f8f69ab1fabf388851a595d0721af042f5"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8ab3919a9997ab7ef2fbbed0cc99bb28d3c13e6d4b1ad36e97e482558a91be90"}, - {file = "kiwisolver-1.4.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fcc700eadbbccbf6bc1bcb9dbe0786b4b1cb91ca0dcda336eef5c2beed37b797"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dfdd7c0b105af050eb3d64997809dc21da247cf44e63dc73ff0fd20b96be55a9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76c6a5964640638cdeaa0c359382e5703e9293030fe730018ca06bc2010c4437"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bbea0db94288e29afcc4c28afbf3a7ccaf2d7e027489c449cf7e8f83c6346eb9"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ceec1a6bc6cab1d6ff5d06592a91a692f90ec7505d6463a88a52cc0eb58545da"}, - {file = "kiwisolver-1.4.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:040c1aebeda72197ef477a906782b5ab0d387642e93bda547336b8957c61022e"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f91de7223d4c7b793867797bacd1ee53bfe7359bd70d27b7b58a04efbb9436c8"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:faae4860798c31530dd184046a900e652c95513796ef51a12bc086710c2eec4d"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0157420efcb803e71d1b28e2c287518b8808b7cf1ab8af36718fd0a2c453eb0"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:06f54715b7737c2fecdbf140d1afb11a33d59508a47bf11bb38ecf21dc9ab79f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fdb7adb641a0d13bdcd4ef48e062363d8a9ad4a182ac7647ec88f695e719ae9f"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win32.whl", hash = "sha256:bb86433b1cfe686da83ce32a9d3a8dd308e85c76b60896d58f082136f10bffac"}, - {file = "kiwisolver-1.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c08e1312a9cf1074d17b17728d3dfce2a5125b2d791527f33ffbe805200a355"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:32d5cf40c4f7c7b3ca500f8985eb3fb3a7dfc023215e876f207956b5ea26632a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f846c260f483d1fd217fe5ed7c173fb109efa6b1fc8381c8b7552c5781756192"}, - {file = "kiwisolver-1.4.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:5ff5cf3571589b6d13bfbfd6bcd7a3f659e42f96b5fd1c4830c4cf21d4f5ef45"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7269d9e5f1084a653d575c7ec012ff57f0c042258bf5db0954bf551c158466e7"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da802a19d6e15dffe4b0c24b38b3af68e6c1a68e6e1d8f30148c83864f3881db"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3aba7311af82e335dd1e36ffff68aaca609ca6290c2cb6d821a39aa075d8e3ff"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:763773d53f07244148ccac5b084da5adb90bfaee39c197554f01b286cf869228"}, - {file = "kiwisolver-1.4.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2270953c0d8cdab5d422bee7d2007f043473f9d2999631c86a223c9db56cbd16"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d099e745a512f7e3bbe7249ca835f4d357c586d78d79ae8f1dcd4d8adeb9bda9"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:74db36e14a7d1ce0986fa104f7d5637aea5c82ca6326ed0ec5694280942d1162"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:7e5bab140c309cb3a6ce373a9e71eb7e4873c70c2dda01df6820474f9889d6d4"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0f114aa76dc1b8f636d077979c0ac22e7cd8f3493abbab152f20eb8d3cda71f3"}, - {file = "kiwisolver-1.4.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:88a2df29d4724b9237fc0c6eaf2a1adae0cdc0b3e9f4d8e7dc54b16812d2d81a"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win32.whl", hash = "sha256:72d40b33e834371fd330fb1472ca19d9b8327acb79a5821d4008391db8e29f20"}, - {file = "kiwisolver-1.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:2c5674c4e74d939b9d91dda0fae10597ac7521768fec9e399c70a1f27e2ea2d9"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3a2b053a0ab7a3960c98725cfb0bf5b48ba82f64ec95fe06f1d06c99b552e130"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd32d6c13807e5c66a7cbb79f90b553642f296ae4518a60d8d76243b0ad2898"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:59ec7b7c7e1a61061850d53aaf8e93db63dce0c936db1fda2658b70e4a1be709"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:da4cfb373035def307905d05041c1d06d8936452fe89d464743ae7fb8371078b"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2400873bccc260b6ae184b2b8a4fec0e4082d30648eadb7c3d9a13405d861e89"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1b04139c4236a0f3aff534479b58f6f849a8b351e1314826c2d230849ed48985"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:4e66e81a5779b65ac21764c295087de82235597a2293d18d943f8e9e32746265"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7931d8f1f67c4be9ba1dd9c451fb0eeca1a25b89e4d3f89e828fe12a519b782a"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:b3f7e75f3015df442238cca659f8baa5f42ce2a8582727981cbfa15fee0ee205"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:bbf1d63eef84b2e8c89011b7f2235b1e0bf7dacc11cac9431fc6468e99ac77fb"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4c380469bd3f970ef677bf2bcba2b6b0b4d5c75e7a020fb863ef75084efad66f"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win32.whl", hash = "sha256:9408acf3270c4b6baad483865191e3e582b638b1654a007c62e3efe96f09a9a3"}, - {file = "kiwisolver-1.4.5-cp37-cp37m-win_amd64.whl", hash = "sha256:5b94529f9b2591b7af5f3e0e730a4e0a41ea174af35a4fd067775f9bdfeee01a"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:11c7de8f692fc99816e8ac50d1d1aef4f75126eefc33ac79aac02c099fd3db71"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:53abb58632235cd154176ced1ae8f0d29a6657aa1aa9decf50b899b755bc2b93"}, - {file = "kiwisolver-1.4.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:88b9f257ca61b838b6f8094a62418421f87ac2a1069f7e896c36a7d86b5d4c29"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3195782b26fc03aa9c6913d5bad5aeb864bdc372924c093b0f1cebad603dd712"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fc579bf0f502e54926519451b920e875f433aceb4624a3646b3252b5caa9e0b6"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5a580c91d686376f0f7c295357595c5a026e6cbc3d77b7c36e290201e7c11ecb"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cfe6ab8da05c01ba6fbea630377b5da2cd9bcbc6338510116b01c1bc939a2c18"}, - {file = "kiwisolver-1.4.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:d2e5a98f0ec99beb3c10e13b387f8db39106d53993f498b295f0c914328b1333"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a51a263952b1429e429ff236d2f5a21c5125437861baeed77f5e1cc2d2c7c6da"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:3edd2fa14e68c9be82c5b16689e8d63d89fe927e56debd6e1dbce7a26a17f81b"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:74d1b44c6cfc897df648cc9fdaa09bc3e7679926e6f96df05775d4fb3946571c"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:76d9289ed3f7501012e05abb8358bbb129149dbd173f1f57a1bf1c22d19ab7cc"}, - {file = "kiwisolver-1.4.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:92dea1ffe3714fa8eb6a314d2b3c773208d865a0e0d35e713ec54eea08a66250"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win32.whl", hash = "sha256:5c90ae8c8d32e472be041e76f9d2f2dbff4d0b0be8bd4041770eddb18cf49a4e"}, - {file = "kiwisolver-1.4.5-cp38-cp38-win_amd64.whl", hash = "sha256:c7940c1dc63eb37a67721b10d703247552416f719c4188c54e04334321351ced"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:9407b6a5f0d675e8a827ad8742e1d6b49d9c1a1da5d952a67d50ef5f4170b18d"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:15568384086b6df3c65353820a4473575dbad192e35010f622c6ce3eebd57af9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0dc9db8e79f0036e8173c466d21ef18e1befc02de8bf8aa8dc0813a6dc8a7046"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cdc8a402aaee9a798b50d8b827d7ecf75edc5fb35ea0f91f213ff927c15f4ff0"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6c3bd3cde54cafb87d74d8db50b909705c62b17c2099b8f2e25b461882e544ff"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:955e8513d07a283056b1396e9a57ceddbd272d9252c14f154d450d227606eb54"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:346f5343b9e3f00b8db8ba359350eb124b98c99efd0b408728ac6ebf38173958"}, - {file = "kiwisolver-1.4.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9098e0049e88c6a24ff64545cdfc50807818ba6c1b739cae221bbbcbc58aad3"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:00bd361b903dc4bbf4eb165f24d1acbee754fce22ded24c3d56eec268658a5cf"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7b8b454bac16428b22560d0a1cf0a09875339cab69df61d7805bf48919415901"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:f1d072c2eb0ad60d4c183f3fb44ac6f73fb7a8f16a2694a91f988275cbf352f9"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:31a82d498054cac9f6d0b53d02bb85811185bcb477d4b60144f915f3b3126342"}, - {file = "kiwisolver-1.4.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6512cb89e334e4700febbffaaa52761b65b4f5a3cf33f960213d5656cea36a77"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win32.whl", hash = "sha256:9db8ea4c388fdb0f780fe91346fd438657ea602d58348753d9fb265ce1bca67f"}, - {file = "kiwisolver-1.4.5-cp39-cp39-win_amd64.whl", hash = "sha256:59415f46a37f7f2efeec758353dd2eae1b07640d8ca0f0c42548ec4125492635"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:5c7b3b3a728dc6faf3fc372ef24f21d1e3cee2ac3e9596691d746e5a536de920"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:620ced262a86244e2be10a676b646f29c34537d0d9cc8eb26c08f53d98013390"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:378a214a1e3bbf5ac4a8708304318b4f890da88c9e6a07699c4ae7174c09a68d"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aaf7be1207676ac608a50cd08f102f6742dbfc70e8d60c4db1c6897f62f71523"}, - {file = "kiwisolver-1.4.5-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ba55dce0a9b8ff59495ddd050a0225d58bd0983d09f87cfe2b6aec4f2c1234e4"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:fd32ea360bcbb92d28933fc05ed09bffcb1704ba3fc7942e81db0fd4f81a7892"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5e7139af55d1688f8b960ee9ad5adafc4ac17c1c473fe07133ac092310d76544"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dced8146011d2bc2e883f9bd68618b8247387f4bbec46d7392b3c3b032640126"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9bf3325c47b11b2e51bca0824ea217c7cd84491d8ac4eefd1e409705ef092bd"}, - {file = "kiwisolver-1.4.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:5794cf59533bc3f1b1c821f7206a3617999db9fbefc345360aafe2e067514929"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:e368f200bbc2e4f905b8e71eb38b3c04333bddaa6a2464a6355487b02bb7fb09"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5d706eba36b4c4d5bc6c6377bb6568098765e990cfc21ee16d13963fab7b3e7"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85267bd1aa8880a9c88a8cb71e18d3d64d2751a790e6ca6c27b8ccc724bcd5ad"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:210ef2c3a1f03272649aff1ef992df2e724748918c4bc2d5a90352849eb40bea"}, - {file = "kiwisolver-1.4.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:11d011a7574eb3b82bcc9c1a1d35c1d7075677fdd15de527d91b46bd35e935ee"}, - {file = "kiwisolver-1.4.5.tar.gz", hash = "sha256:e57e563a57fb22a142da34f38acc2fc1a5c864bc29ca1517a88abc963e60d6ec"}, -] - -[[package]] -name = "lark" -version = "1.1.9" -description = "a modern parsing library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "lark-1.1.9-py3-none-any.whl", hash = "sha256:a0dd3a87289f8ccbb325901e4222e723e7d745dbfc1803eaf5f3d2ace19cf2db"}, - {file = "lark-1.1.9.tar.gz", hash = "sha256:15fa5236490824c2c4aba0e22d2d6d823575dcaf4cdd1848e34b6ad836240fba"}, -] - -[package.extras] -atomic-cache = ["atomicwrites"] -interegular = ["interegular (>=0.3.1,<0.4.0)"] -nearley = ["js2py"] -regex = ["regex"] - -[[package]] -name = "libcst" -version = "1.3.1" -description = "A concrete syntax tree with AST-like properties for Python 3.0 through 3.12 programs." -optional = false -python-versions = ">=3.9" -files = [ - {file = "libcst-1.3.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:de93193cba6d54f2a4419e94ba2de642b111f52e4fa01bb6e2c655914585f65b"}, - {file = "libcst-1.3.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a2d64d86dcd6c80a5dac2e243c5ed7a7a193242209ac33bad4b0639b24f6d131"}, - {file = "libcst-1.3.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:db084f7bbf825c7bd5ed256290066d0336df6a7dc3a029c9870a64cd2298b87f"}, - {file = "libcst-1.3.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:16880711be03a1f5da7028fe791ba5b482a50d830225a70272dc332dfd927652"}, - {file = "libcst-1.3.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:189bb28c19c5dd3c64583f969b72f7732dbdb1dee9eca3acc85099e4cef9148b"}, - {file = "libcst-1.3.1-cp310-cp310-win_amd64.whl", hash = "sha256:181372386c986e3de07d7a93f269214cd825adc714f1f9da8252b44f05e181c4"}, - {file = "libcst-1.3.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8c2020f7449270e3ff0bdc481ae244d812f2d9a8b7dbff0ea66b830f4b350f54"}, - {file = "libcst-1.3.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:be3bf9aaafebda6a21e241e819f0ab67e186e898c3562704e41241cf8738353a"}, - {file = "libcst-1.3.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a0d250fb6a2c1d158f30d25ba5e33e3ed3672d2700d480dd47beffd1431a008"}, - {file = "libcst-1.3.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ad5741b251d901f3da1819ac539192230cc6f8f81aaf04eb4ec0009c1c97285"}, - {file = "libcst-1.3.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b740dc0c3d1adbd91442fb878343d5a11e23a0e3ac5484be301fd8d148bcb085"}, - {file = "libcst-1.3.1-cp311-cp311-win_amd64.whl", hash = "sha256:9e6bc95fa7dde79cde63a34a0412cd4a3d9fcc27be781a590f8c45c840c83658"}, - {file = "libcst-1.3.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:4186076ce12141609ce950d61867b2a73ea199a7a9870dbafa76ad600e075b3c"}, - {file = "libcst-1.3.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ed52a1a2fe4d8603de51649db5e438317b8116ebb9fc09ec68703535fe6c1c8"}, - {file = "libcst-1.3.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c0886a9963597367b227345f19b24931b3ed6a4703fff237760745f90f0e6a20"}, - {file = "libcst-1.3.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:904c4cc5c801a5747e64b43e0accc87c67a4c804842d977ee215872c4cf8cf88"}, - {file = "libcst-1.3.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7cdb7e8a118b60e064a02f6cbfa4d328212a3a115d125244495190f405709d5f"}, - {file = "libcst-1.3.1-cp312-cp312-win_amd64.whl", hash = "sha256:431badf0e544b79c0ac9682dbd291ff63ddbc3c3aca0d13d3cc7a10c3a9db8a2"}, - {file = "libcst-1.3.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:701f5335e4fd566871497b9af1e871c98e1ef10c30b3b244f39343d709213401"}, - {file = "libcst-1.3.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7c6e709623b68ca9148e8ecbdc145f7b83befb26032e4bf6a8122500ba558b17"}, - {file = "libcst-1.3.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ede0f026a82b03b33a559ec566860085ece2e76d8f9bc21cb053eedf9cde8c79"}, - {file = "libcst-1.3.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c12b7b01d8745f82dd86a82acd2a9f8e8e7d6c94ddcfda996896e83d1a8d5c42"}, - {file = "libcst-1.3.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2995ca687118a9d3d41876f7270bc29305a2d402f4b8c81a3cff0aeee6d4c81"}, - {file = "libcst-1.3.1-cp39-cp39-win_amd64.whl", hash = "sha256:2dbac1ac0a9d59ea7bbc3f87cdcca5bfe98835e31c668e95cb6f3d907ffc53fc"}, - {file = "libcst-1.3.1.tar.gz", hash = "sha256:03b1df1ae02456f1d465fcd5ead1d0d454bb483caefd8c8e6bde515ffdb53d1b"}, -] - -[package.dependencies] -pyyaml = ">=5.2" - -[package.extras] -dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (>=4.5.4)", "fixit (==2.1.0)", "flake8 (==7.0.0)", "hypothesis (>=4.36.0)", "hypothesmith (>=0.0.4)", "jinja2 (==3.1.3)", "jupyter (>=1.0.0)", "maturin (>=0.8.3,<1.5)", "nbsphinx (>=0.4.2)", "prompt-toolkit (>=2.0.9)", "pyre-check (==0.9.18)", "setuptools-rust (>=1.5.2)", "setuptools-scm (>=6.0.1)", "slotscheck (>=0.7.1)", "sphinx-rtd-theme (>=0.4.3)", "ufmt (==2.5.1)", "usort (==1.0.8.post1)"] - -[[package]] -name = "llvmlite" -version = "0.42.0" -description = "lightweight wrapper around basic LLVM functionality" -optional = false -python-versions = ">=3.9" -files = [ - {file = "llvmlite-0.42.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3366938e1bf63d26c34fbfb4c8e8d2ded57d11e0567d5bb243d89aab1eb56098"}, - {file = "llvmlite-0.42.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c35da49666a21185d21b551fc3caf46a935d54d66969d32d72af109b5e7d2b6f"}, - {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70f44ccc3c6220bd23e0ba698a63ec2a7d3205da0d848804807f37fc243e3f77"}, - {file = "llvmlite-0.42.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:763f8d8717a9073b9e0246998de89929071d15b47f254c10eef2310b9aac033d"}, - {file = "llvmlite-0.42.0-cp310-cp310-win_amd64.whl", hash = "sha256:8d90edf400b4ceb3a0e776b6c6e4656d05c7187c439587e06f86afceb66d2be5"}, - {file = "llvmlite-0.42.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ae511caed28beaf1252dbaf5f40e663f533b79ceb408c874c01754cafabb9cbf"}, - {file = "llvmlite-0.42.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81e674c2fe85576e6c4474e8c7e7aba7901ac0196e864fe7985492b737dbab65"}, - {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb3975787f13eb97629052edb5017f6c170eebc1c14a0433e8089e5db43bcce6"}, - {file = "llvmlite-0.42.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5bece0cdf77f22379f19b1959ccd7aee518afa4afbd3656c6365865f84903f9"}, - {file = "llvmlite-0.42.0-cp311-cp311-win_amd64.whl", hash = "sha256:7e0c4c11c8c2aa9b0701f91b799cb9134a6a6de51444eff5a9087fc7c1384275"}, - {file = "llvmlite-0.42.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:08fa9ab02b0d0179c688a4216b8939138266519aaa0aa94f1195a8542faedb56"}, - {file = "llvmlite-0.42.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b2fce7d355068494d1e42202c7aff25d50c462584233013eb4470c33b995e3ee"}, - {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebe66a86dc44634b59a3bc860c7b20d26d9aaffcd30364ebe8ba79161a9121f4"}, - {file = "llvmlite-0.42.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d47494552559e00d81bfb836cf1c4d5a5062e54102cc5767d5aa1e77ccd2505c"}, - {file = "llvmlite-0.42.0-cp312-cp312-win_amd64.whl", hash = "sha256:05cb7e9b6ce69165ce4d1b994fbdedca0c62492e537b0cc86141b6e2c78d5888"}, - {file = "llvmlite-0.42.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bdd3888544538a94d7ec99e7c62a0cdd8833609c85f0c23fcb6c5c591aec60ad"}, - {file = "llvmlite-0.42.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d0936c2067a67fb8816c908d5457d63eba3e2b17e515c5fe00e5ee2bace06040"}, - {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a78ab89f1924fc11482209f6799a7a3fc74ddc80425a7a3e0e8174af0e9e2301"}, - {file = "llvmlite-0.42.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7599b65c7af7abbc978dbf345712c60fd596aa5670496561cc10e8a71cebfb2"}, - {file = "llvmlite-0.42.0-cp39-cp39-win_amd64.whl", hash = "sha256:43d65cc4e206c2e902c1004dd5418417c4efa6c1d04df05c6c5675a27e8ca90e"}, - {file = "llvmlite-0.42.0.tar.gz", hash = "sha256:f92b09243c0cc3f457da8b983f67bd8e1295d0f5b3746c7a1861d7a99403854a"}, -] - -[[package]] -name = "lm-format-enforcer" -version = "0.8.3" -description = "Enforce the output format (JSON Schema, Regex etc) of a language model" -optional = false -python-versions = ">=3.8,<4.0" -files = [ - {file = "lm_format_enforcer-0.8.3-py3-none-any.whl", hash = "sha256:d12364a90d8dd3a824c3b6a2cdbc8ec40dd6fff12f5e566100f4a9e79a847092"}, - {file = "lm_format_enforcer-0.8.3.tar.gz", hash = "sha256:3f9eac9f14af43ec77ebe99b251793bbbbffd3566dacffadd4a217d8be90760c"}, -] - -[package.dependencies] -interegular = ">=0.3.2" -pydantic = ">=1.10.8" - -[[package]] -name = "markdown-it-py" -version = "3.0.0" -description = "Python port of markdown-it. Markdown parsing, done right!" -optional = false -python-versions = ">=3.8" -files = [ - {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, - {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, -] - -[package.dependencies] -mdurl = ">=0.1,<1.0" - -[package.extras] -benchmarking = ["psutil", "pytest", "pytest-benchmark"] -code-style = ["pre-commit (>=3.0,<4.0)"] -compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] -linkify = ["linkify-it-py (>=1,<3)"] -plugins = ["mdit-py-plugins"] -profiling = ["gprof2dot"] -rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] -testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] - -[[package]] -name = "markupsafe" -version = "2.1.5" -description = "Safely add untrusted strings to HTML/XML markup." -optional = false -python-versions = ">=3.7" -files = [ - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win32.whl", hash = "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4"}, - {file = "MarkupSafe-2.1.5-cp310-cp310-win_amd64.whl", hash = "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win32.whl", hash = "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906"}, - {file = "MarkupSafe-2.1.5-cp311-cp311-win_amd64.whl", hash = "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win32.whl", hash = "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad"}, - {file = "MarkupSafe-2.1.5-cp312-cp312-win_amd64.whl", hash = "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win32.whl", hash = "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371"}, - {file = "MarkupSafe-2.1.5-cp37-cp37m-win_amd64.whl", hash = "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win32.whl", hash = "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff"}, - {file = "MarkupSafe-2.1.5-cp38-cp38-win_amd64.whl", hash = "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win32.whl", hash = "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf"}, - {file = "MarkupSafe-2.1.5-cp39-cp39-win_amd64.whl", hash = "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5"}, - {file = "MarkupSafe-2.1.5.tar.gz", hash = "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b"}, -] - -[[package]] -name = "marshmallow" -version = "3.21.1" -description = "A lightweight library for converting complex datatypes to and from native Python datatypes." -optional = false -python-versions = ">=3.8" -files = [ - {file = "marshmallow-3.21.1-py3-none-any.whl", hash = "sha256:f085493f79efb0644f270a9bf2892843142d80d7174bbbd2f3713f2a589dc633"}, - {file = "marshmallow-3.21.1.tar.gz", hash = "sha256:4e65e9e0d80fc9e609574b9983cf32579f305c718afb30d7233ab818571768c3"}, -] - -[package.dependencies] -packaging = ">=17.0" - -[package.extras] -dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.2.6)", "sphinx-issues (==4.0.0)", "sphinx-version-warning (==1.1.2)"] -tests = ["pytest", "pytz", "simplejson"] - -[[package]] -name = "matplotlib" -version = "3.8.4" -description = "Python plotting package" -optional = false -python-versions = ">=3.9" -files = [ - {file = "matplotlib-3.8.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:abc9d838f93583650c35eca41cfcec65b2e7cb50fd486da6f0c49b5e1ed23014"}, - {file = "matplotlib-3.8.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f65c9f002d281a6e904976007b2d46a1ee2bcea3a68a8c12dda24709ddc9106"}, - {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce1edd9f5383b504dbc26eeea404ed0a00656c526638129028b758fd43fc5f10"}, - {file = "matplotlib-3.8.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecd79298550cba13a43c340581a3ec9c707bd895a6a061a78fa2524660482fc0"}, - {file = "matplotlib-3.8.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:90df07db7b599fe7035d2f74ab7e438b656528c68ba6bb59b7dc46af39ee48ef"}, - {file = "matplotlib-3.8.4-cp310-cp310-win_amd64.whl", hash = "sha256:ac24233e8f2939ac4fd2919eed1e9c0871eac8057666070e94cbf0b33dd9c338"}, - {file = "matplotlib-3.8.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:72f9322712e4562e792b2961971891b9fbbb0e525011e09ea0d1f416c4645661"}, - {file = "matplotlib-3.8.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:232ce322bfd020a434caaffbd9a95333f7c2491e59cfc014041d95e38ab90d1c"}, - {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6addbd5b488aedb7f9bc19f91cd87ea476206f45d7116fcfe3d31416702a82fa"}, - {file = "matplotlib-3.8.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc4ccdc64e3039fc303defd119658148f2349239871db72cd74e2eeaa9b80b71"}, - {file = "matplotlib-3.8.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b7a2a253d3b36d90c8993b4620183b55665a429da8357a4f621e78cd48b2b30b"}, - {file = "matplotlib-3.8.4-cp311-cp311-win_amd64.whl", hash = "sha256:8080d5081a86e690d7688ffa542532e87f224c38a6ed71f8fbed34dd1d9fedae"}, - {file = "matplotlib-3.8.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6485ac1f2e84676cff22e693eaa4fbed50ef5dc37173ce1f023daef4687df616"}, - {file = "matplotlib-3.8.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c89ee9314ef48c72fe92ce55c4e95f2f39d70208f9f1d9db4e64079420d8d732"}, - {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50bac6e4d77e4262c4340d7a985c30912054745ec99756ce213bfbc3cb3808eb"}, - {file = "matplotlib-3.8.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f51c4c869d4b60d769f7b4406eec39596648d9d70246428745a681c327a8ad30"}, - {file = "matplotlib-3.8.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b12ba985837e4899b762b81f5b2845bd1a28f4fdd1a126d9ace64e9c4eb2fb25"}, - {file = "matplotlib-3.8.4-cp312-cp312-win_amd64.whl", hash = "sha256:7a6769f58ce51791b4cb8b4d7642489df347697cd3e23d88266aaaee93b41d9a"}, - {file = "matplotlib-3.8.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:843cbde2f0946dadd8c5c11c6d91847abd18ec76859dc319362a0964493f0ba6"}, - {file = "matplotlib-3.8.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1c13f041a7178f9780fb61cc3a2b10423d5e125480e4be51beaf62b172413b67"}, - {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb44f53af0a62dc80bba4443d9b27f2fde6acfdac281d95bc872dc148a6509cc"}, - {file = "matplotlib-3.8.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:606e3b90897554c989b1e38a258c626d46c873523de432b1462f295db13de6f9"}, - {file = "matplotlib-3.8.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9bb0189011785ea794ee827b68777db3ca3f93f3e339ea4d920315a0e5a78d54"}, - {file = "matplotlib-3.8.4-cp39-cp39-win_amd64.whl", hash = "sha256:6209e5c9aaccc056e63b547a8152661324404dd92340a6e479b3a7f24b42a5d0"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c7064120a59ce6f64103c9cefba8ffe6fba87f2c61d67c401186423c9a20fd35"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0e47eda4eb2614300fc7bb4657fced3e83d6334d03da2173b09e447418d499f"}, - {file = "matplotlib-3.8.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:493e9f6aa5819156b58fce42b296ea31969f2aab71c5b680b4ea7a3cb5c07d94"}, - {file = "matplotlib-3.8.4.tar.gz", hash = "sha256:8aac397d5e9ec158960e31c381c5ffc52ddd52bd9a47717e2a694038167dffea"}, -] - -[package.dependencies] -contourpy = ">=1.0.1" -cycler = ">=0.10" -fonttools = ">=4.22.0" -kiwisolver = ">=1.3.1" -numpy = ">=1.21" -packaging = ">=20.0" -pillow = ">=8" -pyparsing = ">=2.3.1" -python-dateutil = ">=2.7" - -[[package]] -name = "matplotlib-inline" -version = "0.1.7" -description = "Inline Matplotlib backend for Jupyter" -optional = false -python-versions = ">=3.8" -files = [ - {file = "matplotlib_inline-0.1.7-py3-none-any.whl", hash = "sha256:df192d39a4ff8f21b1895d72e6a13f5fcc5099f00fa84384e0ea28c2cc0653ca"}, - {file = "matplotlib_inline-0.1.7.tar.gz", hash = "sha256:8423b23ec666be3d16e16b60bdd8ac4e86e840ebd1dd11a30b9f117f2fa0ab90"}, -] - -[package.dependencies] -traitlets = "*" - -[[package]] -name = "mdurl" -version = "0.1.2" -description = "Markdown URL utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, - {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, -] - -[[package]] -name = "mistune" -version = "3.0.2" -description = "A sane and fast Markdown parser with useful plugins and renderers" -optional = false -python-versions = ">=3.7" -files = [ - {file = "mistune-3.0.2-py3-none-any.whl", hash = "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205"}, - {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, -] - -[[package]] -name = "modal" -version = "0.62.68" -description = "Python client library for Modal" -optional = false -python-versions = ">=3.8" -files = [ - {file = "modal-0.62.68-py3-none-any.whl", hash = "sha256:8d8e2032825e4cce68161b5620c20290fc339186733b91f1666018670e8f3a78"}, -] - -[package.dependencies] -aiohttp = "*" -aiostream = ">=0.5.2,<0.6.0" -certifi = "*" -click = ">=8.1.0" -fastapi = "*" -grpclib = "0.4.7" -protobuf = ">=3.19,<4.24.0 || >4.24.0,<6.0" -rich = ">=12.0.0" -synchronicity = ">=0.6.5,<0.7.0" -toml = "*" -typer = ">=0.9.0,<0.10.0" -types-certifi = "*" -types-toml = "*" -typing-extensions = ">=4.6,<5.0" -watchfiles = "*" - -[[package]] -name = "mplcursors" -version = "0.5.3" -description = "Interactive data selection cursors for Matplotlib." -optional = false -python-versions = ">=3.7" -files = [ - {file = "mplcursors-0.5.3.tar.gz", hash = "sha256:cb220c8dffaafde195e138faec5654737267f7685d1f0503618cdc333aca0966"}, -] - -[package.dependencies] -matplotlib = ">=3.1,<3.7.1 || >3.7.1" - -[package.extras] -docs = ["pandas", "pydata_sphinx_theme (!=0.10.1)", "sphinx", "sphinx-gallery"] - -[[package]] -name = "mpmath" -version = "1.3.0" -description = "Python library for arbitrary-precision floating-point arithmetic" -optional = false -python-versions = "*" -files = [ - {file = "mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c"}, - {file = "mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f"}, -] - -[package.extras] -develop = ["codecov", "pycodestyle", "pytest (>=4.6)", "pytest-cov", "wheel"] -docs = ["sphinx"] -gmpy = ["gmpy2 (>=2.1.0a4)"] -tests = ["pytest (>=4.6)"] - -[[package]] -name = "msgpack" -version = "1.0.8" -description = "MessagePack serializer" -optional = false -python-versions = ">=3.8" -files = [ - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, - {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, - {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, - {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, - {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, - {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, - {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, - {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, - {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, - {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, - {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8-py3-none-any.whl", hash = "sha256:24f727df1e20b9876fa6e95f840a2a2651e34c0ad147676356f4bf5fbb0206ca"}, -] - -[[package]] -name = "msgspec" -version = "0.18.6" -description = "A fast serialization and validation library, with builtin support for JSON, MessagePack, YAML, and TOML." -optional = false -python-versions = ">=3.8" -files = [ - {file = "msgspec-0.18.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:77f30b0234eceeff0f651119b9821ce80949b4d667ad38f3bfed0d0ebf9d6d8f"}, - {file = "msgspec-0.18.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1a76b60e501b3932782a9da039bd1cd552b7d8dec54ce38332b87136c64852dd"}, - {file = "msgspec-0.18.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06acbd6edf175bee0e36295d6b0302c6de3aaf61246b46f9549ca0041a9d7177"}, - {file = "msgspec-0.18.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40a4df891676d9c28a67c2cc39947c33de516335680d1316a89e8f7218660410"}, - {file = "msgspec-0.18.6-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a6896f4cd5b4b7d688018805520769a8446df911eb93b421c6c68155cdf9dd5a"}, - {file = "msgspec-0.18.6-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:3ac4dd63fd5309dd42a8c8c36c1563531069152be7819518be0a9d03be9788e4"}, - {file = "msgspec-0.18.6-cp310-cp310-win_amd64.whl", hash = "sha256:fda4c357145cf0b760000c4ad597e19b53adf01382b711f281720a10a0fe72b7"}, - {file = "msgspec-0.18.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e77e56ffe2701e83a96e35770c6adb655ffc074d530018d1b584a8e635b4f36f"}, - {file = "msgspec-0.18.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d5351afb216b743df4b6b147691523697ff3a2fc5f3d54f771e91219f5c23aaa"}, - {file = "msgspec-0.18.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3232fabacef86fe8323cecbe99abbc5c02f7698e3f5f2e248e3480b66a3596b"}, - {file = "msgspec-0.18.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3b524df6ea9998bbc99ea6ee4d0276a101bcc1aa8d14887bb823914d9f60d07"}, - {file = "msgspec-0.18.6-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:37f67c1d81272131895bb20d388dd8d341390acd0e192a55ab02d4d6468b434c"}, - {file = "msgspec-0.18.6-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d0feb7a03d971c1c0353de1a8fe30bb6579c2dc5ccf29b5f7c7ab01172010492"}, - {file = "msgspec-0.18.6-cp311-cp311-win_amd64.whl", hash = "sha256:41cf758d3f40428c235c0f27bc6f322d43063bc32da7b9643e3f805c21ed57b4"}, - {file = "msgspec-0.18.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d86f5071fe33e19500920333c11e2267a31942d18fed4d9de5bc2fbab267d28c"}, - {file = "msgspec-0.18.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce13981bfa06f5eb126a3a5a38b1976bddb49a36e4f46d8e6edecf33ccf11df1"}, - {file = "msgspec-0.18.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e97dec6932ad5e3ee1e3c14718638ba333befc45e0661caa57033cd4cc489466"}, - {file = "msgspec-0.18.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad237100393f637b297926cae1868b0d500f764ccd2f0623a380e2bcfb2809ca"}, - {file = "msgspec-0.18.6-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db1d8626748fa5d29bbd15da58b2d73af25b10aa98abf85aab8028119188ed57"}, - {file = "msgspec-0.18.6-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:d70cb3d00d9f4de14d0b31d38dfe60c88ae16f3182988246a9861259c6722af6"}, - {file = "msgspec-0.18.6-cp312-cp312-win_amd64.whl", hash = "sha256:1003c20bfe9c6114cc16ea5db9c5466e49fae3d7f5e2e59cb70693190ad34da0"}, - {file = "msgspec-0.18.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f7d9faed6dfff654a9ca7d9b0068456517f63dbc3aa704a527f493b9200b210a"}, - {file = "msgspec-0.18.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:9da21f804c1a1471f26d32b5d9bc0480450ea77fbb8d9db431463ab64aaac2cf"}, - {file = "msgspec-0.18.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:46eb2f6b22b0e61c137e65795b97dc515860bf6ec761d8fb65fdb62aa094ba61"}, - {file = "msgspec-0.18.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c8355b55c80ac3e04885d72db515817d9fbb0def3bab936bba104e99ad22cf46"}, - {file = "msgspec-0.18.6-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9080eb12b8f59e177bd1eb5c21e24dd2ba2fa88a1dbc9a98e05ad7779b54c681"}, - {file = "msgspec-0.18.6-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:cc001cf39becf8d2dcd3f413a4797c55009b3a3cdbf78a8bf5a7ca8fdb76032c"}, - {file = "msgspec-0.18.6-cp38-cp38-win_amd64.whl", hash = "sha256:fac5834e14ac4da1fca373753e0c4ec9c8069d1fe5f534fa5208453b6065d5be"}, - {file = "msgspec-0.18.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:974d3520fcc6b824a6dedbdf2b411df31a73e6e7414301abac62e6b8d03791b4"}, - {file = "msgspec-0.18.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fd62e5818731a66aaa8e9b0a1e5543dc979a46278da01e85c3c9a1a4f047ef7e"}, - {file = "msgspec-0.18.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7481355a1adcf1f08dedd9311193c674ffb8bf7b79314b4314752b89a2cf7f1c"}, - {file = "msgspec-0.18.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6aa85198f8f154cf35d6f979998f6dadd3dc46a8a8c714632f53f5d65b315c07"}, - {file = "msgspec-0.18.6-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0e24539b25c85c8f0597274f11061c102ad6b0c56af053373ba4629772b407be"}, - {file = "msgspec-0.18.6-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c61ee4d3be03ea9cd089f7c8e36158786cd06e51fbb62529276452bbf2d52ece"}, - {file = "msgspec-0.18.6-cp39-cp39-win_amd64.whl", hash = "sha256:b5c390b0b0b7da879520d4ae26044d74aeee5144f83087eb7842ba59c02bc090"}, - {file = "msgspec-0.18.6.tar.gz", hash = "sha256:a59fc3b4fcdb972d09138cb516dbde600c99d07c38fd9372a6ef500d2d031b4e"}, -] - -[package.extras] -dev = ["attrs", "coverage", "furo", "gcovr", "ipython", "msgpack", "mypy", "pre-commit", "pyright", "pytest", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "tomli", "tomli-w"] -doc = ["furo", "ipython", "sphinx", "sphinx-copybutton", "sphinx-design"] -test = ["attrs", "msgpack", "mypy", "pyright", "pytest", "pyyaml", "tomli", "tomli-w"] -toml = ["tomli", "tomli-w"] -yaml = ["pyyaml"] - -[[package]] -name = "multidict" -version = "6.0.5" -description = "multidict implementation" -optional = false -python-versions = ">=3.7" -files = [ - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:228b644ae063c10e7f324ab1ab6b548bdf6f8b47f3ec234fef1093bc2735e5f9"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:896ebdcf62683551312c30e20614305f53125750803b614e9e6ce74a96232604"}, - {file = "multidict-6.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:411bf8515f3be9813d06004cac41ccf7d1cd46dfe233705933dd163b60e37600"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d147090048129ce3c453f0292e7697d333db95e52616b3793922945804a433c"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:215ed703caf15f578dca76ee6f6b21b7603791ae090fbf1ef9d865571039ade5"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c6390cf87ff6234643428991b7359b5f59cc15155695deb4eda5c777d2b880f"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21fd81c4ebdb4f214161be351eb5bcf385426bf023041da2fd9e60681f3cebae"}, - {file = "multidict-6.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3cc2ad10255f903656017363cd59436f2111443a76f996584d1077e43ee51182"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6939c95381e003f54cd4c5516740faba40cf5ad3eeff460c3ad1d3e0ea2549bf"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:220dd781e3f7af2c2c1053da9fa96d9cf3072ca58f057f4c5adaaa1cab8fc442"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:766c8f7511df26d9f11cd3a8be623e59cca73d44643abab3f8c8c07620524e4a"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:fe5d7785250541f7f5019ab9cba2c71169dc7d74d0f45253f8313f436458a4ef"}, - {file = "multidict-6.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c1c1496e73051918fcd4f58ff2e0f2f3066d1c76a0c6aeffd9b45d53243702cc"}, - {file = "multidict-6.0.5-cp310-cp310-win32.whl", hash = "sha256:7afcdd1fc07befad18ec4523a782cde4e93e0a2bf71239894b8d61ee578c1319"}, - {file = "multidict-6.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:99f60d34c048c5c2fabc766108c103612344c46e35d4ed9ae0673d33c8fb26e8"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f285e862d2f153a70586579c15c44656f888806ed0e5b56b64489afe4a2dbfba"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:53689bb4e102200a4fafa9de9c7c3c212ab40a7ab2c8e474491914d2305f187e"}, - {file = "multidict-6.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:612d1156111ae11d14afaf3a0669ebf6c170dbb735e510a7438ffe2369a847fd"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7be7047bd08accdb7487737631d25735c9a04327911de89ff1b26b81745bd4e3"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de170c7b4fe6859beb8926e84f7d7d6c693dfe8e27372ce3b76f01c46e489fcf"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:04bde7a7b3de05732a4eb39c94574db1ec99abb56162d6c520ad26f83267de29"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:85f67aed7bb647f93e7520633d8f51d3cbc6ab96957c71272b286b2f30dc70ed"}, - {file = "multidict-6.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:425bf820055005bfc8aa9a0b99ccb52cc2f4070153e34b701acc98d201693733"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d3eb1ceec286eba8220c26f3b0096cf189aea7057b6e7b7a2e60ed36b373b77f"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7901c05ead4b3fb75113fb1dd33eb1253c6d3ee37ce93305acd9d38e0b5f21a4"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e0e79d91e71b9867c73323a3444724d496c037e578a0e1755ae159ba14f4f3d1"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:29bfeb0dff5cb5fdab2023a7a9947b3b4af63e9c47cae2a10ad58394b517fddc"}, - {file = "multidict-6.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e030047e85cbcedbfc073f71836d62dd5dadfbe7531cae27789ff66bc551bd5e"}, - {file = "multidict-6.0.5-cp311-cp311-win32.whl", hash = "sha256:2f4848aa3baa109e6ab81fe2006c77ed4d3cd1e0ac2c1fbddb7b1277c168788c"}, - {file = "multidict-6.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:2faa5ae9376faba05f630d7e5e6be05be22913782b927b19d12b8145968a85ea"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:51d035609b86722963404f711db441cf7134f1889107fb171a970c9701f92e1e"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cbebcd5bcaf1eaf302617c114aa67569dd3f090dd0ce8ba9e35e9985b41ac35b"}, - {file = "multidict-6.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ffc42c922dbfddb4a4c3b438eb056828719f07608af27d163191cb3e3aa6cc5"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ceb3b7e6a0135e092de86110c5a74e46bda4bd4fbfeeb3a3bcec79c0f861e450"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79660376075cfd4b2c80f295528aa6beb2058fd289f4c9252f986751a4cd0496"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e4428b29611e989719874670fd152b6625500ad6c686d464e99f5aaeeaca175a"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d84a5c3a5f7ce6db1f999fb9438f686bc2e09d38143f2d93d8406ed2dd6b9226"}, - {file = "multidict-6.0.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:76c0de87358b192de7ea9649beb392f107dcad9ad27276324c24c91774ca5271"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79a6d2ba910adb2cbafc95dad936f8b9386e77c84c35bc0add315b856d7c3abb"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:92d16a3e275e38293623ebf639c471d3e03bb20b8ebb845237e0d3664914caef"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:fb616be3538599e797a2017cccca78e354c767165e8858ab5116813146041a24"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:14c2976aa9038c2629efa2c148022ed5eb4cb939e15ec7aace7ca932f48f9ba6"}, - {file = "multidict-6.0.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:435a0984199d81ca178b9ae2c26ec3d49692d20ee29bc4c11a2a8d4514c67eda"}, - {file = "multidict-6.0.5-cp312-cp312-win32.whl", hash = "sha256:9fe7b0653ba3d9d65cbe7698cca585bf0f8c83dbbcc710db9c90f478e175f2d5"}, - {file = "multidict-6.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:01265f5e40f5a17f8241d52656ed27192be03bfa8764d88e8220141d1e4b3556"}, - {file = "multidict-6.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:19fe01cea168585ba0f678cad6f58133db2aa14eccaf22f88e4a6dccadfad8b3"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf7a982604375a8d49b6cc1b781c1747f243d91b81035a9b43a2126c04766f5"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:107c0cdefe028703fb5dafe640a409cb146d44a6ae201e55b35a4af8e95457dd"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:403c0911cd5d5791605808b942c88a8155c2592e05332d2bf78f18697a5fa15e"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aeaf541ddbad8311a87dd695ed9642401131ea39ad7bc8cf3ef3967fd093b626"}, - {file = "multidict-6.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e4972624066095e52b569e02b5ca97dbd7a7ddd4294bf4e7247d52635630dd83"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d946b0a9eb8aaa590df1fe082cee553ceab173e6cb5b03239716338629c50c7a"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b55358304d7a73d7bdf5de62494aaf70bd33015831ffd98bc498b433dfe5b10c"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:a3145cb08d8625b2d3fee1b2d596a8766352979c9bffe5d7833e0503d0f0b5e5"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d65f25da8e248202bd47445cec78e0025c0fe7582b23ec69c3b27a640dd7a8e3"}, - {file = "multidict-6.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:c9bf56195c6bbd293340ea82eafd0071cb3d450c703d2c93afb89f93b8386ccc"}, - {file = "multidict-6.0.5-cp37-cp37m-win32.whl", hash = "sha256:69db76c09796b313331bb7048229e3bee7928eb62bab5e071e9f7fcc4879caee"}, - {file = "multidict-6.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:fce28b3c8a81b6b36dfac9feb1de115bab619b3c13905b419ec71d03a3fc1423"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76f067f5121dcecf0d63a67f29080b26c43c71a98b10c701b0677e4a065fbd54"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b82cc8ace10ab5bd93235dfaab2021c70637005e1ac787031f4d1da63d493c1d"}, - {file = "multidict-6.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5cb241881eefd96b46f89b1a056187ea8e9ba14ab88ba632e68d7a2ecb7aadf7"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e8e94e6912639a02ce173341ff62cc1201232ab86b8a8fcc05572741a5dc7d93"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09a892e4a9fb47331da06948690ae38eaa2426de97b4ccbfafbdcbe5c8f37ff8"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:55205d03e8a598cfc688c71ca8ea5f66447164efff8869517f175ea632c7cb7b"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37b15024f864916b4951adb95d3a80c9431299080341ab9544ed148091b53f50"}, - {file = "multidict-6.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2a1dee728b52b33eebff5072817176c172050d44d67befd681609b4746e1c2e"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:edd08e6f2f1a390bf137080507e44ccc086353c8e98c657e666c017718561b89"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:60d698e8179a42ec85172d12f50b1668254628425a6bd611aba022257cac1386"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:3d25f19500588cbc47dc19081d78131c32637c25804df8414463ec908631e453"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4cc0ef8b962ac7a5e62b9e826bd0cd5040e7d401bc45a6835910ed699037a461"}, - {file = "multidict-6.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:eca2e9d0cc5a889850e9bbd68e98314ada174ff6ccd1129500103df7a94a7a44"}, - {file = "multidict-6.0.5-cp38-cp38-win32.whl", hash = "sha256:4a6a4f196f08c58c59e0b8ef8ec441d12aee4125a7d4f4fef000ccb22f8d7241"}, - {file = "multidict-6.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:0275e35209c27a3f7951e1ce7aaf93ce0d163b28948444bec61dd7badc6d3f8c"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e7be68734bd8c9a513f2b0cfd508802d6609da068f40dc57d4e3494cefc92929"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1d9ea7a7e779d7a3561aade7d596649fbecfa5c08a7674b11b423783217933f9"}, - {file = "multidict-6.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ea1456df2a27c73ce51120fa2f519f1bea2f4a03a917f4a43c8707cf4cbbae1a"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf590b134eb70629e350691ecca88eac3e3b8b3c86992042fb82e3cb1830d5e1"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c0631926c4f58e9a5ccce555ad7747d9a9f8b10619621f22f9635f069f6233e"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dce1c6912ab9ff5f179eaf6efe7365c1f425ed690b03341911bf4939ef2f3046"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0868d64af83169e4d4152ec612637a543f7a336e4a307b119e98042e852ad9c"}, - {file = "multidict-6.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:141b43360bfd3bdd75f15ed811850763555a251e38b2405967f8e25fb43f7d40"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7df704ca8cf4a073334e0427ae2345323613e4df18cc224f647f251e5e75a527"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6214c5a5571802c33f80e6c84713b2c79e024995b9c5897f794b43e714daeec9"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:cd6c8fca38178e12c00418de737aef1261576bd1b6e8c6134d3e729a4e858b38"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:e02021f87a5b6932fa6ce916ca004c4d441509d33bbdbeca70d05dff5e9d2479"}, - {file = "multidict-6.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ebd8d160f91a764652d3e51ce0d2956b38efe37c9231cd82cfc0bed2e40b581c"}, - {file = "multidict-6.0.5-cp39-cp39-win32.whl", hash = "sha256:04da1bb8c8dbadf2a18a452639771951c662c5ad03aefe4884775454be322c9b"}, - {file = "multidict-6.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:d6f6d4f185481c9669b9447bf9d9cf3b95a0e9df9d169bbc17e363b7d5487755"}, - {file = "multidict-6.0.5-py3-none-any.whl", hash = "sha256:0d63c74e3d7ab26de115c49bffc92cc77ed23395303d496eae515d4204a625e7"}, - {file = "multidict-6.0.5.tar.gz", hash = "sha256:f7e301075edaf50500f0b341543c41194d8df3ae5caf4702f2095f3ca73dd8da"}, -] - -[[package]] -name = "multiprocess" -version = "0.70.16" -description = "better multiprocessing and multithreading in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "multiprocess-0.70.16-pp310-pypy310_pp73-macosx_10_13_x86_64.whl", hash = "sha256:476887be10e2f59ff183c006af746cb6f1fd0eadcfd4ef49e605cbe2659920ee"}, - {file = "multiprocess-0.70.16-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:d951bed82c8f73929ac82c61f01a7b5ce8f3e5ef40f5b52553b4f547ce2b08ec"}, - {file = "multiprocess-0.70.16-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:37b55f71c07e2d741374998c043b9520b626a8dddc8b3129222ca4f1a06ef67a"}, - {file = "multiprocess-0.70.16-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:ba8c31889abf4511c7308a8c52bb4a30b9d590e7f58523302ba00237702ca054"}, - {file = "multiprocess-0.70.16-pp39-pypy39_pp73-macosx_10_13_x86_64.whl", hash = "sha256:0dfd078c306e08d46d7a8d06fb120313d87aa43af60d66da43ffff40b44d2f41"}, - {file = "multiprocess-0.70.16-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:e7b9d0f307cd9bd50851afaac0dba2cb6c44449efff697df7c7645f7d3f2be3a"}, - {file = "multiprocess-0.70.16-py310-none-any.whl", hash = "sha256:c4a9944c67bd49f823687463660a2d6daae94c289adff97e0f9d696ba6371d02"}, - {file = "multiprocess-0.70.16-py311-none-any.whl", hash = "sha256:af4cabb0dac72abfb1e794fa7855c325fd2b55a10a44628a3c1ad3311c04127a"}, - {file = "multiprocess-0.70.16-py312-none-any.whl", hash = "sha256:fc0544c531920dde3b00c29863377f87e1632601092ea2daca74e4beb40faa2e"}, - {file = "multiprocess-0.70.16-py38-none-any.whl", hash = "sha256:a71d82033454891091a226dfc319d0cfa8019a4e888ef9ca910372a446de4435"}, - {file = "multiprocess-0.70.16-py39-none-any.whl", hash = "sha256:a0bafd3ae1b732eac64be2e72038231c1ba97724b60b09400d68f229fcc2fbf3"}, - {file = "multiprocess-0.70.16.tar.gz", hash = "sha256:161af703d4652a0e1410be6abccecde4a7ddffd19341be0a7011b94aeb171ac1"}, -] - -[package.dependencies] -dill = ">=0.3.8" - -[[package]] -name = "mypy-extensions" -version = "1.0.0" -description = "Type system extensions for programs checked with the mypy type checker." -optional = false -python-versions = ">=3.5" -files = [ - {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, - {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, -] - -[[package]] -name = "nbclient" -version = "0.10.0" -description = "A client library for executing notebooks. Formerly nbconvert's ExecutePreprocessor." -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "nbclient-0.10.0-py3-none-any.whl", hash = "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f"}, - {file = "nbclient-0.10.0.tar.gz", hash = "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09"}, -] - -[package.dependencies] -jupyter-client = ">=6.1.12" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -nbformat = ">=5.1" -traitlets = ">=5.4" - -[package.extras] -dev = ["pre-commit"] -docs = ["autodoc-traits", "mock", "moto", "myst-parser", "nbclient[test]", "sphinx (>=1.7)", "sphinx-book-theme", "sphinxcontrib-spelling"] -test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=7.0.0)", "pytest (>=7.0,<8)", "pytest-asyncio", "pytest-cov (>=4.0)", "testpath", "xmltodict"] - -[[package]] -name = "nbconvert" -version = "7.16.3" -description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)." -optional = false -python-versions = ">=3.8" -files = [ - {file = "nbconvert-7.16.3-py3-none-any.whl", hash = "sha256:ddeff14beeeedf3dd0bc506623e41e4507e551736de59df69a91f86700292b3b"}, - {file = "nbconvert-7.16.3.tar.gz", hash = "sha256:a6733b78ce3d47c3f85e504998495b07e6ea9cf9bf6ec1c98dda63ec6ad19142"}, -] - -[package.dependencies] -beautifulsoup4 = "*" -bleach = "!=5.0.0" -defusedxml = "*" -jinja2 = ">=3.0" -jupyter-core = ">=4.7" -jupyterlab-pygments = "*" -markupsafe = ">=2.0" -mistune = ">=2.0.3,<4" -nbclient = ">=0.5.0" -nbformat = ">=5.7" -packaging = "*" -pandocfilters = ">=1.4.1" -pygments = ">=2.4.1" -tinycss2 = "*" -traitlets = ">=5.1" - -[package.extras] -all = ["nbconvert[docs,qtpdf,serve,test,webpdf]"] -docs = ["ipykernel", "ipython", "myst-parser", "nbsphinx (>=0.2.12)", "pydata-sphinx-theme", "sphinx (==5.0.2)", "sphinxcontrib-spelling"] -qtpdf = ["nbconvert[qtpng]"] -qtpng = ["pyqtwebengine (>=5.15)"] -serve = ["tornado (>=6.1)"] -test = ["flaky", "ipykernel", "ipywidgets (>=7.5)", "pytest (>=7)"] -webpdf = ["playwright"] - -[[package]] -name = "nbformat" -version = "5.10.4" -description = "The Jupyter Notebook format" -optional = false -python-versions = ">=3.8" -files = [ - {file = "nbformat-5.10.4-py3-none-any.whl", hash = "sha256:3b48d6c8fbca4b299bf3982ea7db1af21580e4fec269ad087b9e81588891200b"}, - {file = "nbformat-5.10.4.tar.gz", hash = "sha256:322168b14f937a5d11362988ecac2a4952d3d8e3a2cbeb2319584631226d5b3a"}, -] - -[package.dependencies] -fastjsonschema = ">=2.15" -jsonschema = ">=2.6" -jupyter-core = ">=4.12,<5.0.dev0 || >=5.1.dev0" -traitlets = ">=5.1" - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx", "sphinxcontrib-github-alt", "sphinxcontrib-spelling"] -test = ["pep440", "pre-commit", "pytest", "testpath"] - -[[package]] -name = "nest-asyncio" -version = "1.6.0" -description = "Patch asyncio to allow nested event loops" -optional = false -python-versions = ">=3.5" -files = [ - {file = "nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c"}, - {file = "nest_asyncio-1.6.0.tar.gz", hash = "sha256:6f172d5449aca15afd6c646851f4e31e02c598d553a667e38cafa997cfec55fe"}, -] - -[[package]] -name = "networkx" -version = "3.1" -description = "Python package for creating and manipulating graphs and networks" -optional = false -python-versions = ">=3.8" -files = [ - {file = "networkx-3.1-py3-none-any.whl", hash = "sha256:4f33f68cb2afcf86f28a45f43efc27a9386b535d567d2127f8f61d51dec58d36"}, - {file = "networkx-3.1.tar.gz", hash = "sha256:de346335408f84de0eada6ff9fafafff9bcda11f0a0dfaa931133debb146ab61"}, -] - -[package.extras] -default = ["matplotlib (>=3.4)", "numpy (>=1.20)", "pandas (>=1.3)", "scipy (>=1.8)"] -developer = ["mypy (>=1.1)", "pre-commit (>=3.2)"] -doc = ["nb2plots (>=0.6)", "numpydoc (>=1.5)", "pillow (>=9.4)", "pydata-sphinx-theme (>=0.13)", "sphinx (>=6.1)", "sphinx-gallery (>=0.12)", "texext (>=0.6.7)"] -extra = ["lxml (>=4.6)", "pydot (>=1.4.2)", "pygraphviz (>=1.10)", "sympy (>=1.10)"] -test = ["codecov (>=2.1)", "pytest (>=7.2)", "pytest-cov (>=4.0)"] - -[[package]] -name = "ninja" -version = "1.11.1.1" -description = "Ninja is a small build system with a focus on speed" -optional = false -python-versions = "*" -files = [ - {file = "ninja-1.11.1.1-py2.py3-none-macosx_10_9_universal2.macosx_10_9_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl", hash = "sha256:376889c76d87b95b5719fdd61dd7db193aa7fd4432e5d52d2e44e4c497bdbbee"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:ecf80cf5afd09f14dcceff28cb3f11dc90fb97c999c89307aea435889cb66877"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:84502ec98f02a037a169c4b0d5d86075eaf6afc55e1879003d6cab51ced2ea4b"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:73b93c14046447c7c5cc892433d4fae65d6364bec6685411cb97a8bcf815f93a"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:18302d96a5467ea98b68e1cae1ae4b4fb2b2a56a82b955193c637557c7273dbd"}, - {file = "ninja-1.11.1.1-py2.py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:aad34a70ef15b12519946c5633344bc775a7656d789d9ed5fdb0d456383716ef"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:d491fc8d89cdcb416107c349ad1e3a735d4c4af5e1cb8f5f727baca6350fdaea"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_i686.whl", hash = "sha256:7563ce1d9fe6ed5af0b8dd9ab4a214bf4ff1f2f6fd6dc29f480981f0f8b8b249"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_ppc64le.whl", hash = "sha256:9df724344202b83018abb45cb1efc22efd337a1496514e7e6b3b59655be85205"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_s390x.whl", hash = "sha256:3e0f9be5bb20d74d58c66cc1c414c3e6aeb45c35b0d0e41e8d739c2c0d57784f"}, - {file = "ninja-1.11.1.1-py2.py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:76482ba746a2618eecf89d5253c0d1e4f1da1270d41e9f54dfbd91831b0f6885"}, - {file = "ninja-1.11.1.1-py2.py3-none-win32.whl", hash = "sha256:fa2ba9d74acfdfbfbcf06fad1b8282de8a7a8c481d9dee45c859a8c93fcc1082"}, - {file = "ninja-1.11.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:95da904130bfa02ea74ff9c0116b4ad266174fafb1c707aa50212bc7859aebf1"}, - {file = "ninja-1.11.1.1-py2.py3-none-win_arm64.whl", hash = "sha256:185e0641bde601e53841525c4196278e9aaf4463758da6dd1e752c0a0f54136a"}, - {file = "ninja-1.11.1.1.tar.gz", hash = "sha256:9d793b08dd857e38d0b6ffe9e6b7145d7c485a42dcfea04905ca0cdb6017cc3c"}, -] - -[package.extras] -test = ["codecov (>=2.0.5)", "coverage (>=4.2)", "flake8 (>=3.0.4)", "pytest (>=4.5.0)", "pytest-cov (>=2.7.1)", "pytest-runner (>=5.1)", "pytest-virtualenv (>=1.7.0)", "virtualenv (>=15.0.3)"] - -[[package]] -name = "notebook-shim" -version = "0.2.4" -description = "A shim layer for notebook traits and config" -optional = false -python-versions = ">=3.7" -files = [ - {file = "notebook_shim-0.2.4-py3-none-any.whl", hash = "sha256:411a5be4e9dc882a074ccbcae671eda64cceb068767e9a3419096986560e1cef"}, - {file = "notebook_shim-0.2.4.tar.gz", hash = "sha256:b4b2cfa1b65d98307ca24361f5b30fe785b53c3fd07b7a47e89acb5e6ac638cb"}, -] - -[package.dependencies] -jupyter-server = ">=1.8,<3" - -[package.extras] -test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync"] - -[[package]] -name = "numba" -version = "0.59.1" -description = "compiling Python code using LLVM" -optional = false -python-versions = ">=3.9" -files = [ - {file = "numba-0.59.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:97385a7f12212c4f4bc28f648720a92514bee79d7063e40ef66c2d30600fd18e"}, - {file = "numba-0.59.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0b77aecf52040de2a1eb1d7e314497b9e56fba17466c80b457b971a25bb1576d"}, - {file = "numba-0.59.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3476a4f641bfd58f35ead42f4dcaf5f132569c4647c6f1360ccf18ee4cda3990"}, - {file = "numba-0.59.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:525ef3f820931bdae95ee5379c670d5c97289c6520726bc6937a4a7d4230ba24"}, - {file = "numba-0.59.1-cp310-cp310-win_amd64.whl", hash = "sha256:990e395e44d192a12105eca3083b61307db7da10e093972ca285c85bef0963d6"}, - {file = "numba-0.59.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:43727e7ad20b3ec23ee4fc642f5b61845c71f75dd2825b3c234390c6d8d64051"}, - {file = "numba-0.59.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:411df625372c77959570050e861981e9d196cc1da9aa62c3d6a836b5cc338966"}, - {file = "numba-0.59.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2801003caa263d1e8497fb84829a7ecfb61738a95f62bc05693fcf1733e978e4"}, - {file = "numba-0.59.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dd2842fac03be4e5324ebbbd4d2d0c8c0fc6e0df75c09477dd45b288a0777389"}, - {file = "numba-0.59.1-cp311-cp311-win_amd64.whl", hash = "sha256:0594b3dfb369fada1f8bb2e3045cd6c61a564c62e50cf1f86b4666bc721b3450"}, - {file = "numba-0.59.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1cce206a3b92836cdf26ef39d3a3242fec25e07f020cc4feec4c4a865e340569"}, - {file = "numba-0.59.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8c8b4477763cb1fbd86a3be7050500229417bf60867c93e131fd2626edb02238"}, - {file = "numba-0.59.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d80bce4ef7e65bf895c29e3889ca75a29ee01da80266a01d34815918e365835"}, - {file = "numba-0.59.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f7ad1d217773e89a9845886401eaaab0a156a90aa2f179fdc125261fd1105096"}, - {file = "numba-0.59.1-cp312-cp312-win_amd64.whl", hash = "sha256:5bf68f4d69dd3a9f26a9b23548fa23e3bcb9042e2935257b471d2a8d3c424b7f"}, - {file = "numba-0.59.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4e0318ae729de6e5dbe64c75ead1a95eb01fabfe0e2ebed81ebf0344d32db0ae"}, - {file = "numba-0.59.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0f68589740a8c38bb7dc1b938b55d1145244c8353078eea23895d4f82c8b9ec1"}, - {file = "numba-0.59.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:649913a3758891c77c32e2d2a3bcbedf4a69f5fea276d11f9119677c45a422e8"}, - {file = "numba-0.59.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9712808e4545270291d76b9a264839ac878c5eb7d8b6e02c970dc0ac29bc8187"}, - {file = "numba-0.59.1-cp39-cp39-win_amd64.whl", hash = "sha256:8d51ccd7008a83105ad6a0082b6a2b70f1142dc7cfd76deb8c5a862367eb8c86"}, - {file = "numba-0.59.1.tar.gz", hash = "sha256:76f69132b96028d2774ed20415e8c528a34e3299a40581bae178f0994a2f370b"}, -] - -[package.dependencies] -llvmlite = "==0.42.*" -numpy = ">=1.22,<1.27" - -[[package]] -name = "numpy" -version = "1.26.4" -description = "Fundamental package for array computing in Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, -] - -[[package]] -name = "nvidia-cublas-cu12" -version = "12.1.3.1" -description = "CUBLAS native runtime libraries" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:ee53ccca76a6fc08fb9701aa95b6ceb242cdaab118c3bb152af4e579af792728"}, - {file = "nvidia_cublas_cu12-12.1.3.1-py3-none-win_amd64.whl", hash = "sha256:2b964d60e8cf11b5e1073d179d85fa340c120e99b3067558f3cf98dd69d02906"}, -] - -[[package]] -name = "nvidia-cuda-cupti-cu12" -version = "12.1.105" -description = "CUDA profiling tools runtime libs." -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:e54fde3983165c624cb79254ae9818a456eb6e87a7fd4d56a2352c24ee542d7e"}, - {file = "nvidia_cuda_cupti_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:bea8236d13a0ac7190bd2919c3e8e6ce1e402104276e6f9694479e48bb0eb2a4"}, -] - -[[package]] -name = "nvidia-cuda-nvrtc-cu12" -version = "12.1.105" -description = "NVRTC native runtime libraries" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:339b385f50c309763ca65456ec75e17bbefcbbf2893f462cb8b90584cd27a1c2"}, - {file = "nvidia_cuda_nvrtc_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:0a98a522d9ff138b96c010a65e145dc1b4850e9ecb75a0172371793752fd46ed"}, -] - -[[package]] -name = "nvidia-cuda-runtime-cu12" -version = "12.1.105" -description = "CUDA Runtime native Libraries" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:6e258468ddf5796e25f1dc591a31029fa317d97a0a94ed93468fc86301d61e40"}, - {file = "nvidia_cuda_runtime_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:dfb46ef84d73fababab44cf03e3b83f80700d27ca300e537f85f636fac474344"}, -] - -[[package]] -name = "nvidia-cudnn-cu12" -version = "8.9.2.26" -description = "cuDNN runtime libraries" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_cudnn_cu12-8.9.2.26-py3-none-manylinux1_x86_64.whl", hash = "sha256:5ccb288774fdfb07a7e7025ffec286971c06d8d7b4fb162525334616d7629ff9"}, -] - -[package.dependencies] -nvidia-cublas-cu12 = "*" - -[[package]] -name = "nvidia-cufft-cu12" -version = "11.0.2.54" -description = "CUFFT native runtime libraries" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-manylinux1_x86_64.whl", hash = "sha256:794e3948a1aa71fd817c3775866943936774d1c14e7628c74f6f7417224cdf56"}, - {file = "nvidia_cufft_cu12-11.0.2.54-py3-none-win_amd64.whl", hash = "sha256:d9ac353f78ff89951da4af698f80870b1534ed69993f10a4cf1d96f21357e253"}, -] - -[[package]] -name = "nvidia-curand-cu12" -version = "10.3.2.106" -description = "CURAND native runtime libraries" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_curand_cu12-10.3.2.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:9d264c5036dde4e64f1de8c50ae753237c12e0b1348738169cd0f8a536c0e1e0"}, - {file = "nvidia_curand_cu12-10.3.2.106-py3-none-win_amd64.whl", hash = "sha256:75b6b0c574c0037839121317e17fd01f8a69fd2ef8e25853d826fec30bdba74a"}, -] - -[[package]] -name = "nvidia-cusolver-cu12" -version = "11.4.5.107" -description = "CUDA solver native runtime libraries" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-manylinux1_x86_64.whl", hash = "sha256:8a7ec542f0412294b15072fa7dab71d31334014a69f953004ea7a118206fe0dd"}, - {file = "nvidia_cusolver_cu12-11.4.5.107-py3-none-win_amd64.whl", hash = "sha256:74e0c3a24c78612192a74fcd90dd117f1cf21dea4822e66d89e8ea80e3cd2da5"}, -] - -[package.dependencies] -nvidia-cublas-cu12 = "*" -nvidia-cusparse-cu12 = "*" -nvidia-nvjitlink-cu12 = "*" - -[[package]] -name = "nvidia-cusparse-cu12" -version = "12.1.0.106" -description = "CUSPARSE native runtime libraries" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-manylinux1_x86_64.whl", hash = "sha256:f3b50f42cf363f86ab21f720998517a659a48131e8d538dc02f8768237bd884c"}, - {file = "nvidia_cusparse_cu12-12.1.0.106-py3-none-win_amd64.whl", hash = "sha256:b798237e81b9719373e8fae8d4f091b70a0cf09d9d85c95a557e11df2d8e9a5a"}, -] - -[package.dependencies] -nvidia-nvjitlink-cu12 = "*" - -[[package]] -name = "nvidia-nccl-cu12" -version = "2.18.1" -description = "NVIDIA Collective Communication Library (NCCL) Runtime" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_nccl_cu12-2.18.1-py3-none-manylinux1_x86_64.whl", hash = "sha256:1a6c4acefcbebfa6de320f412bf7866de856e786e0462326ba1bac40de0b5e71"}, -] - -[[package]] -name = "nvidia-nvjitlink-cu12" -version = "12.4.127" -description = "Nvidia JIT LTO Library" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-manylinux2014_x86_64.whl", hash = "sha256:06b3b9b25bf3f8af351d664978ca26a16d2c5127dbd53c0497e28d1fb9611d57"}, - {file = "nvidia_nvjitlink_cu12-12.4.127-py3-none-win_amd64.whl", hash = "sha256:fd9020c501d27d135f983c6d3e244b197a7ccad769e34df53a42e276b0e25fa1"}, -] - -[[package]] -name = "nvidia-nvtx-cu12" -version = "12.1.105" -description = "NVIDIA Tools Extension" -optional = false -python-versions = ">=3" -files = [ - {file = "nvidia_nvtx_cu12-12.1.105-py3-none-manylinux1_x86_64.whl", hash = "sha256:dc21cf308ca5691e7c04d962e213f8a4aa9bbfa23d95412f452254c2caeb09e5"}, - {file = "nvidia_nvtx_cu12-12.1.105-py3-none-win_amd64.whl", hash = "sha256:65f4d98982b31b60026e0e6de73fbdfc09d08a96f4656dd3665ca616a11e1e82"}, -] - -[[package]] -name = "openai" -version = "1.19.0" -description = "The official Python library for the openai API" -optional = false -python-versions = ">=3.7.1" -files = [ - {file = "openai-1.19.0-py3-none-any.whl", hash = "sha256:fef51776830930f98401fc867c24b969e3bc121f5326edbb72ed56cdfdc4ffd0"}, - {file = "openai-1.19.0.tar.gz", hash = "sha256:6a1c3538e1fa1907f19d82a0017d792d5180533ecfe1a8f22b4b5119d7a3f5a0"}, -] - -[package.dependencies] -anyio = ">=3.5.0,<5" -distro = ">=1.7.0,<2" -httpx = ">=0.23.0,<1" -pydantic = ">=1.9.0,<3" -sniffio = "*" -tqdm = ">4" -typing-extensions = ">=4.7,<5" - -[package.extras] -datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] - -[[package]] -name = "orjson" -version = "3.10.1" -description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -optional = false -python-versions = ">=3.8" -files = [ - {file = "orjson-3.10.1-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:8ec2fc456d53ea4a47768f622bb709be68acd455b0c6be57e91462259741c4f3"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e900863691d327758be14e2a491931605bd0aded3a21beb6ce133889830b659"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab6ecbd6fe57785ebc86ee49e183f37d45f91b46fc601380c67c5c5e9c0014a2"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af7c68b01b876335cccfb4eee0beef2b5b6eae1945d46a09a7c24c9faac7a77"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:915abfb2e528677b488a06eba173e9d7706a20fdfe9cdb15890b74ef9791b85e"}, - {file = "orjson-3.10.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe3fd4a36eff9c63d25503b439531d21828da9def0059c4f472e3845a081aa0b"}, - {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d229564e72cfc062e6481a91977a5165c5a0fdce11ddc19ced8471847a67c517"}, - {file = "orjson-3.10.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:9e00495b18304173ac843b5c5fbea7b6f7968564d0d49bef06bfaeca4b656f4e"}, - {file = "orjson-3.10.1-cp310-none-win32.whl", hash = "sha256:fd78ec55179545c108174ba19c1795ced548d6cac4d80d014163033c047ca4ea"}, - {file = "orjson-3.10.1-cp310-none-win_amd64.whl", hash = "sha256:50ca42b40d5a442a9e22eece8cf42ba3d7cd4cd0f2f20184b4d7682894f05eec"}, - {file = "orjson-3.10.1-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:b345a3d6953628df2f42502297f6c1e1b475cfbf6268013c94c5ac80e8abc04c"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:caa7395ef51af4190d2c70a364e2f42138e0e5fcb4bc08bc9b76997659b27dab"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:b01d701decd75ae092e5f36f7b88a1e7a1d3bb7c9b9d7694de850fb155578d5a"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b5028981ba393f443d8fed9049211b979cadc9d0afecf162832f5a5b152c6297"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:31ff6a222ea362b87bf21ff619598a4dc1106aaafaea32b1c4876d692891ec27"}, - {file = "orjson-3.10.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e852a83d7803d3406135fb7a57cf0c1e4a3e73bac80ec621bd32f01c653849c5"}, - {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2567bc928ed3c3fcd90998009e8835de7c7dc59aabcf764b8374d36044864f3b"}, - {file = "orjson-3.10.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4ce98cac60b7bb56457bdd2ed7f0d5d7f242d291fdc0ca566c83fa721b52e92d"}, - {file = "orjson-3.10.1-cp311-none-win32.whl", hash = "sha256:813905e111318acb356bb8029014c77b4c647f8b03f314e7b475bd9ce6d1a8ce"}, - {file = "orjson-3.10.1-cp311-none-win_amd64.whl", hash = "sha256:03a3ca0b3ed52bed1a869163a4284e8a7b0be6a0359d521e467cdef7e8e8a3ee"}, - {file = "orjson-3.10.1-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:f02c06cee680b1b3a8727ec26c36f4b3c0c9e2b26339d64471034d16f74f4ef5"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1aa2f127ac546e123283e437cc90b5ecce754a22306c7700b11035dad4ccf85"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2cf29b4b74f585225196944dffdebd549ad2af6da9e80db7115984103fb18a96"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1b130c20b116f413caf6059c651ad32215c28500dce9cd029a334a2d84aa66f"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d31f9a709e6114492136e87c7c6da5e21dfedebefa03af85f3ad72656c493ae9"}, - {file = "orjson-3.10.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d1d169461726f271ab31633cf0e7e7353417e16fb69256a4f8ecb3246a78d6e"}, - {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:57c294d73825c6b7f30d11c9e5900cfec9a814893af7f14efbe06b8d0f25fba9"}, - {file = "orjson-3.10.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d7f11dbacfa9265ec76b4019efffabaabba7a7ebf14078f6b4df9b51c3c9a8ea"}, - {file = "orjson-3.10.1-cp312-none-win32.whl", hash = "sha256:d89e5ed68593226c31c76ab4de3e0d35c760bfd3fbf0a74c4b2be1383a1bf123"}, - {file = "orjson-3.10.1-cp312-none-win_amd64.whl", hash = "sha256:aa76c4fe147fd162107ce1692c39f7189180cfd3a27cfbc2ab5643422812da8e"}, - {file = "orjson-3.10.1-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a2c6a85c92d0e494c1ae117befc93cf8e7bca2075f7fe52e32698da650b2c6d1"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9813f43da955197d36a7365eb99bed42b83680801729ab2487fef305b9ced866"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec917b768e2b34b7084cb6c68941f6de5812cc26c6f1a9fecb728e36a3deb9e8"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5252146b3172d75c8a6d27ebca59c9ee066ffc5a277050ccec24821e68742fdf"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:536429bb02791a199d976118b95014ad66f74c58b7644d21061c54ad284e00f4"}, - {file = "orjson-3.10.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7dfed3c3e9b9199fb9c3355b9c7e4649b65f639e50ddf50efdf86b45c6de04b5"}, - {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:2b230ec35f188f003f5b543644ae486b2998f6afa74ee3a98fc8ed2e45960afc"}, - {file = "orjson-3.10.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:01234249ba19c6ab1eb0b8be89f13ea21218b2d72d496ef085cfd37e1bae9dd8"}, - {file = "orjson-3.10.1-cp38-none-win32.whl", hash = "sha256:8a884fbf81a3cc22d264ba780920d4885442144e6acaa1411921260416ac9a54"}, - {file = "orjson-3.10.1-cp38-none-win_amd64.whl", hash = "sha256:dab5f802d52b182163f307d2b1f727d30b1762e1923c64c9c56dd853f9671a49"}, - {file = "orjson-3.10.1-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:a51fd55d4486bc5293b7a400f9acd55a2dc3b5fc8420d5ffe9b1d6bb1a056a5e"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53521542a6db1411b3bfa1b24ddce18605a3abdc95a28a67b33f9145f26aa8f2"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:27d610df96ac18ace4931411d489637d20ab3b8f63562b0531bba16011998db0"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:79244b1456e5846d44e9846534bd9e3206712936d026ea8e6a55a7374d2c0694"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d751efaa8a49ae15cbebdda747a62a9ae521126e396fda8143858419f3b03610"}, - {file = "orjson-3.10.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:27ff69c620a4fff33267df70cfd21e0097c2a14216e72943bd5414943e376d77"}, - {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:ebc58693464146506fde0c4eb1216ff6d4e40213e61f7d40e2f0dde9b2f21650"}, - {file = "orjson-3.10.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5be608c3972ed902e0143a5b8776d81ac1059436915d42defe5c6ae97b3137a4"}, - {file = "orjson-3.10.1-cp39-none-win32.whl", hash = "sha256:4ae10753e7511d359405aadcbf96556c86e9dbf3a948d26c2c9f9a150c52b091"}, - {file = "orjson-3.10.1-cp39-none-win_amd64.whl", hash = "sha256:fb5bc4caa2c192077fdb02dce4e5ef8639e7f20bec4e3a834346693907362932"}, - {file = "orjson-3.10.1.tar.gz", hash = "sha256:a883b28d73370df23ed995c466b4f6c708c1f7a9bdc400fe89165c96c7603204"}, -] - -[[package]] -name = "outlines" -version = "0.0.37" -description = "Probabilistic Generative Model Programming" -optional = false -python-versions = ">=3.8" -files = [ - {file = "outlines-0.0.37-py3-none-any.whl", hash = "sha256:795ef2b3bcf58f6ddb44012f66d943385a7a1ba5efea205bc36745f82e7f597f"}, - {file = "outlines-0.0.37.tar.gz", hash = "sha256:0d2708587c98822469c40994308590929afebeaba36611f8db970752fd283c7d"}, -] - -[package.dependencies] -cloudpickle = "*" -diskcache = "*" -interegular = "*" -jinja2 = "*" -joblib = "*" -jsonschema = "*" -lark = "*" -nest-asyncio = "*" -numba = "*" -numpy = "*" -pydantic = ">=2.0" -referencing = "*" -requests = "*" -scipy = "*" -torch = ">=2.1.0" -transformers = "*" - -[package.extras] -serve = ["fastapi", "pydantic (>=2.0)", "ray (==2.9.0)", "uvicorn", "vllm (>=0.3.0)"] -test = ["accelerate", "beartype (<0.16.0)", "coverage[toml] (>=5.1)", "datasets", "diff-cover", "huggingface-hub", "llama-cpp-python", "openai (>=1.0.0)", "pre-commit", "pytest", "pytest-benchmark", "pytest-cov", "pytest-mock", "responses", "transformers"] - -[[package]] -name = "overrides" -version = "7.7.0" -description = "A decorator to automatically detect mismatch when overriding a method." -optional = false -python-versions = ">=3.6" -files = [ - {file = "overrides-7.7.0-py3-none-any.whl", hash = "sha256:c7ed9d062f78b8e4c1a7b70bd8796b35ead4d9f510227ef9c5dc7626c60d7e49"}, - {file = "overrides-7.7.0.tar.gz", hash = "sha256:55158fa3d93b98cc75299b1e67078ad9003ca27945c76162c1c0766d6f91820a"}, -] - -[[package]] -name = "packaging" -version = "24.0" -description = "Core utilities for Python packages" -optional = false -python-versions = ">=3.7" -files = [ - {file = "packaging-24.0-py3-none-any.whl", hash = "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5"}, - {file = "packaging-24.0.tar.gz", hash = "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"}, -] - -[[package]] -name = "pandas" -version = "2.2.2" -description = "Powerful data structures for data analysis, time series, and statistics" -optional = false -python-versions = ">=3.9" -files = [ - {file = "pandas-2.2.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:90c6fca2acf139569e74e8781709dccb6fe25940488755716d1d354d6bc58bce"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4abfe0be0d7221be4f12552995e58723c7422c80a659da13ca382697de830c08"}, - {file = "pandas-2.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8635c16bf3d99040fdf3ca3db669a7250ddf49c55dc4aa8fe0ae0fa8d6dcc1f0"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:40ae1dffb3967a52203105a077415a86044a2bea011b5f321c6aa64b379a3f51"}, - {file = "pandas-2.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8e5a0b00e1e56a842f922e7fae8ae4077aee4af0acb5ae3622bd4b4c30aedf99"}, - {file = "pandas-2.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:ddf818e4e6c7c6f4f7c8a12709696d193976b591cc7dc50588d3d1a6b5dc8772"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:696039430f7a562b74fa45f540aca068ea85fa34c244d0deee539cb6d70aa288"}, - {file = "pandas-2.2.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8e90497254aacacbc4ea6ae5e7a8cd75629d6ad2b30025a4a8b09aa4faf55151"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58b84b91b0b9f4bafac2a0ac55002280c094dfc6402402332c0913a59654ab2b"}, - {file = "pandas-2.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2123dc9ad6a814bcdea0f099885276b31b24f7edf40f6cdbc0912672e22eee"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2925720037f06e89af896c70bca73459d7e6a4be96f9de79e2d440bd499fe0db"}, - {file = "pandas-2.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0cace394b6ea70c01ca1595f839cf193df35d1575986e484ad35c4aeae7266c1"}, - {file = "pandas-2.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:873d13d177501a28b2756375d59816c365e42ed8417b41665f346289adc68d24"}, - {file = "pandas-2.2.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:9dfde2a0ddef507a631dc9dc4af6a9489d5e2e740e226ad426a05cabfbd7c8ef"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cb51fe389360f3b5a4d57dbd2848a5f033350336ca3b340d1c53a1fad33bcad"}, - {file = "pandas-2.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eee3a87076c0756de40b05c5e9a6069c035ba43e8dd71c379e68cab2c20f16ad"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3e374f59e440d4ab45ca2fffde54b81ac3834cf5ae2cdfa69c90bc03bde04d76"}, - {file = "pandas-2.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:43498c0bdb43d55cb162cdc8c06fac328ccb5d2eabe3cadeb3529ae6f0517c32"}, - {file = "pandas-2.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:d187d355ecec3629624fccb01d104da7d7f391db0311145817525281e2804d23"}, - {file = "pandas-2.2.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0ca6377b8fca51815f382bd0b697a0814c8bda55115678cbc94c30aacbb6eff2"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:001910ad31abc7bf06f49dcc903755d2f7f3a9186c0c040b827e522e9cef0863"}, - {file = "pandas-2.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66b479b0bd07204e37583c191535505410daa8df638fd8e75ae1b383851fe921"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:a77e9d1c386196879aa5eb712e77461aaee433e54c68cf253053a73b7e49c33a"}, - {file = "pandas-2.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:92fd6b027924a7e178ac202cfbe25e53368db90d56872d20ffae94b96c7acc57"}, - {file = "pandas-2.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:640cef9aa381b60e296db324337a554aeeb883ead99dc8f6c18e81a93942f5f4"}, - {file = "pandas-2.2.2.tar.gz", hash = "sha256:9e79019aba43cb4fda9e4d983f8e88ca0373adbb697ae9c6c43093218de28b54"}, -] - -[package.dependencies] -numpy = {version = ">=1.22.4", markers = "python_version < \"3.11\""} -python-dateutil = ">=2.8.2" -pytz = ">=2020.1" -tzdata = ">=2022.7" - -[package.extras] -all = ["PyQt5 (>=5.15.9)", "SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)", "beautifulsoup4 (>=4.11.2)", "bottleneck (>=1.3.6)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=2022.12.0)", "fsspec (>=2022.11.0)", "gcsfs (>=2022.11.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.9.2)", "matplotlib (>=3.6.3)", "numba (>=0.56.4)", "numexpr (>=2.8.4)", "odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "pandas-gbq (>=0.19.0)", "psycopg2 (>=2.9.6)", "pyarrow (>=10.0.1)", "pymysql (>=1.0.2)", "pyreadstat (>=1.2.0)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "qtpy (>=2.3.0)", "s3fs (>=2022.11.0)", "scipy (>=1.10.0)", "tables (>=3.8.0)", "tabulate (>=0.9.0)", "xarray (>=2022.12.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)", "zstandard (>=0.19.0)"] -aws = ["s3fs (>=2022.11.0)"] -clipboard = ["PyQt5 (>=5.15.9)", "qtpy (>=2.3.0)"] -compression = ["zstandard (>=0.19.0)"] -computation = ["scipy (>=1.10.0)", "xarray (>=2022.12.0)"] -consortium-standard = ["dataframe-api-compat (>=0.1.7)"] -excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.1.0)", "python-calamine (>=0.1.7)", "pyxlsb (>=1.0.10)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.5)"] -feather = ["pyarrow (>=10.0.1)"] -fss = ["fsspec (>=2022.11.0)"] -gcp = ["gcsfs (>=2022.11.0)", "pandas-gbq (>=0.19.0)"] -hdf5 = ["tables (>=3.8.0)"] -html = ["beautifulsoup4 (>=4.11.2)", "html5lib (>=1.1)", "lxml (>=4.9.2)"] -mysql = ["SQLAlchemy (>=2.0.0)", "pymysql (>=1.0.2)"] -output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.9.0)"] -parquet = ["pyarrow (>=10.0.1)"] -performance = ["bottleneck (>=1.3.6)", "numba (>=0.56.4)", "numexpr (>=2.8.4)"] -plot = ["matplotlib (>=3.6.3)"] -postgresql = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "psycopg2 (>=2.9.6)"] -pyarrow = ["pyarrow (>=10.0.1)"] -spss = ["pyreadstat (>=1.2.0)"] -sql-other = ["SQLAlchemy (>=2.0.0)", "adbc-driver-postgresql (>=0.8.0)", "adbc-driver-sqlite (>=0.8.0)"] -test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-xdist (>=2.2.0)"] -xml = ["lxml (>=4.9.2)"] - -[[package]] -name = "pandocfilters" -version = "1.5.1" -description = "Utilities for writing pandoc filters in python" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pandocfilters-1.5.1-py2.py3-none-any.whl", hash = "sha256:93be382804a9cdb0a7267585f157e5d1731bbe5545a85b268d6f5fe6232de2bc"}, - {file = "pandocfilters-1.5.1.tar.gz", hash = "sha256:002b4a555ee4ebc03f8b66307e287fa492e4a77b4ea14d3f934328297bb4939e"}, -] - -[[package]] -name = "parso" -version = "0.8.4" -description = "A Python Parser" -optional = false -python-versions = ">=3.6" -files = [ - {file = "parso-0.8.4-py2.py3-none-any.whl", hash = "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18"}, - {file = "parso-0.8.4.tar.gz", hash = "sha256:eb3a7b58240fb99099a345571deecc0f9540ea5f4dd2fe14c2a99d6b281ab92d"}, -] - -[package.extras] -qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["docopt", "pytest"] - -[[package]] -name = "pastel" -version = "0.2.1" -description = "Bring colors to your terminal." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "pastel-0.2.1-py2.py3-none-any.whl", hash = "sha256:4349225fcdf6c2bb34d483e523475de5bb04a5c10ef711263452cb37d7dd4364"}, - {file = "pastel-0.2.1.tar.gz", hash = "sha256:e6581ac04e973cac858828c6202c1e1e81fee1dc7de7683f3e1ffe0bfd8a573d"}, -] - -[[package]] -name = "pathspec" -version = "0.12.1" -description = "Utility library for gitignore style pattern matching of file paths." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pathspec-0.12.1-py3-none-any.whl", hash = "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08"}, - {file = "pathspec-0.12.1.tar.gz", hash = "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712"}, -] - -[[package]] -name = "pexpect" -version = "4.9.0" -description = "Pexpect allows easy control of interactive console applications." -optional = false -python-versions = "*" -files = [ - {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, - {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, -] - -[package.dependencies] -ptyprocess = ">=0.5" - -[[package]] -name = "pillow" -version = "10.3.0" -description = "Python Imaging Library (Fork)" -optional = false -python-versions = ">=3.8" -files = [ - {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] -docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-removed-in", "sphinxext-opengraph"] -fpx = ["olefile"] -mic = ["olefile"] -tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] -typing = ["typing-extensions"] -xmp = ["defusedxml"] - -[[package]] -name = "platformdirs" -version = "4.2.0" -description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -optional = false -python-versions = ">=3.8" -files = [ - {file = "platformdirs-4.2.0-py3-none-any.whl", hash = "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068"}, - {file = "platformdirs-4.2.0.tar.gz", hash = "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"}, -] - -[package.extras] -docs = ["furo (>=2023.9.10)", "proselint (>=0.13)", "sphinx (>=7.2.6)", "sphinx-autodoc-typehints (>=1.25.2)"] -test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.4.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.12)"] - -[[package]] -name = "pluggy" -version = "1.4.0" -description = "plugin and hook calling mechanisms for python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pluggy-1.4.0-py3-none-any.whl", hash = "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981"}, - {file = "pluggy-1.4.0.tar.gz", hash = "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"}, -] - -[package.extras] -dev = ["pre-commit", "tox"] -testing = ["pytest", "pytest-benchmark"] - -[[package]] -name = "poethepoet" -version = "0.25.1" -description = "A task runner that works well with poetry." -optional = false -python-versions = ">=3.8" -files = [ - {file = "poethepoet-0.25.1-py3-none-any.whl", hash = "sha256:fee433f68424593bca6b357f0bf997d64edf42c7305c0d5d335bd570b8d2352b"}, - {file = "poethepoet-0.25.1.tar.gz", hash = "sha256:98f4446533a4b2bdb08843e211f918b1f2e7f8baf6d1803ef78f64661ed62463"}, -] - -[package.dependencies] -pastel = ">=0.2.1,<0.3.0" -tomli = ">=1.2.2" - -[package.extras] -poetry-plugin = ["poetry (>=1.0,<2.0)"] - -[[package]] -name = "prometheus-client" -version = "0.20.0" -description = "Python client for the Prometheus monitoring system." -optional = false -python-versions = ">=3.8" -files = [ - {file = "prometheus_client-0.20.0-py3-none-any.whl", hash = "sha256:cde524a85bce83ca359cc837f28b8c0db5cac7aa653a588fd7e84ba061c329e7"}, - {file = "prometheus_client-0.20.0.tar.gz", hash = "sha256:287629d00b147a32dcb2be0b9df905da599b2d82f80377083ec8463309a4bb89"}, -] - -[package.extras] -twisted = ["twisted"] - -[[package]] -name = "prompt-toolkit" -version = "3.0.43" -description = "Library for building powerful interactive command lines in Python" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "prompt_toolkit-3.0.43-py3-none-any.whl", hash = "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"}, - {file = "prompt_toolkit-3.0.43.tar.gz", hash = "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d"}, -] - -[package.dependencies] -wcwidth = "*" - -[[package]] -name = "protobuf" -version = "5.26.1" -description = "" -optional = false -python-versions = ">=3.8" -files = [ - {file = "protobuf-5.26.1-cp310-abi3-win32.whl", hash = "sha256:3c388ea6ddfe735f8cf69e3f7dc7611e73107b60bdfcf5d0f024c3ccd3794e23"}, - {file = "protobuf-5.26.1-cp310-abi3-win_amd64.whl", hash = "sha256:e6039957449cb918f331d32ffafa8eb9255769c96aa0560d9a5bf0b4e00a2a33"}, - {file = "protobuf-5.26.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:38aa5f535721d5bb99861166c445c4105c4e285c765fbb2ac10f116e32dcd46d"}, - {file = "protobuf-5.26.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:fbfe61e7ee8c1860855696e3ac6cfd1b01af5498facc6834fcc345c9684fb2ca"}, - {file = "protobuf-5.26.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:f7417703f841167e5a27d48be13389d52ad705ec09eade63dfc3180a959215d7"}, - {file = "protobuf-5.26.1-cp38-cp38-win32.whl", hash = "sha256:d693d2504ca96750d92d9de8a103102dd648fda04540495535f0fec7577ed8fc"}, - {file = "protobuf-5.26.1-cp38-cp38-win_amd64.whl", hash = "sha256:9b557c317ebe6836835ec4ef74ec3e994ad0894ea424314ad3552bc6e8835b4e"}, - {file = "protobuf-5.26.1-cp39-cp39-win32.whl", hash = "sha256:b9ba3ca83c2e31219ffbeb9d76b63aad35a3eb1544170c55336993d7a18ae72c"}, - {file = "protobuf-5.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:7ee014c2c87582e101d6b54260af03b6596728505c79f17c8586e7523aaa8f8c"}, - {file = "protobuf-5.26.1-py3-none-any.whl", hash = "sha256:da612f2720c0183417194eeaa2523215c4fcc1a1949772dc65f05047e08d5932"}, - {file = "protobuf-5.26.1.tar.gz", hash = "sha256:8ca2a1d97c290ec7b16e4e5dff2e5ae150cc1582f55b5ab300d45cb0dfa90e51"}, -] - -[[package]] -name = "psutil" -version = "5.9.8" -description = "Cross-platform lib for process and system monitoring in Python." -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -files = [ - {file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"}, - {file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"}, - {file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"}, - {file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"}, - {file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"}, - {file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"}, - {file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"}, - {file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"}, - {file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"}, - {file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"}, - {file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"}, - {file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"}, - {file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"}, -] - -[package.extras] -test = ["enum34", "ipaddress", "mock", "pywin32", "wmi"] - -[[package]] -name = "ptyprocess" -version = "0.7.0" -description = "Run a subprocess in a pseudo terminal" -optional = false -python-versions = "*" -files = [ - {file = "ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35"}, - {file = "ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220"}, -] - -[[package]] -name = "pure-eval" -version = "0.2.2" -description = "Safely evaluate AST nodes without side effects" -optional = false -python-versions = "*" -files = [ - {file = "pure_eval-0.2.2-py3-none-any.whl", hash = "sha256:01eaab343580944bc56080ebe0a674b39ec44a945e6d09ba7db3cb8cec289350"}, - {file = "pure_eval-0.2.2.tar.gz", hash = "sha256:2b45320af6dfaa1750f543d714b6d1c520a1688dec6fd24d339063ce0aaa9ac3"}, -] - -[package.extras] -tests = ["pytest"] - -[[package]] -name = "pyarrow" -version = "15.0.2" -description = "Python library for Apache Arrow" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pyarrow-15.0.2-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:88b340f0a1d05b5ccc3d2d986279045655b1fe8e41aba6ca44ea28da0d1455d8"}, - {file = "pyarrow-15.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eaa8f96cecf32da508e6c7f69bb8401f03745c050c1dd42ec2596f2e98deecac"}, - {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23c6753ed4f6adb8461e7c383e418391b8d8453c5d67e17f416c3a5d5709afbd"}, - {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f639c059035011db8c0497e541a8a45d98a58dbe34dc8fadd0ef128f2cee46e5"}, - {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:290e36a59a0993e9a5224ed2fb3e53375770f07379a0ea03ee2fce2e6d30b423"}, - {file = "pyarrow-15.0.2-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:06c2bb2a98bc792f040bef31ad3e9be6a63d0cb39189227c08a7d955db96816e"}, - {file = "pyarrow-15.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:f7a197f3670606a960ddc12adbe8075cea5f707ad7bf0dffa09637fdbb89f76c"}, - {file = "pyarrow-15.0.2-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:5f8bc839ea36b1f99984c78e06e7a06054693dc2af8920f6fb416b5bca9944e4"}, - {file = "pyarrow-15.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f5e81dfb4e519baa6b4c80410421528c214427e77ca0ea9461eb4097c328fa33"}, - {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a4f240852b302a7af4646c8bfe9950c4691a419847001178662a98915fd7ee7"}, - {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e7d9cfb5a1e648e172428c7a42b744610956f3b70f524aa3a6c02a448ba853e"}, - {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:2d4f905209de70c0eb5b2de6763104d5a9a37430f137678edfb9a675bac9cd98"}, - {file = "pyarrow-15.0.2-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:90adb99e8ce5f36fbecbbc422e7dcbcbed07d985eed6062e459e23f9e71fd197"}, - {file = "pyarrow-15.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:b116e7fd7889294cbd24eb90cd9bdd3850be3738d61297855a71ac3b8124ee38"}, - {file = "pyarrow-15.0.2-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:25335e6f1f07fdaa026a61c758ee7d19ce824a866b27bba744348fa73bb5a440"}, - {file = "pyarrow-15.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:90f19e976d9c3d8e73c80be84ddbe2f830b6304e4c576349d9360e335cd627fc"}, - {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a22366249bf5fd40ddacc4f03cd3160f2d7c247692945afb1899bab8a140ddfb"}, - {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2a335198f886b07e4b5ea16d08ee06557e07db54a8400cc0d03c7f6a22f785f"}, - {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:3e6d459c0c22f0b9c810a3917a1de3ee704b021a5fb8b3bacf968eece6df098f"}, - {file = "pyarrow-15.0.2-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:033b7cad32198754d93465dcfb71d0ba7cb7cd5c9afd7052cab7214676eec38b"}, - {file = "pyarrow-15.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:29850d050379d6e8b5a693098f4de7fd6a2bea4365bfd073d7c57c57b95041ee"}, - {file = "pyarrow-15.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:7167107d7fb6dcadb375b4b691b7e316f4368f39f6f45405a05535d7ad5e5058"}, - {file = "pyarrow-15.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:e85241b44cc3d365ef950432a1b3bd44ac54626f37b2e3a0cc89c20e45dfd8bf"}, - {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:248723e4ed3255fcd73edcecc209744d58a9ca852e4cf3d2577811b6d4b59818"}, - {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ff3bdfe6f1b81ca5b73b70a8d482d37a766433823e0c21e22d1d7dde76ca33f"}, - {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_28_aarch64.whl", hash = "sha256:f3d77463dee7e9f284ef42d341689b459a63ff2e75cee2b9302058d0d98fe142"}, - {file = "pyarrow-15.0.2-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:8c1faf2482fb89766e79745670cbca04e7018497d85be9242d5350cba21357e1"}, - {file = "pyarrow-15.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:28f3016958a8e45a1069303a4a4f6a7d4910643fc08adb1e2e4a7ff056272ad3"}, - {file = "pyarrow-15.0.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:89722cb64286ab3d4daf168386f6968c126057b8c7ec3ef96302e81d8cdb8ae4"}, - {file = "pyarrow-15.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cd0ba387705044b3ac77b1b317165c0498299b08261d8122c96051024f953cd5"}, - {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad2459bf1f22b6a5cdcc27ebfd99307d5526b62d217b984b9f5c974651398832"}, - {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58922e4bfece8b02abf7159f1f53a8f4d9f8e08f2d988109126c17c3bb261f22"}, - {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:adccc81d3dc0478ea0b498807b39a8d41628fa9210729b2f718b78cb997c7c91"}, - {file = "pyarrow-15.0.2-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:8bd2baa5fe531571847983f36a30ddbf65261ef23e496862ece83bdceb70420d"}, - {file = "pyarrow-15.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:6669799a1d4ca9da9c7e06ef48368320f5856f36f9a4dd31a11839dda3f6cc8c"}, - {file = "pyarrow-15.0.2.tar.gz", hash = "sha256:9c9bc803cb3b7bfacc1e96ffbfd923601065d9d3f911179d81e72d99fd74a3d9"}, -] - -[package.dependencies] -numpy = ">=1.16.6,<2" - -[[package]] -name = "pyarrow-hotfix" -version = "0.6" -description = "" -optional = false -python-versions = ">=3.5" -files = [ - {file = "pyarrow_hotfix-0.6-py3-none-any.whl", hash = "sha256:dcc9ae2d220dff0083be6a9aa8e0cdee5182ad358d4931fce825c545e5c89178"}, - {file = "pyarrow_hotfix-0.6.tar.gz", hash = "sha256:79d3e030f7ff890d408a100ac16d6f00b14d44a502d7897cd9fc3e3a534e9945"}, -] - -[[package]] -name = "pycnite" -version = "2023.10.11" -description = "Python bytecode utilities" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycnite-2023.10.11-py3-none-any.whl", hash = "sha256:7d02eb0ec4b405d8812ce053434dacfc2335dcd458ab58a1a8bf64f72d40bd76"}, - {file = "pycnite-2023.10.11.tar.gz", hash = "sha256:ad8616982beecc39f2090999aa8fe0b044b1f6733ec39484cb5e0900b3c88aa1"}, -] - -[[package]] -name = "pycparser" -version = "2.22" -description = "C parser in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc"}, - {file = "pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6"}, -] - -[[package]] -name = "pydantic" -version = "2.7.0" -description = "Data validation using Python type hints" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic-2.7.0-py3-none-any.whl", hash = "sha256:9dee74a271705f14f9a1567671d144a851c675b072736f0a7b2608fd9e495352"}, - {file = "pydantic-2.7.0.tar.gz", hash = "sha256:b5ecdd42262ca2462e2624793551e80911a1e989f462910bb81aef974b4bb383"}, -] - -[package.dependencies] -annotated-types = ">=0.4.0" -email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} -pydantic-core = "2.18.1" -typing-extensions = ">=4.6.1" - -[package.extras] -email = ["email-validator (>=2.0.0)"] - -[[package]] -name = "pydantic-core" -version = "2.18.1" -description = "Core functionality for Pydantic validation and serialization" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pydantic_core-2.18.1-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ee9cf33e7fe14243f5ca6977658eb7d1042caaa66847daacbd2117adb258b226"}, - {file = "pydantic_core-2.18.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6b7bbb97d82659ac8b37450c60ff2e9f97e4eb0f8a8a3645a5568b9334b08b50"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:df4249b579e75094f7e9bb4bd28231acf55e308bf686b952f43100a5a0be394c"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d0491006a6ad20507aec2be72e7831a42efc93193d2402018007ff827dc62926"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ae80f72bb7a3e397ab37b53a2b49c62cc5496412e71bc4f1277620a7ce3f52b"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58aca931bef83217fca7a390e0486ae327c4af9c3e941adb75f8772f8eeb03a1"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1be91ad664fc9245404a789d60cba1e91c26b1454ba136d2a1bf0c2ac0c0505a"}, - {file = "pydantic_core-2.18.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:667880321e916a8920ef49f5d50e7983792cf59f3b6079f3c9dac2b88a311d17"}, - {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:f7054fdc556f5421f01e39cbb767d5ec5c1139ea98c3e5b350e02e62201740c7"}, - {file = "pydantic_core-2.18.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:030e4f9516f9947f38179249778709a460a3adb516bf39b5eb9066fcfe43d0e6"}, - {file = "pydantic_core-2.18.1-cp310-none-win32.whl", hash = "sha256:2e91711e36e229978d92642bfc3546333a9127ecebb3f2761372e096395fc649"}, - {file = "pydantic_core-2.18.1-cp310-none-win_amd64.whl", hash = "sha256:9a29726f91c6cb390b3c2338f0df5cd3e216ad7a938762d11c994bb37552edb0"}, - {file = "pydantic_core-2.18.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:9ece8a49696669d483d206b4474c367852c44815fca23ac4e48b72b339807f80"}, - {file = "pydantic_core-2.18.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7a5d83efc109ceddb99abd2c1316298ced2adb4570410defe766851a804fcd5b"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5f7973c381283783cd1043a8c8f61ea5ce7a3a58b0369f0ee0ee975eaf2f2a1b"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:54c7375c62190a7845091f521add19b0f026bcf6ae674bdb89f296972272e86d"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd63cec4e26e790b70544ae5cc48d11b515b09e05fdd5eff12e3195f54b8a586"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:561cf62c8a3498406495cfc49eee086ed2bb186d08bcc65812b75fda42c38294"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68717c38a68e37af87c4da20e08f3e27d7e4212e99e96c3d875fbf3f4812abfc"}, - {file = "pydantic_core-2.18.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2d5728e93d28a3c63ee513d9ffbac9c5989de8c76e049dbcb5bfe4b923a9739d"}, - {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f0f17814c505f07806e22b28856c59ac80cee7dd0fbb152aed273e116378f519"}, - {file = "pydantic_core-2.18.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d816f44a51ba5175394bc6c7879ca0bd2be560b2c9e9f3411ef3a4cbe644c2e9"}, - {file = "pydantic_core-2.18.1-cp311-none-win32.whl", hash = "sha256:09f03dfc0ef8c22622eaa8608caa4a1e189cfb83ce847045eca34f690895eccb"}, - {file = "pydantic_core-2.18.1-cp311-none-win_amd64.whl", hash = "sha256:27f1009dc292f3b7ca77feb3571c537276b9aad5dd4efb471ac88a8bd09024e9"}, - {file = "pydantic_core-2.18.1-cp311-none-win_arm64.whl", hash = "sha256:48dd883db92e92519201f2b01cafa881e5f7125666141a49ffba8b9facc072b0"}, - {file = "pydantic_core-2.18.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:b6b0e4912030c6f28bcb72b9ebe4989d6dc2eebcd2a9cdc35fefc38052dd4fe8"}, - {file = "pydantic_core-2.18.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f3202a429fe825b699c57892d4371c74cc3456d8d71b7f35d6028c96dfecad31"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3982b0a32d0a88b3907e4b0dc36809fda477f0757c59a505d4e9b455f384b8b"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:25595ac311f20e5324d1941909b0d12933f1fd2171075fcff763e90f43e92a0d"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14fe73881cf8e4cbdaded8ca0aa671635b597e42447fec7060d0868b52d074e6"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca976884ce34070799e4dfc6fbd68cb1d181db1eefe4a3a94798ddfb34b8867f"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:684d840d2c9ec5de9cb397fcb3f36d5ebb6fa0d94734f9886032dd796c1ead06"}, - {file = "pydantic_core-2.18.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:54764c083bbe0264f0f746cefcded6cb08fbbaaf1ad1d78fb8a4c30cff999a90"}, - {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:201713f2f462e5c015b343e86e68bd8a530a4f76609b33d8f0ec65d2b921712a"}, - {file = "pydantic_core-2.18.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fd1a9edb9dd9d79fbeac1ea1f9a8dd527a6113b18d2e9bcc0d541d308dae639b"}, - {file = "pydantic_core-2.18.1-cp312-none-win32.whl", hash = "sha256:d5e6b7155b8197b329dc787356cfd2684c9d6a6b1a197f6bbf45f5555a98d411"}, - {file = "pydantic_core-2.18.1-cp312-none-win_amd64.whl", hash = "sha256:9376d83d686ec62e8b19c0ac3bf8d28d8a5981d0df290196fb6ef24d8a26f0d6"}, - {file = "pydantic_core-2.18.1-cp312-none-win_arm64.whl", hash = "sha256:c562b49c96906b4029b5685075fe1ebd3b5cc2601dfa0b9e16c2c09d6cbce048"}, - {file = "pydantic_core-2.18.1-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:3e352f0191d99fe617371096845070dee295444979efb8f27ad941227de6ad09"}, - {file = "pydantic_core-2.18.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0295d52b012cbe0d3059b1dba99159c3be55e632aae1999ab74ae2bd86a33d7"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56823a92075780582d1ffd4489a2e61d56fd3ebb4b40b713d63f96dd92d28144"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dd3f79e17b56741b5177bcc36307750d50ea0698df6aa82f69c7db32d968c1c2"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:38a5024de321d672a132b1834a66eeb7931959c59964b777e8f32dbe9523f6b1"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ce426ee691319d4767748c8e0895cfc56593d725594e415f274059bcf3cb76"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2adaeea59849ec0939af5c5d476935f2bab4b7f0335b0110f0f069a41024278e"}, - {file = "pydantic_core-2.18.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:9b6431559676a1079eac0f52d6d0721fb8e3c5ba43c37bc537c8c83724031feb"}, - {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:85233abb44bc18d16e72dc05bf13848a36f363f83757541f1a97db2f8d58cfd9"}, - {file = "pydantic_core-2.18.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:641a018af4fe48be57a2b3d7a1f0f5dbca07c1d00951d3d7463f0ac9dac66622"}, - {file = "pydantic_core-2.18.1-cp38-none-win32.whl", hash = "sha256:63d7523cd95d2fde0d28dc42968ac731b5bb1e516cc56b93a50ab293f4daeaad"}, - {file = "pydantic_core-2.18.1-cp38-none-win_amd64.whl", hash = "sha256:907a4d7720abfcb1c81619863efd47c8a85d26a257a2dbebdb87c3b847df0278"}, - {file = "pydantic_core-2.18.1-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:aad17e462f42ddbef5984d70c40bfc4146c322a2da79715932cd8976317054de"}, - {file = "pydantic_core-2.18.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:94b9769ba435b598b547c762184bcfc4783d0d4c7771b04a3b45775c3589ca44"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80e0e57cc704a52fb1b48f16d5b2c8818da087dbee6f98d9bf19546930dc64b5"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76b86e24039c35280ceee6dce7e62945eb93a5175d43689ba98360ab31eebc4a"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:12a05db5013ec0ca4a32cc6433f53faa2a014ec364031408540ba858c2172bb0"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:250ae39445cb5475e483a36b1061af1bc233de3e9ad0f4f76a71b66231b07f88"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a32204489259786a923e02990249c65b0f17235073149d0033efcebe80095570"}, - {file = "pydantic_core-2.18.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6395a4435fa26519fd96fdccb77e9d00ddae9dd6c742309bd0b5610609ad7fb2"}, - {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2533ad2883f001efa72f3d0e733fb846710c3af6dcdd544fe5bf14fa5fe2d7db"}, - {file = "pydantic_core-2.18.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b560b72ed4816aee52783c66854d96157fd8175631f01ef58e894cc57c84f0f6"}, - {file = "pydantic_core-2.18.1-cp39-none-win32.whl", hash = "sha256:582cf2cead97c9e382a7f4d3b744cf0ef1a6e815e44d3aa81af3ad98762f5a9b"}, - {file = "pydantic_core-2.18.1-cp39-none-win_amd64.whl", hash = "sha256:ca71d501629d1fa50ea7fa3b08ba884fe10cefc559f5c6c8dfe9036c16e8ae89"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e178e5b66a06ec5bf51668ec0d4ac8cfb2bdcb553b2c207d58148340efd00143"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:72722ce529a76a4637a60be18bd789d8fb871e84472490ed7ddff62d5fed620d"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fe0c1ce5b129455e43f941f7a46f61f3d3861e571f2905d55cdbb8b5c6f5e2c"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4284c621f06a72ce2cb55f74ea3150113d926a6eb78ab38340c08f770eb9b4d"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a0c3e718f4e064efde68092d9d974e39572c14e56726ecfaeebbe6544521f47"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2027493cc44c23b598cfaf200936110433d9caa84e2c6cf487a83999638a96ac"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:76909849d1a6bffa5a07742294f3fa1d357dc917cb1fe7b470afbc3a7579d539"}, - {file = "pydantic_core-2.18.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ee7ccc7fb7e921d767f853b47814c3048c7de536663e82fbc37f5eb0d532224b"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ee2794111c188548a4547eccc73a6a8527fe2af6cf25e1a4ebda2fd01cdd2e60"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:a139fe9f298dc097349fb4f28c8b81cc7a202dbfba66af0e14be5cfca4ef7ce5"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d074b07a10c391fc5bbdcb37b2f16f20fcd9e51e10d01652ab298c0d07908ee2"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c69567ddbac186e8c0aadc1f324a60a564cfe25e43ef2ce81bcc4b8c3abffbae"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:baf1c7b78cddb5af00971ad5294a4583188bda1495b13760d9f03c9483bb6203"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2684a94fdfd1b146ff10689c6e4e815f6a01141781c493b97342cdc5b06f4d5d"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:73c1bc8a86a5c9e8721a088df234265317692d0b5cd9e86e975ce3bc3db62a59"}, - {file = "pydantic_core-2.18.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:e60defc3c15defb70bb38dd605ff7e0fae5f6c9c7cbfe0ad7868582cb7e844a6"}, - {file = "pydantic_core-2.18.1.tar.gz", hash = "sha256:de9d3e8717560eb05e28739d1b35e4eac2e458553a52a301e51352a7ffc86a35"}, -] - -[package.dependencies] -typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" - -[[package]] -name = "pydot" -version = "2.0.0" -description = "Python interface to Graphviz's Dot" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pydot-2.0.0-py3-none-any.whl", hash = "sha256:408a47913ea7bd5d2d34b274144880c1310c4aee901f353cf21fe2e526a4ea28"}, - {file = "pydot-2.0.0.tar.gz", hash = "sha256:60246af215123fa062f21cd791be67dda23a6f280df09f68919e637a1e4f3235"}, -] - -[package.dependencies] -pyparsing = ">=3" - -[package.extras] -dev = ["black", "chardet"] -release = ["zest.releaser[recommended]"] -tests = ["black", "chardet", "tox"] - -[[package]] -name = "pygments" -version = "2.17.2" -description = "Pygments is a syntax highlighting package written in Python." -optional = false -python-versions = ">=3.7" -files = [ - {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, - {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, -] - -[package.extras] -plugins = ["importlib-metadata"] -windows-terminal = ["colorama (>=0.4.6)"] - -[[package]] -name = "pyjwt" -version = "2.8.0" -description = "JSON Web Token implementation in Python" -optional = false -python-versions = ">=3.7" -files = [ - {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, - {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, -] - -[package.extras] -crypto = ["cryptography (>=3.4.0)"] -dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] -tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] - -[[package]] -name = "pynvml" -version = "11.5.0" -description = "Python Bindings for the NVIDIA Management Library" -optional = false -python-versions = ">=3.6" -files = [ - {file = "pynvml-11.5.0-py3-none-any.whl", hash = "sha256:5cce014ac01b098d08f06178f86c37be409b80b2e903a5a03ce15eed60f55e25"}, - {file = "pynvml-11.5.0.tar.gz", hash = "sha256:d027b21b95b1088b9fc278117f9f61b7c67f8e33a787e9f83f735f0f71ac32d0"}, -] - -[[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 = "8.1.1" -description = "pytest: simple powerful testing with Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytest-8.1.1-py3-none-any.whl", hash = "sha256:2a8386cfc11fa9d2c50ee7b2a57e7d898ef90470a7a34c4b949ff59662bb78b7"}, - {file = "pytest-8.1.1.tar.gz", hash = "sha256:ac978141a75948948817d360297b7aae0fcb9d6ff6bc9ec6d514b85d5a65c044"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "sys_platform == \"win32\""} -exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} -iniconfig = "*" -packaging = "*" -pluggy = ">=1.4,<2.0" -tomli = {version = ">=1", markers = "python_version < \"3.11\""} - -[package.extras] -testing = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] - -[[package]] -name = "pytest-mock" -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.14.0.tar.gz", hash = "sha256:2719255a1efeceadbc056d6bf3df3d1c5015530fb40cf347c0f9afac88410bd0"}, - {file = "pytest_mock-3.14.0-py3-none-any.whl", hash = "sha256:0b72c38033392a5f4621342fe11e9219ac11ec9d375f8e2a0c164539e0d70f6f"}, -] - -[package.dependencies] -pytest = ">=6.2.5" - -[package.extras] -dev = ["pre-commit", "pytest-asyncio", "tox"] - -[[package]] -name = "python-dateutil" -version = "2.9.0.post0" -description = "Extensions to the standard Python datetime module" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" -files = [ - {file = "python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3"}, - {file = "python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427"}, -] - -[package.dependencies] -six = ">=1.5" - -[[package]] -name = "python-dotenv" -version = "1.0.1" -description = "Read key-value pairs from a .env file and set them as environment variables" -optional = false -python-versions = ">=3.8" -files = [ - {file = "python-dotenv-1.0.1.tar.gz", hash = "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca"}, - {file = "python_dotenv-1.0.1-py3-none-any.whl", hash = "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"}, -] - -[package.extras] -cli = ["click (>=5.0)"] - -[[package]] -name = "python-json-logger" -version = "2.0.7" -description = "A python library adding a json log formatter" -optional = false -python-versions = ">=3.6" -files = [ - {file = "python-json-logger-2.0.7.tar.gz", hash = "sha256:23e7ec02d34237c5aa1e29a070193a4ea87583bb4e7f8fd06d3de8264c4b2e1c"}, - {file = "python_json_logger-2.0.7-py3-none-any.whl", hash = "sha256:f380b826a991ebbe3de4d897aeec42760035ac760345e57b812938dc8b35e2bd"}, -] - -[[package]] -name = "pytype" -version = "2024.4.11" -description = "Python type inferencer" -optional = false -python-versions = ">=3.8" -files = [ - {file = "pytype-2024.4.11-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b3647b554eea009c9069d58440aed6b9e66d2f3ba20ef2674ffd5cd3b0bf83d7"}, - {file = "pytype-2024.4.11-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fd24126c5b7bbda52fb48ad9d5b4811a7b090a944c8f39a45b94246668722e8c"}, - {file = "pytype-2024.4.11-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ae6c825b7067f420810f8846147df4fdc966c340a859f51317ba26c860cda906"}, - {file = "pytype-2024.4.11-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b140b180cb287bbbce025e4c929cf3b020aecf4f91899ef609073ab02f2ba8e2"}, - {file = "pytype-2024.4.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a3483a44f457766be85c2a2ef779f3c23f2a95973e5fdb844a030149fc1d92e3"}, - {file = "pytype-2024.4.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2986961d1a2ef778a9fea2f81c90cbe6f4a196ff783b26a30e7f3c9be1545c10"}, - {file = "pytype-2024.4.11-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:ad59ed48b185d61f5ab8bceb173e6680eeabf109c5f15290ad88b7be1096e493"}, - {file = "pytype-2024.4.11-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50da73a6a24337d00c9c88697ced74909dd00e3b9238ad619490ff83a6588d52"}, - {file = "pytype-2024.4.11-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51c5470061ace9908a8306e3aa460e89e8fc15e63557625c387f932b852d3fa8"}, - {file = "pytype-2024.4.11-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:4484bbe24c58c289cea0c7549a507f20eea29caa68dcaf1e857eba584324641e"}, - {file = "pytype-2024.4.11-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ad2f7cbfc9843a3f6f1d776318a2f4d96b0ca2d054128280be58eef293c0795"}, - {file = "pytype-2024.4.11-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa1069fcaaa9859273ec31650632e7bba9af0e82083e8f7d08f9b742d6d36055"}, - {file = "pytype-2024.4.11.tar.gz", hash = "sha256:3315f51ce482d8c07e2f1857c47ccd17beb4a9bfde9afa7d1e6669f1e8881081"}, -] - -[package.dependencies] -attrs = ">=21.4.0" -immutabledict = ">=4.1.0" -importlab = ">=0.8" -jinja2 = ">=3.1.2" -libcst = ">=1.0.1" -msgspec = ">=0.18.6" -networkx = "<3.2" -ninja = ">=1.10.0.post2" -pycnite = ">=2023.10.11" -pydot = ">=1.4.2" -tabulate = ">=0.8.10" -toml = ">=0.10.2" -typing-extensions = ">=4.3.0" - -[[package]] -name = "pytz" -version = "2024.1" -description = "World timezone definitions, modern and historical" -optional = false -python-versions = "*" -files = [ - {file = "pytz-2024.1-py2.py3-none-any.whl", hash = "sha256:328171f4e3623139da4983451950b28e95ac706e13f3f2630a879749e7a8b319"}, - {file = "pytz-2024.1.tar.gz", hash = "sha256:2a29735ea9c18baf14b448846bde5a48030ed267578472d8955cd0e7443a9812"}, -] - -[[package]] -name = "pywin32" -version = "306" -description = "Python for Window Extensions" -optional = false -python-versions = "*" -files = [ - {file = "pywin32-306-cp310-cp310-win32.whl", hash = "sha256:06d3420a5155ba65f0b72f2699b5bacf3109f36acbe8923765c22938a69dfc8d"}, - {file = "pywin32-306-cp310-cp310-win_amd64.whl", hash = "sha256:84f4471dbca1887ea3803d8848a1616429ac94a4a8d05f4bc9c5dcfd42ca99c8"}, - {file = "pywin32-306-cp311-cp311-win32.whl", hash = "sha256:e65028133d15b64d2ed8f06dd9fbc268352478d4f9289e69c190ecd6818b6407"}, - {file = "pywin32-306-cp311-cp311-win_amd64.whl", hash = "sha256:a7639f51c184c0272e93f244eb24dafca9b1855707d94c192d4a0b4c01e1100e"}, - {file = "pywin32-306-cp311-cp311-win_arm64.whl", hash = "sha256:70dba0c913d19f942a2db25217d9a1b726c278f483a919f1abfed79c9cf64d3a"}, - {file = "pywin32-306-cp312-cp312-win32.whl", hash = "sha256:383229d515657f4e3ed1343da8be101000562bf514591ff383ae940cad65458b"}, - {file = "pywin32-306-cp312-cp312-win_amd64.whl", hash = "sha256:37257794c1ad39ee9be652da0462dc2e394c8159dfd913a8a4e8eb6fd346da0e"}, - {file = "pywin32-306-cp312-cp312-win_arm64.whl", hash = "sha256:5821ec52f6d321aa59e2db7e0a35b997de60c201943557d108af9d4ae1ec7040"}, - {file = "pywin32-306-cp37-cp37m-win32.whl", hash = "sha256:1c73ea9a0d2283d889001998059f5eaaba3b6238f767c9cf2833b13e6a685f65"}, - {file = "pywin32-306-cp37-cp37m-win_amd64.whl", hash = "sha256:72c5f621542d7bdd4fdb716227be0dd3f8565c11b280be6315b06ace35487d36"}, - {file = "pywin32-306-cp38-cp38-win32.whl", hash = "sha256:e4c092e2589b5cf0d365849e73e02c391c1349958c5ac3e9d5ccb9a28e017b3a"}, - {file = "pywin32-306-cp38-cp38-win_amd64.whl", hash = "sha256:e8ac1ae3601bee6ca9f7cb4b5363bf1c0badb935ef243c4733ff9a393b1690c0"}, - {file = "pywin32-306-cp39-cp39-win32.whl", hash = "sha256:e25fd5b485b55ac9c057f67d94bc203f3f6595078d1fb3b458c9c28b7153a802"}, - {file = "pywin32-306-cp39-cp39-win_amd64.whl", hash = "sha256:39b61c15272833b5c329a2989999dcae836b1eed650252ab1b7bfbe1d59f30f4"}, -] - -[[package]] -name = "pywinpty" -version = "2.0.13" -description = "Pseudo terminal support for Windows from Python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "pywinpty-2.0.13-cp310-none-win_amd64.whl", hash = "sha256:697bff211fb5a6508fee2dc6ff174ce03f34a9a233df9d8b5fe9c8ce4d5eaf56"}, - {file = "pywinpty-2.0.13-cp311-none-win_amd64.whl", hash = "sha256:b96fb14698db1284db84ca38c79f15b4cfdc3172065b5137383910567591fa99"}, - {file = "pywinpty-2.0.13-cp312-none-win_amd64.whl", hash = "sha256:2fd876b82ca750bb1333236ce98488c1be96b08f4f7647cfdf4129dfad83c2d4"}, - {file = "pywinpty-2.0.13-cp38-none-win_amd64.whl", hash = "sha256:61d420c2116c0212808d31625611b51caf621fe67f8a6377e2e8b617ea1c1f7d"}, - {file = "pywinpty-2.0.13-cp39-none-win_amd64.whl", hash = "sha256:71cb613a9ee24174730ac7ae439fd179ca34ccb8c5349e8d7b72ab5dea2c6f4b"}, - {file = "pywinpty-2.0.13.tar.gz", hash = "sha256:c34e32351a3313ddd0d7da23d27f835c860d32fe4ac814d372a3ea9594f41dde"}, -] - -[[package]] -name = "pyyaml" -version = "6.0.1" -description = "YAML parser and emitter for Python" -optional = false -python-versions = ">=3.6" -files = [ - {file = "PyYAML-6.0.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a"}, - {file = "PyYAML-6.0.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, - {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, - {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, - {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, - {file = "PyYAML-6.0.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, - {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, - {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, - {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_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"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, - {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, - {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win32.whl", hash = "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585"}, - {file = "PyYAML-6.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa"}, - {file = "PyYAML-6.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3"}, - {file = "PyYAML-6.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win32.whl", hash = "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba"}, - {file = "PyYAML-6.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867"}, - {file = "PyYAML-6.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, - {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, - {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, - {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, - {file = "PyYAML-6.0.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, - {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, - {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, - {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, - {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, -] - -[[package]] -name = "pyzmq" -version = "26.0.0" -description = "Python bindings for 0MQ" -optional = false -python-versions = ">=3.7" -files = [ - {file = "pyzmq-26.0.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:a86409f3f8eae7af5a47babd831a119bdf552e831f04d2225a313305e8e35e7c"}, - {file = "pyzmq-26.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d36a46975925b8bf14b69fe6d4097bc96c91f94ceb954d56853a2211a5cc3433"}, - {file = "pyzmq-26.0.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcac700269d081ded42ed3833f9d0effe734148376204af9c0ef0fd25a3fea55"}, - {file = "pyzmq-26.0.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:49efc420e36d2e8adc5dae41c2c1e8bb37a069e40a880cbe414a032136b194b0"}, - {file = "pyzmq-26.0.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02773b96ef6a17a57680c3609645785c390198be31a4505c01ce0c846f9e7d0e"}, - {file = "pyzmq-26.0.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:ce2c53f4963a358ba91b58ccecb84fab6d5f0622230d105c2589f7556ec53cc9"}, - {file = "pyzmq-26.0.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:06525d996afdb0da3e8b7df0b654261455f6e86c2c3574c3f00d2bd335be78eb"}, - {file = "pyzmq-26.0.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bd3537f049dc0488adb3df29a77635eaff2a8d1d3d29a09714db6e2d10caba1a"}, - {file = "pyzmq-26.0.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9ce158ab54994c60fdde83300dc1e447446baacbe4ec9e4e80096f9b9a125c13"}, - {file = "pyzmq-26.0.0-cp310-cp310-win32.whl", hash = "sha256:271c9178a94b009651f8ad3ff9bb9ca45778aaf66c9e325a44d81a7498fcaa59"}, - {file = "pyzmq-26.0.0-cp310-cp310-win_amd64.whl", hash = "sha256:4216eee101d104a017042f0e4af0a45875400ff3794f1a59476e210b1a9760e2"}, - {file = "pyzmq-26.0.0-cp310-cp310-win_arm64.whl", hash = "sha256:44271793067025a07d38ad4be11f08187cce850fafd1890b42046abbcdca2fc0"}, - {file = "pyzmq-26.0.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:1e87178437460b6df18e761650ef080d3ad5a41813cc3df7f9fd78714fca04c0"}, - {file = "pyzmq-26.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0397c7431f3fc2bac497992d7447b036bc0d8bb3e15b158b2013201857ff2354"}, - {file = "pyzmq-26.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a5b4dc4d7a3f859026083906724ad1ae743261548b61d0d5abcf2d994122c2b"}, - {file = "pyzmq-26.0.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:952e85c5e86f9ba100b78b60719b76e1ff3e13bb403cb6de687bb92e7b2179e7"}, - {file = "pyzmq-26.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07fdeac8612a9dca6fcad6cb43c7efb75f53ba75da981fbafa949ddcde1d5662"}, - {file = "pyzmq-26.0.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:39b8ed8d2e5da8b8351c6aa627601b3b52e8eb5e25cf6bcd26b6f012dec7870b"}, - {file = "pyzmq-26.0.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f6f618d7d7c9c37053a36e6dc5435c53e9e0c7a67e6fd00b69c209d07a8db4dc"}, - {file = "pyzmq-26.0.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:72ae3078b1c47552e0e39fd81fc0472e880316897a733dbb3570819be19da48a"}, - {file = "pyzmq-26.0.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5d7fcc648445dbfd6ce9973ec7b4a33ee9307b7e88cf4816f4403ccbaf8de9ca"}, - {file = "pyzmq-26.0.0-cp311-cp311-win32.whl", hash = "sha256:9982799d7d7807beb1b26f1aa9a192baccb1a14c5d00eca881a42a0ae562671b"}, - {file = "pyzmq-26.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:60f91afc76a3fc5d65dfba4f6b6020c462674b5eab6cbf00dec133d79656072d"}, - {file = "pyzmq-26.0.0-cp311-cp311-win_arm64.whl", hash = "sha256:120887d773e878136e9b33bbba656df0d4c6e2861694d07d058ec60ce1108b24"}, - {file = "pyzmq-26.0.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:469f4febd63c26b20132e54cc40048d5698123794b103758ccd21b8a45890dc3"}, - {file = "pyzmq-26.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c919895132cae5a458d5a17047fd33c9eb271f15bb3485add34429cfd7b76a71"}, - {file = "pyzmq-26.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e0e94ca9a8f23000d54e11ecd727b69fb1994baf3b6b1eedb881cdd3196ecec"}, - {file = "pyzmq-26.0.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a824b3301ddd003cdceb9b537804e751ac5922a845b19d4e50b4789d1cd28b24"}, - {file = "pyzmq-26.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:af9f5b1b76753584c871c1c96db8a18650886b3adf9fc8c7d4019343eb329c28"}, - {file = "pyzmq-26.0.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:9691a6ab55d011e83d7438f6711b93b7f8aa21ee8cf3e7ad6d6d9ea26a8f3a1f"}, - {file = "pyzmq-26.0.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58176e2437462568b5099acf17401be64205e175e72767a8250eef84ee9ec4f5"}, - {file = "pyzmq-26.0.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d492921b398d640a1f796306531bc6911a94ce5528b798ed14e0620abd9b948d"}, - {file = "pyzmq-26.0.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:f85bb2c47b5fd70e3cbb280e380ab97bdf9f02e1a363cb472fe0a297ac24029d"}, - {file = "pyzmq-26.0.0-cp312-cp312-win32.whl", hash = "sha256:c2e36399f0433b14a91f956bd7ecf94799c57a6f992889d45440cb05b3de8025"}, - {file = "pyzmq-26.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:12ca1afb065e5b21a32b1e35bfcbc8762efc0f7555c166acaec36c93b52d7ccf"}, - {file = "pyzmq-26.0.0-cp312-cp312-win_arm64.whl", hash = "sha256:f66c925f62ce28946525c32a094e346dd8da6c828d568d7ecda97f5ae36089c3"}, - {file = "pyzmq-26.0.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:e495ff09514fc657c5fb2cba0aac082ce0494c6217230783297da9008333a8db"}, - {file = "pyzmq-26.0.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:5736c9a54c27319a65ffc72dbf684538f2773237e94ba50b7f1f74f4e3cb9115"}, - {file = "pyzmq-26.0.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cd62830100b9b1adb51da4094142bd680d51daf9a0f6f3f39e1f80474eddc011"}, - {file = "pyzmq-26.0.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544a7ee271fac41ddc0ba11f4b128ddd5f2bf0a3186d25be331ed8bfbb253536"}, - {file = "pyzmq-26.0.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:694625c2c22be57149e9439757ee02ee4fb6432f7054dc5008bbbc33ef388d1c"}, - {file = "pyzmq-26.0.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:90ba8f7c6f34c2c11179b293050417c14661035969ef3f8867200ea6901f9000"}, - {file = "pyzmq-26.0.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ab2e55046263c8b24e64116e80b63cf701df747b44aadcf317aa47c8af2dfe67"}, - {file = "pyzmq-26.0.0-cp37-cp37m-win32.whl", hash = "sha256:7353d231686bbc96c458b934f134ff9165a1e9dd0a2ea8f724469e44bcc2c07a"}, - {file = "pyzmq-26.0.0-cp37-cp37m-win_amd64.whl", hash = "sha256:1df2b992eabc59f078ca916e9ac8b5bd463536bf7828c13940b35b8555ed7861"}, - {file = "pyzmq-26.0.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2397364289334840c81ff1ef95a5a5ee326de01c1437cc38f7e16785a7b653d9"}, - {file = "pyzmq-26.0.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c952cf06edbbd2d67f627037e2c8e3187ca834d6b9a222e3a3037f80d393a345"}, - {file = "pyzmq-26.0.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:55f390adb763196d75a2e8c18277b4344f8a7f94f223b5d096324c5b47c2471e"}, - {file = "pyzmq-26.0.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1da5e11862a994360319df4f425e89662563683334e1079684eb77b9a6478ae2"}, - {file = "pyzmq-26.0.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:72340614ea23904cff824109eb025648bdf32775d87f5814d3ba6f2335a853f3"}, - {file = "pyzmq-26.0.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa7431d12ebb5433a92e99dc326d45eaf52a90046032bac4c558b4bdeee5dc7a"}, - {file = "pyzmq-26.0.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a2b13008a693c0ffccaeeebcc5ab5f2398cced3b5bf482ba89a38fe56b00eb10"}, - {file = "pyzmq-26.0.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9d68284ce48617c97e675ed8a89db12a098eaa871a026999c9a10351f547f1fe"}, - {file = "pyzmq-26.0.0-cp38-cp38-win32.whl", hash = "sha256:8783857a8c8df648a70c81ea3ff53ee71e5bf18468ca5ac3414f419fe8f3bd93"}, - {file = "pyzmq-26.0.0-cp38-cp38-win_amd64.whl", hash = "sha256:36d0f2fcbdba1fda8ff213bd17db7ddcba848aa70480ade3fe70401dce606511"}, - {file = "pyzmq-26.0.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:dd87df01bc8eca392f0d505924087ccafdc4885a498e68df9f09eca9fdc736f1"}, - {file = "pyzmq-26.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:abc08b2e688714216870a6ab974733d4a1fcf0437d250ac8feed59c4c5c3f395"}, - {file = "pyzmq-26.0.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dd13a30454adcf2f361155ea563ec99036678131a17c6b1a3f74426212c14ddc"}, - {file = "pyzmq-26.0.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a0562054930471b386a44b0887504687c4e7adf4ba89bddc2e5959d16c371764"}, - {file = "pyzmq-26.0.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc7badded4b025dbc25f34b95503b71c952235e6e40de40995c0c120efb4ff6d"}, - {file = "pyzmq-26.0.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f971e77358384b8bcf3e9a7577cf84f97adbd6359f943e30cbff66087afcb279"}, - {file = "pyzmq-26.0.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca4ebbef3f5fbd271eafc7c22ebbb88b74232f08b0e51759113f30a8d01f6843"}, - {file = "pyzmq-26.0.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc98fbd4ce4ef8a0fbe97ab6d495aaa7764461e5a45f24c04f1d234e7bb80293"}, - {file = "pyzmq-26.0.0-cp39-cp39-win32.whl", hash = "sha256:a5207bc2a923118e9afb57fee679be016ea138c27d1be5747118966e2d5d9450"}, - {file = "pyzmq-26.0.0-cp39-cp39-win_amd64.whl", hash = "sha256:e0c08a6070358a2984900a4518e2dacbfaf24aac018ab086d7ac2f6069b13340"}, - {file = "pyzmq-26.0.0-cp39-cp39-win_arm64.whl", hash = "sha256:eae3dcc185c405cf645480745c45346a1f42afce240f69a589095e41bd2b9e3d"}, - {file = "pyzmq-26.0.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:71a8f010e23dfd61c531084a2b72a81885017da28352540f0b7799ca8423c044"}, - {file = "pyzmq-26.0.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5b48b7e417c56486932fb0c01fecd24916fe6bc359c03a654aa8c63fa33e3d76"}, - {file = "pyzmq-26.0.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2806942185b40a3477d9b300c6f71354dd2be37e3f61a43193c96caa51e284d1"}, - {file = "pyzmq-26.0.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ed127aff75a3df142ae7a883c49a85b0b2f863b59fa1b8e4280335f5ebab5fd0"}, - {file = "pyzmq-26.0.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:903b77dd2f17286496fa3ec902bc523f4502b0c64a2892df4b021222a2ba95fe"}, - {file = "pyzmq-26.0.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:321a6872a9371709a62b3a4a14c1e9b5b47549371197c0c2164d2288510cd6d6"}, - {file = "pyzmq-26.0.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cac954dc83c84e9d9d65f2359d402d7e79ae094d7808d578c9e9cc2c350c5a64"}, - {file = "pyzmq-26.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ac6f54c399638858e0b2a3153f23934604f3a8c9bb5a9cf865060cc658b1e096"}, - {file = "pyzmq-26.0.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40af30c4cd0a046029d7b5272d02a649f9b1f89fb1361bbc90ba08d55ac88273"}, - {file = "pyzmq-26.0.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:814245422f1c7707634397621dbcbeea7671fdc5c43d1ae592f4e0e45179e7fb"}, - {file = "pyzmq-26.0.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6d3d7ef786e778351e6c51b45906e16506ad98bb78b99304032cb1876dfc81d2"}, - {file = "pyzmq-26.0.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:36a85da0eab4c5337d0de7f975cca011208a59e9d0637e0c1b571764f1dd4a8f"}, - {file = "pyzmq-26.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1d64889bfe4109f4a59a72b1d21416550465020642d6f556efd044951386bd38"}, - {file = "pyzmq-26.0.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80fdea3e9e34c480bfccbb910f75380196ae9d1c12880c21743c845ebe6b13aa"}, - {file = "pyzmq-26.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:7129efc54dc48f566eed5422bc555ba4e472e40a1f9de328577c90ade47ccf5d"}, - {file = "pyzmq-26.0.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0ec5147095d6065b0e3a38a1a34f7859ab46496f3d5ce71134165893e9f83674"}, - {file = "pyzmq-26.0.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a1cc0445038a394479ad36b7e3cf55a19ee40099c031f65de872b8ee7025e79"}, - {file = "pyzmq-26.0.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b377b520e618c30c827966c274dd62ce7e15c72ce8767fae6193b6bdd1deb502"}, - {file = "pyzmq-26.0.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc907b26d287e6981d1e531c8fc21a0f94fe46a17493a8322eb3c75f8b561334"}, - {file = "pyzmq-26.0.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:580dd4b1c2edd51f284df0209bf439899f425ed00cb803a85ddc6cf10c866688"}, - {file = "pyzmq-26.0.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:08db8071020181173c70cf2dad239e5e21e5b2e95f95b0ece0da39a70f5a483c"}, - {file = "pyzmq-26.0.0.tar.gz", hash = "sha256:10ff405db5cee3bbd7aa143d78b25d90356097aed7864e50f0ae644e08759fe9"}, -] - -[package.dependencies] -cffi = {version = "*", markers = "implementation_name == \"pypy\""} - -[[package]] -name = "quantile-python" -version = "1.1" -description = "Python Implementation of Graham Cormode and S. Muthukrishnan's Effective Computation of Biased Quantiles over Data Streams in ICDE'05" -optional = false -python-versions = "*" -files = [ - {file = "quantile-python-1.1.tar.gz", hash = "sha256:558629e88c497ef3b9b1081349c1ae6a61b53590e317724298ff54c674db7969"}, -] - -[[package]] -name = "ray" -version = "2.10.0" -description = "Ray provides a simple, universal API for building distributed applications." -optional = false -python-versions = ">=3.8" -files = [ - {file = "ray-2.10.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:8a174268c7b6ca9826e4884b837395b695a45c17049927965d1b4cc370184ba2"}, - {file = "ray-2.10.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c193deed7e3f604cdb37047f5646cab14f4337693dd32add8bc902dfadb89f75"}, - {file = "ray-2.10.0-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:a3db89d22afc7a0a976249715dd90ffe69f7692d32cb599cd1afbc38482060f7"}, - {file = "ray-2.10.0-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:cb74f7d2aa5a21e5f9dcb315a4f9bde822328e76ba95cd0ba370cfda098a67f4"}, - {file = "ray-2.10.0-cp310-cp310-win_amd64.whl", hash = "sha256:44ab600fe0b5a12675d0d42d564994ac4e53286217c4de1c4eb00d74ae79ef24"}, - {file = "ray-2.10.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:8eb606b7d247213b377ccca0f8d425f9c61a48b23e9b2e4566bc75f66d797bb5"}, - {file = "ray-2.10.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8eb11aec8a65946f7546d0e703158c03a85a8be27332dbbf86d9411802700e7e"}, - {file = "ray-2.10.0-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:5b4ec4b5707e18382685d0703ed04afd1602359a3056f6ae4b37588a0551eef3"}, - {file = "ray-2.10.0-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:c7d1438cba8726ec9a59c96964e007b60a0728436647f48c383228692c2f2ee0"}, - {file = "ray-2.10.0-cp311-cp311-win_amd64.whl", hash = "sha256:eceecea4133e63f5d607cc9f2a4278de51eeeeef552f694895e381aae9ff8522"}, - {file = "ray-2.10.0-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:fb92f2d6d4eca602dfb0d3d459a09be59668e1560ce4bd89b692892f25b1933b"}, - {file = "ray-2.10.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:31aa60373fc7291752ee89a5f5ad8effec682b1f165911f38ae95fc43bc668a9"}, - {file = "ray-2.10.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:5b7d41eb04f6b67c38170edc0406dc71537eabfd6e5d4e3399a36385ff8b0194"}, - {file = "ray-2.10.0-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:8a44535e6266fa09e3eb4fc9035906decfc9f3aeda86fe66b1e738a01a51939a"}, - {file = "ray-2.10.0-cp38-cp38-win_amd64.whl", hash = "sha256:77ba4120d694e7c3dc7d93a9d3cb33925827d04ad11af2d21fa0db66f227d27a"}, - {file = "ray-2.10.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:6b49a8c2b40f02a56a2af2b6026c1eedd485747c6e4c2cf9ac433af6e572bdbb"}, - {file = "ray-2.10.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5fe8fb8847304dd3a6e435b95af9e5436309f2b3612c63c56bf4ac8dea73f9f4"}, - {file = "ray-2.10.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f215eb704f2cb72e984d5a85fe435b4d74808c906950176789ba2101ce739082"}, - {file = "ray-2.10.0-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:32d97e5343578a3d37ab5f30148fa193dec46a21fa21f15b6f23fe48a420831a"}, - {file = "ray-2.10.0-cp39-cp39-win_amd64.whl", hash = "sha256:917d081fc98500f244ebc0e8da836025e1e4fa52f21030b8336cb0a2c79e84e2"}, -] - -[package.dependencies] -aiosignal = "*" -click = ">=7.0" -filelock = "*" -frozenlist = "*" -jsonschema = "*" -msgpack = ">=1.0.0,<2.0.0" -packaging = "*" -protobuf = ">=3.15.3,<3.19.5 || >3.19.5" -pyyaml = "*" -requests = "*" - -[package.extras] -air = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "fsspec", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "numpy (>=1.20)", "opencensus", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "tensorboardX (>=1.9)", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] -all = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "dm-tree", "fastapi", "fsspec", "grpcio (!=1.56.0)", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "gymnasium (==0.28.1)", "lz4", "numpy (>=1.20)", "opencensus", "opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk", "pandas", "pandas (>=1.3)", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pyarrow (>=6.0.1)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "pyyaml", "ray-cpp (==2.10.0)", "requests", "rich", "scikit-image", "scipy", "smart-open", "starlette", "tensorboardX (>=1.9)", "typer", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] -client = ["grpcio (!=1.56.0)"] -cpp = ["ray-cpp (==2.10.0)"] -data = ["fsspec", "numpy (>=1.20)", "pandas (>=1.3)", "pyarrow (>=6.0.1)"] -default = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "opencensus", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "virtualenv (>=20.0.24,!=20.21.1)"] -observability = ["opentelemetry-api", "opentelemetry-exporter-otlp", "opentelemetry-sdk"] -rllib = ["dm-tree", "fsspec", "gymnasium (==0.28.1)", "lz4", "pandas", "pyarrow (>=6.0.1)", "pyyaml", "requests", "rich", "scikit-image", "scipy", "tensorboardX (>=1.9)", "typer"] -serve = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "opencensus", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] -serve-grpc = ["aiohttp (>=3.7)", "aiohttp-cors", "colorful", "fastapi", "grpcio (>=1.32.0)", "grpcio (>=1.42.0)", "opencensus", "prometheus-client (>=0.7.1)", "py-spy (>=0.2.0)", "pydantic (<2.0.dev0 || >=2.5.dev0,<3)", "requests", "smart-open", "starlette", "uvicorn[standard]", "virtualenv (>=20.0.24,!=20.21.1)", "watchfiles"] -train = ["fsspec", "pandas", "pyarrow (>=6.0.1)", "requests", "tensorboardX (>=1.9)"] -tune = ["fsspec", "pandas", "pyarrow (>=6.0.1)", "requests", "tensorboardX (>=1.9)"] - -[[package]] -name = "referencing" -version = "0.34.0" -description = "JSON Referencing + Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "referencing-0.34.0-py3-none-any.whl", hash = "sha256:d53ae300ceddd3169f1ffa9caf2cb7b769e92657e4fafb23d34b93679116dfd4"}, - {file = "referencing-0.34.0.tar.gz", hash = "sha256:5773bd84ef41799a5a8ca72dc34590c041eb01bf9aa02632b4a973fb0181a844"}, -] - -[package.dependencies] -attrs = ">=22.2.0" -rpds-py = ">=0.7.0" - -[[package]] -name = "regex" -version = "2023.12.25" -description = "Alternative regular expression module, to replace re." -optional = false -python-versions = ">=3.7" -files = [ - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8"}, - {file = "regex-2023.12.25-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5"}, - {file = "regex-2023.12.25-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423"}, - {file = "regex-2023.12.25-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f"}, - {file = "regex-2023.12.25-cp310-cp310-win32.whl", hash = "sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630"}, - {file = "regex-2023.12.25-cp310-cp310-win_amd64.whl", hash = "sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97"}, - {file = "regex-2023.12.25-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa"}, - {file = "regex-2023.12.25-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd"}, - {file = "regex-2023.12.25-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4"}, - {file = "regex-2023.12.25-cp311-cp311-win32.whl", hash = "sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87"}, - {file = "regex-2023.12.25-cp311-cp311-win_amd64.whl", hash = "sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d"}, - {file = "regex-2023.12.25-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3"}, - {file = "regex-2023.12.25-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf"}, - {file = "regex-2023.12.25-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d"}, - {file = "regex-2023.12.25-cp312-cp312-win32.whl", hash = "sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5"}, - {file = "regex-2023.12.25-cp312-cp312-win_amd64.whl", hash = "sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232"}, - {file = "regex-2023.12.25-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f"}, - {file = "regex-2023.12.25-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347"}, - {file = "regex-2023.12.25-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39"}, - {file = "regex-2023.12.25-cp37-cp37m-win32.whl", hash = "sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c"}, - {file = "regex-2023.12.25-cp37-cp37m-win_amd64.whl", hash = "sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64"}, - {file = "regex-2023.12.25-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988"}, - {file = "regex-2023.12.25-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756"}, - {file = "regex-2023.12.25-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2"}, - {file = "regex-2023.12.25-cp38-cp38-win32.whl", hash = "sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb"}, - {file = "regex-2023.12.25-cp38-cp38-win_amd64.whl", hash = "sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7"}, - {file = "regex-2023.12.25-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6"}, - {file = "regex-2023.12.25-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f"}, - {file = "regex-2023.12.25-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20"}, - {file = "regex-2023.12.25-cp39-cp39-win32.whl", hash = "sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9"}, - {file = "regex-2023.12.25-cp39-cp39-win_amd64.whl", hash = "sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91"}, - {file = "regex-2023.12.25.tar.gz", hash = "sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5"}, -] - -[[package]] -name = "requests" -version = "2.31.0" -description = "Python HTTP for Humans." -optional = false -python-versions = ">=3.7" -files = [ - {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, - {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, -] - -[package.dependencies] -certifi = ">=2017.4.17" -charset-normalizer = ">=2,<4" -idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<3" - -[package.extras] -socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] - -[[package]] -name = "rfc3339-validator" -version = "0.1.4" -description = "A pure python RFC3339 validator" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, - {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, -] - -[package.dependencies] -six = "*" - -[[package]] -name = "rfc3986-validator" -version = "0.1.1" -description = "Pure python rfc3986 validator" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" -files = [ - {file = "rfc3986_validator-0.1.1-py2.py3-none-any.whl", hash = "sha256:2f235c432ef459970b4306369336b9d5dbdda31b510ca1e327636e01f528bfa9"}, - {file = "rfc3986_validator-0.1.1.tar.gz", hash = "sha256:3d44bde7921b3b9ec3ae4e3adca370438eccebc676456449b145d533b240d055"}, -] - -[[package]] -name = "rich" -version = "13.7.1" -description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" -optional = false -python-versions = ">=3.7.0" -files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, -] - -[package.dependencies] -markdown-it-py = ">=2.2.0" -pygments = ">=2.13.0,<3.0.0" - -[package.extras] -jupyter = ["ipywidgets (>=7.5.1,<9)"] - -[[package]] -name = "rpds-py" -version = "0.18.0" -description = "Python bindings to Rust's persistent data structures (rpds)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "rpds_py-0.18.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e"}, - {file = "rpds_py-0.18.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1"}, - {file = "rpds_py-0.18.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e"}, - {file = "rpds_py-0.18.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88"}, - {file = "rpds_py-0.18.0-cp310-none-win32.whl", hash = "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337"}, - {file = "rpds_py-0.18.0-cp310-none-win_amd64.whl", hash = "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4"}, - {file = "rpds_py-0.18.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5"}, - {file = "rpds_py-0.18.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b"}, - {file = "rpds_py-0.18.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836"}, - {file = "rpds_py-0.18.0-cp311-none-win32.whl", hash = "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1"}, - {file = "rpds_py-0.18.0-cp311-none-win_amd64.whl", hash = "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0"}, - {file = "rpds_py-0.18.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3"}, - {file = "rpds_py-0.18.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f"}, - {file = "rpds_py-0.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7"}, - {file = "rpds_py-0.18.0-cp312-none-win32.whl", hash = "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98"}, - {file = "rpds_py-0.18.0-cp312-none-win_amd64.whl", hash = "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e"}, - {file = "rpds_py-0.18.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d"}, - {file = "rpds_py-0.18.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c"}, - {file = "rpds_py-0.18.0-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594"}, - {file = "rpds_py-0.18.0-cp38-none-win32.whl", hash = "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e"}, - {file = "rpds_py-0.18.0-cp38-none-win_amd64.whl", hash = "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33"}, - {file = "rpds_py-0.18.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9"}, - {file = "rpds_py-0.18.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024"}, - {file = "rpds_py-0.18.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20"}, - {file = "rpds_py-0.18.0-cp39-none-win32.whl", hash = "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7"}, - {file = "rpds_py-0.18.0-cp39-none-win_amd64.whl", hash = "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_i686.whl", hash = "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984"}, - {file = "rpds_py-0.18.0-pp310-pypy310_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_i686.whl", hash = "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da"}, - {file = "rpds_py-0.18.0-pp38-pypy38_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_i686.whl", hash = "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432"}, - {file = "rpds_py-0.18.0-pp39-pypy39_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f"}, - {file = "rpds_py-0.18.0.tar.gz", hash = "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d"}, -] - -[[package]] -name = "ruff" -version = "0.1.15" -description = "An extremely fast Python linter and code formatter, written in Rust." -optional = false -python-versions = ">=3.7" -files = [ - {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"}, - {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"}, - {file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"}, - {file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"}, - {file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"}, - {file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"}, - {file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"}, - {file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"}, - {file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"}, - {file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"}, - {file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"}, -] - -[[package]] -name = "safetensors" -version = "0.4.3" -description = "" -optional = false -python-versions = ">=3.7" -files = [ - {file = "safetensors-0.4.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:dcf5705cab159ce0130cd56057f5f3425023c407e170bca60b4868048bae64fd"}, - {file = "safetensors-0.4.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:bb4f8c5d0358a31e9a08daeebb68f5e161cdd4018855426d3f0c23bb51087055"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70a5319ef409e7f88686a46607cbc3c428271069d8b770076feaf913664a07ac"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb9c65bd82f9ef3ce4970dc19ee86be5f6f93d032159acf35e663c6bea02b237"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:edb5698a7bc282089f64c96c477846950358a46ede85a1c040e0230344fdde10"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:efcc860be094b8d19ac61b452ec635c7acb9afa77beb218b1d7784c6d41fe8ad"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d88b33980222085dd6001ae2cad87c6068e0991d4f5ccf44975d216db3b57376"}, - {file = "safetensors-0.4.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5fc6775529fb9f0ce2266edd3e5d3f10aab068e49f765e11f6f2a63b5367021d"}, - {file = "safetensors-0.4.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9c6ad011c1b4e3acff058d6b090f1da8e55a332fbf84695cf3100c649cc452d1"}, - {file = "safetensors-0.4.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:8c496c5401c1b9c46d41a7688e8ff5b0310a3b9bae31ce0f0ae870e1ea2b8caf"}, - {file = "safetensors-0.4.3-cp310-none-win32.whl", hash = "sha256:38e2a8666178224a51cca61d3cb4c88704f696eac8f72a49a598a93bbd8a4af9"}, - {file = "safetensors-0.4.3-cp310-none-win_amd64.whl", hash = "sha256:393e6e391467d1b2b829c77e47d726f3b9b93630e6a045b1d1fca67dc78bf632"}, - {file = "safetensors-0.4.3-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:22f3b5d65e440cec0de8edaa672efa888030802e11c09b3d6203bff60ebff05a"}, - {file = "safetensors-0.4.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7c4fa560ebd4522adddb71dcd25d09bf211b5634003f015a4b815b7647d62ebe"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9afd5358719f1b2cf425fad638fc3c887997d6782da317096877e5b15b2ce93"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d8c5093206ef4b198600ae484230402af6713dab1bd5b8e231905d754022bec7"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0b2104df1579d6ba9052c0ae0e3137c9698b2d85b0645507e6fd1813b70931a"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8cf18888606dad030455d18f6c381720e57fc6a4170ee1966adb7ebc98d4d6a3"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0bf4f9d6323d9f86eef5567eabd88f070691cf031d4c0df27a40d3b4aaee755b"}, - {file = "safetensors-0.4.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:585c9ae13a205807b63bef8a37994f30c917ff800ab8a1ca9c9b5d73024f97ee"}, - {file = "safetensors-0.4.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:faefeb3b81bdfb4e5a55b9bbdf3d8d8753f65506e1d67d03f5c851a6c87150e9"}, - {file = "safetensors-0.4.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:befdf0167ad626f22f6aac6163477fcefa342224a22f11fdd05abb3995c1783c"}, - {file = "safetensors-0.4.3-cp311-none-win32.whl", hash = "sha256:a7cef55929dcbef24af3eb40bedec35d82c3c2fa46338bb13ecf3c5720af8a61"}, - {file = "safetensors-0.4.3-cp311-none-win_amd64.whl", hash = "sha256:840b7ac0eff5633e1d053cc9db12fdf56b566e9403b4950b2dc85393d9b88d67"}, - {file = "safetensors-0.4.3-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:22d21760dc6ebae42e9c058d75aa9907d9f35e38f896e3c69ba0e7b213033856"}, - {file = "safetensors-0.4.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d22c1a10dff3f64d0d68abb8298a3fd88ccff79f408a3e15b3e7f637ef5c980"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b1648568667f820b8c48317c7006221dc40aced1869908c187f493838a1362bc"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:446e9fe52c051aeab12aac63d1017e0f68a02a92a027b901c4f8e931b24e5397"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fef5d70683643618244a4f5221053567ca3e77c2531e42ad48ae05fae909f542"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a1f4430cc0c9d6afa01214a4b3919d0a029637df8e09675ceef1ca3f0dfa0df"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2d603846a8585b9432a0fd415db1d4c57c0f860eb4aea21f92559ff9902bae4d"}, - {file = "safetensors-0.4.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a844cdb5d7cbc22f5f16c7e2a0271170750763c4db08381b7f696dbd2c78a361"}, - {file = "safetensors-0.4.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:88887f69f7a00cf02b954cdc3034ffb383b2303bc0ab481d4716e2da51ddc10e"}, - {file = "safetensors-0.4.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ee463219d9ec6c2be1d331ab13a8e0cd50d2f32240a81d498266d77d07b7e71e"}, - {file = "safetensors-0.4.3-cp312-none-win32.whl", hash = "sha256:d0dd4a1db09db2dba0f94d15addc7e7cd3a7b0d393aa4c7518c39ae7374623c3"}, - {file = "safetensors-0.4.3-cp312-none-win_amd64.whl", hash = "sha256:d14d30c25897b2bf19b6fb5ff7e26cc40006ad53fd4a88244fdf26517d852dd7"}, - {file = "safetensors-0.4.3-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d1456f814655b224d4bf6e7915c51ce74e389b413be791203092b7ff78c936dd"}, - {file = "safetensors-0.4.3-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:455d538aa1aae4a8b279344a08136d3f16334247907b18a5c3c7fa88ef0d3c46"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf476bca34e1340ee3294ef13e2c625833f83d096cfdf69a5342475602004f95"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:02ef3a24face643456020536591fbd3c717c5abaa2737ec428ccbbc86dffa7a4"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7de32d0d34b6623bb56ca278f90db081f85fb9c5d327e3c18fd23ac64f465768"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a0deb16a1d3ea90c244ceb42d2c6c276059616be21a19ac7101aa97da448faf"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c59d51f182c729f47e841510b70b967b0752039f79f1de23bcdd86462a9b09ee"}, - {file = "safetensors-0.4.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1f598b713cc1a4eb31d3b3203557ac308acf21c8f41104cdd74bf640c6e538e3"}, - {file = "safetensors-0.4.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5757e4688f20df083e233b47de43845d1adb7e17b6cf7da5f8444416fc53828d"}, - {file = "safetensors-0.4.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fe746d03ed8d193674a26105e4f0fe6c726f5bb602ffc695b409eaf02f04763d"}, - {file = "safetensors-0.4.3-cp37-none-win32.whl", hash = "sha256:0d5ffc6a80f715c30af253e0e288ad1cd97a3d0086c9c87995e5093ebc075e50"}, - {file = "safetensors-0.4.3-cp37-none-win_amd64.whl", hash = "sha256:a11c374eb63a9c16c5ed146457241182f310902bd2a9c18255781bb832b6748b"}, - {file = "safetensors-0.4.3-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:b1e31be7945f66be23f4ec1682bb47faa3df34cb89fc68527de6554d3c4258a4"}, - {file = "safetensors-0.4.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:03a4447c784917c9bf01d8f2ac5080bc15c41692202cd5f406afba16629e84d6"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d244bcafeb1bc06d47cfee71727e775bca88a8efda77a13e7306aae3813fa7e4"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53c4879b9c6bd7cd25d114ee0ef95420e2812e676314300624594940a8d6a91f"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:74707624b81f1b7f2b93f5619d4a9f00934d5948005a03f2c1845ffbfff42212"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d52c958dc210265157573f81d34adf54e255bc2b59ded6218500c9b15a750eb"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f9568f380f513a60139971169c4a358b8731509cc19112369902eddb33faa4d"}, - {file = "safetensors-0.4.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0d9cd8e1560dfc514b6d7859247dc6a86ad2f83151a62c577428d5102d872721"}, - {file = "safetensors-0.4.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:89f9f17b0dacb913ed87d57afbc8aad85ea42c1085bd5de2f20d83d13e9fc4b2"}, - {file = "safetensors-0.4.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:1139eb436fd201c133d03c81209d39ac57e129f5e74e34bb9ab60f8d9b726270"}, - {file = "safetensors-0.4.3-cp38-none-win32.whl", hash = "sha256:d9c289f140a9ae4853fc2236a2ffc9a9f2d5eae0cb673167e0f1b8c18c0961ac"}, - {file = "safetensors-0.4.3-cp38-none-win_amd64.whl", hash = "sha256:622afd28968ef3e9786562d352659a37de4481a4070f4ebac883f98c5836563e"}, - {file = "safetensors-0.4.3-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8651c7299cbd8b4161a36cd6a322fa07d39cd23535b144d02f1c1972d0c62f3c"}, - {file = "safetensors-0.4.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e375d975159ac534c7161269de24ddcd490df2157b55c1a6eeace6cbb56903f0"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:084fc436e317f83f7071fc6a62ca1c513b2103db325cd09952914b50f51cf78f"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:41a727a7f5e6ad9f1db6951adee21bbdadc632363d79dc434876369a17de6ad6"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e7dbbde64b6c534548696808a0e01276d28ea5773bc9a2dfb97a88cd3dffe3df"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbae3b4b9d997971431c346edbfe6e41e98424a097860ee872721e176040a893"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:01e4b22e3284cd866edeabe4f4d896229495da457229408d2e1e4810c5187121"}, - {file = "safetensors-0.4.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0dd37306546b58d3043eb044c8103a02792cc024b51d1dd16bd3dd1f334cb3ed"}, - {file = "safetensors-0.4.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d8815b5e1dac85fc534a97fd339e12404db557878c090f90442247e87c8aeaea"}, - {file = "safetensors-0.4.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e011cc162503c19f4b1fd63dfcddf73739c7a243a17dac09b78e57a00983ab35"}, - {file = "safetensors-0.4.3-cp39-none-win32.whl", hash = "sha256:01feb3089e5932d7e662eda77c3ecc389f97c0883c4a12b5cfdc32b589a811c3"}, - {file = "safetensors-0.4.3-cp39-none-win_amd64.whl", hash = "sha256:3f9cdca09052f585e62328c1c2923c70f46814715c795be65f0b93f57ec98a02"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:1b89381517891a7bb7d1405d828b2bf5d75528299f8231e9346b8eba092227f9"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:cd6fff9e56df398abc5866b19a32124815b656613c1c5ec0f9350906fd798aac"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:840caf38d86aa7014fe37ade5d0d84e23dcfbc798b8078015831996ecbc206a3"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f9650713b2cfa9537a2baf7dd9fee458b24a0aaaa6cafcea8bdd5fb2b8efdc34"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e4119532cd10dba04b423e0f86aecb96cfa5a602238c0aa012f70c3a40c44b50"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e066e8861eef6387b7c772344d1fe1f9a72800e04ee9a54239d460c400c72aab"}, - {file = "safetensors-0.4.3-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:90964917f5b0fa0fa07e9a051fbef100250c04d150b7026ccbf87a34a54012e0"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:c41e1893d1206aa7054029681778d9a58b3529d4c807002c156d58426c225173"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae7613a119a71a497d012ccc83775c308b9c1dab454806291427f84397d852fd"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f9bac020faba7f5dc481e881b14b6425265feabb5bfc552551d21189c0eddc3"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:420a98f593ff9930f5822560d14c395ccbc57342ddff3b463bc0b3d6b1951550"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f5e6883af9a68c0028f70a4c19d5a6ab6238a379be36ad300a22318316c00cb0"}, - {file = "safetensors-0.4.3-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:cdd0a3b5da66e7f377474599814dbf5cbf135ff059cc73694de129b58a5e8a2c"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:9bfb92f82574d9e58401d79c70c716985dc049b635fef6eecbb024c79b2c46ad"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3615a96dd2dcc30eb66d82bc76cda2565f4f7bfa89fcb0e31ba3cea8a1a9ecbb"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:868ad1b6fc41209ab6bd12f63923e8baeb1a086814cb2e81a65ed3d497e0cf8f"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b7ffba80aa49bd09195145a7fd233a7781173b422eeb995096f2b30591639517"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0acbe31340ab150423347e5b9cc595867d814244ac14218932a5cf1dd38eb39"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:19bbdf95de2cf64f25cd614c5236c8b06eb2cfa47cbf64311f4b5d80224623a3"}, - {file = "safetensors-0.4.3-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b852e47eb08475c2c1bd8131207b405793bfc20d6f45aff893d3baaad449ed14"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5d07cbca5b99babb692d76d8151bec46f461f8ad8daafbfd96b2fca40cadae65"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:1ab6527a20586d94291c96e00a668fa03f86189b8a9defa2cdd34a1a01acc7d5"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:02318f01e332cc23ffb4f6716e05a492c5f18b1d13e343c49265149396284a44"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec4b52ce9a396260eb9731eb6aea41a7320de22ed73a1042c2230af0212758ce"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:018b691383026a2436a22b648873ed11444a364324e7088b99cd2503dd828400"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:309b10dbcab63269ecbf0e2ca10ce59223bb756ca5d431ce9c9eeabd446569da"}, - {file = "safetensors-0.4.3-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:b277482120df46e27a58082df06a15aebda4481e30a1c21eefd0921ae7e03f65"}, - {file = "safetensors-0.4.3.tar.gz", hash = "sha256:2f85fc50c4e07a21e95c24e07460fe6f7e2859d0ce88092838352b798ce711c2"}, -] - -[package.extras] -all = ["safetensors[jax]", "safetensors[numpy]", "safetensors[paddlepaddle]", "safetensors[pinned-tf]", "safetensors[quality]", "safetensors[testing]", "safetensors[torch]"] -dev = ["safetensors[all]"] -jax = ["flax (>=0.6.3)", "jax (>=0.3.25)", "jaxlib (>=0.3.25)", "safetensors[numpy]"] -mlx = ["mlx (>=0.0.9)"] -numpy = ["numpy (>=1.21.6)"] -paddlepaddle = ["paddlepaddle (>=2.4.1)", "safetensors[numpy]"] -pinned-tf = ["safetensors[numpy]", "tensorflow (==2.11.0)"] -quality = ["black (==22.3)", "click (==8.0.4)", "flake8 (>=3.8.3)", "isort (>=5.5.4)"] -tensorflow = ["safetensors[numpy]", "tensorflow (>=2.11.0)"] -testing = ["h5py (>=3.7.0)", "huggingface-hub (>=0.12.1)", "hypothesis (>=6.70.2)", "pytest (>=7.2.0)", "pytest-benchmark (>=4.0.0)", "safetensors[numpy]", "setuptools-rust (>=1.5.2)"] -torch = ["safetensors[numpy]", "torch (>=1.10)"] - -[[package]] -name = "scikit-learn" -version = "1.4.0" -description = "A set of python modules for machine learning and data mining" -optional = false -python-versions = ">=3.9" -files = [ - {file = "scikit-learn-1.4.0.tar.gz", hash = "sha256:d4373c984eba20e393216edd51a3e3eede56cbe93d4247516d205643c3b93121"}, - {file = "scikit_learn-1.4.0-1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fce93a7473e2f4ee4cc280210968288d6a7d7ad8dc6fa7bb7892145e407085f9"}, - {file = "scikit_learn-1.4.0-1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d77df3d1e15fc37a9329999979fa7868ba8655dbab21fe97fc7ddabac9e08cc7"}, - {file = "scikit_learn-1.4.0-1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2404659fedec40eeafa310cd14d613e564d13dbf8f3c752d31c095195ec05de6"}, - {file = "scikit_learn-1.4.0-1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e98632da8f6410e6fb6bf66937712c949b4010600ccd3f22a5388a83e610cc3c"}, - {file = "scikit_learn-1.4.0-1-cp310-cp310-win_amd64.whl", hash = "sha256:11b3b140f70fbc9f6a08884631ae8dd60a4bb2d7d6d1de92738ea42b740d8992"}, - {file = "scikit_learn-1.4.0-1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a8341eabdc754d5ab91641a7763243845e96b6d68e03e472531e88a4f1b09f21"}, - {file = "scikit_learn-1.4.0-1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:d1f6bce875ac2bb6b52514f67c185c564ccd299a05b65b7bab091a4c13dde12d"}, - {file = "scikit_learn-1.4.0-1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c408b46b2fd61952d519ea1af2f8f0a7a703e1433923ab1704c4131520b2083b"}, - {file = "scikit_learn-1.4.0-1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b465dd1dcd237b7b1dcd1a9048ccbf70a98c659474324fa708464c3a2533fad"}, - {file = "scikit_learn-1.4.0-1-cp311-cp311-win_amd64.whl", hash = "sha256:0db8e22c42f7980fe5eb22069b1f84c48966f3e0d23a01afde5999e3987a2501"}, - {file = "scikit_learn-1.4.0-1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e7eef6ea2ed289af40e88c0be9f7704ca8b5de18508a06897c3fe21e0905efdf"}, - {file = "scikit_learn-1.4.0-1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:349669b01435bc4dbf25c6410b0892073befdaec52637d1a1d1ff53865dc8db3"}, - {file = "scikit_learn-1.4.0-1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d439c584e58434d0350701bd33f6c10b309e851fccaf41c121aed55f6851d8cf"}, - {file = "scikit_learn-1.4.0-1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0e2427d9ef46477625ab9b55c1882844fe6fc500f418c3f8e650200182457bc"}, - {file = "scikit_learn-1.4.0-1-cp312-cp312-win_amd64.whl", hash = "sha256:d3d75343940e7bf9b85c830c93d34039fa015eeb341c5c0b4cd7a90dadfe00d4"}, - {file = "scikit_learn-1.4.0-1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:76986d22e884ab062b1beecdd92379656e9d3789ecc1f9870923c178de55f9fe"}, - {file = "scikit_learn-1.4.0-1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:e22446ad89f1cb7657f0d849dcdc345b48e2d10afa3daf2925fdb740f85b714c"}, - {file = "scikit_learn-1.4.0-1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74812c9eabb265be69d738a8ea8d4884917a59637fcbf88a5f0e9020498bc6b3"}, - {file = "scikit_learn-1.4.0-1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad2a63e0dd386b92da3270887a29b308af4d7c750d8c4995dfd9a4798691bcc"}, - {file = "scikit_learn-1.4.0-1-cp39-cp39-win_amd64.whl", hash = "sha256:53b9e29177897c37e2ff9d4ba6ca12fdb156e22523e463db05def303f5c72b5c"}, - {file = "scikit_learn-1.4.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb8f044a8f5962613ce1feb4351d66f8d784bd072d36393582f351859b065f7d"}, - {file = "scikit_learn-1.4.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:a6372c90bbf302387792108379f1ec77719c1618d88496d0df30cb8e370b4661"}, - {file = "scikit_learn-1.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:785ce3c352bf697adfda357c3922c94517a9376002971bc5ea50896144bc8916"}, - {file = "scikit_learn-1.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0aba2a20d89936d6e72d95d05e3bf1db55bca5c5920926ad7b92c34f5e7d3bbe"}, - {file = "scikit_learn-1.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:2bac5d56b992f8f06816f2cd321eb86071c6f6d44bb4b1cb3d626525820d754b"}, - {file = "scikit_learn-1.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:27ae4b0f1b2c77107c096a7e05b33458354107b47775428d1f11b23e30a73e8a"}, - {file = "scikit_learn-1.4.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5c5c62ffb52c3ffb755eb21fa74cc2cbf2c521bd53f5c04eaa10011dbecf5f80"}, - {file = "scikit_learn-1.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f0d2018ac6fa055dab65fe8a485967990d33c672d55bc254c56c35287b02fab"}, - {file = "scikit_learn-1.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:91a8918c415c4b4bf1d60c38d32958849a9191c2428ab35d30b78354085c7c7a"}, - {file = "scikit_learn-1.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:80a21de63275f8bcd7877b3e781679d2ff1eddfed515a599f95b2502a3283d42"}, - {file = "scikit_learn-1.4.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:0f33bbafb310c26b81c4d41ecaebdbc1f63498a3f13461d50ed9a2e8f24d28e4"}, - {file = "scikit_learn-1.4.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:8b6ac1442ec714b4911e5aef8afd82c691b5c88b525ea58299d455acc4e8dcec"}, - {file = "scikit_learn-1.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05fc5915b716c6cc60a438c250108e9a9445b522975ed37e416d5ea4f9a63381"}, - {file = "scikit_learn-1.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:842b7d6989f3c574685e18da6f91223eb32301d0f93903dd399894250835a6f7"}, - {file = "scikit_learn-1.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:88bcb586fdff865372df1bc6be88bb7e6f9e0aa080dab9f54f5cac7eca8e2b6b"}, - {file = "scikit_learn-1.4.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f77674647dd31f56cb12ed13ed25b6ed43a056fffef051715022d2ebffd7a7d1"}, - {file = "scikit_learn-1.4.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:833999872e2920ce00f3a50839946bdac7539454e200eb6db54898a41f4bfd43"}, - {file = "scikit_learn-1.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:970ec697accaef10fb4f51763f3a7b1250f9f0553cf05514d0e94905322a0172"}, - {file = "scikit_learn-1.4.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:923d778f378ebacca2c672ab1740e5a413e437fb45ab45ab02578f8b689e5d43"}, - {file = "scikit_learn-1.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:1d041bc95006b545b59e458399e3175ab11ca7a03dc9a74a573ac891f5df1489"}, -] - -[package.dependencies] -joblib = ">=1.2.0" -numpy = ">=1.19.5" -scipy = ">=1.6.0" -threadpoolctl = ">=2.0.0" - -[package.extras] -benchmark = ["matplotlib (>=3.3.4)", "memory-profiler (>=0.57.0)", "pandas (>=1.1.5)"] -docs = ["Pillow (>=7.1.2)", "matplotlib (>=3.3.4)", "memory-profiler (>=0.57.0)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)", "sphinx (>=6.0.0)", "sphinx-copybutton (>=0.5.2)", "sphinx-gallery (>=0.15.0)", "sphinx-prompt (>=1.3.0)", "sphinxext-opengraph (>=0.4.2)"] -examples = ["matplotlib (>=3.3.4)", "pandas (>=1.1.5)", "plotly (>=5.14.0)", "pooch (>=1.6.0)", "scikit-image (>=0.17.2)", "seaborn (>=0.9.0)"] -tests = ["black (>=23.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.3)", "numpydoc (>=1.2.0)", "pandas (>=1.1.5)", "polars (>=0.19.12)", "pooch (>=1.6.0)", "pyamg (>=4.0.0)", "pyarrow (>=12.0.0)", "pytest (>=7.1.2)", "pytest-cov (>=2.9.0)", "ruff (>=0.0.272)", "scikit-image (>=0.17.2)"] - -[[package]] -name = "scipy" -version = "1.13.0" -description = "Fundamental algorithms for scientific computing in Python" -optional = false -python-versions = ">=3.9" -files = [ - {file = "scipy-1.13.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba419578ab343a4e0a77c0ef82f088238a93eef141b2b8017e46149776dfad4d"}, - {file = "scipy-1.13.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:22789b56a999265431c417d462e5b7f2b487e831ca7bef5edeb56efe4c93f86e"}, - {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:05f1432ba070e90d42d7fd836462c50bf98bd08bed0aa616c359eed8a04e3922"}, - {file = "scipy-1.13.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b8434f6f3fa49f631fae84afee424e2483289dfc30a47755b4b4e6b07b2633a4"}, - {file = "scipy-1.13.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:dcbb9ea49b0167de4167c40eeee6e167caeef11effb0670b554d10b1e693a8b9"}, - {file = "scipy-1.13.0-cp310-cp310-win_amd64.whl", hash = "sha256:1d2f7bb14c178f8b13ebae93f67e42b0a6b0fc50eba1cd8021c9b6e08e8fb1cd"}, - {file = "scipy-1.13.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fbcf8abaf5aa2dc8d6400566c1a727aed338b5fe880cde64907596a89d576fa"}, - {file = "scipy-1.13.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:5e4a756355522eb60fcd61f8372ac2549073c8788f6114449b37e9e8104f15a5"}, - {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b5acd8e1dbd8dbe38d0004b1497019b2dbbc3d70691e65d69615f8a7292865d7"}, - {file = "scipy-1.13.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ff7dad5d24a8045d836671e082a490848e8639cabb3dbdacb29f943a678683d"}, - {file = "scipy-1.13.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:4dca18c3ffee287ddd3bc8f1dabaf45f5305c5afc9f8ab9cbfab855e70b2df5c"}, - {file = "scipy-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:a2f471de4d01200718b2b8927f7d76b5d9bde18047ea0fa8bd15c5ba3f26a1d6"}, - {file = "scipy-1.13.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0de696f589681c2802f9090fff730c218f7c51ff49bf252b6a97ec4a5d19e8b"}, - {file = "scipy-1.13.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:b2a3ff461ec4756b7e8e42e1c681077349a038f0686132d623fa404c0bee2551"}, - {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bf9fe63e7a4bf01d3645b13ff2aa6dea023d38993f42aaac81a18b1bda7a82a"}, - {file = "scipy-1.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e7626dfd91cdea5714f343ce1176b6c4745155d234f1033584154f60ef1ff42"}, - {file = "scipy-1.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:109d391d720fcebf2fbe008621952b08e52907cf4c8c7efc7376822151820820"}, - {file = "scipy-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8930ae3ea371d6b91c203b1032b9600d69c568e537b7988a3073dfe4d4774f21"}, - {file = "scipy-1.13.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5407708195cb38d70fd2d6bb04b1b9dd5c92297d86e9f9daae1576bd9e06f602"}, - {file = "scipy-1.13.0-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:ac38c4c92951ac0f729c4c48c9e13eb3675d9986cc0c83943784d7390d540c78"}, - {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09c74543c4fbeb67af6ce457f6a6a28e5d3739a87f62412e4a16e46f164f0ae5"}, - {file = "scipy-1.13.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28e286bf9ac422d6beb559bc61312c348ca9b0f0dae0d7c5afde7f722d6ea13d"}, - {file = "scipy-1.13.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:33fde20efc380bd23a78a4d26d59fc8704e9b5fd9b08841693eb46716ba13d86"}, - {file = "scipy-1.13.0-cp39-cp39-win_amd64.whl", hash = "sha256:45c08bec71d3546d606989ba6e7daa6f0992918171e2a6f7fbedfa7361c2de1e"}, - {file = "scipy-1.13.0.tar.gz", hash = "sha256:58569af537ea29d3f78e5abd18398459f195546bb3be23d16677fb26616cc11e"}, -] - -[package.dependencies] -numpy = ">=1.22.4,<2.3" - -[package.extras] -dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy", "pycodestyle", "pydevtool", "rich-click", "ruff", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.12.0)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] -test = ["array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "mpmath", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] - -[[package]] -name = "send2trash" -version = "1.8.3" -description = "Send file to trash natively under Mac OS X, Windows and Linux" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "Send2Trash-1.8.3-py3-none-any.whl", hash = "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9"}, - {file = "Send2Trash-1.8.3.tar.gz", hash = "sha256:b18e7a3966d99871aefeb00cfbcfdced55ce4871194810fc71f4aa484b953abf"}, -] - -[package.extras] -nativelib = ["pyobjc-framework-Cocoa", "pywin32"] -objc = ["pyobjc-framework-Cocoa"] -win32 = ["pywin32"] - -[[package]] -name = "sentencepiece" -version = "0.2.0" -description = "SentencePiece python wrapper" -optional = false -python-versions = "*" -files = [ - {file = "sentencepiece-0.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:188779e1298a1c8b8253c7d3ad729cb0a9891e5cef5e5d07ce4592c54869e227"}, - {file = "sentencepiece-0.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bed9cf85b296fa2b76fc2547b9cbb691a523864cebaee86304c43a7b4cb1b452"}, - {file = "sentencepiece-0.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d7b67e724bead13f18db6e1d10b6bbdc454af574d70efbb36f27d90387be1ca3"}, - {file = "sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fde4b08cfe237be4484c6c7c2e2c75fb862cfeab6bd5449ce4caeafd97b767a"}, - {file = "sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4c378492056202d1c48a4979650981635fd97875a00eabb1f00c6a236b013b5e"}, - {file = "sentencepiece-0.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1380ce6540a368de2ef6d7e6ba14ba8f3258df650d39ba7d833b79ee68a52040"}, - {file = "sentencepiece-0.2.0-cp310-cp310-win32.whl", hash = "sha256:a1151d6a6dd4b43e552394aed0edfe9292820272f0194bd56c7c1660a0c06c3d"}, - {file = "sentencepiece-0.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:d490142b0521ef22bc1085f061d922a2a6666175bb6b42e588ff95c0db6819b2"}, - {file = "sentencepiece-0.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:17982700c4f6dbb55fa3594f3d7e5dd1c8659a274af3738e33c987d2a27c9d5c"}, - {file = "sentencepiece-0.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:7c867012c0e8bcd5bdad0f791609101cb5c66acb303ab3270218d6debc68a65e"}, - {file = "sentencepiece-0.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7fd6071249c74f779c5b27183295b9202f8dedb68034e716784364443879eaa6"}, - {file = "sentencepiece-0.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:27f90c55a65013cbb8f4d7aab0599bf925cde4adc67ae43a0d323677b5a1c6cb"}, - {file = "sentencepiece-0.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b293734059ef656dcd65be62ff771507bea8fed0a711b6733976e1ed3add4553"}, - {file = "sentencepiece-0.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e58b47f933aca74c6a60a79dcb21d5b9e47416256c795c2d58d55cec27f9551d"}, - {file = "sentencepiece-0.2.0-cp311-cp311-win32.whl", hash = "sha256:c581258cf346b327c62c4f1cebd32691826306f6a41d8c4bec43b010dee08e75"}, - {file = "sentencepiece-0.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:0993dbc665f4113017892f1b87c3904a44d0640eda510abcacdfb07f74286d36"}, - {file = "sentencepiece-0.2.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:ea5f536e32ea8ec96086ee00d7a4a131ce583a1b18d130711707c10e69601cb2"}, - {file = "sentencepiece-0.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d0cb51f53b6aae3c36bafe41e86167c71af8370a039f542c43b0cce5ef24a68c"}, - {file = "sentencepiece-0.2.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3212121805afc58d8b00ab4e7dd1f8f76c203ddb9dc94aa4079618a31cf5da0f"}, - {file = "sentencepiece-0.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a3149e3066c2a75e0d68a43eb632d7ae728c7925b517f4c05c40f6f7280ce08"}, - {file = "sentencepiece-0.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:632f3594d3e7ac8b367bca204cb3fd05a01d5b21455acd097ea4c0e30e2f63d7"}, - {file = "sentencepiece-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f295105c6bdbb05bd5e1b0cafbd78ff95036f5d3641e7949455a3f4e5e7c3109"}, - {file = "sentencepiece-0.2.0-cp312-cp312-win32.whl", hash = "sha256:fb89f811e5efd18bab141afc3fea3de141c3f69f3fe9e898f710ae7fe3aab251"}, - {file = "sentencepiece-0.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:7a673a72aab81fef5ebe755c6e0cc60087d1f3a4700835d40537183c1703a45f"}, - {file = "sentencepiece-0.2.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4547683f330289ec4f093027bfeb87f9ef023b2eb6f879fdc4a8187c7e0ffb90"}, - {file = "sentencepiece-0.2.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7cd6175f7eaec7142d2bf6f6597ce7db4c9ac89acf93fcdb17410c3a8b781eeb"}, - {file = "sentencepiece-0.2.0-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:859ba1acde782609a0910a26a60e16c191a82bf39b5621107552c0cd79fad00f"}, - {file = "sentencepiece-0.2.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbbef6cc277f8f18f36959e305f10b1c620442d75addc79c21d7073ae581b50"}, - {file = "sentencepiece-0.2.0-cp36-cp36m-win32.whl", hash = "sha256:536b934e244829e3fe6c4f198652cd82da48adb9aa145c9f00889542726dee3d"}, - {file = "sentencepiece-0.2.0-cp36-cp36m-win_amd64.whl", hash = "sha256:0a91aaa3c769b52440df56fafda683b3aa48e3f2169cf7ee5b8c8454a7f3ae9b"}, - {file = "sentencepiece-0.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:787e480ca4c1d08c9985a7eb1eae4345c107729c99e9b5a9a00f2575fc7d4b4b"}, - {file = "sentencepiece-0.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4d158189eb2ecffea3a51edf6d25e110b3678ec47f1a40f2d541eafbd8f6250"}, - {file = "sentencepiece-0.2.0-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d1e5ca43013e8935f25457a4fca47e315780172c3e821b4b13a890668911c792"}, - {file = "sentencepiece-0.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7140d9e5a74a0908493bb4a13f1f16a401297bd755ada4c707e842fbf6f0f5bf"}, - {file = "sentencepiece-0.2.0-cp37-cp37m-win32.whl", hash = "sha256:6cf333625234f247ab357b0bd9836638405ea9082e1543d5b8408f014979dcbf"}, - {file = "sentencepiece-0.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ff88712338b01031910e8e61e7239aff3ce8869ee31a47df63cb38aadd591bea"}, - {file = "sentencepiece-0.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:20813a68d4c221b1849c62c30e1281ea81687894d894b8d4a0f4677d9311e0f5"}, - {file = "sentencepiece-0.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:926ef920ae2e8182db31d3f5d081ada57804e3e1d3a8c4ef8b117f9d9fb5a945"}, - {file = "sentencepiece-0.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:89f65f69636b7e9c015b79dff9c9985a9bc7d19ded6f79ef9f1ec920fdd73ecf"}, - {file = "sentencepiece-0.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f67eae0dbe6f2d7d6ba50a354623d787c99965f068b81e145d53240198021b0"}, - {file = "sentencepiece-0.2.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:98501e075f35dd1a1d5a20f65be26839fcb1938752ec61539af008a5aa6f510b"}, - {file = "sentencepiece-0.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3d1d2cc4882e8d6a1adf9d5927d7716f80617fc693385661caff21888972269"}, - {file = "sentencepiece-0.2.0-cp38-cp38-win32.whl", hash = "sha256:b99a308a2e5e569031ab164b74e6fab0b6f37dfb493c32f7816225f4d411a6dd"}, - {file = "sentencepiece-0.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:cdb701eec783d3ec86b7cd4c763adad8eaf6b46db37ee1c36e5e6c44b3fe1b5f"}, - {file = "sentencepiece-0.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:1e0f9c4d0a6b0af59b613175f019916e28ade076e21242fd5be24340d8a2f64a"}, - {file = "sentencepiece-0.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:298f21cc1366eb60311aedba3169d30f885c363ddbf44214b0a587d2908141ad"}, - {file = "sentencepiece-0.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3f1ec95aa1e5dab11f37ac7eff190493fd87770f7a8b81ebc9dd768d1a3c8704"}, - {file = "sentencepiece-0.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b06b70af54daa4b4904cbb90b4eb6d35c9f3252fdc86c9c32d5afd4d30118d8"}, - {file = "sentencepiece-0.2.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22e37bac44dd6603388cb598c64ff7a76e41ca774646f21c23aadfbf5a2228ab"}, - {file = "sentencepiece-0.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0461324897735512a32d222e3d886e24ad6a499761952b6bda2a9ee6e4313ea5"}, - {file = "sentencepiece-0.2.0-cp39-cp39-win32.whl", hash = "sha256:38aed822fb76435fa1f12185f10465a94ab9e51d5e8a9159e9a540ce926f0ffd"}, - {file = "sentencepiece-0.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:d8cf876516548b5a1d6ac4745d8b554f5c07891d55da557925e5c13ff0b4e6ad"}, - {file = "sentencepiece-0.2.0.tar.gz", hash = "sha256:a52c19171daaf2e697dc6cbe67684e0fa341b1248966f6aebb541de654d15843"}, -] - -[[package]] -name = "sentry-sdk" -version = "1.45.0" -description = "Python client for Sentry (https://sentry.io)" -optional = false -python-versions = "*" -files = [ - {file = "sentry-sdk-1.45.0.tar.gz", hash = "sha256:509aa9678c0512344ca886281766c2e538682f8acfa50fd8d405f8c417ad0625"}, - {file = "sentry_sdk-1.45.0-py2.py3-none-any.whl", hash = "sha256:1ce29e30240cc289a027011103a8c83885b15ef2f316a60bcc7c5300afa144f1"}, -] - -[package.dependencies] -certifi = "*" -fastapi = {version = ">=0.79.0", optional = true, markers = "extra == \"fastapi\""} -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} - -[package.extras] -aiohttp = ["aiohttp (>=3.5)"] -arq = ["arq (>=0.23)"] -asyncpg = ["asyncpg (>=0.23)"] -beam = ["apache-beam (>=2.12)"] -bottle = ["bottle (>=0.12.13)"] -celery = ["celery (>=3)"] -celery-redbeat = ["celery-redbeat (>=2)"] -chalice = ["chalice (>=1.16.0)"] -clickhouse-driver = ["clickhouse-driver (>=0.2.0)"] -django = ["django (>=1.8)"] -falcon = ["falcon (>=1.4)"] -fastapi = ["fastapi (>=0.79.0)"] -flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] -grpcio = ["grpcio (>=1.21.1)"] -httpx = ["httpx (>=0.16.0)"] -huey = ["huey (>=2)"] -loguru = ["loguru (>=0.5)"] -openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] -opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] -pure-eval = ["asttokens", "executing", "pure-eval"] -pymongo = ["pymongo (>=3.1)"] -pyspark = ["pyspark (>=2.4.4)"] -quart = ["blinker (>=1.1)", "quart (>=0.16.1)"] -rq = ["rq (>=0.6)"] -sanic = ["sanic (>=0.8)"] -sqlalchemy = ["sqlalchemy (>=1.2)"] -starlette = ["starlette (>=0.19.1)"] -starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] - -[[package]] -name = "sigtools" -version = "4.0.1" -description = "Utilities for working with inspect.Signature objects." -optional = false -python-versions = ">=3.6" -files = [ - {file = "sigtools-4.0.1-py2.py3-none-any.whl", hash = "sha256:d216b4cf920bbab0fce636ddc429ed8463a5b533d9e1492acb45a2a1bc36ac6c"}, - {file = "sigtools-4.0.1.tar.gz", hash = "sha256:4b8e135a9cd4d2ea00da670c093372d74e672ba3abb87f4c98d8e73dea54445c"}, -] - -[package.dependencies] -attrs = "*" - -[package.extras] -test = ["coverage", "mock", "repeated-test (>=2.2.1)", "sphinx"] -tests = ["coverage", "mock", "repeated-test (>=2.2.1)", "sphinx"] - -[[package]] -name = "six" -version = "1.16.0" -description = "Python 2 and 3 compatibility utilities" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, - {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 = "soupsieve" -version = "2.5" -description = "A modern CSS selector implementation for Beautiful Soup." -optional = false -python-versions = ">=3.8" -files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, -] - -[[package]] -name = "stack-data" -version = "0.6.3" -description = "Extract data from python stack frames and tracebacks for informative displays" -optional = false -python-versions = "*" -files = [ - {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, - {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, -] - -[package.dependencies] -asttokens = ">=2.1.0" -executing = ">=1.2.0" -pure-eval = "*" - -[package.extras] -tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] - -[[package]] -name = "starlette" -version = "0.37.2" -description = "The little ASGI library that shines." -optional = false -python-versions = ">=3.8" -files = [ - {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, - {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, -] - -[package.dependencies] -anyio = ">=3.4.0,<5" - -[package.extras] -full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart (>=0.0.7)", "pyyaml"] - -[[package]] -name = "starlette-exporter" -version = "0.17.1" -description = "Prometheus metrics exporter for Starlette applications." -optional = false -python-versions = "*" -files = [ - {file = "starlette_exporter-0.17.1-py3-none-any.whl", hash = "sha256:40f8667a6cf6c569c4eb090a7cc0bbd426ff5a732b8fea232100064cec8256d0"}, - {file = "starlette_exporter-0.17.1.tar.gz", hash = "sha256:c9979c87efdef956e880075f4358deec234208c62d71448a58b7a3a45056209d"}, -] - -[package.dependencies] -prometheus-client = ">=0.12" -starlette = "*" - -[[package]] -name = "sympy" -version = "1.12" -description = "Computer algebra system (CAS) in Python" -optional = false -python-versions = ">=3.8" -files = [ - {file = "sympy-1.12-py3-none-any.whl", hash = "sha256:c3588cd4295d0c0f603d0f2ae780587e64e2efeedb3521e46b9bb1d08d184fa5"}, - {file = "sympy-1.12.tar.gz", hash = "sha256:ebf595c8dac3e0fdc4152c51878b498396ec7f30e7a914d6071e674d49420fb8"}, -] - -[package.dependencies] -mpmath = ">=0.19" - -[[package]] -name = "synchronicity" -version = "0.6.7" -description = "Export blocking and async library versions from a single async implementation" -optional = false -python-versions = ">=3.8" -files = [ - {file = "synchronicity-0.6.7-py3-none-any.whl", hash = "sha256:188397ea96ebbff20d7990259af30f7679e04aebccb89e14eb6cbc3243543f4b"}, -] - -[package.dependencies] -sigtools = "4.0.1" -typing-extensions = ">=4.6" - -[[package]] -name = "tabulate" -version = "0.9.0" -description = "Pretty-print tabular data" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tabulate-0.9.0-py3-none-any.whl", hash = "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f"}, - {file = "tabulate-0.9.0.tar.gz", hash = "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c"}, -] - -[package.extras] -widechars = ["wcwidth"] - -[[package]] -name = "termcolor" -version = "2.4.0" -description = "ANSI color formatting for output in terminal" -optional = false -python-versions = ">=3.8" -files = [ - {file = "termcolor-2.4.0-py3-none-any.whl", hash = "sha256:9297c0df9c99445c2412e832e882a7884038a25617c60cea2ad69488d4040d63"}, - {file = "termcolor-2.4.0.tar.gz", hash = "sha256:aab9e56047c8ac41ed798fa36d892a37aca6b3e9159f3e0c24bc64a9b3ac7b7a"}, -] - -[package.extras] -tests = ["pytest", "pytest-cov"] - -[[package]] -name = "terminado" -version = "0.18.1" -description = "Tornado websocket backend for the Xterm.js Javascript terminal emulator library." -optional = false -python-versions = ">=3.8" -files = [ - {file = "terminado-0.18.1-py3-none-any.whl", hash = "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0"}, - {file = "terminado-0.18.1.tar.gz", hash = "sha256:de09f2c4b85de4765f7714688fff57d3e75bad1f909b589fde880460c753fd2e"}, -] - -[package.dependencies] -ptyprocess = {version = "*", markers = "os_name != \"nt\""} -pywinpty = {version = ">=1.1.0", markers = "os_name == \"nt\""} -tornado = ">=6.1.0" - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["pre-commit", "pytest (>=7.0)", "pytest-timeout"] -typing = ["mypy (>=1.6,<2.0)", "traitlets (>=5.11.1)"] - -[[package]] -name = "threadpoolctl" -version = "3.4.0" -description = "threadpoolctl" -optional = false -python-versions = ">=3.8" -files = [ - {file = "threadpoolctl-3.4.0-py3-none-any.whl", hash = "sha256:8f4c689a65b23e5ed825c8436a92b818aac005e0f3715f6a1664d7c7ee29d262"}, - {file = "threadpoolctl-3.4.0.tar.gz", hash = "sha256:f11b491a03661d6dd7ef692dd422ab34185d982466c49c8f98c8f716b5c93196"}, -] - -[[package]] -name = "tinycss2" -version = "1.2.1" -description = "A tiny CSS parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tinycss2-1.2.1-py3-none-any.whl", hash = "sha256:2b80a96d41e7c3914b8cda8bc7f705a4d9c49275616e886103dd839dfc847847"}, - {file = "tinycss2-1.2.1.tar.gz", hash = "sha256:8cff3a8f066c2ec677c06dbc7b45619804a6938478d9d73c284b29d14ecb0627"}, -] - -[package.dependencies] -webencodings = ">=0.4" - -[package.extras] -doc = ["sphinx", "sphinx_rtd_theme"] -test = ["flake8", "isort", "pytest"] - -[[package]] -name = "tokenizers" -version = "0.15.2" -description = "" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tokenizers-0.15.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:52f6130c9cbf70544287575a985bf44ae1bda2da7e8c24e97716080593638012"}, - {file = "tokenizers-0.15.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:054c1cc9c6d68f7ffa4e810b3d5131e0ba511b6e4be34157aa08ee54c2f8d9ee"}, - {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9b9b070fdad06e347563b88c278995735292ded1132f8657084989a4c84a6d5"}, - {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea621a7eef4b70e1f7a4e84dd989ae3f0eeb50fc8690254eacc08acb623e82f1"}, - {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf7fd9a5141634fa3aa8d6b7be362e6ae1b4cda60da81388fa533e0b552c98fd"}, - {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:44f2a832cd0825295f7179eaf173381dc45230f9227ec4b44378322d900447c9"}, - {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8b9ec69247a23747669ec4b0ca10f8e3dfb3545d550258129bd62291aabe8605"}, - {file = "tokenizers-0.15.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40b6a4c78da863ff26dbd5ad9a8ecc33d8a8d97b535172601cf00aee9d7ce9ce"}, - {file = "tokenizers-0.15.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:5ab2a4d21dcf76af60e05af8063138849eb1d6553a0d059f6534357bce8ba364"}, - {file = "tokenizers-0.15.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a47acfac7e511f6bbfcf2d3fb8c26979c780a91e06fb5b9a43831b2c0153d024"}, - {file = "tokenizers-0.15.2-cp310-none-win32.whl", hash = "sha256:064ff87bb6acdbd693666de9a4b692add41308a2c0ec0770d6385737117215f2"}, - {file = "tokenizers-0.15.2-cp310-none-win_amd64.whl", hash = "sha256:3b919afe4df7eb6ac7cafd2bd14fb507d3f408db7a68c43117f579c984a73843"}, - {file = "tokenizers-0.15.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:89cd1cb93e4b12ff39bb2d626ad77e35209de9309a71e4d3d4672667b4b256e7"}, - {file = "tokenizers-0.15.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cfed5c64e5be23d7ee0f0e98081a25c2a46b0b77ce99a4f0605b1ec43dd481fa"}, - {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a907d76dcfda37023ba203ab4ceeb21bc5683436ebefbd895a0841fd52f6f6f2"}, - {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:20ea60479de6fc7b8ae756b4b097572372d7e4032e2521c1bbf3d90c90a99ff0"}, - {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:48e2b9335be2bc0171df9281385c2ed06a15f5cf121c44094338306ab7b33f2c"}, - {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:112a1dd436d2cc06e6ffdc0b06d55ac019a35a63afd26475205cb4b1bf0bfbff"}, - {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4620cca5c2817177ee8706f860364cc3a8845bc1e291aaf661fb899e5d1c45b0"}, - {file = "tokenizers-0.15.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ccd73a82751c523b3fc31ff8194702e4af4db21dc20e55b30ecc2079c5d43cb7"}, - {file = "tokenizers-0.15.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:107089f135b4ae7817affe6264f8c7a5c5b4fd9a90f9439ed495f54fcea56fb4"}, - {file = "tokenizers-0.15.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0ff110ecc57b7aa4a594396525a3451ad70988e517237fe91c540997c4e50e29"}, - {file = "tokenizers-0.15.2-cp311-none-win32.whl", hash = "sha256:6d76f00f5c32da36c61f41c58346a4fa7f0a61be02f4301fd30ad59834977cc3"}, - {file = "tokenizers-0.15.2-cp311-none-win_amd64.whl", hash = "sha256:cc90102ed17271cf0a1262babe5939e0134b3890345d11a19c3145184b706055"}, - {file = "tokenizers-0.15.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f86593c18d2e6248e72fb91c77d413a815153b8ea4e31f7cd443bdf28e467670"}, - {file = "tokenizers-0.15.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0774bccc6608eca23eb9d620196687c8b2360624619623cf4ba9dc9bd53e8b51"}, - {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d0222c5b7c9b26c0b4822a82f6a7011de0a9d3060e1da176f66274b70f846b98"}, - {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3835738be1de66624fff2f4f6f6684775da4e9c00bde053be7564cbf3545cc66"}, - {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:0143e7d9dcd811855c1ce1ab9bf5d96d29bf5e528fd6c7824d0465741e8c10fd"}, - {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db35825f6d54215f6b6009a7ff3eedee0848c99a6271c870d2826fbbedf31a38"}, - {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3f5e64b0389a2be47091d8cc53c87859783b837ea1a06edd9d8e04004df55a5c"}, - {file = "tokenizers-0.15.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e0480c452217edd35eca56fafe2029fb4d368b7c0475f8dfa3c5c9c400a7456"}, - {file = "tokenizers-0.15.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a33ab881c8fe70474980577e033d0bc9a27b7ab8272896e500708b212995d834"}, - {file = "tokenizers-0.15.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a308a607ca9de2c64c1b9ba79ec9a403969715a1b8ba5f998a676826f1a7039d"}, - {file = "tokenizers-0.15.2-cp312-none-win32.whl", hash = "sha256:b8fcfa81bcb9447df582c5bc96a031e6df4da2a774b8080d4f02c0c16b42be0b"}, - {file = "tokenizers-0.15.2-cp312-none-win_amd64.whl", hash = "sha256:38d7ab43c6825abfc0b661d95f39c7f8af2449364f01d331f3b51c94dcff7221"}, - {file = "tokenizers-0.15.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:38bfb0204ff3246ca4d5e726e8cc8403bfc931090151e6eede54d0e0cf162ef0"}, - {file = "tokenizers-0.15.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9c861d35e8286a53e06e9e28d030b5a05bcbf5ac9d7229e561e53c352a85b1fc"}, - {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:936bf3842db5b2048eaa53dade907b1160f318e7c90c74bfab86f1e47720bdd6"}, - {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:620beacc3373277700d0e27718aa8b25f7b383eb8001fba94ee00aeea1459d89"}, - {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2735ecbbf37e52db4ea970e539fd2d450d213517b77745114f92867f3fc246eb"}, - {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:473c83c5e2359bb81b0b6fde870b41b2764fcdd36d997485e07e72cc3a62264a"}, - {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:968fa1fb3c27398b28a4eca1cbd1e19355c4d3a6007f7398d48826bbe3a0f728"}, - {file = "tokenizers-0.15.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:865c60ae6eaebdde7da66191ee9b7db52e542ed8ee9d2c653b6d190a9351b980"}, - {file = "tokenizers-0.15.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:7c0d8b52664ab2d4a8d6686eb5effc68b78608a9008f086a122a7b2996befbab"}, - {file = "tokenizers-0.15.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:f33dfbdec3784093a9aebb3680d1f91336c56d86cc70ddf88708251da1fe9064"}, - {file = "tokenizers-0.15.2-cp37-cp37m-macosx_10_12_x86_64.whl", hash = "sha256:d44ba80988ff9424e33e0a49445072ac7029d8c0e1601ad25a0ca5f41ed0c1d6"}, - {file = "tokenizers-0.15.2-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:dce74266919b892f82b1b86025a613956ea0ea62a4843d4c4237be2c5498ed3a"}, - {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0ef06b9707baeb98b316577acb04f4852239d856b93e9ec3a299622f6084e4be"}, - {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c73e2e74bbb07910da0d37c326869f34113137b23eadad3fc00856e6b3d9930c"}, - {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4eeb12daf02a59e29f578a865f55d87cd103ce62bd8a3a5874f8fdeaa82e336b"}, - {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9ba9f6895af58487ca4f54e8a664a322f16c26bbb442effd01087eba391a719e"}, - {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ccec77aa7150e38eec6878a493bf8c263ff1fa8a62404e16c6203c64c1f16a26"}, - {file = "tokenizers-0.15.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3f40604f5042ff210ba82743dda2b6aa3e55aa12df4e9f2378ee01a17e2855e"}, - {file = "tokenizers-0.15.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:5645938a42d78c4885086767c70923abad047163d809c16da75d6b290cb30bbe"}, - {file = "tokenizers-0.15.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:05a77cbfebe28a61ab5c3891f9939cc24798b63fa236d84e5f29f3a85a200c00"}, - {file = "tokenizers-0.15.2-cp37-none-win32.whl", hash = "sha256:361abdc068e8afe9c5b818769a48624687fb6aaed49636ee39bec4e95e1a215b"}, - {file = "tokenizers-0.15.2-cp37-none-win_amd64.whl", hash = "sha256:7ef789f83eb0f9baeb4d09a86cd639c0a5518528f9992f38b28e819df397eb06"}, - {file = "tokenizers-0.15.2-cp38-cp38-macosx_10_12_x86_64.whl", hash = "sha256:4fe1f74a902bee74a3b25aff180fbfbf4f8b444ab37c4d496af7afd13a784ed2"}, - {file = "tokenizers-0.15.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4c4b89038a684f40a6b15d6b09f49650ac64d951ad0f2a3ea9169687bbf2a8ba"}, - {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d05a1b06f986d41aed5f2de464c003004b2df8aaf66f2b7628254bcbfb72a438"}, - {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:508711a108684111ec8af89d3a9e9e08755247eda27d0ba5e3c50e9da1600f6d"}, - {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:daa348f02d15160cb35439098ac96e3a53bacf35885072611cd9e5be7d333daa"}, - {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:494fdbe5932d3416de2a85fc2470b797e6f3226c12845cadf054dd906afd0442"}, - {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c2d60f5246f4da9373f75ff18d64c69cbf60c3bca597290cea01059c336d2470"}, - {file = "tokenizers-0.15.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93268e788825f52de4c7bdcb6ebc1fcd4a5442c02e730faa9b6b08f23ead0e24"}, - {file = "tokenizers-0.15.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6fc7083ab404019fc9acafe78662c192673c1e696bd598d16dc005bd663a5cf9"}, - {file = "tokenizers-0.15.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:41e39b41e5531d6b2122a77532dbea60e171ef87a3820b5a3888daa847df4153"}, - {file = "tokenizers-0.15.2-cp38-none-win32.whl", hash = "sha256:06cd0487b1cbfabefb2cc52fbd6b1f8d4c37799bd6c6e1641281adaa6b2504a7"}, - {file = "tokenizers-0.15.2-cp38-none-win_amd64.whl", hash = "sha256:5179c271aa5de9c71712e31cb5a79e436ecd0d7532a408fa42a8dbfa4bc23fd9"}, - {file = "tokenizers-0.15.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:82f8652a74cc107052328b87ea8b34291c0f55b96d8fb261b3880216a9f9e48e"}, - {file = "tokenizers-0.15.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:02458bee6f5f3139f1ebbb6d042b283af712c0981f5bc50edf771d6b762d5e4f"}, - {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c9a09cd26cca2e1c349f91aa665309ddb48d71636370749414fbf67bc83c5343"}, - {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:158be8ea8554e5ed69acc1ce3fbb23a06060bd4bbb09029431ad6b9a466a7121"}, - {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ddba9a2b0c8c81633eca0bb2e1aa5b3a15362b1277f1ae64176d0f6eba78ab1"}, - {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3ef5dd1d39797044642dbe53eb2bc56435308432e9c7907728da74c69ee2adca"}, - {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:454c203164e07a860dbeb3b1f4a733be52b0edbb4dd2e5bd75023ffa8b49403a"}, - {file = "tokenizers-0.15.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0cf6b7f1d4dc59af960e6ffdc4faffe6460bbfa8dce27a58bf75755ffdb2526d"}, - {file = "tokenizers-0.15.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2ef09bbc16519f6c25d0c7fc0c6a33a6f62923e263c9d7cca4e58b8c61572afb"}, - {file = "tokenizers-0.15.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c9a2ebdd2ad4ec7a68e7615086e633857c85e2f18025bd05d2a4399e6c5f7169"}, - {file = "tokenizers-0.15.2-cp39-none-win32.whl", hash = "sha256:918fbb0eab96fe08e72a8c2b5461e9cce95585d82a58688e7f01c2bd546c79d0"}, - {file = "tokenizers-0.15.2-cp39-none-win_amd64.whl", hash = "sha256:524e60da0135e106b254bd71f0659be9f89d83f006ea9093ce4d1fab498c6d0d"}, - {file = "tokenizers-0.15.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:6a9b648a58281c4672212fab04e60648fde574877d0139cd4b4f93fe28ca8944"}, - {file = "tokenizers-0.15.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7c7d18b733be6bbca8a55084027f7be428c947ddf871c500ee603e375013ffba"}, - {file = "tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:13ca3611de8d9ddfbc4dc39ef54ab1d2d4aaa114ac8727dfdc6a6ec4be017378"}, - {file = "tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:237d1bf3361cf2e6463e6c140628e6406766e8b27274f5fcc62c747ae3c6f094"}, - {file = "tokenizers-0.15.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67a0fe1e49e60c664915e9fb6b0cb19bac082ab1f309188230e4b2920230edb3"}, - {file = "tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4e022fe65e99230b8fd89ebdfea138c24421f91c1a4f4781a8f5016fd5cdfb4d"}, - {file = "tokenizers-0.15.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d857be2df69763362ac699f8b251a8cd3fac9d21893de129bc788f8baaef2693"}, - {file = "tokenizers-0.15.2-pp37-pypy37_pp73-macosx_10_12_x86_64.whl", hash = "sha256:708bb3e4283177236309e698da5fcd0879ce8fd37457d7c266d16b550bcbbd18"}, - {file = "tokenizers-0.15.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:64c35e09e9899b72a76e762f9854e8750213f67567787d45f37ce06daf57ca78"}, - {file = "tokenizers-0.15.2-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c1257f4394be0d3b00de8c9e840ca5601d0a4a8438361ce9c2b05c7d25f6057b"}, - {file = "tokenizers-0.15.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02272fe48280e0293a04245ca5d919b2c94a48b408b55e858feae9618138aeda"}, - {file = "tokenizers-0.15.2-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:dc3ad9ebc76eabe8b1d7c04d38be884b8f9d60c0cdc09b0aa4e3bcf746de0388"}, - {file = "tokenizers-0.15.2-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:32e16bdeffa7c4f46bf2152172ca511808b952701d13e7c18833c0b73cb5c23f"}, - {file = "tokenizers-0.15.2-pp38-pypy38_pp73-macosx_10_12_x86_64.whl", hash = "sha256:fb16ba563d59003028b678d2361a27f7e4ae0ab29c7a80690efa20d829c81fdb"}, - {file = "tokenizers-0.15.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:2277c36d2d6cdb7876c274547921a42425b6810d38354327dd65a8009acf870c"}, - {file = "tokenizers-0.15.2-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1cf75d32e8d250781940d07f7eece253f2fe9ecdb1dc7ba6e3833fa17b82fcbc"}, - {file = "tokenizers-0.15.2-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1b3b31884dc8e9b21508bb76da80ebf7308fdb947a17affce815665d5c4d028"}, - {file = "tokenizers-0.15.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10122d8d8e30afb43bb1fe21a3619f62c3e2574bff2699cf8af8b0b6c5dc4a3"}, - {file = "tokenizers-0.15.2-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:d88b96ff0fe8e91f6ef01ba50b0d71db5017fa4e3b1d99681cec89a85faf7bf7"}, - {file = "tokenizers-0.15.2-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:37aaec5a52e959892870a7c47cef80c53797c0db9149d458460f4f31e2fb250e"}, - {file = "tokenizers-0.15.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e2ea752f2b0fe96eb6e2f3adbbf4d72aaa1272079b0dfa1145507bd6a5d537e6"}, - {file = "tokenizers-0.15.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4b19a808d8799fda23504a5cd31d2f58e6f52f140380082b352f877017d6342b"}, - {file = "tokenizers-0.15.2-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:64c86e5e068ac8b19204419ed8ca90f9d25db20578f5881e337d203b314f4104"}, - {file = "tokenizers-0.15.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de19c4dc503c612847edf833c82e9f73cd79926a384af9d801dcf93f110cea4e"}, - {file = "tokenizers-0.15.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea09acd2fe3324174063d61ad620dec3bcf042b495515f27f638270a7d466e8b"}, - {file = "tokenizers-0.15.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:cf27fd43472e07b57cf420eee1e814549203d56de00b5af8659cb99885472f1f"}, - {file = "tokenizers-0.15.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7ca22bd897537a0080521445d91a58886c8c04084a6a19e6c78c586e0cfa92a5"}, - {file = "tokenizers-0.15.2.tar.gz", hash = "sha256:e6e9c6e019dd5484be5beafc775ae6c925f4c69a3487040ed09b45e13df2cb91"}, -] - -[package.dependencies] -huggingface_hub = ">=0.16.4,<1.0" - -[package.extras] -dev = ["tokenizers[testing]"] -docs = ["setuptools_rust", "sphinx", "sphinx_rtd_theme"] -testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests"] - -[[package]] -name = "toml" -version = "0.10.2" -description = "Python Library for Tom's Obvious, Minimal Language" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, - {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, -] - -[[package]] -name = "tomli" -version = "2.0.1" -description = "A lil' TOML parser" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tomli-2.0.1-py3-none-any.whl", hash = "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc"}, - {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, -] - -[[package]] -name = "torch" -version = "2.1.2" -description = "Tensors and Dynamic neural networks in Python with strong GPU acceleration" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "torch-2.1.2-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:3a871edd6c02dae77ad810335c0833391c1a4ce49af21ea8cf0f6a5d2096eea8"}, - {file = "torch-2.1.2-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:bef6996c27d8f6e92ea4e13a772d89611da0e103b48790de78131e308cf73076"}, - {file = "torch-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:0e13034fd5fb323cbbc29e56d0637a3791e50dd589616f40c79adfa36a5a35a1"}, - {file = "torch-2.1.2-cp310-none-macosx_10_9_x86_64.whl", hash = "sha256:d9b535cad0df3d13997dbe8bd68ac33e0e3ae5377639c9881948e40794a61403"}, - {file = "torch-2.1.2-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:f9a55d55af02826ebfbadf4e9b682f0f27766bc33df8236b48d28d705587868f"}, - {file = "torch-2.1.2-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:a6ebbe517097ef289cc7952783588c72de071d4b15ce0f8b285093f0916b1162"}, - {file = "torch-2.1.2-cp311-cp311-manylinux2014_aarch64.whl", hash = "sha256:8f32ce591616a30304f37a7d5ea80b69ca9e1b94bba7f308184bf616fdaea155"}, - {file = "torch-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e0ee6cf90c8970e05760f898d58f9ac65821c37ffe8b04269ec787aa70962b69"}, - {file = "torch-2.1.2-cp311-none-macosx_10_9_x86_64.whl", hash = "sha256:76d37967c31c99548ad2c4d3f2cf191db48476f2e69b35a0937137116da356a1"}, - {file = "torch-2.1.2-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:e2d83f07b4aac983453ea5bf8f9aa9dacf2278a8d31247f5d9037f37befc60e4"}, - {file = "torch-2.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:f41fe0c7ecbf903a568c73486139a75cfab287a0f6c17ed0698fdea7a1e8641d"}, - {file = "torch-2.1.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:e3225f47d50bb66f756fe9196a768055d1c26b02154eb1f770ce47a2578d3aa7"}, - {file = "torch-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:33d59cd03cb60106857f6c26b36457793637512998666ee3ce17311f217afe2b"}, - {file = "torch-2.1.2-cp38-none-macosx_10_9_x86_64.whl", hash = "sha256:8e221deccd0def6c2badff6be403e0c53491805ed9915e2c029adbcdb87ab6b5"}, - {file = "torch-2.1.2-cp38-none-macosx_11_0_arm64.whl", hash = "sha256:05b18594f60a911a0c4f023f38a8bda77131fba5fd741bda626e97dcf5a3dd0a"}, - {file = "torch-2.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:9ca96253b761e9aaf8e06fb30a66ee301aecbf15bb5a303097de1969077620b6"}, - {file = "torch-2.1.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:d93ba70f67b08c2ae5598ee711cbc546a1bc8102cef938904b8c85c2089a51a0"}, - {file = "torch-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:255b50bc0608db177e6a3cc118961d77de7e5105f07816585fa6f191f33a9ff3"}, - {file = "torch-2.1.2-cp39-none-macosx_10_9_x86_64.whl", hash = "sha256:6984cd5057c0c977b3c9757254e989d3f1124f4ce9d07caa6cb637783c71d42a"}, - {file = "torch-2.1.2-cp39-none-macosx_11_0_arm64.whl", hash = "sha256:bc195d7927feabc0eb7c110e457c955ed2ab616f3c7c28439dd4188cf589699f"}, -] - -[package.dependencies] -filelock = "*" -fsspec = "*" -jinja2 = "*" -networkx = "*" -nvidia-cublas-cu12 = {version = "12.1.3.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cuda-cupti-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cuda-nvrtc-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cuda-runtime-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cudnn-cu12 = {version = "8.9.2.26", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cufft-cu12 = {version = "11.0.2.54", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-curand-cu12 = {version = "10.3.2.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cusolver-cu12 = {version = "11.4.5.107", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-cusparse-cu12 = {version = "12.1.0.106", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-nccl-cu12 = {version = "2.18.1", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -nvidia-nvtx-cu12 = {version = "12.1.105", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -sympy = "*" -triton = {version = "2.1.0", markers = "platform_system == \"Linux\" and platform_machine == \"x86_64\""} -typing-extensions = "*" - -[package.extras] -dynamo = ["jinja2"] -opt-einsum = ["opt-einsum (>=3.3)"] - -[[package]] -name = "tornado" -version = "6.4" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -optional = false -python-versions = ">= 3.8" -files = [ - {file = "tornado-6.4-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:02ccefc7d8211e5a7f9e8bc3f9e5b0ad6262ba2fbb683a6443ecc804e5224ce0"}, - {file = "tornado-6.4-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:27787de946a9cffd63ce5814c33f734c627a87072ec7eed71f7fc4417bb16263"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f7894c581ecdcf91666a0912f18ce5e757213999e183ebfc2c3fdbf4d5bd764e"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e43bc2e5370a6a8e413e1e1cd0c91bedc5bd62a74a532371042a18ef19e10579"}, - {file = "tornado-6.4-cp38-abi3-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f0251554cdd50b4b44362f73ad5ba7126fc5b2c2895cc62b14a1c2d7ea32f212"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:fd03192e287fbd0899dd8f81c6fb9cbbc69194d2074b38f384cb6fa72b80e9c2"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl", hash = "sha256:88b84956273fbd73420e6d4b8d5ccbe913c65d31351b4c004ae362eba06e1f78"}, - {file = "tornado-6.4-cp38-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:71ddfc23a0e03ef2df1c1397d859868d158c8276a0603b96cf86892bff58149f"}, - {file = "tornado-6.4-cp38-abi3-win32.whl", hash = "sha256:6f8a6c77900f5ae93d8b4ae1196472d0ccc2775cc1dfdc9e7727889145c45052"}, - {file = "tornado-6.4-cp38-abi3-win_amd64.whl", hash = "sha256:10aeaa8006333433da48dec9fe417877f8bcc21f48dda8d661ae79da357b2a63"}, - {file = "tornado-6.4.tar.gz", hash = "sha256:72291fa6e6bc84e626589f1c29d90a5a6d593ef5ae68052ee2ef000dfd273dee"}, -] - -[[package]] -name = "tqdm" -version = "4.66.2" -description = "Fast, Extensible Progress Meter" -optional = false -python-versions = ">=3.7" -files = [ - {file = "tqdm-4.66.2-py3-none-any.whl", hash = "sha256:1ee4f8a893eb9bef51c6e35730cebf234d5d0b6bd112b0271e10ed7c24a02bd9"}, - {file = "tqdm-4.66.2.tar.gz", hash = "sha256:6cd52cdf0fef0e0f543299cfc96fec90d7b8a7e88745f411ec33eb44d5ed3531"}, -] - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] -notebook = ["ipywidgets (>=6)"] -slack = ["slack-sdk"] -telegram = ["requests"] - -[[package]] -name = "traitlets" -version = "5.14.2" -description = "Traitlets Python configuration system" -optional = false -python-versions = ">=3.8" -files = [ - {file = "traitlets-5.14.2-py3-none-any.whl", hash = "sha256:fcdf85684a772ddeba87db2f398ce00b40ff550d1528c03c14dbf6a02003cd80"}, - {file = "traitlets-5.14.2.tar.gz", hash = "sha256:8cdd83c040dab7d1dee822678e5f5d100b514f7b72b01615b26fc5718916fdf9"}, -] - -[package.extras] -docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<8.1)", "pytest-mock", "pytest-mypy-testing"] - -[[package]] -name = "transformers" -version = "4.39.3" -description = "State-of-the-art Machine Learning for JAX, PyTorch and TensorFlow" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "transformers-4.39.3-py3-none-any.whl", hash = "sha256:7838034a12cca3168247f9d2d1dba6724c9de3ae0f73a108258c6b8fc5912601"}, - {file = "transformers-4.39.3.tar.gz", hash = "sha256:2586e5ff4150f122716fc40f5530e92871befc051848fbe82600969c535b762d"}, -] - -[package.dependencies] -filelock = "*" -huggingface-hub = ">=0.19.3,<1.0" -numpy = ">=1.17" -packaging = ">=20.0" -pyyaml = ">=5.1" -regex = "!=2019.12.17" -requests = "*" -safetensors = ">=0.4.1" -tokenizers = ">=0.14,<0.19" -tqdm = ">=4.27" - -[package.extras] -accelerate = ["accelerate (>=0.21.0)"] -agents = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "datasets (!=2.5.0)", "diffusers", "opencv-python", "sentencepiece (>=0.1.91,!=0.1.92)", "torch"] -all = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.6,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm", "tokenizers (>=0.14,<0.19)", "torch", "torchaudio", "torchvision"] -audio = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] -codecarbon = ["codecarbon (==1.2.0)"] -deepspeed = ["accelerate (>=0.21.0)", "deepspeed (>=0.9.3)"] -deepspeed-testing = ["GitPython (<3.1.19)", "accelerate (>=0.21.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "deepspeed (>=0.9.3)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder (>=0.3.0)", "nltk", "optuna", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.1.5)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "timeout-decorator"] -dev = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "av (==9.2.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "decord (==0.6.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "flax (>=0.4.1,<=0.7.0)", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.1.5)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "tensorflow (>=2.6,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "timm", "tokenizers (>=0.14,<0.19)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] -dev-tensorflow = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "isort (>=5.5.4)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "nltk", "onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.1.5)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "tensorboard", "tensorflow (>=2.6,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timeout-decorator", "tokenizers (>=0.14,<0.19)", "urllib3 (<2.0.0)"] -dev-torch = ["GitPython (<3.1.19)", "Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "beautifulsoup4", "codecarbon (==1.2.0)", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "fugashi (>=1.0)", "hf-doc-builder", "hf-doc-builder (>=0.3.0)", "ipadic (>=1.0.0,<2.0)", "isort (>=5.5.4)", "kenlm", "librosa", "nltk", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "optuna", "parameterized", "phonemizer", "protobuf", "psutil", "pyctcdecode (>=0.4.0)", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-timeout", "pytest-xdist", "ray[tune] (>=2.7.0)", "rhoknp (>=1.1.0,<1.3.1)", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.1.5)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "scikit-learn", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "tensorboard", "timeout-decorator", "timm", "tokenizers (>=0.14,<0.19)", "torch", "torchaudio", "torchvision", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)", "urllib3 (<2.0.0)"] -docs = ["Pillow (>=10.0.1,<=15.0)", "accelerate (>=0.21.0)", "av (==9.2.0)", "codecarbon (==1.2.0)", "decord (==0.6.0)", "flax (>=0.4.1,<=0.7.0)", "hf-doc-builder", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "kenlm", "keras-nlp (>=0.3.1)", "librosa", "onnxconverter-common", "optax (>=0.0.8,<=0.1.4)", "optuna", "phonemizer", "protobuf", "pyctcdecode (>=0.4.0)", "ray[tune] (>=2.7.0)", "sentencepiece (>=0.1.91,!=0.1.92)", "sigopt", "tensorflow (>=2.6,<2.16)", "tensorflow-text (<2.16)", "tf2onnx", "timm", "tokenizers (>=0.14,<0.19)", "torch", "torchaudio", "torchvision"] -docs-specific = ["hf-doc-builder"] -flax = ["flax (>=0.4.1,<=0.7.0)", "jax (>=0.4.1,<=0.4.13)", "jaxlib (>=0.4.1,<=0.4.13)", "optax (>=0.0.8,<=0.1.4)"] -flax-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] -ftfy = ["ftfy"] -integrations = ["optuna", "ray[tune] (>=2.7.0)", "sigopt"] -ja = ["fugashi (>=1.0)", "ipadic (>=1.0.0,<2.0)", "rhoknp (>=1.1.0,<1.3.1)", "sudachidict-core (>=20220729)", "sudachipy (>=0.6.6)", "unidic (>=1.0.2)", "unidic-lite (>=1.0.7)"] -modelcreation = ["cookiecutter (==1.7.3)"] -natten = ["natten (>=0.14.6,<0.15.0)"] -onnx = ["onnxconverter-common", "onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)", "tf2onnx"] -onnxruntime = ["onnxruntime (>=1.4.0)", "onnxruntime-tools (>=1.4.2)"] -optuna = ["optuna"] -quality = ["GitPython (<3.1.19)", "datasets (!=2.5.0)", "hf-doc-builder (>=0.3.0)", "isort (>=5.5.4)", "ruff (==0.1.5)", "urllib3 (<2.0.0)"] -ray = ["ray[tune] (>=2.7.0)"] -retrieval = ["datasets (!=2.5.0)", "faiss-cpu"] -sagemaker = ["sagemaker (>=2.31.0)"] -sentencepiece = ["protobuf", "sentencepiece (>=0.1.91,!=0.1.92)"] -serving = ["fastapi", "pydantic", "starlette", "uvicorn"] -sigopt = ["sigopt"] -sklearn = ["scikit-learn"] -speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -testing = ["GitPython (<3.1.19)", "beautifulsoup4", "cookiecutter (==1.7.3)", "datasets (!=2.5.0)", "dill (<0.3.5)", "evaluate (>=0.2.0)", "faiss-cpu", "hf-doc-builder (>=0.3.0)", "nltk", "parameterized", "protobuf", "psutil", "pydantic", "pytest (>=7.2.0,<8.0.0)", "pytest-timeout", "pytest-xdist", "rjieba", "rouge-score (!=0.0.7,!=0.0.8,!=0.1,!=0.1.1)", "ruff (==0.1.5)", "sacrebleu (>=1.4.12,<2.0.0)", "sacremoses", "tensorboard", "timeout-decorator"] -tf = ["keras-nlp (>=0.3.1)", "onnxconverter-common", "tensorflow (>=2.6,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"] -tf-cpu = ["keras-nlp (>=0.3.1)", "onnxconverter-common", "tensorflow-cpu (>=2.6,<2.16)", "tensorflow-text (<2.16)", "tf2onnx"] -tf-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)"] -timm = ["timm"] -tokenizers = ["tokenizers (>=0.14,<0.19)"] -torch = ["accelerate (>=0.21.0)", "torch"] -torch-speech = ["kenlm", "librosa", "phonemizer", "pyctcdecode (>=0.4.0)", "torchaudio"] -torch-vision = ["Pillow (>=10.0.1,<=15.0)", "torchvision"] -torchhub = ["filelock", "huggingface-hub (>=0.19.3,<1.0)", "importlib-metadata", "numpy (>=1.17)", "packaging (>=20.0)", "protobuf", "regex (!=2019.12.17)", "requests", "sentencepiece (>=0.1.91,!=0.1.92)", "tokenizers (>=0.14,<0.19)", "torch", "tqdm (>=4.27)"] -video = ["av (==9.2.0)", "decord (==0.6.0)"] -vision = ["Pillow (>=10.0.1,<=15.0)"] - -[[package]] -name = "triton" -version = "2.1.0" -description = "A language and compiler for custom Deep Learning operations" -optional = false -python-versions = "*" -files = [ - {file = "triton-2.1.0-0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:66439923a30d5d48399b08a9eae10370f6c261a5ec864a64983bae63152d39d7"}, - {file = "triton-2.1.0-0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:919b06453f0033ea52c13eaf7833de0e57db3178d23d4e04f9fc71c4f2c32bf8"}, - {file = "triton-2.1.0-0-cp37-cp37m-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ae4bb8a91de790e1866405211c4d618379781188f40d5c4c399766914e84cd94"}, - {file = "triton-2.1.0-0-cp38-cp38-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:39f6fb6bdccb3e98f3152e3fbea724f1aeae7d749412bbb1fa9c441d474eba26"}, - {file = "triton-2.1.0-0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:21544e522c02005a626c8ad63d39bdff2f31d41069592919ef281e964ed26446"}, - {file = "triton-2.1.0-0-pp37-pypy37_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:143582ca31dd89cd982bd3bf53666bab1c7527d41e185f9e3d8a3051ce1b663b"}, - {file = "triton-2.1.0-0-pp38-pypy38_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:82fc5aeeedf6e36be4e4530cbdcba81a09d65c18e02f52dc298696d45721f3bd"}, - {file = "triton-2.1.0-0-pp39-pypy39_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:81a96d110a738ff63339fc892ded095b31bd0d205e3aace262af8400d40b6fa8"}, -] - -[package.dependencies] -filelock = "*" - -[package.extras] -build = ["cmake (>=3.18)", "lit"] -tests = ["autopep8", "flake8", "isort", "numpy", "pytest", "scipy (>=1.7.1)"] -tutorials = ["matplotlib", "pandas", "tabulate"] - -[[package]] -name = "typer" -version = "0.9.4" -description = "Typer, build great CLIs. Easy to code. Based on Python type hints." -optional = false -python-versions = ">=3.6" -files = [ - {file = "typer-0.9.4-py3-none-any.whl", hash = "sha256:aa6c4a4e2329d868b80ecbaf16f807f2b54e192209d7ac9dd42691d63f7a54eb"}, - {file = "typer-0.9.4.tar.gz", hash = "sha256:f714c2d90afae3a7929fcd72a3abb08df305e1ff61719381384211c4070af57f"}, -] - -[package.dependencies] -click = ">=7.1.1,<9.0.0" -typing-extensions = ">=3.7.4.3" - -[package.extras] -all = ["colorama (>=0.4.3,<0.5.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] -dev = ["autoflake (>=1.3.1,<2.0.0)", "flake8 (>=3.8.3,<4.0.0)", "pre-commit (>=2.17.0,<3.0.0)"] -doc = ["cairosvg (>=2.5.2,<3.0.0)", "mdx-include (>=1.4.1,<2.0.0)", "mkdocs (>=1.1.2,<2.0.0)", "mkdocs-material (>=8.1.4,<9.0.0)", "pillow (>=9.3.0,<10.0.0)"] -test = ["black (>=22.3.0,<23.0.0)", "coverage (>=6.2,<7.0)", "isort (>=5.0.6,<6.0.0)", "mypy (==0.971)", "pytest (>=4.4.0,<8.0.0)", "pytest-cov (>=2.10.0,<5.0.0)", "pytest-sugar (>=0.9.4,<0.10.0)", "pytest-xdist (>=1.32.0,<4.0.0)", "rich (>=10.11.0,<14.0.0)", "shellingham (>=1.3.0,<2.0.0)"] - -[[package]] -name = "types-certifi" -version = "2021.10.8.3" -description = "Typing stubs for certifi" -optional = false -python-versions = "*" -files = [ - {file = "types-certifi-2021.10.8.3.tar.gz", hash = "sha256:72cf7798d165bc0b76e1c10dd1ea3097c7063c42c21d664523b928e88b554a4f"}, - {file = "types_certifi-2021.10.8.3-py3-none-any.whl", hash = "sha256:b2d1e325e69f71f7c78e5943d410e650b4707bb0ef32e4ddf3da37f54176e88a"}, -] - -[[package]] -name = "types-python-dateutil" -version = "2.9.0.20240316" -description = "Typing stubs for python-dateutil" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, - {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, -] - -[[package]] -name = "types-toml" -version = "0.10.8.20240310" -description = "Typing stubs for toml" -optional = false -python-versions = ">=3.8" -files = [ - {file = "types-toml-0.10.8.20240310.tar.gz", hash = "sha256:3d41501302972436a6b8b239c850b26689657e25281b48ff0ec06345b8830331"}, - {file = "types_toml-0.10.8.20240310-py3-none-any.whl", hash = "sha256:627b47775d25fa29977d9c70dc0cbab3f314f32c8d8d0c012f2ef5de7aaec05d"}, -] - -[[package]] -name = "typing-extensions" -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.11.0-py3-none-any.whl", hash = "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a"}, - {file = "typing_extensions-4.11.0.tar.gz", hash = "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0"}, -] - -[[package]] -name = "tzdata" -version = "2024.1" -description = "Provider of IANA time zone data" -optional = false -python-versions = ">=2" -files = [ - {file = "tzdata-2024.1-py2.py3-none-any.whl", hash = "sha256:9068bc196136463f5245e51efda838afa15aaeca9903f49050dfa2679db4d252"}, - {file = "tzdata-2024.1.tar.gz", hash = "sha256:2674120f8d891909751c38abcdfd386ac0a5a1127954fbc332af6b5ceae07efd"}, -] - -[[package]] -name = "uri-template" -version = "1.3.0" -description = "RFC 6570 URI Template Processor" -optional = false -python-versions = ">=3.7" -files = [ - {file = "uri-template-1.3.0.tar.gz", hash = "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7"}, - {file = "uri_template-1.3.0-py3-none-any.whl", hash = "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363"}, -] - -[package.extras] -dev = ["flake8", "flake8-annotations", "flake8-bandit", "flake8-bugbear", "flake8-commas", "flake8-comprehensions", "flake8-continuation", "flake8-datetimez", "flake8-docstrings", "flake8-import-order", "flake8-literal", "flake8-modern-annotations", "flake8-noqa", "flake8-pyproject", "flake8-requirements", "flake8-typechecking-import", "flake8-use-fstring", "mypy", "pep8-naming", "types-PyYAML"] - -[[package]] -name = "urllib3" -version = "2.2.1" -description = "HTTP library with thread-safe connection pooling, file post, and more." -optional = false -python-versions = ">=3.8" -files = [ - {file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"}, - {file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"}, -] - -[package.extras] -brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] -h2 = ["h2 (>=4,<5)"] -socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] -zstd = ["zstandard (>=0.18.0)"] - -[[package]] -name = "uvicorn" -version = "0.29.0" -description = "The lightning-fast ASGI server." -optional = false -python-versions = ">=3.8" -files = [ - {file = "uvicorn-0.29.0-py3-none-any.whl", hash = "sha256:2c2aac7ff4f4365c206fd773a39bf4ebd1047c238f8b8268ad996829323473de"}, - {file = "uvicorn-0.29.0.tar.gz", hash = "sha256:6a69214c0b6a087462412670b3ef21224fa48cae0e452b5883e8e8bdfdd11dd0"}, -] - -[package.dependencies] -click = ">=7.0" -colorama = {version = ">=0.4", optional = true, markers = "sys_platform == \"win32\" and extra == \"standard\""} -h11 = ">=0.8" -httptools = {version = ">=0.5.0", optional = true, markers = "extra == \"standard\""} -python-dotenv = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -pyyaml = {version = ">=5.1", optional = true, markers = "extra == \"standard\""} -typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} -uvloop = {version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1", optional = true, markers = "(sys_platform != \"win32\" and sys_platform != \"cygwin\") and platform_python_implementation != \"PyPy\" and extra == \"standard\""} -watchfiles = {version = ">=0.13", optional = true, markers = "extra == \"standard\""} -websockets = {version = ">=10.4", optional = true, markers = "extra == \"standard\""} - -[package.extras] -standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] - -[[package]] -name = "uvloop" -version = "0.19.0" -description = "Fast implementation of asyncio event loop on top of libuv" -optional = false -python-versions = ">=3.8.0" -files = [ - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:de4313d7f575474c8f5a12e163f6d89c0a878bc49219641d49e6f1444369a90e"}, - {file = "uvloop-0.19.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5588bd21cf1fcf06bded085f37e43ce0e00424197e7c10e77afd4bbefffef428"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b1fd71c3843327f3bbc3237bedcdb6504fd50368ab3e04d0410e52ec293f5b8"}, - {file = "uvloop-0.19.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a05128d315e2912791de6088c34136bfcdd0c7cbc1cf85fd6fd1bb321b7c849"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:cd81bdc2b8219cb4b2556eea39d2e36bfa375a2dd021404f90a62e44efaaf957"}, - {file = "uvloop-0.19.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5f17766fb6da94135526273080f3455a112f82570b2ee5daa64d682387fe0dcd"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4ce6b0af8f2729a02a5d1575feacb2a94fc7b2e983868b009d51c9a9d2149bef"}, - {file = "uvloop-0.19.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:31e672bb38b45abc4f26e273be83b72a0d28d074d5b370fc4dcf4c4eb15417d2"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:570fc0ed613883d8d30ee40397b79207eedd2624891692471808a95069a007c1"}, - {file = "uvloop-0.19.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5138821e40b0c3e6c9478643b4660bd44372ae1e16a322b8fc07478f92684e24"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:91ab01c6cd00e39cde50173ba4ec68a1e578fee9279ba64f5221810a9e786533"}, - {file = "uvloop-0.19.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:47bf3e9312f63684efe283f7342afb414eea4d3011542155c7e625cd799c3b12"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:da8435a3bd498419ee8c13c34b89b5005130a476bda1d6ca8cfdde3de35cd650"}, - {file = "uvloop-0.19.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:02506dc23a5d90e04d4f65c7791e65cf44bd91b37f24cfc3ef6cf2aff05dc7ec"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2693049be9d36fef81741fddb3f441673ba12a34a704e7b4361efb75cf30befc"}, - {file = "uvloop-0.19.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7010271303961c6f0fe37731004335401eb9075a12680738731e9c92ddd96ad6"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:5daa304d2161d2918fa9a17d5635099a2f78ae5b5960e742b2fcfbb7aefaa593"}, - {file = "uvloop-0.19.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:7207272c9520203fea9b93843bb775d03e1cf88a80a936ce760f60bb5add92f3"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:78ab247f0b5671cc887c31d33f9b3abfb88d2614b84e4303f1a63b46c046c8bd"}, - {file = "uvloop-0.19.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:472d61143059c84947aa8bb74eabbace30d577a03a1805b77933d6bd13ddebbd"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45bf4c24c19fb8a50902ae37c5de50da81de4922af65baf760f7c0c42e1088be"}, - {file = "uvloop-0.19.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:271718e26b3e17906b28b67314c45d19106112067205119dddbd834c2b7ce797"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:34175c9fd2a4bc3adc1380e1261f60306344e3407c20a4d684fd5f3be010fa3d"}, - {file = "uvloop-0.19.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:e27f100e1ff17f6feeb1f33968bc185bf8ce41ca557deee9d9bbbffeb72030b7"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:13dfdf492af0aa0a0edf66807d2b465607d11c4fa48f4a1fd41cbea5b18e8e8b"}, - {file = "uvloop-0.19.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e3d4e85ac060e2342ff85e90d0c04157acb210b9ce508e784a944f852a40e67"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8ca4956c9ab567d87d59d49fa3704cf29e37109ad348f2d5223c9bf761a332e7"}, - {file = "uvloop-0.19.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f467a5fd23b4fc43ed86342641f3936a68ded707f4627622fa3f82a120e18256"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:492e2c32c2af3f971473bc22f086513cedfc66a130756145a931a90c3958cb17"}, - {file = "uvloop-0.19.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:2df95fca285a9f5bfe730e51945ffe2fa71ccbfdde3b0da5772b4ee4f2e770d5"}, - {file = "uvloop-0.19.0.tar.gz", hash = "sha256:0246f4fd1bf2bf702e06b0d45ee91677ee5c31242f39aab4ea6fe0c51aedd0fd"}, -] - -[package.extras] -docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)"] -test = ["Cython (>=0.29.36,<0.30.0)", "aiohttp (==3.9.0b0)", "aiohttp (>=3.8.1)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil", "pyOpenSSL (>=23.0.0,<23.1.0)", "pycodestyle (>=2.9.0,<2.10.0)"] - -[[package]] -name = "vllm" -version = "0.3.3" -description = "A high-throughput and memory-efficient inference and serving engine for LLMs" -optional = false -python-versions = ">=3.8" -files = [ - {file = "vllm-0.3.3-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:a49f2de8088b36d74f0a29cfaf0d23c09ac01b707aff1a6b606c0ba991074285"}, - {file = "vllm-0.3.3-cp311-cp311-manylinux1_x86_64.whl", hash = "sha256:12676b8fae922fff52bdb029b5295692a1393be800e5eec8fe3ee8eef7c080fd"}, - {file = "vllm-0.3.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fdbe8d3727312472159921ace712007749600ca557860d03e16a52ac1127c163"}, - {file = "vllm-0.3.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:128d5906cf10c66c0fc8553344766cbfede6d924ae4c02589d6b66ab556b954a"}, - {file = "vllm-0.3.3.tar.gz", hash = "sha256:24b70159bbcfd441bfa9d3e226ba8f5db74837c5325fea4a2104cf46c5d8246e"}, -] - -[package.dependencies] -cupy-cuda12x = "12.1.0" -fastapi = "*" -ninja = "*" -numpy = "*" -outlines = ">=0.0.27" -prometheus-client = ">=0.18.0" -psutil = "*" -pydantic = ">=2.0" -pynvml = "11.5.0" -ray = ">=2.9" -sentencepiece = "*" -torch = "2.1.2" -transformers = ">=4.38.0" -triton = ">=2.1.0" -uvicorn = {version = "*", extras = ["standard"]} -xformers = "0.0.23.post1" - -[[package]] -name = "watchfiles" -version = "0.21.0" -description = "Simple, modern and high performance file watching and code reload in python." -optional = false -python-versions = ">=3.8" -files = [ - {file = "watchfiles-0.21.0-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:27b4035013f1ea49c6c0b42d983133b136637a527e48c132d368eb19bf1ac6aa"}, - {file = "watchfiles-0.21.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c81818595eff6e92535ff32825f31c116f867f64ff8cdf6562cd1d6b2e1e8f3e"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6c107ea3cf2bd07199d66f156e3ea756d1b84dfd43b542b2d870b77868c98c03"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d9ac347653ebd95839a7c607608703b20bc07e577e870d824fa4801bc1cb124"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5eb86c6acb498208e7663ca22dbe68ca2cf42ab5bf1c776670a50919a56e64ab"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f564bf68404144ea6b87a78a3f910cc8de216c6b12a4cf0b27718bf4ec38d303"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3d0f32ebfaa9c6011f8454994f86108c2eb9c79b8b7de00b36d558cadcedaa3d"}, - {file = "watchfiles-0.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d45d9b699ecbac6c7bd8e0a2609767491540403610962968d258fd6405c17c"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:aff06b2cac3ef4616e26ba17a9c250c1fe9dd8a5d907d0193f84c499b1b6e6a9"}, - {file = "watchfiles-0.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d9792dff410f266051025ecfaa927078b94cc7478954b06796a9756ccc7e14a9"}, - {file = "watchfiles-0.21.0-cp310-none-win32.whl", hash = "sha256:214cee7f9e09150d4fb42e24919a1e74d8c9b8a9306ed1474ecaddcd5479c293"}, - {file = "watchfiles-0.21.0-cp310-none-win_amd64.whl", hash = "sha256:1ad7247d79f9f55bb25ab1778fd47f32d70cf36053941f07de0b7c4e96b5d235"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:668c265d90de8ae914f860d3eeb164534ba2e836811f91fecc7050416ee70aa7"}, - {file = "watchfiles-0.21.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:3a23092a992e61c3a6a70f350a56db7197242f3490da9c87b500f389b2d01eef"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e7941bbcfdded9c26b0bf720cb7e6fd803d95a55d2c14b4bd1f6a2772230c586"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:11cd0c3100e2233e9c53106265da31d574355c288e15259c0d40a4405cbae317"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d78f30cbe8b2ce770160d3c08cff01b2ae9306fe66ce899b73f0409dc1846c1b"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6674b00b9756b0af620aa2a3346b01f8e2a3dc729d25617e1b89cf6af4a54eb1"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd7ac678b92b29ba630d8c842d8ad6c555abda1b9ef044d6cc092dacbfc9719d"}, - {file = "watchfiles-0.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9c873345680c1b87f1e09e0eaf8cf6c891b9851d8b4d3645e7efe2ec20a20cc7"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49f56e6ecc2503e7dbe233fa328b2be1a7797d31548e7a193237dcdf1ad0eee0"}, - {file = "watchfiles-0.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:02d91cbac553a3ad141db016e3350b03184deaafeba09b9d6439826ee594b365"}, - {file = "watchfiles-0.21.0-cp311-none-win32.whl", hash = "sha256:ebe684d7d26239e23d102a2bad2a358dedf18e462e8808778703427d1f584400"}, - {file = "watchfiles-0.21.0-cp311-none-win_amd64.whl", hash = "sha256:4566006aa44cb0d21b8ab53baf4b9c667a0ed23efe4aaad8c227bfba0bf15cbe"}, - {file = "watchfiles-0.21.0-cp311-none-win_arm64.whl", hash = "sha256:c550a56bf209a3d987d5a975cdf2063b3389a5d16caf29db4bdddeae49f22078"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:51ddac60b96a42c15d24fbdc7a4bfcd02b5a29c047b7f8bf63d3f6f5a860949a"}, - {file = "watchfiles-0.21.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:511f0b034120cd1989932bf1e9081aa9fb00f1f949fbd2d9cab6264916ae89b1"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cfb92d49dbb95ec7a07511bc9efb0faff8fe24ef3805662b8d6808ba8409a71a"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3f92944efc564867bbf841c823c8b71bb0be75e06b8ce45c084b46411475a915"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:642d66b75eda909fd1112d35c53816d59789a4b38c141a96d62f50a3ef9b3360"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d23bcd6c8eaa6324fe109d8cac01b41fe9a54b8c498af9ce464c1aeeb99903d6"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18d5b4da8cf3e41895b34e8c37d13c9ed294954907929aacd95153508d5d89d7"}, - {file = "watchfiles-0.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1b8d1eae0f65441963d805f766c7e9cd092f91e0c600c820c764a4ff71a0764c"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1fd9a5205139f3c6bb60d11f6072e0552f0a20b712c85f43d42342d162be1235"}, - {file = "watchfiles-0.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a1e3014a625bcf107fbf38eece0e47fa0190e52e45dc6eee5a8265ddc6dc5ea7"}, - {file = "watchfiles-0.21.0-cp312-none-win32.whl", hash = "sha256:9d09869f2c5a6f2d9df50ce3064b3391d3ecb6dced708ad64467b9e4f2c9bef3"}, - {file = "watchfiles-0.21.0-cp312-none-win_amd64.whl", hash = "sha256:18722b50783b5e30a18a8a5db3006bab146d2b705c92eb9a94f78c72beb94094"}, - {file = "watchfiles-0.21.0-cp312-none-win_arm64.whl", hash = "sha256:a3b9bec9579a15fb3ca2d9878deae789df72f2b0fdaf90ad49ee389cad5edab6"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:4ea10a29aa5de67de02256a28d1bf53d21322295cb00bd2d57fcd19b850ebd99"}, - {file = "watchfiles-0.21.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:40bca549fdc929b470dd1dbfcb47b3295cb46a6d2c90e50588b0a1b3bd98f429"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9b37a7ba223b2f26122c148bb8d09a9ff312afca998c48c725ff5a0a632145f7"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ec8c8900dc5c83650a63dd48c4d1d245343f904c4b64b48798c67a3767d7e165"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ad3fe0a3567c2f0f629d800409cd528cb6251da12e81a1f765e5c5345fd0137"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d353c4cfda586db2a176ce42c88f2fc31ec25e50212650c89fdd0f560ee507b"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83a696da8922314ff2aec02987eefb03784f473281d740bf9170181829133765"}, - {file = "watchfiles-0.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a03651352fc20975ee2a707cd2d74a386cd303cc688f407296064ad1e6d1562"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:3ad692bc7792be8c32918c699638b660c0de078a6cbe464c46e1340dadb94c19"}, - {file = "watchfiles-0.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06247538e8253975bdb328e7683f8515ff5ff041f43be6c40bff62d989b7d0b0"}, - {file = "watchfiles-0.21.0-cp38-none-win32.whl", hash = "sha256:9a0aa47f94ea9a0b39dd30850b0adf2e1cd32a8b4f9c7aa443d852aacf9ca214"}, - {file = "watchfiles-0.21.0-cp38-none-win_amd64.whl", hash = "sha256:8d5f400326840934e3507701f9f7269247f7c026d1b6cfd49477d2be0933cfca"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:7f762a1a85a12cc3484f77eee7be87b10f8c50b0b787bb02f4e357403cad0c0e"}, - {file = "watchfiles-0.21.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6e9be3ef84e2bb9710f3f777accce25556f4a71e15d2b73223788d528fcc2052"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c48a10d17571d1275701e14a601e36959ffada3add8cdbc9e5061a6e3579a5d"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c889025f59884423428c261f212e04d438de865beda0b1e1babab85ef4c0f01"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:66fac0c238ab9a2e72d026b5fb91cb902c146202bbd29a9a1a44e8db7b710b6f"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b4a21f71885aa2744719459951819e7bf5a906a6448a6b2bbce8e9cc9f2c8128"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1c9198c989f47898b2c22201756f73249de3748e0fc9de44adaf54a8b259cc0c"}, - {file = "watchfiles-0.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d8f57c4461cd24fda22493109c45b3980863c58a25b8bec885ca8bea6b8d4b28"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:853853cbf7bf9408b404754b92512ebe3e3a83587503d766d23e6bf83d092ee6"}, - {file = "watchfiles-0.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d5b1dc0e708fad9f92c296ab2f948af403bf201db8fb2eb4c8179db143732e49"}, - {file = "watchfiles-0.21.0-cp39-none-win32.whl", hash = "sha256:59137c0c6826bd56c710d1d2bda81553b5e6b7c84d5a676747d80caf0409ad94"}, - {file = "watchfiles-0.21.0-cp39-none-win_amd64.whl", hash = "sha256:6cb8fdc044909e2078c248986f2fc76f911f72b51ea4a4fbbf472e01d14faa58"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab03a90b305d2588e8352168e8c5a1520b721d2d367f31e9332c4235b30b8994"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:927c589500f9f41e370b0125c12ac9e7d3a2fd166b89e9ee2828b3dda20bfe6f"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bd467213195e76f838caf2c28cd65e58302d0254e636e7c0fca81efa4a2e62c"}, - {file = "watchfiles-0.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:02b73130687bc3f6bb79d8a170959042eb56eb3a42df3671c79b428cd73f17cc"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:08dca260e85ffae975448e344834d765983237ad6dc308231aa16e7933db763e"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:3ccceb50c611c433145502735e0370877cced72a6c70fd2410238bcbc7fe51d8"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57d430f5fb63fea141ab71ca9c064e80de3a20b427ca2febcbfcef70ff0ce895"}, - {file = "watchfiles-0.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd5fad9b9c0dd89904bbdea978ce89a2b692a7ee8a0ce19b940e538c88a809c"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:be6dd5d52b73018b21adc1c5d28ac0c68184a64769052dfeb0c5d9998e7f56a2"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:b3cab0e06143768499384a8a5efb9c4dc53e19382952859e4802f294214f36ec"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c6ed10c2497e5fedadf61e465b3ca12a19f96004c15dcffe4bd442ebadc2d85"}, - {file = "watchfiles-0.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:43babacef21c519bc6631c5fce2a61eccdfc011b4bcb9047255e9620732c8097"}, - {file = "watchfiles-0.21.0.tar.gz", hash = "sha256:c76c635fabf542bb78524905718c39f736a98e5ab25b23ec6d4abede1a85a6a3"}, -] - -[package.dependencies] -anyio = ">=3.0.0" - -[[package]] -name = "wcwidth" -version = "0.2.13" -description = "Measures the displayed width of unicode strings in a terminal" -optional = false -python-versions = "*" -files = [ - {file = "wcwidth-0.2.13-py2.py3-none-any.whl", hash = "sha256:3da69048e4540d84af32131829ff948f1e022c1c6bdb8d6102117aac784f6859"}, - {file = "wcwidth-0.2.13.tar.gz", hash = "sha256:72ea0c06399eb286d978fdedb6923a9eb47e1c486ce63e9b4e64fc18303972b5"}, -] - -[[package]] -name = "webcolors" -version = "1.13" -description = "A library for working with the color formats defined by HTML and CSS." -optional = false -python-versions = ">=3.7" -files = [ - {file = "webcolors-1.13-py3-none-any.whl", hash = "sha256:29bc7e8752c0a1bd4a1f03c14d6e6a72e93d82193738fa860cbff59d0fcc11bf"}, - {file = "webcolors-1.13.tar.gz", hash = "sha256:c225b674c83fa923be93d235330ce0300373d02885cef23238813b0d5668304a"}, -] - -[package.extras] -docs = ["furo", "sphinx", "sphinx-copybutton", "sphinx-inline-tabs", "sphinx-notfound-page", "sphinxext-opengraph"] -tests = ["pytest", "pytest-cov"] - -[[package]] -name = "webencodings" -version = "0.5.1" -description = "Character encoding aliases for legacy web content" -optional = false -python-versions = "*" -files = [ - {file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"}, - {file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"}, -] - -[[package]] -name = "websocket-client" -version = "1.7.0" -description = "WebSocket client for Python with low level API options" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websocket-client-1.7.0.tar.gz", hash = "sha256:10e511ea3a8c744631d3bd77e61eb17ed09304c413ad42cf6ddfa4c7787e8fe6"}, - {file = "websocket_client-1.7.0-py3-none-any.whl", hash = "sha256:f4c3d22fec12a2461427a29957ff07d35098ee2d976d3ba244e688b8b4057588"}, -] - -[package.extras] -docs = ["Sphinx (>=6.0)", "sphinx-rtd-theme (>=1.1.0)"] -optional = ["python-socks", "wsaccel"] -test = ["websockets"] - -[[package]] -name = "websockets" -version = "12.0" -description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" -optional = false -python-versions = ">=3.8" -files = [ - {file = "websockets-12.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d554236b2a2006e0ce16315c16eaa0d628dab009c33b63ea03f41c6107958374"}, - {file = "websockets-12.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2d225bb6886591b1746b17c0573e29804619c8f755b5598d875bb4235ea639be"}, - {file = "websockets-12.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:eb809e816916a3b210bed3c82fb88eaf16e8afcf9c115ebb2bacede1797d2547"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c588f6abc13f78a67044c6b1273a99e1cf31038ad51815b3b016ce699f0d75c2"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5aa9348186d79a5f232115ed3fa9020eab66d6c3437d72f9d2c8ac0c6858c558"}, - {file = "websockets-12.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6350b14a40c95ddd53e775dbdbbbc59b124a5c8ecd6fbb09c2e52029f7a9f480"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:70ec754cc2a769bcd218ed8d7209055667b30860ffecb8633a834dde27d6307c"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:6e96f5ed1b83a8ddb07909b45bd94833b0710f738115751cdaa9da1fb0cb66e8"}, - {file = "websockets-12.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:4d87be612cbef86f994178d5186add3d94e9f31cc3cb499a0482b866ec477603"}, - {file = "websockets-12.0-cp310-cp310-win32.whl", hash = "sha256:befe90632d66caaf72e8b2ed4d7f02b348913813c8b0a32fae1cc5fe3730902f"}, - {file = "websockets-12.0-cp310-cp310-win_amd64.whl", hash = "sha256:363f57ca8bc8576195d0540c648aa58ac18cf85b76ad5202b9f976918f4219cf"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:5d873c7de42dea355d73f170be0f23788cf3fa9f7bed718fd2830eefedce01b4"}, - {file = "websockets-12.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3f61726cae9f65b872502ff3c1496abc93ffbe31b278455c418492016e2afc8f"}, - {file = "websockets-12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ed2fcf7a07334c77fc8a230755c2209223a7cc44fc27597729b8ef5425aa61a3"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e332c210b14b57904869ca9f9bf4ca32f5427a03eeb625da9b616c85a3a506c"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5693ef74233122f8ebab026817b1b37fe25c411ecfca084b29bc7d6efc548f45"}, - {file = "websockets-12.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e9e7db18b4539a29cc5ad8c8b252738a30e2b13f033c2d6e9d0549b45841c04"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:6e2df67b8014767d0f785baa98393725739287684b9f8d8a1001eb2839031447"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bea88d71630c5900690fcb03161ab18f8f244805c59e2e0dc4ffadae0a7ee0ca"}, - {file = "websockets-12.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:dff6cdf35e31d1315790149fee351f9e52978130cef6c87c4b6c9b3baf78bc53"}, - {file = "websockets-12.0-cp311-cp311-win32.whl", hash = "sha256:3e3aa8c468af01d70332a382350ee95f6986db479ce7af14d5e81ec52aa2b402"}, - {file = "websockets-12.0-cp311-cp311-win_amd64.whl", hash = "sha256:25eb766c8ad27da0f79420b2af4b85d29914ba0edf69f547cc4f06ca6f1d403b"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0e6e2711d5a8e6e482cacb927a49a3d432345dfe7dea8ace7b5790df5932e4df"}, - {file = "websockets-12.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:dbcf72a37f0b3316e993e13ecf32f10c0e1259c28ffd0a85cee26e8549595fbc"}, - {file = "websockets-12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12743ab88ab2af1d17dd4acb4645677cb7063ef4db93abffbf164218a5d54c6b"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7b645f491f3c48d3f8a00d1fce07445fab7347fec54a3e65f0725d730d5b99cb"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9893d1aa45a7f8b3bc4510f6ccf8db8c3b62120917af15e3de247f0780294b92"}, - {file = "websockets-12.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f38a7b376117ef7aff996e737583172bdf535932c9ca021746573bce40165ed"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:f764ba54e33daf20e167915edc443b6f88956f37fb606449b4a5b10ba42235a5"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:1e4b3f8ea6a9cfa8be8484c9221ec0257508e3a1ec43c36acdefb2a9c3b00aa2"}, - {file = "websockets-12.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fdf06fd06c32205a07e47328ab49c40fc1407cdec801d698a7c41167ea45113"}, - {file = "websockets-12.0-cp312-cp312-win32.whl", hash = "sha256:baa386875b70cbd81798fa9f71be689c1bf484f65fd6fb08d051a0ee4e79924d"}, - {file = "websockets-12.0-cp312-cp312-win_amd64.whl", hash = "sha256:ae0a5da8f35a5be197f328d4727dbcfafa53d1824fac3d96cdd3a642fe09394f"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5f6ffe2c6598f7f7207eef9a1228b6f5c818f9f4d53ee920aacd35cec8110438"}, - {file = "websockets-12.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9edf3fc590cc2ec20dc9d7a45108b5bbaf21c0d89f9fd3fd1685e223771dc0b2"}, - {file = "websockets-12.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8572132c7be52632201a35f5e08348137f658e5ffd21f51f94572ca6c05ea81d"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:604428d1b87edbf02b233e2c207d7d528460fa978f9e391bd8aaf9c8311de137"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1a9d160fd080c6285e202327aba140fc9a0d910b09e423afff4ae5cbbf1c7205"}, - {file = "websockets-12.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87b4aafed34653e465eb77b7c93ef058516cb5acf3eb21e42f33928616172def"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b2ee7288b85959797970114deae81ab41b731f19ebcd3bd499ae9ca0e3f1d2c8"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:7fa3d25e81bfe6a89718e9791128398a50dec6d57faf23770787ff441d851967"}, - {file = "websockets-12.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:a571f035a47212288e3b3519944f6bf4ac7bc7553243e41eac50dd48552b6df7"}, - {file = "websockets-12.0-cp38-cp38-win32.whl", hash = "sha256:3c6cc1360c10c17463aadd29dd3af332d4a1adaa8796f6b0e9f9df1fdb0bad62"}, - {file = "websockets-12.0-cp38-cp38-win_amd64.whl", hash = "sha256:1bf386089178ea69d720f8db6199a0504a406209a0fc23e603b27b300fdd6892"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ab3d732ad50a4fbd04a4490ef08acd0517b6ae6b77eb967251f4c263011a990d"}, - {file = "websockets-12.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a1d9697f3337a89691e3bd8dc56dea45a6f6d975f92e7d5f773bc715c15dde28"}, - {file = "websockets-12.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1df2fbd2c8a98d38a66f5238484405b8d1d16f929bb7a33ed73e4801222a6f53"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23509452b3bc38e3a057382c2e941d5ac2e01e251acce7adc74011d7d8de434c"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2e5fc14ec6ea568200ea4ef46545073da81900a2b67b3e666f04adf53ad452ec"}, - {file = "websockets-12.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46e71dbbd12850224243f5d2aeec90f0aaa0f2dde5aeeb8fc8df21e04d99eff9"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b81f90dcc6c85a9b7f29873beb56c94c85d6f0dac2ea8b60d995bd18bf3e2aae"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a02413bc474feda2849c59ed2dfb2cddb4cd3d2f03a2fedec51d6e959d9b608b"}, - {file = "websockets-12.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:bbe6013f9f791944ed31ca08b077e26249309639313fff132bfbf3ba105673b9"}, - {file = "websockets-12.0-cp39-cp39-win32.whl", hash = "sha256:cbe83a6bbdf207ff0541de01e11904827540aa069293696dd528a6640bd6a5f6"}, - {file = "websockets-12.0-cp39-cp39-win_amd64.whl", hash = "sha256:fc4e7fa5414512b481a2483775a8e8be7803a35b30ca805afa4998a84f9fd9e8"}, - {file = "websockets-12.0-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:248d8e2446e13c1d4326e0a6a4e9629cb13a11195051a73acf414812700badbd"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f44069528d45a933997a6fef143030d8ca8042f0dfaad753e2906398290e2870"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e37d36f0d19f0a4413d3e18c0d03d0c268ada2061868c1e6f5ab1a6d575077"}, - {file = "websockets-12.0-pp310-pypy310_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d829f975fc2e527a3ef2f9c8f25e553eb7bc779c6665e8e1d52aa22800bb38b"}, - {file = "websockets-12.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2c71bd45a777433dd9113847af751aae36e448bc6b8c361a566cb043eda6ec30"}, - {file = "websockets-12.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:0bee75f400895aef54157b36ed6d3b308fcab62e5260703add87f44cee9c82a6"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:423fc1ed29f7512fceb727e2d2aecb952c46aa34895e9ed96071821309951123"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a5e9964ef509016759f2ef3f2c1e13f403725a5e6a1775555994966a66e931"}, - {file = "websockets-12.0-pp38-pypy38_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3181df4583c4d3994d31fb235dc681d2aaad744fbdbf94c4802485ececdecf2"}, - {file = "websockets-12.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:b067cb952ce8bf40115f6c19f478dc71c5e719b7fbaa511359795dfd9d1a6468"}, - {file = "websockets-12.0-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:00700340c6c7ab788f176d118775202aadea7602c5cc6be6ae127761c16d6b0b"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e469d01137942849cff40517c97a30a93ae79917752b34029f0ec72df6b46399"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffefa1374cd508d633646d51a8e9277763a9b78ae71324183693959cf94635a7"}, - {file = "websockets-12.0-pp39-pypy39_pp73-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba0cab91b3956dfa9f512147860783a1829a8d905ee218a9837c18f683239611"}, - {file = "websockets-12.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2cb388a5bfb56df4d9a406783b7f9dbefb888c09b71629351cc6b036e9259370"}, - {file = "websockets-12.0-py3-none-any.whl", hash = "sha256:dc284bbc8d7c78a6c69e0c7325ab46ee5e40bb4d50e494d8131a07ef47500e9e"}, - {file = "websockets-12.0.tar.gz", hash = "sha256:81df9cbcbb6c260de1e007e58c011bfebe2dafc8435107b0537f393dd38c8b1b"}, -] - -[[package]] -name = "widgetsnbextension" -version = "4.0.10" -description = "Jupyter interactive widgets for Jupyter Notebook" -optional = false -python-versions = ">=3.7" -files = [ - {file = "widgetsnbextension-4.0.10-py3-none-any.whl", hash = "sha256:d37c3724ec32d8c48400a435ecfa7d3e259995201fbefa37163124a9fcb393cc"}, - {file = "widgetsnbextension-4.0.10.tar.gz", hash = "sha256:64196c5ff3b9a9183a8e699a4227fb0b7002f252c814098e66c4d1cd0644688f"}, -] - -[[package]] -name = "xformers" -version = "0.0.23.post1" -description = "XFormers: A collection of composable Transformer building blocks." -optional = false -python-versions = ">=3.7" -files = [ - {file = "xformers-0.0.23.post1-cp310-cp310-manylinux2014_x86_64.whl", hash = "sha256:f3491e4b1077314a4535fc78c36b592a13b794eefffaa308db879f7147424a96"}, - {file = "xformers-0.0.23.post1-cp310-cp310-win_amd64.whl", hash = "sha256:ef0744c5d1abcad7f8692b5a30ee72a71215451cbde020e2fb37af20f46ba76f"}, - {file = "xformers-0.0.23.post1-cp311-cp311-manylinux2014_x86_64.whl", hash = "sha256:2aea20e84852fafe87f4103b4adfe5f324915defa403e98fadc5a97f333f7105"}, - {file = "xformers-0.0.23.post1-cp311-cp311-win_amd64.whl", hash = "sha256:372995c113c3505648f0c2d2daac53a6df60a22f30eae98e47daca5efd38fe71"}, - {file = "xformers-0.0.23.post1-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:17e26c66cd25ad529705228f62744ed3f86f0fe3c54fa4e23c78cd7da7a71776"}, - {file = "xformers-0.0.23.post1-cp38-cp38-win_amd64.whl", hash = "sha256:aad762aebfe7ea3f6b9132afbf5ae88cdaf87d0c377d199dfee193e1a72d0d24"}, - {file = "xformers-0.0.23.post1-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:a117e4cc835d9a19c653d79b5c66e37c72f713241e2d85b6561a15006f84b6e6"}, - {file = "xformers-0.0.23.post1-cp39-cp39-win_amd64.whl", hash = "sha256:e08e4ebbd9fbfe9545de4028b7f604d21dc4e301dc651b3fc1bb95ae6797524f"}, - {file = "xformers-0.0.23.post1.tar.gz", hash = "sha256:b443b158bd7b5275b485d2c6aee94ebc2152878fd784e379b1c8bcb1d67f3b81"}, -] - -[package.dependencies] -numpy = "*" -torch = "2.1.2" - -[[package]] -name = "xxhash" -version = "3.4.1" -description = "Python binding for xxHash" -optional = false -python-versions = ">=3.7" -files = [ - {file = "xxhash-3.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:91dbfa55346ad3e18e738742236554531a621042e419b70ad8f3c1d9c7a16e7f"}, - {file = "xxhash-3.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:665a65c2a48a72068fcc4d21721510df5f51f1142541c890491afc80451636d2"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb11628470a6004dc71a09fe90c2f459ff03d611376c1debeec2d648f44cb693"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bef2a7dc7b4f4beb45a1edbba9b9194c60a43a89598a87f1a0226d183764189"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9c0f7b2d547d72c7eda7aa817acf8791f0146b12b9eba1d4432c531fb0352228"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00f2fdef6b41c9db3d2fc0e7f94cb3db86693e5c45d6de09625caad9a469635b"}, - {file = "xxhash-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23cfd9ca09acaf07a43e5a695143d9a21bf00f5b49b15c07d5388cadf1f9ce11"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6a9ff50a3cf88355ca4731682c168049af1ca222d1d2925ef7119c1a78e95b3b"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:f1d7c69a1e9ca5faa75546fdd267f214f63f52f12692f9b3a2f6467c9e67d5e7"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:672b273040d5d5a6864a36287f3514efcd1d4b1b6a7480f294c4b1d1ee1b8de0"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:4178f78d70e88f1c4a89ff1ffe9f43147185930bb962ee3979dba15f2b1cc799"}, - {file = "xxhash-3.4.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9804b9eb254d4b8cc83ab5a2002128f7d631dd427aa873c8727dba7f1f0d1c2b"}, - {file = "xxhash-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c09c49473212d9c87261d22c74370457cfff5db2ddfc7fd1e35c80c31a8c14ce"}, - {file = "xxhash-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:ebbb1616435b4a194ce3466d7247df23499475c7ed4eb2681a1fa42ff766aff6"}, - {file = "xxhash-3.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:25dc66be3db54f8a2d136f695b00cfe88018e59ccff0f3b8f545869f376a8a46"}, - {file = "xxhash-3.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:58c49083801885273e262c0f5bbeac23e520564b8357fbb18fb94ff09d3d3ea5"}, - {file = "xxhash-3.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b526015a973bfbe81e804a586b703f163861da36d186627e27524f5427b0d520"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:36ad4457644c91a966f6fe137d7467636bdc51a6ce10a1d04f365c70d6a16d7e"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:248d3e83d119770f96003271fe41e049dd4ae52da2feb8f832b7a20e791d2920"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2070b6d5bbef5ee031666cf21d4953c16e92c2f8a24a94b5c240f8995ba3b1d0"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b2746035f518f0410915e247877f7df43ef3372bf36cfa52cc4bc33e85242641"}, - {file = "xxhash-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a8ba6181514681c2591840d5632fcf7356ab287d4aff1c8dea20f3c78097088"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aac5010869240e95f740de43cd6a05eae180c59edd182ad93bf12ee289484fa"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4cb11d8debab1626181633d184b2372aaa09825bde709bf927704ed72765bed1"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b29728cff2c12f3d9f1d940528ee83918d803c0567866e062683f300d1d2eff3"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:a15cbf3a9c40672523bdb6ea97ff74b443406ba0ab9bca10ceccd9546414bd84"}, - {file = "xxhash-3.4.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:6e66df260fed01ed8ea790c2913271641c58481e807790d9fca8bfd5a3c13844"}, - {file = "xxhash-3.4.1-cp311-cp311-win32.whl", hash = "sha256:e867f68a8f381ea12858e6d67378c05359d3a53a888913b5f7d35fbf68939d5f"}, - {file = "xxhash-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:200a5a3ad9c7c0c02ed1484a1d838b63edcf92ff538770ea07456a3732c577f4"}, - {file = "xxhash-3.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:1d03f1c0d16d24ea032e99f61c552cb2b77d502e545187338bea461fde253583"}, - {file = "xxhash-3.4.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c4bbba9b182697a52bc0c9f8ec0ba1acb914b4937cd4a877ad78a3b3eeabefb3"}, - {file = "xxhash-3.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9fd28a9da300e64e434cfc96567a8387d9a96e824a9be1452a1e7248b7763b78"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6066d88c9329ab230e18998daec53d819daeee99d003955c8db6fc4971b45ca3"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:93805bc3233ad89abf51772f2ed3355097a5dc74e6080de19706fc447da99cd3"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64da57d5ed586ebb2ecdde1e997fa37c27fe32fe61a656b77fabbc58e6fbff6e"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a97322e9a7440bf3c9805cbaac090358b43f650516486746f7fa482672593df"}, - {file = "xxhash-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe750d512982ee7d831838a5dee9e9848f3fb440e4734cca3f298228cc957a6"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:fd79d4087727daf4d5b8afe594b37d611ab95dc8e29fe1a7517320794837eb7d"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:743612da4071ff9aa4d055f3f111ae5247342931dedb955268954ef7201a71ff"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:b41edaf05734092f24f48c0958b3c6cbaaa5b7e024880692078c6b1f8247e2fc"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:a90356ead70d715fe64c30cd0969072de1860e56b78adf7c69d954b43e29d9fa"}, - {file = "xxhash-3.4.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ac56eebb364e44c85e1d9e9cc5f6031d78a34f0092fea7fc80478139369a8b4a"}, - {file = "xxhash-3.4.1-cp312-cp312-win32.whl", hash = "sha256:911035345932a153c427107397c1518f8ce456f93c618dd1c5b54ebb22e73747"}, - {file = "xxhash-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:f31ce76489f8601cc7b8713201ce94b4bd7b7ce90ba3353dccce7e9e1fee71fa"}, - {file = "xxhash-3.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:b5beb1c6a72fdc7584102f42c4d9df232ee018ddf806e8c90906547dfb43b2da"}, - {file = "xxhash-3.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6d42b24d1496deb05dee5a24ed510b16de1d6c866c626c2beb11aebf3be278b9"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b685fab18876b14a8f94813fa2ca80cfb5ab6a85d31d5539b7cd749ce9e3624"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:419ffe34c17ae2df019a4685e8d3934d46b2e0bbe46221ab40b7e04ed9f11137"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e041ce5714f95251a88670c114b748bca3bf80cc72400e9f23e6d0d59cf2681"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc860d887c5cb2f524899fb8338e1bb3d5789f75fac179101920d9afddef284b"}, - {file = "xxhash-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:312eba88ffe0a05e332e3a6f9788b73883752be63f8588a6dc1261a3eaaaf2b2"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:e01226b6b6a1ffe4e6bd6d08cfcb3ca708b16f02eb06dd44f3c6e53285f03e4f"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9f3025a0d5d8cf406a9313cd0d5789c77433ba2004b1c75439b67678e5136537"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:6d3472fd4afef2a567d5f14411d94060099901cd8ce9788b22b8c6f13c606a93"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:43984c0a92f06cac434ad181f329a1445017c33807b7ae4f033878d860a4b0f2"}, - {file = "xxhash-3.4.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a55e0506fdb09640a82ec4f44171273eeabf6f371a4ec605633adb2837b5d9d5"}, - {file = "xxhash-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:faec30437919555b039a8bdbaba49c013043e8f76c999670aef146d33e05b3a0"}, - {file = "xxhash-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:c9e1b646af61f1fc7083bb7b40536be944f1ac67ef5e360bca2d73430186971a"}, - {file = "xxhash-3.4.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:961d948b7b1c1b6c08484bbce3d489cdf153e4122c3dfb07c2039621243d8795"}, - {file = "xxhash-3.4.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:719a378930504ab159f7b8e20fa2aa1896cde050011af838af7e7e3518dd82de"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:74fb5cb9406ccd7c4dd917f16630d2e5e8cbbb02fc2fca4e559b2a47a64f4940"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5dab508ac39e0ab988039bc7f962c6ad021acd81fd29145962b068df4148c476"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8c59f3e46e7daf4c589e8e853d700ef6607afa037bfad32c390175da28127e8c"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc07256eff0795e0f642df74ad096f8c5d23fe66bc138b83970b50fc7f7f6c5"}, - {file = "xxhash-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e9f749999ed80f3955a4af0eb18bb43993f04939350b07b8dd2f44edc98ffee9"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:7688d7c02149a90a3d46d55b341ab7ad1b4a3f767be2357e211b4e893efbaaf6"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a8b4977963926f60b0d4f830941c864bed16aa151206c01ad5c531636da5708e"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:8106d88da330f6535a58a8195aa463ef5281a9aa23b04af1848ff715c4398fb4"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4c76a77dbd169450b61c06fd2d5d436189fc8ab7c1571d39265d4822da16df22"}, - {file = "xxhash-3.4.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:11f11357c86d83e53719c592021fd524efa9cf024dc7cb1dfb57bbbd0d8713f2"}, - {file = "xxhash-3.4.1-cp38-cp38-win32.whl", hash = "sha256:0c786a6cd74e8765c6809892a0d45886e7c3dc54de4985b4a5eb8b630f3b8e3b"}, - {file = "xxhash-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:aabf37fb8fa27430d50507deeab2ee7b1bcce89910dd10657c38e71fee835594"}, - {file = "xxhash-3.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6127813abc1477f3a83529b6bbcfeddc23162cece76fa69aee8f6a8a97720562"}, - {file = "xxhash-3.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ef2e194262f5db16075caea7b3f7f49392242c688412f386d3c7b07c7733a70a"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71be94265b6c6590f0018bbf73759d21a41c6bda20409782d8117e76cd0dfa8b"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10e0a619cdd1c0980e25eb04e30fe96cf8f4324758fa497080af9c21a6de573f"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fa122124d2e3bd36581dd78c0efa5f429f5220313479fb1072858188bc2d5ff1"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17032f5a4fea0a074717fe33477cb5ee723a5f428de7563e75af64bfc1b1e10"}, - {file = "xxhash-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca7783b20e3e4f3f52f093538895863f21d18598f9a48211ad757680c3bd006f"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d77d09a1113899fad5f354a1eb4f0a9afcf58cefff51082c8ad643ff890e30cf"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:21287bcdd299fdc3328cc0fbbdeaa46838a1c05391264e51ddb38a3f5b09611f"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:dfd7a6cc483e20b4ad90224aeb589e64ec0f31e5610ab9957ff4314270b2bf31"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:543c7fcbc02bbb4840ea9915134e14dc3dc15cbd5a30873a7a5bf66039db97ec"}, - {file = "xxhash-3.4.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:fe0a98d990e433013f41827b62be9ab43e3cf18e08b1483fcc343bda0d691182"}, - {file = "xxhash-3.4.1-cp39-cp39-win32.whl", hash = "sha256:b9097af00ebf429cc7c0e7d2fdf28384e4e2e91008130ccda8d5ae653db71e54"}, - {file = "xxhash-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:d699b921af0dcde50ab18be76c0d832f803034d80470703700cb7df0fbec2832"}, - {file = "xxhash-3.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:2be491723405e15cc099ade1280133ccfbf6322d2ef568494fb7d07d280e7eee"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:431625fad7ab5649368c4849d2b49a83dc711b1f20e1f7f04955aab86cd307bc"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc6dbd5fc3c9886a9e041848508b7fb65fd82f94cc793253990f81617b61fe49"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3ff8dbd0ec97aec842476cb8ccc3e17dd288cd6ce3c8ef38bff83d6eb927817"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef73a53fe90558a4096e3256752268a8bdc0322f4692ed928b6cd7ce06ad4fe3"}, - {file = "xxhash-3.4.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:450401f42bbd274b519d3d8dcf3c57166913381a3d2664d6609004685039f9d3"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a162840cf4de8a7cd8720ff3b4417fbc10001eefdd2d21541a8226bb5556e3bb"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b736a2a2728ba45017cb67785e03125a79d246462dfa892d023b827007412c52"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d0ae4c2e7698adef58710d6e7a32ff518b66b98854b1c68e70eee504ad061d8"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6322c4291c3ff174dcd104fae41500e75dad12be6f3085d119c2c8a80956c51"}, - {file = "xxhash-3.4.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:dd59ed668801c3fae282f8f4edadf6dc7784db6d18139b584b6d9677ddde1b6b"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:92693c487e39523a80474b0394645b393f0ae781d8db3474ccdcead0559ccf45"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4603a0f642a1e8d7f3ba5c4c25509aca6a9c1cc16f85091004a7028607ead663"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6fa45e8cbfbadb40a920fe9ca40c34b393e0b067082d94006f7f64e70c7490a6"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:595b252943b3552de491ff51e5bb79660f84f033977f88f6ca1605846637b7c6"}, - {file = "xxhash-3.4.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:562d8b8f783c6af969806aaacf95b6c7b776929ae26c0cd941d54644ea7ef51e"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:41ddeae47cf2828335d8d991f2d2b03b0bdc89289dc64349d712ff8ce59d0647"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c44d584afdf3c4dbb3277e32321d1a7b01d6071c1992524b6543025fb8f4206f"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd7bddb3a5b86213cc3f2c61500c16945a1b80ecd572f3078ddbbe68f9dabdfb"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9ecb6c987b62437c2f99c01e97caf8d25660bf541fe79a481d05732e5236719c"}, - {file = "xxhash-3.4.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:696b4e18b7023527d5c50ed0626ac0520edac45a50ec7cf3fc265cd08b1f4c03"}, - {file = "xxhash-3.4.1.tar.gz", hash = "sha256:0379d6cf1ff987cd421609a264ce025e74f346e3e145dd106c0cc2e3ec3f99a9"}, -] - -[[package]] -name = "yarl" -version = "1.9.4" -description = "Yet another URL library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a8c1df72eb746f4136fe9a2e72b0c9dc1da1cbd23b5372f94b5820ff8ae30e0e"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a3a6ed1d525bfb91b3fc9b690c5a21bb52de28c018530ad85093cc488bee2dd2"}, - {file = "yarl-1.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c38c9ddb6103ceae4e4498f9c08fac9b590c5c71b0370f98714768e22ac6fa66"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d9e09c9d74f4566e905a0b8fa668c58109f7624db96a2171f21747abc7524234"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8477c1ee4bd47c57d49621a062121c3023609f7a13b8a46953eb6c9716ca392"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d5ff2c858f5f6a42c2a8e751100f237c5e869cbde669a724f2062d4c4ef93551"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:357495293086c5b6d34ca9616a43d329317feab7917518bc97a08f9e55648455"}, - {file = "yarl-1.9.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54525ae423d7b7a8ee81ba189f131054defdb122cde31ff17477951464c1691c"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:801e9264d19643548651b9db361ce3287176671fb0117f96b5ac0ee1c3530d53"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e516dc8baf7b380e6c1c26792610230f37147bb754d6426462ab115a02944385"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:7d5aaac37d19b2904bb9dfe12cdb08c8443e7ba7d2852894ad448d4b8f442863"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:54beabb809ffcacbd9d28ac57b0db46e42a6e341a030293fb3185c409e626b8b"}, - {file = "yarl-1.9.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bac8d525a8dbc2a1507ec731d2867025d11ceadcb4dd421423a5d42c56818541"}, - {file = "yarl-1.9.4-cp310-cp310-win32.whl", hash = "sha256:7855426dfbddac81896b6e533ebefc0af2f132d4a47340cee6d22cac7190022d"}, - {file = "yarl-1.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:848cd2a1df56ddbffeb375535fb62c9d1645dde33ca4d51341378b3f5954429b"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:35a2b9396879ce32754bd457d31a51ff0a9d426fd9e0e3c33394bf4b9036b099"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c7d56b293cc071e82532f70adcbd8b61909eec973ae9d2d1f9b233f3d943f2c"}, - {file = "yarl-1.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d8a1c6c0be645c745a081c192e747c5de06e944a0d21245f4cf7c05e457c36e0"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b3c1ffe10069f655ea2d731808e76e0f452fc6c749bea04781daf18e6039525"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:549d19c84c55d11687ddbd47eeb348a89df9cb30e1993f1b128f4685cd0ebbf8"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7409f968456111140c1c95301cadf071bd30a81cbd7ab829169fb9e3d72eae9"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23a6d84d9d1738dbc6e38167776107e63307dfc8ad108e580548d1f2c587f42"}, - {file = "yarl-1.9.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d8b889777de69897406c9fb0b76cdf2fd0f31267861ae7501d93003d55f54fbe"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:03caa9507d3d3c83bca08650678e25364e1843b484f19986a527630ca376ecce"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:4e9035df8d0880b2f1c7f5031f33f69e071dfe72ee9310cfc76f7b605958ceb9"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:c0ec0ed476f77db9fb29bca17f0a8fcc7bc97ad4c6c1d8959c507decb22e8572"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:ee04010f26d5102399bd17f8df8bc38dc7ccd7701dc77f4a68c5b8d733406958"}, - {file = "yarl-1.9.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:49a180c2e0743d5d6e0b4d1a9e5f633c62eca3f8a86ba5dd3c471060e352ca98"}, - {file = "yarl-1.9.4-cp311-cp311-win32.whl", hash = "sha256:81eb57278deb6098a5b62e88ad8281b2ba09f2f1147c4767522353eaa6260b31"}, - {file = "yarl-1.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:d1d2532b340b692880261c15aee4dc94dd22ca5d61b9db9a8a361953d36410b1"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0d2454f0aef65ea81037759be5ca9947539667eecebca092733b2eb43c965a81"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:44d8ffbb9c06e5a7f529f38f53eda23e50d1ed33c6c869e01481d3fafa6b8142"}, - {file = "yarl-1.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:aaaea1e536f98754a6e5c56091baa1b6ce2f2700cc4a00b0d49eca8dea471074"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3777ce5536d17989c91696db1d459574e9a9bd37660ea7ee4d3344579bb6f129"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9fc5fc1eeb029757349ad26bbc5880557389a03fa6ada41703db5e068881e5f2"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea65804b5dc88dacd4a40279af0cdadcfe74b3e5b4c897aa0d81cf86927fee78"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa102d6d280a5455ad6a0f9e6d769989638718e938a6a0a2ff3f4a7ff8c62cc4"}, - {file = "yarl-1.9.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09efe4615ada057ba2d30df871d2f668af661e971dfeedf0c159927d48bbeff0"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:008d3e808d03ef28542372d01057fd09168419cdc8f848efe2804f894ae03e51"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f5cb257bc2ec58f437da2b37a8cd48f666db96d47b8a3115c29f316313654ff"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:992f18e0ea248ee03b5a6e8b3b4738850ae7dbb172cc41c966462801cbf62cf7"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:0e9d124c191d5b881060a9e5060627694c3bdd1fe24c5eecc8d5d7d0eb6faabc"}, - {file = "yarl-1.9.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3986b6f41ad22988e53d5778f91855dc0399b043fc8946d4f2e68af22ee9ff10"}, - {file = "yarl-1.9.4-cp312-cp312-win32.whl", hash = "sha256:4b21516d181cd77ebd06ce160ef8cc2a5e9ad35fb1c5930882baff5ac865eee7"}, - {file = "yarl-1.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a9bd00dc3bc395a662900f33f74feb3e757429e545d831eef5bb280252631984"}, - {file = "yarl-1.9.4-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:63b20738b5aac74e239622d2fe30df4fca4942a86e31bf47a81a0e94c14df94f"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7d7f7de27b8944f1fee2c26a88b4dabc2409d2fea7a9ed3df79b67277644e17"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c74018551e31269d56fab81a728f683667e7c28c04e807ba08f8c9e3bba32f14"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ca06675212f94e7a610e85ca36948bb8fc023e458dd6c63ef71abfd482481aa5"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5aef935237d60a51a62b86249839b51345f47564208c6ee615ed2a40878dccdd"}, - {file = "yarl-1.9.4-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b134fd795e2322b7684155b7855cc99409d10b2e408056db2b93b51a52accc7"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d25039a474c4c72a5ad4b52495056f843a7ff07b632c1b92ea9043a3d9950f6e"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f7d6b36dd2e029b6bcb8a13cf19664c7b8e19ab3a58e0fefbb5b8461447ed5ec"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:957b4774373cf6f709359e5c8c4a0af9f6d7875db657adb0feaf8d6cb3c3964c"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:d7eeb6d22331e2fd42fce928a81c697c9ee2d51400bd1a28803965883e13cead"}, - {file = "yarl-1.9.4-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6a962e04b8f91f8c4e5917e518d17958e3bdee71fd1d8b88cdce74dd0ebbf434"}, - {file = "yarl-1.9.4-cp37-cp37m-win32.whl", hash = "sha256:f3bc6af6e2b8f92eced34ef6a96ffb248e863af20ef4fde9448cc8c9b858b749"}, - {file = "yarl-1.9.4-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4d7a90a92e528aadf4965d685c17dacff3df282db1121136c382dc0b6014d2"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ec61d826d80fc293ed46c9dd26995921e3a82146feacd952ef0757236fc137be"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8be9e837ea9113676e5754b43b940b50cce76d9ed7d2461df1af39a8ee674d9f"}, - {file = "yarl-1.9.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bef596fdaa8f26e3d66af846bbe77057237cb6e8efff8cd7cc8dff9a62278bbf"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2d47552b6e52c3319fede1b60b3de120fe83bde9b7bddad11a69fb0af7db32f1"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84fc30f71689d7fc9168b92788abc977dc8cefa806909565fc2951d02f6b7d57"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4aa9741085f635934f3a2583e16fcf62ba835719a8b2b28fb2917bb0537c1dfa"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:206a55215e6d05dbc6c98ce598a59e6fbd0c493e2de4ea6cc2f4934d5a18d130"}, - {file = "yarl-1.9.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:07574b007ee20e5c375a8fe4a0789fad26db905f9813be0f9fef5a68080de559"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:5a2e2433eb9344a163aced6a5f6c9222c0786e5a9e9cac2c89f0b28433f56e23"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6ad6d10ed9b67a382b45f29ea028f92d25bc0bc1daf6c5b801b90b5aa70fb9ec"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:6fe79f998a4052d79e1c30eeb7d6c1c1056ad33300f682465e1b4e9b5a188b78"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:a825ec844298c791fd28ed14ed1bffc56a98d15b8c58a20e0e08c1f5f2bea1be"}, - {file = "yarl-1.9.4-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:8619d6915b3b0b34420cf9b2bb6d81ef59d984cb0fde7544e9ece32b4b3043c3"}, - {file = "yarl-1.9.4-cp38-cp38-win32.whl", hash = "sha256:686a0c2f85f83463272ddffd4deb5e591c98aac1897d65e92319f729c320eece"}, - {file = "yarl-1.9.4-cp38-cp38-win_amd64.whl", hash = "sha256:a00862fb23195b6b8322f7d781b0dc1d82cb3bcac346d1e38689370cc1cc398b"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:604f31d97fa493083ea21bd9b92c419012531c4e17ea6da0f65cacdcf5d0bd27"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8a854227cf581330ffa2c4824d96e52ee621dd571078a252c25e3a3b3d94a1b1"}, - {file = "yarl-1.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ba6f52cbc7809cd8d74604cce9c14868306ae4aa0282016b641c661f981a6e91"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a6327976c7c2f4ee6816eff196e25385ccc02cb81427952414a64811037bbc8b"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8397a3817d7dcdd14bb266283cd1d6fc7264a48c186b986f32e86d86d35fbac5"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0381b4ce23ff92f8170080c97678040fc5b08da85e9e292292aba67fdac6c34"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23d32a2594cb5d565d358a92e151315d1b2268bc10f4610d098f96b147370136"}, - {file = "yarl-1.9.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ddb2a5c08a4eaaba605340fdee8fc08e406c56617566d9643ad8bf6852778fc7"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:26a1dc6285e03f3cc9e839a2da83bcbf31dcb0d004c72d0730e755b33466c30e"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:18580f672e44ce1238b82f7fb87d727c4a131f3a9d33a5e0e82b793362bf18b4"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:29e0f83f37610f173eb7e7b5562dd71467993495e568e708d99e9d1944f561ec"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:1f23e4fe1e8794f74b6027d7cf19dc25f8b63af1483d91d595d4a07eca1fb26c"}, - {file = "yarl-1.9.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:db8e58b9d79200c76956cefd14d5c90af54416ff5353c5bfd7cbe58818e26ef0"}, - {file = "yarl-1.9.4-cp39-cp39-win32.whl", hash = "sha256:c7224cab95645c7ab53791022ae77a4509472613e839dab722a72abe5a684575"}, - {file = "yarl-1.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:824d6c50492add5da9374875ce72db7a0733b29c2394890aef23d533106e2b15"}, - {file = "yarl-1.9.4-py3-none-any.whl", hash = "sha256:928cecb0ef9d5a7946eb6ff58417ad2fe9375762382f1bf5c55e61645f2c43ad"}, - {file = "yarl-1.9.4.tar.gz", hash = "sha256:566db86717cf8080b99b58b083b773a908ae40f06681e87e589a976faf8246bf"}, -] - -[package.dependencies] -idna = ">=2.0" -multidict = ">=4.0" - -[metadata] -lock-version = "2.0" -python-versions = ">=3.10,<3.11" -content-hash = "82c667b3887cd7e7ebf4bfda4ca5b1ab42a4b925f75c15b3961eee3e9aa0f9d8" diff --git a/model-serving/pyproject.toml b/model-serving/pyproject.toml deleted file mode 100644 index 5faf1f1c1..000000000 --- a/model-serving/pyproject.toml +++ /dev/null @@ -1,64 +0,0 @@ -[tool.poetry] -name = "model-api" -version = "0.1.0" -description = "" -authors = ["Julep Developers "] -readme = "README.md" -packages = [{include = "model_api"}] - -[tool.poetry.dependencies] -python = ">=3.10,<3.11" -uvicorn = ">=0.25.0,<1.0.0" -fastapi = ">=0.108.0,<1.0.0" -torch = "==2.1.2" -environs = ">=10.0.0,<11.0.0" -pynvml = ">=11.5.0,<12.0.0" -psutil = ">=5.9.8,<6.0.0" -starlette-exporter = "^0.17.1" -sentry-sdk = {extras = ["fastapi"], version = "^1.39.1"} -vllm = "^0.3.3" -aioprometheus = {extras = ["starlette"], version = "^23.12.0"} -lm-format-enforcer = "^0.8.3" -interegular = "^0.3.3" -pydantic = {extras = ["email"], version = ">=2.0.1,<3.0.0"} -scikit-learn = "=1.4.0" - - -[tool.poetry.group.dev.dependencies] -black = "^24.4.0" -pytest = "^8.0.0" -pytest-mock = "^3.12.0" -modal = "^0.62.24" -ipython = "^8.21.0" -ruff = "^0.1.9" -poethepoet = "^0.25.1" -pytype = ">=2024.4.11" -julep = "^0.2.1" -jupyterlab = "^4.1.1" -ipywidgets = "^8.1.2" -matplotlib = "^3.8.2" -ipympl = "^0.9.3" -mplcursors = "^0.5.3" -datasets = "^2.17.0" -imbalanced-learn = "^0.12.0" -pyjwt = "^2.8.0" -fire = "^0.5.0" - -[build-system] -requires = ["poetry-core"] -build-backend = "poetry.core.masonry.api" - -[tool.poe.tasks] -format = "black ." -lint = "ruff model_api/**/*.py tests/**/*.py --fix --unsafe-fixes" -typecheck = "pytype -j auto -k model_api" -check = [ - "format", - "lint", - "typecheck", -] - -test = [ - "check", - { cmd = "pytest" } -] diff --git a/model-serving/test_chat_template.py b/model-serving/test_chat_template.py deleted file mode 100644 index 345875f8d..000000000 --- a/model-serving/test_chat_template.py +++ /dev/null @@ -1,727 +0,0 @@ -from transformers import AutoTokenizer - -model_name = "julep-ai/samantha-1-turbo" -tokenizer = AutoTokenizer.from_pretrained(model_name) - - -# Set the template -template_path = "./model_api/chat_template.jinja" -with open(template_path, "r") as f: - chat_template = f.read() - - -def to_prompt(messages, chat_template=chat_template, **kwargs): - prompt = tokenizer.apply_chat_template( - messages, chat_template=chat_template, tokenize=False, **kwargs - ) - - return prompt - - -def test_function_call_none_last_user_continue(): - messages = [ - {"role": "system", "name": "situation", "content": "I am talking to John"}, - {"role": "assistant", "name": "Samantha", "content": "Hey John"}, - {"role": "user", "name": "John", "content": "Hey!"}, - {"role": "assistant", "name": "Samantha", "continue": True}, - ] - - prompt = to_prompt(messages, add_generation_prompt=True) - - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>me -""" - ) - - -def test_function_call_none_last_not_continue(): - messages = [ - {"role": "system", "name": "situation", "content": "I am talking to John"}, - {"role": "assistant", "name": "Samantha", "content": "Hey John"}, - {"role": "user", "name": "John", "content": "Hey!"}, - ] - - prompt = to_prompt(messages, add_generation_prompt=True) - - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>""" - ) - - -def test_function_call_auto_functions_not_passed(): - messages = [ - {"role": "system", "name": "situation", "content": "I am talking to John"}, - {"role": "assistant", "name": "Samantha", "content": "Hey John"}, - {"role": "user", "name": "John", "content": "Hey!"}, - ] - - prompt = to_prompt(messages, add_generation_prompt=True) - - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>""" - ) - - -def test_function_call_none_functions_not_passed(): - messages = [ - {"role": "system", "name": "situation", "content": "I am talking to John"}, - {"role": "assistant", "name": "Samantha", "content": "Hey John"}, - {"role": "user", "name": "John", "content": "Hey!"}, - ] - - prompt = to_prompt(messages, add_generation_prompt=True) - - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>""" - ) - - -def test_function_call_auto_functions_passed(): - functions = [ - { - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ] - - messages = [ - {"role": "system", "name": "situation", "content": "I am talking to John"}, - {"role": "system", "name": "functions", "content": functions}, - {"role": "assistant", "name": "Samantha", "content": "Hey John"}, - {"role": "user", "name": "John", "content": "Hey!"}, - ] - - prompt = to_prompt(messages, add_generation_prompt=True) - - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>functions -Available functions: - -{ - "description": "Generate an anagram of a given word", - "name": "generate_anagram", - "parameters": { - "properties": { - "word": { - "description": "The word to generate an anagram of", - "type": "string" - } - }, - "required": [ - "word" - ], - "type": "object" - } -}<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>""" - ) - - -# def test_function_call_none_functions_passed(): -# messages = [ -# {"role": "system", "name": "situation", "content": "I am talking to John"}, -# {"role": "assistant", "name": "Samantha", "content": "Hey John"}, -# {"role": "user", "name": "John", "content": "Hey!"}, -# ] -# functions = [ -# { -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ] -# prompt = to_prompt( -# messages, -# bos="<|im_start|>", -# eos="<|im_end|>", -# functions=functions, -# function_call=None, -# ) -# assert ( -# prompt -# == """<|im_start|>situation -# I am talking to John<|im_end|> -# <|im_start|>functions -# Available functions: - -# { -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of" -# } -# }, -# "required": [ -# "word" -# ] -# } -# }<|im_end|> -# <|im_start|>me (Samantha) -# Hey John<|im_end|> -# <|im_start|>person (John) -# Hey!<|im_end|> -# <|im_start|>""" -# ) - - -def test_function_call_none_last_continue(): - messages = [ - {"role": "system", "name": "situation", "content": "I am talking to John"}, - {"role": "assistant", "name": "Samantha", "content": "Hey John"}, - {"role": "user", "name": "John", "content": "Hey!"}, - {"role": "assistant", "name": "Samantha", "content": "Hi", "continue": True}, - ] - - prompt = to_prompt(messages, add_generation_prompt=True) - - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>me -Hi""" - ) - - -def test_function_call_none_last_continue_function_call(): - messages = [ - {"role": "system", "name": "situation", "content": "I am talking to John"}, - {"role": "assistant", "name": "Samantha", "content": "Hey John"}, - {"role": "user", "name": "John", "content": "Hey!"}, - {"role": "function_call", "content": "{}", "continue": True}, - ] - - prompt = to_prompt(messages, add_generation_prompt=True) - - assert ( - prompt - == """<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>function_call -{}""" - ) - - -# def test_function_call_auto_last_not_continue(): -# messages = [ -# {"role": "system", "name": "situation", "content": "I am talking to John"}, -# {"role": "assistant", "name": "Samantha", "content": "Hey John"}, -# {"role": "user", "name": "John", "content": "Hey!"}, -# ] -# functions = [ -# ({ -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ), -# ({ -# "name": "other_func", -# "description": "Logic", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ), -# ] -# prompt = to_prompt( -# messages, -# bos="<|im_start|>", -# eos="<|im_end|>", -# functions=functions, -# function_call="auto", -# ) -# assert ( -# prompt -# == """<|im_start|>situation -# I am talking to John<|im_end|> -# <|im_start|>functions -# Available functions: -# -# { -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of" -# } -# }, -# "required": [ -# "word" -# ] -# } -# } -# { -# "name": "other_func", -# "description": "Logic", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of" -# } -# }, -# "required": [ -# "word" -# ] -# } -# }<|im_end|> -# <|im_start|>me (Samantha) -# Hey John<|im_end|> -# <|im_start|>person (John) -# Hey!<|im_end|> -# <|im_start|>""" -# ) - - -# def test_function_call_auto_last_continue(): -# messages = [ -# {"role": "system", "name": "situation", "content": "I am talking to John"}, -# {"role": "assistant", "name": "Samantha", "content": "Hey John"}, -# {"role": "user", "name": "John", "content": "Hey!"}, -# {"role": "assistant", "name": "Samantha", "continue": True}, -# ] -# functions = [ -# ({ -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ) -# ] -# prompt = to_prompt( -# messages, -# bos="<|im_start|>", -# eos="<|im_end|>", -# functions=functions, -# function_call="auto", -# ) -# assert ( -# prompt -# == """<|im_start|>situation -# I am talking to John<|im_end|> -# <|im_start|>functions -# Available functions: -# -# { -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of" -# } -# }, -# "required": [ -# "word" -# ] -# } -# }<|im_end|> -# <|im_start|>me (Samantha) -# Hey John<|im_end|> -# <|im_start|>person (John) -# Hey!<|im_end|> -# <|im_start|>me (Samantha) -# """ -# ) - - -# def test_function_call_auto_last_continue_function_call(): -# messages = [ -# {"role": "system", "name": "situation", "content": "I am talking to John"}, -# {"role": "assistant", "name": "Samantha", "content": "Hey John"}, -# {"role": "user", "name": "John", "content": "Hey!"}, -# {"role": "function_call", "continue": True}, -# ] -# functions = [ -# ({ -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ) -# ] -# prompt = to_prompt( -# messages, -# bos="<|im_start|>", -# eos="<|im_end|>", -# functions=functions, -# function_call="auto", -# ) -# assert ( -# prompt -# == """<|im_start|>situation -# I am talking to John<|im_end|> -# <|im_start|>functions -# Available functions: -# -# { -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of" -# } -# }, -# "required": [ -# "word" -# ] -# } -# }<|im_end|> -# <|im_start|>me (Samantha) -# Hey John<|im_end|> -# <|im_start|>person (John) -# Hey!<|im_end|> -# <|im_start|>function_call -# """ -# ) - - -# def test_function_call_func_name_last_not_continue(): -# messages = [ -# {"role": "system", "name": "situation", "content": "I am talking to John"}, -# {"role": "assistant", "name": "Samantha", "content": "Hey John"}, -# {"role": "user", "name": "John", "content": "Hey!"}, -# ] -# functions = [ -# ({ -# "name": "other_func", -# "description": "Logic", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ), -# ({ -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ), -# ] -# prompt = to_prompt( -# messages, -# bos="<|im_start|>", -# eos="<|im_end|>", -# functions=functions, -# function_call=FunctionCall(**{"name": "generate_anagram"}, -# ) -# assert ( -# prompt -# == """<|im_start|>situation -# I am talking to John<|im_end|> -# <|im_start|>functions -# Available functions: -# -# { -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of" -# } -# }, -# "required": [ -# "word" -# ] -# } -# }<|im_end|> -# <|im_start|>me (Samantha) -# Hey John<|im_end|> -# <|im_start|>person (John) -# Hey!<|im_end|> -# <|im_start|>function_call -# {"name": "generate_anagram",""" -# ) - - -# def test_function_call_func_name_last_not_continue_invalid_function_name(): -# messages = [ -# {"role": "system", "name": "situation", "content": "I am talking to John"}, -# {"role": "assistant", "name": "Samantha", "content": "Hey John"}, -# {"role": "user", "name": "John", "content": "Hey!"}, -# ] -# functions = [ -# ({ -# "name": "other_func", -# "description": "Logic", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ), -# ({ -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ), -# ] -# with pytest.raises(InvalidFunctionName) as e_info: -# to_prompt( -# messages, -# bos="<|im_start|>", -# eos="<|im_end|>", -# functions=functions, -# function_call=FunctionCall(**{"name": "unknown"}, -# ) -# assert e_info.value.args[0] == "Invalid function name: unknown" - - -# def test_function_call_func_name_last_continue(): -# messages = [ -# {"role": "system", "name": "situation", "content": "I am talking to John"}, -# {"role": "assistant", "name": "Samantha", "content": "Hey John"}, -# {"role": "user", "name": "John", "content": "Hey!"}, -# {"role": "assistant", "name": "Samantha", "continue": True}, -# ] -# functions = [ -# ({ -# "name": "generate_anagram", -# "description": "Generate an anagram of a given word", -# "parameters": { -# "type": "object", -# "properties": { -# "word": { -# "type": "string", -# "description": "The word to generate an anagram of", -# } -# }, -# "required": ["word"], -# }, -# } -# ) -# ] -# with pytest.raises(InvalidPromptException) as e_info: -# to_prompt( -# messages, -# bos="<|im_start|>", -# eos="<|im_end|>", -# functions=functions, -# function_call=FunctionCall(**{"name": "generate_anagram"}, -# ) -# assert e_info.value.args[0] == ( -# "Invalid prompt format: Conflicting instructions, " -# "please remove the last instruction with 'continue' " -# "flag set to 'true' or set the flag to 'false'. " -# "You can either remove `functions` and/or `function_call` parameters." -# ) - - -def test_function_call_func_name_last_continue_function_call(): - functions = [ - { - "name": "generate_anagram", - "description": "Generate an anagram of a given word", - "parameters": { - "type": "object", - "properties": { - "word": { - "type": "string", - "description": "The word to generate an anagram of", - } - }, - "required": ["word"], - }, - } - ] - - messages = [ - {"role": "system", "name": "situation", "content": "I am talking to John"}, - {"role": "system", "name": "functions", "content": functions}, - {"role": "assistant", "name": "Samantha", "content": "Hey John"}, - {"role": "user", "name": "John", "content": "Hey!"}, - { - "role": "function_call", - "content": '{"name": "generate_anagram", ', - "continue": True, - }, - ] - - prompt = to_prompt(messages, add_generation_prompt=True) - - expected = """\ -<|im_start|>situation -I am talking to John<|im_end|> -<|im_start|>functions -Available functions: - -{ - "description": "Generate an anagram of a given word", - "name": "generate_anagram", - "parameters": { - "properties": { - "word": { - "description": "The word to generate an anagram of", - "type": "string" - } - }, - "required": [ - "word" - ], - "type": "object" - } -}<|im_end|> -<|im_start|>me (Samantha) -Hey John<|im_end|> -<|im_start|>person (John) -Hey!<|im_end|> -<|im_start|>function_call -{"name": "generate_anagram",""" - - assert prompt == expected - - -if __name__ == "__main__": - test_function_call_none_last_user_continue() - test_function_call_none_last_not_continue() - test_function_call_auto_functions_not_passed() - test_function_call_none_functions_not_passed() - test_function_call_none_last_continue() - test_function_call_none_last_continue_function_call() - test_function_call_auto_functions_passed() - test_function_call_func_name_last_continue_function_call() diff --git a/model-serving/tests/__init__.py b/model-serving/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/model-serving/tests/fixtures.py b/model-serving/tests/fixtures.py deleted file mode 100644 index 013ecd90e..000000000 --- a/model-serving/tests/fixtures.py +++ /dev/null @@ -1,44 +0,0 @@ -import os -import uuid -import pytest -from fastapi.testclient import TestClient - -auth_key = "myauthkey" -os.environ["API_KEY"] = auth_key -os.environ["TEMPERATURE_SCALING_FACTOR"] = "1.0" -os.environ["TEMPERATURE_SCALING_POWER"] = "1.0" -MODEL_NAME = os.environ.get("MODEL_NAME", "julep-ai/samantha-1-turbo") - -from model_api.web import create_app # noqa: E402 - -args = [ - "--model", - MODEL_NAME, - "--trust-remote-code", - "--max-model-len", - "1024", - "--enforce-eager", - "--dtype", - "bfloat16", - "--gpu-memory-utilization", - "0.97", - "--max-num-seqs", - "1", -] - -app = create_app(args) - - -@pytest.fixture(scope="session") -def unauthorized_client(): - return TestClient(app) - - -@pytest.fixture(scope="session") -def client(): - return TestClient(app, headers={"X-Auth-Key": auth_key}) - - -@pytest.fixture -def request_id(): - return str(uuid.uuid4()) diff --git a/model-serving/tests/test_chat_completions.py b/model-serving/tests/test_chat_completions.py deleted file mode 100644 index 545746721..000000000 --- a/model-serving/tests/test_chat_completions.py +++ /dev/null @@ -1,691 +0,0 @@ -# ruff: noqa: F401, F811 -import pytest -from pytest_mock import mocker - -from model_api.logits_processors import ( - drop_disallowed_start_tags, - fix_function_call_prediction, -) -from model_api.protocol import SamplingParams -import model_api.web -from tests.fixtures import client, unauthorized_client, request_id, MODEL_NAME - - -def test_security(unauthorized_client): - response = unauthorized_client.post("/v1/chat/completions") - assert response.status_code == 403 - - -def test_check_model(client): - body = dict( - model="some_nonexistent_model", - messages=[], - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert response.status_code == 404 - - -def test_logit_bias_not_supported(client): - body = dict( - model=MODEL_NAME, - logit_bias={"a": 1.0}, - messages=[], - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert response.status_code == 400 - - -def test_functions_and_tools(client): - body = dict( - model=MODEL_NAME, - functions=[ - { - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string", - }, - }, - ], - tools=[ - { - "type": "function", - "id": "tool-1", - "function": { - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string", - }, - }, - } - ], - messages=[], - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert response.status_code == 400 - - -def test_do_not_insert_default_situation_if_messages_empty(client, request_id, mocker): - expected_prompt = "" - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - body = dict( - model=MODEL_NAME, - messages=[], - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 - - -def test_insert_default_situation(client, request_id, mocker): - expected_prompt = """<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>person (User) -hi<|im_end|> -<|im_start|>me -""" - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - body = dict( - model=MODEL_NAME, - messages=[ - { - "role": "user", - "name": "User", - "content": "hi", - } - ], - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 - - -def test_escape_special_tokens(client, request_id, mocker): - st = list( - model_api.web.engine.engine.tokenizer.tokenizer.special_tokens_map.values() - )[0] - if isinstance(st, list): - st = st[0] - expected_prompt = f"""<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>person (User) -{st[0]} {st[1:]}<|im_end|> -<|im_start|>me -""" - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - - body = dict( - model=MODEL_NAME, - messages=[ - { - "role": "user", - "name": "User", - "content": st, - } - ], - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 - - -def test_function_called_by_name(client, request_id, mocker): - expected_prompt = """<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>functions -Available functions: - -{ - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string" - } -}<|im_end|> -<|im_start|>person (User) -hi<|im_end|> -<|im_start|>function_call -{"name": "func_name",""" - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - - body = dict( - model=MODEL_NAME, - messages=[ - { - "role": "user", - "name": "User", - "content": "hi", - } - ], - functions=[ - { - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string", - }, - }, - ], - function_call={"name": "func_name"}, - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 - - -def test_function_is_none(client, request_id, mocker): - expected_prompt = """<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>person (User) -hi<|im_end|> -<|im_start|>me -""" - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - - body = dict( - model=MODEL_NAME, - messages=[ - { - "role": "user", - "name": "User", - "content": "hi", - } - ], - functions=[ - { - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string", - }, - }, - ], - function_call="none", - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 - - -@pytest.mark.skip(reason="fix AsyncEngineDeadError") -def test_function_is_auto(client, request_id, mocker): - expected_prompt = """<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>functions -Available functions: - -{ - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string" - } -}<|im_end|> -<|im_start|>person (User) -hi<|im_end|> -<|im_start|>""" - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - - body = dict( - model=MODEL_NAME, - messages=[ - { - "role": "user", - "name": "User", - "content": "hi", - } - ], - functions=[ - { - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string", - }, - }, - ], - function_call="auto", - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 - - -# def test_rescale_temperature(client, request_id, mocker): -# expected_prompt = f"""<|im_start|>situation -# You are a helpful AI Assistant<|im_end|> -# <|im_start|>person (User) -# hi<|im_end|> -# <|im_start|>me -# """ -# temperature = 0.7 -# expected_sampling_params = SamplingParams( -# n=1, -# best_of=1, -# presence_penalty=0.0, -# frequency_penalty=0.75, -# repetition_penalty=1.0, -# temperature=0.0, -# top_p=0.99, -# top_k=-1, -# min_p=0.0, -# seed=None, -# use_beam_search=False, -# length_penalty=1.0, -# early_stopping=False, -# stop=["<", "<|"], -# stop_token_ids=[], -# include_stop_str_in_output=False, -# ignore_eos=False, -# max_tokens=1, -# logprobs=None, -# prompt_logprobs=None, -# skip_special_tokens=True, -# spaces_between_special_tokens=False, -# ) - -# mocker.patch("model_api.web.random_uuid", return_value=request_id) -# spy = mocker.spy(model_api.web.engine, "generate") - -# body = dict( -# model=MODEL_NAME, -# temperature=temperature, -# messages=[ -# { -# "role": "user", -# "name": "User", -# "content": "hi", -# } -# ], -# max_tokens=1, -# stop=["<", "<|"], -# frequency_penalty=0.75, -# ) -# response = client.post( -# "/v1/chat/completions", -# json=body, -# ) -# assert spy.call_count == 1 -# spy.assert_called_once_with( -# expected_prompt, expected_sampling_params, f"cmpl-{request_id}" -# ) -# assert response.status_code == 200 - - -@pytest.mark.skip(reason="fix AsyncEngineDeadError") -def test_logits_processor_fix_function_call_prediction(client, request_id, mocker): - expected_prompt = """<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>functions -Available functions: - -{ - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string" - } -}<|im_end|> -<|im_start|>person (User) -hi<|im_end|> -<|im_start|>""" - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - logits_processors=[fix_function_call_prediction], - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - - body = dict( - model=MODEL_NAME, - messages=[ - { - "role": "user", - "name": "User", - "content": "hi", - } - ], - functions=[ - { - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string", - }, - }, - ], - function_call="auto", - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 - - -@pytest.mark.skip(reason="fix SamplingParams comparison") -def test_logits_processor_drop_disallowed_start_tags(client, request_id, mocker): - expected_prompt = """<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>person (User) -hi<|im_end|> -<|im_start|>me -""" - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - logits_processors=[drop_disallowed_start_tags], - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - - body = dict( - model=MODEL_NAME, - messages=[ - { - "role": "user", - "name": "User", - "content": "hi", - } - ], - functions=[ - { - "name": "func_name", - "description": "func_desc", - "parameters": { - "param1": "string", - }, - }, - ], - function_call="none", - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/chat/completions", - json=body, - ) - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 diff --git a/model-serving/tests/test_completions.py b/model-serving/tests/test_completions.py deleted file mode 100644 index c3f1e218c..000000000 --- a/model-serving/tests/test_completions.py +++ /dev/null @@ -1,263 +0,0 @@ -# ruff: noqa: F401, F811 -from pytest_mock import mocker - -from model_api.logits_processors import drop_disallowed_start_tags -from model_api.protocol import SamplingParams -import model_api.web -from tests.fixtures import client, unauthorized_client, request_id, MODEL - - -def test_security(unauthorized_client): - response = unauthorized_client.post("/v1/completions") - assert response.status_code == 403 - - -def test_check_model(client): - body = dict( - model="some_nonexistent_model", - prompt="some text", - ) - response = client.post( - "/v1/completions", - json=body, - ) - assert response.status_code == 404 - - -def test_logit_bias_not_supported(client): - body = dict( - model=MODEL, - logit_bias={"a": 1.0}, - prompt="some text", - ) - response = client.post( - "/v1/completions", - json=body, - ) - assert response.status_code == 400 - - -def test_remove_last_space(client, request_id, mocker): - expected_prompt = """<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>person (User) -hi<|im_end|> -<|im_start|>me """ - prompt = expected_prompt - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - - body = dict( - model=MODEL, - prompt=prompt, - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/completions", - json=body, - ) - assert spy.call_count == 1 - - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 - - -def test_remove_last_space_2(client, request_id, mocker): - st = list( - model_api.web.engine.engine.tokenizer.tokenizer.special_tokens_map.values() - )[0] - if isinstance(st, list): - st = st[0] - expected_prompt = """<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>person (User) -hi<|im_end|> -<|im_start|>me""" - prompt = expected_prompt + " " - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - - body = dict( - model=MODEL, - prompt=prompt, - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/completions", - json=body, - ) - - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 - - -# def test_rescale_temperature(client, request_id, mocker): -# expected_prompt = f"""<|im_start|>situation -# You are a helpful AI Assistant<|im_end|> -# <|im_start|>person (User) -# hi<|im_end|> -# <|im_start|>me -# """ -# prompt = expected_prompt -# temperature = 0.7 -# expected_sampling_params = SamplingParams( -# n=1, -# best_of=1, -# presence_penalty=0.0, -# frequency_penalty=0.75, -# repetition_penalty=1.0, -# temperature=0.0, -# top_p=0.99, -# top_k=-1, -# min_p=0.01, -# seed=None, -# use_beam_search=False, -# length_penalty=1.0, -# early_stopping=False, -# stop=["<", "<|"], -# stop_token_ids=[], -# include_stop_str_in_output=False, -# ignore_eos=False, -# max_tokens=1, -# logprobs=None, -# prompt_logprobs=None, -# skip_special_tokens=True, -# spaces_between_special_tokens=False, -# ) - -# mocker.patch("model_api.web.random_uuid", return_value=request_id) -# spy = mocker.spy(model_api.web.engine, "generate") - -# body = dict( -# model=MODEL, -# temperature=temperature, -# prompt=prompt, -# max_tokens=1, -# stop=["<", "<|"], -# frequency_penalty=0.75, -# ) -# response = client.post( -# "/v1/completions", -# json=body, -# ) -# assert spy.call_count == 1 -# spy.assert_called_once_with( -# expected_prompt, expected_sampling_params, f"cmpl-{request_id}" -# ) -# assert response.status_code == 200 - - -def test_logits_processor_drop_disallowed_start_tags(client, request_id, mocker): - expected_prompt = """<|im_start|>situation -You are a helpful AI Assistant<|im_end|> -<|im_start|>person (User) -hi<|im_end|> -<|im_start|>""" - prompt = expected_prompt - expected_sampling_params = SamplingParams( - n=1, - best_of=1, - presence_penalty=0.0, - frequency_penalty=0.75, - repetition_penalty=1.0, - temperature=0.75, - top_p=0.99, - top_k=-1, - min_p=0.01, - seed=None, - use_beam_search=False, - length_penalty=1.0, - early_stopping=False, - stop=["<", "<|"], - stop_token_ids=[], - include_stop_str_in_output=False, - ignore_eos=False, - max_tokens=1, - logprobs=None, - prompt_logprobs=None, - skip_special_tokens=True, - spaces_between_special_tokens=False, - logits_processors=[drop_disallowed_start_tags], - ) - - mocker.patch("model_api.web.random_uuid", return_value=request_id) - spy = mocker.spy(model_api.web.engine, "generate") - - body = dict( - model=MODEL, - prompt=prompt, - max_tokens=1, - stop=["<", "<|"], - temperature=0.75, - frequency_penalty=0.75, - ) - response = client.post( - "/v1/completions", - json=body, - ) - assert spy.call_count == 1 - spy.assert_called_once_with( - expected_prompt, expected_sampling_params, f"cmpl-{request_id}" - ) - assert response.status_code == 200 diff --git a/model-serving/update_tokenizer_template.py b/model-serving/update_tokenizer_template.py deleted file mode 100644 index e2a590b17..000000000 --- a/model-serving/update_tokenizer_template.py +++ /dev/null @@ -1,34 +0,0 @@ -from fire import Fire -from transformers import AutoTokenizer - - -def update_tokenizer_template( - tokenizer_name: str = "julep-ai/samantha-1-turbo", - template_path: str = "./model_api/chat_template.jinja", - save_to_dir: str | None = None, - push_to_hub: bool = False, -): - assert ( - save_to_dir is not None or push_to_hub - ), "You must specify a directory to save the tokenizer to or push to the Hugging Face model hub." - - # Load the tokenizer - tokenizer = AutoTokenizer.from_pretrained(tokenizer_name) - - # Set the template - with open(template_path, "r") as f: - template = f.read() - - tokenizer.chat_template = template - - # Save the tokenizer - if save_to_dir: - tokenizer.save_pretrained(save_to_dir) - - # Push to the Hugging Face model hub - if push_to_hub: - tokenizer.push_to_hub(tokenizer_name) - - -if __name__ == "__main__": - Fire(update_tokenizer_template) From 068cef0b98c0452a60805e15acb011c1cc320d53 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 11 Aug 2024 18:50:57 -0400 Subject: [PATCH 033/110] feat: Remove support for presets Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Chat.py | 34 ------------------- agents-api/poetry.lock | 6 ++-- sdks/python/julep/api/__init__.py | 2 -- sdks/python/julep/api/client.py | 11 ------ sdks/python/julep/api/reference.md | 8 ----- sdks/python/julep/api/types/__init__.py | 2 -- .../api/types/chat_default_chat_settings.py | 6 ---- .../julep/api/types/chat_generation_preset.py | 18 ---------- sdks/ts/src/api/index.ts | 2 -- sdks/ts/src/api/models/Chat_ChatInput.ts | 5 --- .../api/models/Chat_DefaultChatSettings.ts | 5 --- .../src/api/models/Chat_GenerationPreset.ts | 17 ---------- sdks/ts/src/api/schemas/$Chat_ChatInput.ts | 9 ----- .../api/schemas/$Chat_DefaultChatSettings.ts | 9 ----- .../src/api/schemas/$Chat_GenerationPreset.ts | 7 ---- typespec/chat/models.tsp | 19 ----------- 16 files changed, 3 insertions(+), 157 deletions(-) delete mode 100644 sdks/python/julep/api/types/chat_generation_preset.py delete mode 100644 sdks/ts/src/api/models/Chat_GenerationPreset.ts delete mode 100644 sdks/ts/src/api/schemas/$Chat_GenerationPreset.ts diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index d00d73a24..1f4f5a67a 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -263,23 +263,6 @@ class ChatInput(ChatInputData): """ Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) """ - preset: ( - Literal[ - "problem_solving", - "conversational", - "fun", - "prose", - "creative", - "business", - "deterministic", - "code", - "multilingual", - ] - | None - ) = None - """ - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - """ repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. @@ -318,23 +301,6 @@ class DefaultChatSettings(OpenAISettings): model_config = ConfigDict( populate_by_name=True, ) - preset: ( - Literal[ - "problem_solving", - "conversational", - "fun", - "prose", - "creative", - "business", - "deterministic", - "code", - "multilingual", - ] - | None - ) = None - """ - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - """ repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 8136f659e..4892624c7 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2236,13 +2236,13 @@ langchain-core = ">=0.2.10,<0.3.0" [[package]] name = "langsmith" -version = "0.1.98" +version = "0.1.99" 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.98-py3-none-any.whl", hash = "sha256:f79e8a128652bbcee4606d10acb6236973b5cd7dde76e3741186d3b97b5698e9"}, - {file = "langsmith-0.1.98.tar.gz", hash = "sha256:e07678219a0502e8f26d35294e72127a39d25e32fafd091af5a7bb661e9a6bd1"}, + {file = "langsmith-0.1.99-py3-none-any.whl", hash = "sha256:ef8d1d74a2674c514aa429b0171a9fbb661207dc3835142cca0e8f1bf97b26b0"}, + {file = "langsmith-0.1.99.tar.gz", hash = "sha256:b5c6a1f158abda61600a4a445081ee848b4a28b758d91f2793dc02aeffafcaf1"}, ] [package.dependencies] diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index 72a573a52..61726cb46 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -32,7 +32,6 @@ ChatCompletionResponseFormatType, ChatDefaultChatSettings, ChatFinishReason, - ChatGenerationPreset, ChatLogProbResponse, ChatMessageChatResponse, ChatMessageChatResponseChoicesItem, @@ -239,7 +238,6 @@ "ChatCompletionResponseFormatType", "ChatDefaultChatSettings", "ChatFinishReason", - "ChatGenerationPreset", "ChatLogProbResponse", "ChatMessageChatResponse", "ChatMessageChatResponseChoicesItem", diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index dd0c4e99f..42c9f45a5 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -42,7 +42,6 @@ from .types.chat_chat_input_data_tool_choice import ChatChatInputDataToolChoice from .types.chat_completion_response_format import ChatCompletionResponseFormat from .types.chat_default_chat_settings import ChatDefaultChatSettings -from .types.chat_generation_preset import ChatGenerationPreset from .types.chat_route_generate_response import ChatRouteGenerateResponse from .types.common_identifier_safe_unicode import CommonIdentifierSafeUnicode from .types.common_limit import CommonLimit @@ -2676,7 +2675,6 @@ def chat_route_generate( logit_bias: typing.Optional[typing.Dict[str, CommonLogitBias]] = OMIT, response_format: typing.Optional[ChatCompletionResponseFormat] = OMIT, agent: typing.Optional[CommonUuid] = OMIT, - preset: typing.Optional[ChatGenerationPreset] = OMIT, repetition_penalty: typing.Optional[float] = OMIT, length_penalty: typing.Optional[float] = OMIT, min_p: typing.Optional[float] = OMIT, @@ -2732,9 +2730,6 @@ def chat_route_generate( agent : typing.Optional[CommonUuid] Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - preset : typing.Optional[ChatGenerationPreset] - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - repetition_penalty : typing.Optional[float] Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. @@ -2808,7 +2803,6 @@ def chat_route_generate( "logit_bias": logit_bias, "response_format": response_format, "agent": agent, - "preset": preset, "repetition_penalty": repetition_penalty, "length_penalty": length_penalty, "min_p": min_p, @@ -6560,7 +6554,6 @@ async def chat_route_generate( logit_bias: typing.Optional[typing.Dict[str, CommonLogitBias]] = OMIT, response_format: typing.Optional[ChatCompletionResponseFormat] = OMIT, agent: typing.Optional[CommonUuid] = OMIT, - preset: typing.Optional[ChatGenerationPreset] = OMIT, repetition_penalty: typing.Optional[float] = OMIT, length_penalty: typing.Optional[float] = OMIT, min_p: typing.Optional[float] = OMIT, @@ -6616,9 +6609,6 @@ async def chat_route_generate( agent : typing.Optional[CommonUuid] Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) - preset : typing.Optional[ChatGenerationPreset] - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - repetition_penalty : typing.Optional[float] Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. @@ -6700,7 +6690,6 @@ async def main() -> None: "logit_bias": logit_bias, "response_format": response_format, "agent": agent, - "preset": preset, "repetition_penalty": repetition_penalty, "length_penalty": length_penalty, "min_p": min_p, diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index 191769362..5bf9bf81f 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -3914,14 +3914,6 @@ client.chat_route_generate(

    -**preset:** `typing.Optional[ChatGenerationPreset]` — Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - -
    -
    - -
    -
    - **repetition_penalty:** `typing.Optional[float]` — Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.
    diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index 640d4cd4e..5f1b1b76a 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -39,7 +39,6 @@ from .chat_completion_response_format_type import ChatCompletionResponseFormatType from .chat_default_chat_settings import ChatDefaultChatSettings from .chat_finish_reason import ChatFinishReason -from .chat_generation_preset import ChatGenerationPreset from .chat_log_prob_response import ChatLogProbResponse from .chat_message_chat_response import ChatMessageChatResponse from .chat_message_chat_response_choices_item import ChatMessageChatResponseChoicesItem @@ -281,7 +280,6 @@ "ChatCompletionResponseFormatType", "ChatDefaultChatSettings", "ChatFinishReason", - "ChatGenerationPreset", "ChatLogProbResponse", "ChatMessageChatResponse", "ChatMessageChatResponseChoicesItem", diff --git a/sdks/python/julep/api/types/chat_default_chat_settings.py b/sdks/python/julep/api/types/chat_default_chat_settings.py index a3972e99f..77864e20b 100644 --- a/sdks/python/julep/api/types/chat_default_chat_settings.py +++ b/sdks/python/julep/api/types/chat_default_chat_settings.py @@ -5,7 +5,6 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .chat_generation_preset import ChatGenerationPreset from .chat_open_ai_settings import ChatOpenAiSettings @@ -14,11 +13,6 @@ class ChatDefaultChatSettings(ChatOpenAiSettings): Default settings for the chat session (also used by the agent) """ - preset: typing.Optional[ChatGenerationPreset] = pydantic_v1.Field(default=None) - """ - Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - """ - repetition_penalty: typing.Optional[float] = pydantic_v1.Field(default=None) """ Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. diff --git a/sdks/python/julep/api/types/chat_generation_preset.py b/sdks/python/julep/api/types/chat_generation_preset.py deleted file mode 100644 index fcf1a3a1b..000000000 --- a/sdks/python/julep/api/types/chat_generation_preset.py +++ /dev/null @@ -1,18 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -ChatGenerationPreset = typing.Union[ - typing.Literal[ - "problem_solving", - "conversational", - "fun", - "prose", - "creative", - "business", - "deterministic", - "code", - "multilingual", - ], - typing.Any, -] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index 0e213abee..0bd8c86e7 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -28,7 +28,6 @@ export type { Chat_CompetionUsage } from "./models/Chat_CompetionUsage"; export type { Chat_CompletionResponseFormat } from "./models/Chat_CompletionResponseFormat"; export type { Chat_DefaultChatSettings } from "./models/Chat_DefaultChatSettings"; export type { Chat_FinishReason } from "./models/Chat_FinishReason"; -export type { Chat_GenerationPreset } from "./models/Chat_GenerationPreset"; export type { Chat_LogProbResponse } from "./models/Chat_LogProbResponse"; export type { Chat_MessageChatResponse } from "./models/Chat_MessageChatResponse"; export type { Chat_MultipleChatOutput } from "./models/Chat_MultipleChatOutput"; @@ -148,7 +147,6 @@ export { $Chat_CompetionUsage } from "./schemas/$Chat_CompetionUsage"; export { $Chat_CompletionResponseFormat } from "./schemas/$Chat_CompletionResponseFormat"; export { $Chat_DefaultChatSettings } from "./schemas/$Chat_DefaultChatSettings"; export { $Chat_FinishReason } from "./schemas/$Chat_FinishReason"; -export { $Chat_GenerationPreset } from "./schemas/$Chat_GenerationPreset"; export { $Chat_LogProbResponse } from "./schemas/$Chat_LogProbResponse"; export { $Chat_MessageChatResponse } from "./schemas/$Chat_MessageChatResponse"; export { $Chat_MultipleChatOutput } from "./schemas/$Chat_MultipleChatOutput"; diff --git a/sdks/ts/src/api/models/Chat_ChatInput.ts b/sdks/ts/src/api/models/Chat_ChatInput.ts index d9b037889..5de17283c 100644 --- a/sdks/ts/src/api/models/Chat_ChatInput.ts +++ b/sdks/ts/src/api/models/Chat_ChatInput.ts @@ -4,7 +4,6 @@ /* eslint-disable */ import type { Chat_ChatInputData } from "./Chat_ChatInputData"; import type { Chat_CompletionResponseFormat } from "./Chat_CompletionResponseFormat"; -import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; import type { Common_logit_bias } from "./Common_logit_bias"; import type { Common_uuid } from "./Common_uuid"; @@ -53,10 +52,6 @@ export type Chat_ChatInput = Chat_ChatInputData & { * Agent ID of the agent to use for this interaction. (Only applicable for multi-agent sessions) */ agent?: Common_uuid; - /** - * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - */ - preset?: Chat_GenerationPreset; /** * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. */ diff --git a/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts b/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts index fc82c8956..5bfeb6bc5 100644 --- a/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts +++ b/sdks/ts/src/api/models/Chat_DefaultChatSettings.ts @@ -2,16 +2,11 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Chat_GenerationPreset } from "./Chat_GenerationPreset"; import type { Chat_OpenAISettings } from "./Chat_OpenAISettings"; /** * Default settings for the chat session (also used by the agent) */ export type Chat_DefaultChatSettings = Chat_OpenAISettings & { - /** - * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - */ - preset?: Chat_GenerationPreset; /** * Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. */ diff --git a/sdks/ts/src/api/models/Chat_GenerationPreset.ts b/sdks/ts/src/api/models/Chat_GenerationPreset.ts deleted file mode 100644 index 1083d3294..000000000 --- a/sdks/ts/src/api/models/Chat_GenerationPreset.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -/** - * Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) - */ -export type Chat_GenerationPreset = - | "problem_solving" - | "conversational" - | "fun" - | "prose" - | "creative" - | "business" - | "deterministic" - | "code" - | "multilingual"; diff --git a/sdks/ts/src/api/schemas/$Chat_ChatInput.ts b/sdks/ts/src/api/schemas/$Chat_ChatInput.ts index e74f1e0a5..635d83534 100644 --- a/sdks/ts/src/api/schemas/$Chat_ChatInput.ts +++ b/sdks/ts/src/api/schemas/$Chat_ChatInput.ts @@ -84,15 +84,6 @@ export const $Chat_ChatInput = { }, ], }, - preset: { - type: "all-of", - description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, - contains: [ - { - type: "Chat_GenerationPreset", - }, - ], - }, repetition_penalty: { type: "number", description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, diff --git a/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts b/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts index 253273536..573bf31bd 100644 --- a/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts +++ b/sdks/ts/src/api/schemas/$Chat_DefaultChatSettings.ts @@ -11,15 +11,6 @@ export const $Chat_DefaultChatSettings = { }, { properties: { - preset: { - type: "all-of", - description: `Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual)`, - contains: [ - { - type: "Chat_GenerationPreset", - }, - ], - }, repetition_penalty: { type: "number", description: `Number between 0 and 2.0. 1.0 is neutral and values larger than that penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.`, diff --git a/sdks/ts/src/api/schemas/$Chat_GenerationPreset.ts b/sdks/ts/src/api/schemas/$Chat_GenerationPreset.ts deleted file mode 100644 index f4a671436..000000000 --- a/sdks/ts/src/api/schemas/$Chat_GenerationPreset.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Chat_GenerationPreset = { - type: "Enum", -} as const; diff --git a/typespec/chat/models.tsp b/typespec/chat/models.tsp index 513e86a39..97f374413 100644 --- a/typespec/chat/models.tsp +++ b/typespec/chat/models.tsp @@ -18,19 +18,6 @@ namespace Chat; // CHAT MODELS // -/** Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) */ -enum GenerationPreset { - problem_solving, - conversational, - fun, - prose, - creative, - business, - deterministic, - code, - multilingual, -} - /** * The reason the model stopped generating tokens. This will be `stop` * if the model hit a natural stop point or a provided stop sequence, @@ -95,11 +82,6 @@ model CommonChatSettings { agent?: uuid; } -model GenerationPresetSettings { - /** Generation preset (one of: problem_solving, conversational, fun, prose, creative, business, deterministic, code, multilingual) */ - preset?: GenerationPreset; -} - model OpenAISettings { /** Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. */ @minValue(-2) @@ -141,7 +123,6 @@ model vLLMSettings { /** Default settings for the chat session (also used by the agent) */ model DefaultChatSettings extends OpenAISettings { - ...GenerationPresetSettings; ...vLLMSettings; } From aefe3abf9cca4bbe5dff8486424b3333f7cfd039 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 11 Aug 2024 18:51:50 -0400 Subject: [PATCH 034/110] feat(agents-api): Add settings to developers relation Signed-off-by: Diwank Tomer --- .../agents_api/common/protocol/developers.py | 22 ++++++ .../agents_api/dependencies/developer_id.py | 47 +++++++------ agents-api/agents_api/models/__init__.py | 1 + .../agents_api/models/developer/__init__.py | 19 ++++++ .../models/developer/get_developer.py | 68 +++++++++++++++++++ ...e_1723400730_add_settings_to_developers.py | 68 +++++++++++++++++++ agents-api/tests/fixtures.py | 4 +- agents-api/tests/test_developer_queries.py | 36 ++++++++++ 8 files changed, 242 insertions(+), 23 deletions(-) create mode 100644 agents-api/agents_api/common/protocol/developers.py create mode 100644 agents-api/agents_api/models/developer/__init__.py create mode 100644 agents-api/agents_api/models/developer/get_developer.py create mode 100644 agents-api/migrations/migrate_1723400730_add_settings_to_developers.py create mode 100644 agents-api/tests/test_developer_queries.py diff --git a/agents-api/agents_api/common/protocol/developers.py b/agents-api/agents_api/common/protocol/developers.py new file mode 100644 index 000000000..4c1cfdac4 --- /dev/null +++ b/agents-api/agents_api/common/protocol/developers.py @@ -0,0 +1,22 @@ +""" +This module defines session-related data structures and settings used across the agents API. +It includes definitions for session settings and session data models. +""" + +from uuid import UUID + +from pydantic import AwareDatetime, BaseModel, EmailStr + + +class Developer(BaseModel): + """ + Represents the data associated with a developer + """ + + id: UUID + email: EmailStr + active: bool + tags: list[str] + settings: dict + created_at: AwareDatetime + created_at: AwareDatetime diff --git a/agents-api/agents_api/dependencies/developer_id.py b/agents-api/agents_api/dependencies/developer_id.py index a088389b5..e9b51e850 100644 --- a/agents-api/agents_api/dependencies/developer_id.py +++ b/agents-api/agents_api/dependencies/developer_id.py @@ -1,44 +1,49 @@ -import uuid from typing import Annotated +from uuid import UUID from fastapi import Header -from pydantic import validate_email -from pydantic_core import PydanticCustomError +from ..common.protocol.developers import Developer from ..env import skip_check_developer_headers +from ..models.developer.get_developer import get_developer, verify_developer from .exceptions import InvalidHeaderFormat async def get_developer_id( - x_developer_id: Annotated[uuid.UUID | None, Header()] = None, -): + x_developer_id: Annotated[UUID | None, Header()] = None, +) -> UUID: if skip_check_developer_headers: - return x_developer_id or uuid.UUID("00000000-0000-0000-0000-000000000000") + return UUID("00000000-0000-0000-0000-000000000000") if not x_developer_id: - raise InvalidHeaderFormat("X-Developer-Id header invalid") + raise InvalidHeaderFormat("X-Developer-Id header required") if isinstance(x_developer_id, str): try: - x_developer_id = uuid.UUID(x_developer_id, version=4) - except ValueError: - raise InvalidHeaderFormat("X-Developer-Id must be a valid UUID") + x_developer_id = UUID(x_developer_id, version=4) + except ValueError as e: + raise InvalidHeaderFormat("X-Developer-Id must be a valid UUID") from e + + verify_developer(developer_id=x_developer_id) return x_developer_id -async def get_developer_email( - x_developer_email: Annotated[str | None, Header()] = None, -): +async def get_developer_data( + x_developer_id: Annotated[UUID | None, Header()] = None, +) -> Developer: if skip_check_developer_headers: - return x_developer_email or "unknown_user@mail.com" + x_developer_id = UUID("00000000-0000-0000-0000-000000000000") + + if not x_developer_id: + raise InvalidHeaderFormat("X-Developer-Id header required") - if not x_developer_email: - raise InvalidHeaderFormat("X-Developer-Email header invalid") + if isinstance(x_developer_id, str): + try: + x_developer_id = UUID(x_developer_id, version=4) + except ValueError as e: + raise InvalidHeaderFormat("X-Developer-Id must be a valid UUID") from e - try: - validate_email(x_developer_email) - except PydanticCustomError: - raise InvalidHeaderFormat("X-Developer-Email header invalid") + developer = get_developer(developer_id=x_developer_id) - return x_developer_email + return developer diff --git a/agents-api/agents_api/models/__init__.py b/agents-api/agents_api/models/__init__.py index e84bb5ecc..b1f918ee7 100644 --- a/agents-api/agents_api/models/__init__.py +++ b/agents-api/agents_api/models/__init__.py @@ -9,6 +9,7 @@ # ruff: noqa: F401, F403, F405 import agents_api.models.agent as agent +import agents_api.models.developer as developer import agents_api.models.docs as docs import agents_api.models.entry as entry import agents_api.models.execution as execution diff --git a/agents-api/agents_api/models/developer/__init__.py b/agents-api/agents_api/models/developer/__init__.py new file mode 100644 index 000000000..a7117c06b --- /dev/null +++ b/agents-api/agents_api/models/developer/__init__.py @@ -0,0 +1,19 @@ +""" +Module: agents_api/models/docs + +This module is responsible for managing document-related operations within the application, particularly for agents and possibly other entities. It serves as a core component of the document management system, enabling features such as document creation, listing, deletion, and embedding of snippets for enhanced search and retrieval capabilities. + +Main functionalities include: +- Creating new documents and associating them with agents or users. +- Listing documents based on various criteria, including ownership and metadata filters. +- Deleting documents by their unique identifiers. +- Embedding document snippets for retrieval purposes. + +The module interacts with other parts of the application, such as the agents and users modules, to provide a comprehensive document management system. Its role is crucial in enabling document search, retrieval, and management features within the context of agents and users. + +This documentation aims to provide clear, concise, and sufficient context for new developers or contributors to understand the module's role without needing to dive deep into the code immediately. +""" + +# ruff: noqa: F401, F403, F405 + +from .get_developer import get_developer, verify_developer diff --git a/agents-api/agents_api/models/developer/get_developer.py b/agents-api/agents_api/models/developer/get_developer.py new file mode 100644 index 000000000..c78517613 --- /dev/null +++ b/agents-api/agents_api/models/developer/get_developer.py @@ -0,0 +1,68 @@ +"""Module for retrieving document snippets from the CozoDB based on document IDs.""" + +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from ...common.protocol.developers import Developer +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + wrap_in_class, +) + + +@rewrap_exceptions({QueryException: partialclass(HTTPException, status_code=401)}) +@cozo_query +@beartype +def verify_developer( + *, + developer_id: UUID, +) -> tuple[str, dict]: + return (verify_developer_id_query(developer_id), {}) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=403), + ValidationError: partialclass(HTTPException, status_code=500), + } +) +@wrap_in_class(Developer, one=True, transform=lambda d: {**d, "id": d["developer_id"]}) +@cozo_query +@beartype +def get_developer( + *, + developer_id: UUID, +) -> tuple[str, dict]: + developer_id = str(developer_id) + + query = """ + input[developer_id] <- [[to_uuid($developer_id)]] + ?[ + developer_id, + email, + active, + tags, + settings, + created_at, + updated_at, + ] := + input[developer_id], + *developers { + developer_id, + email, + active, + tags, + settings, + created_at, + updated_at, + } + """ + + return (query, {"developer_id": developer_id}) diff --git a/agents-api/migrations/migrate_1723400730_add_settings_to_developers.py b/agents-api/migrations/migrate_1723400730_add_settings_to_developers.py new file mode 100644 index 000000000..e10e71510 --- /dev/null +++ b/agents-api/migrations/migrate_1723400730_add_settings_to_developers.py @@ -0,0 +1,68 @@ +# /usr/bin/env python3 + +MIGRATION_ID = "add_settings_to_developers" +CREATED_AT = 1723400730.539554 + + +def up(client): + client.run( + """ + ?[ + developer_id, + email, + active, + tags, + settings, + created_at, + updated_at, + ] := *developers { + developer_id, + email, + active, + created_at, + updated_at, + }, + tags = [], + settings = {} + + :replace developers { + developer_id: Uuid, + => + email: String, + active: Bool default true, + tags: [String] default [], + settings: Json, + created_at: Float default now(), + updated_at: Float default now(), + } + """ + ) + + +def down(client): + client.run( + """ + ?[ + developer_id, + email, + active, + created_at, + updated_at, + ] := *developers { + developer_id, + email, + active, + created_at, + updated_at, + } + + :replace developers { + developer_id: Uuid, + => + email: String, + active: Bool default true, + created_at: Float default now(), + updated_at: Float default now(), + } + """ + ) diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index e04cb316d..f2b76e3e3 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -50,8 +50,8 @@ def test_developer_id(cozo_client=cozo_client): cozo_client.run( f""" - ?[developer_id, email] <- [["{str(developer_id)}", "developers@julep.ai"]] - :insert developers {{ developer_id, email }} + ?[developer_id, email, settings] <- [["{str(developer_id)}", "developers@julep.ai", {{}}]] + :insert developers {{ developer_id, email, settings }} """ ) diff --git a/agents-api/tests/test_developer_queries.py b/agents-api/tests/test_developer_queries.py new file mode 100644 index 000000000..569733fa5 --- /dev/null +++ b/agents-api/tests/test_developer_queries.py @@ -0,0 +1,36 @@ +# Tests for agent queries +from uuid import uuid4 + +from ward import raises, test + +from agents_api.common.protocol.developers import Developer +from agents_api.models.developer.get_developer import get_developer, verify_developer +from tests.fixtures import cozo_client, test_developer_id + + +@test("model: get developer") +def _(client=cozo_client, developer_id=test_developer_id): + developer = get_developer( + developer_id=developer_id, + client=client, + ) + + assert isinstance(developer, Developer) + assert developer.id + + +@test("model: verify developer exists") +def _(client=cozo_client, developer_id=test_developer_id): + verify_developer( + developer_id=developer_id, + client=client, + ) + + +@test("model: verify developer not exists") +def _(client=cozo_client): + with raises(Exception): + verify_developer( + developer_id=uuid4(), + client=client, + ) From 892226d79db1151eeda07dfbeb1390d155f93f50 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 11 Aug 2024 19:23:42 -0400 Subject: [PATCH 035/110] feat(agents-api): Preliminary implementation of session.chat Signed-off-by: Diwank Tomer --- .../agents_api/models/entry/create_entries.py | 4 + .../agents_api/models/entry/delete_entries.py | 5 +- agents-api/agents_api/models/utils.py | 22 +++++ .../agents_api/routers/sessions/chat.py | 86 +++++++++++++++---- 4 files changed, 98 insertions(+), 19 deletions(-) diff --git a/agents-api/agents_api/models/entry/create_entries.py b/agents-api/agents_api/models/entry/create_entries.py index 72551dacc..68d644266 100644 --- a/agents-api/agents_api/models/entry/create_entries.py +++ b/agents-api/agents_api/models/entry/create_entries.py @@ -12,6 +12,7 @@ from ...common.utils.messages import content_to_json from ..utils import ( cozo_query, + mark_session_updated_query, partialclass, rewrap_exceptions, verify_developer_id_query, @@ -41,6 +42,7 @@ def create_entries( developer_id: UUID, session_id: UUID, data: list[CreateEntryRequest], + mark_session_as_updated: bool = True, ) -> tuple[list[str], dict]: developer_id = str(developer_id) session_id = str(session_id) @@ -76,6 +78,8 @@ def create_entries( verify_developer_owns_resource_query( developer_id, "sessions", session_id=session_id ), + mark_session_as_updated + and mark_session_updated_query(developer_id, session_id), create_query, ] diff --git a/agents-api/agents_api/models/entry/delete_entries.py b/agents-api/agents_api/models/entry/delete_entries.py index a156275b0..f64bfbf73 100644 --- a/agents-api/agents_api/models/entry/delete_entries.py +++ b/agents-api/agents_api/models/entry/delete_entries.py @@ -9,6 +9,7 @@ from ...common.utils.datetime import utcnow from ..utils import ( cozo_query, + mark_session_updated_query, partialclass, rewrap_exceptions, verify_developer_id_query, @@ -37,7 +38,7 @@ @cozo_query @beartype def delete_entries_for_session( - *, developer_id: UUID, session_id: UUID + *, developer_id: UUID, session_id: UUID, mark_session_as_updated: bool = True ) -> tuple[list[str], dict]: """ Constructs and returns a datalog query for deleting entries associated with a given session ID from the 'cozodb' database. @@ -79,6 +80,8 @@ def delete_entries_for_session( verify_developer_owns_resource_query( developer_id, "sessions", session_id=session_id ), + mark_session_as_updated + and mark_session_updated_query(developer_id, session_id), delete_query, ] diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index 73c3f3e0b..2939b2208 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -64,6 +64,28 @@ class NewCls(cls): return NewCls +def mark_session_updated_query(developer_id: UUID | str, session_id: UUID | str) -> str: + return f""" + input[developer_id, session_id] <- [[ + to_uuid("{str(developer_id)}"), + to_uuid("{str(session_id)}"), + ]] + + ?[developer_id, session_id, updated_at] := + input[developer_id, session_id], + *sessions {{ + session_id, + }}, + updated_at = [floor(now()), true] + + :update sessions {{ + developer_id, + session_id, + updated_at, + }} + """ + + def verify_developer_id_query(developer_id: UUID | str) -> str: return f""" matched[count(developer_id)] := diff --git a/agents-api/agents_api/routers/sessions/chat.py b/agents-api/agents_api/routers/sessions/chat.py index 368fe2ea2..225e25163 100644 --- a/agents-api/agents_api/routers/sessions/chat.py +++ b/agents-api/agents_api/routers/sessions/chat.py @@ -1,17 +1,24 @@ from typing import Annotated -from uuid import UUID +from uuid import UUID, uuid4 -from fastapi import Depends -from pydantic import UUID4 +from fastapi import BackgroundTasks, Depends from starlette.status import HTTP_201_CREATED from ...autogen.openapi_model import ( ChatInput, ChatResponse, + CreateEntryRequest, + DocReference, History, ) +from ...clients.embed import embed +from ...clients.litellm import acompletion +from ...common.protocol.developers import Developer +from ...common.protocol.sessions import ChatContext from ...common.utils.template import render_template -from ...dependencies.developer_id import get_developer_id +from ...dependencies.developer_id import get_developer_data +from ...models.docs.search_docs_hybrid import search_docs_hybrid +from ...models.entry.create_entries import create_entries from ...models.entry.get_history import get_history from ...models.session.prepare_chat_context import prepare_chat_context from .router import router @@ -23,48 +30,91 @@ tags=["sessions", "chat"], ) async def chat( - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + developer: Annotated[Developer, Depends(get_developer_data)], session_id: UUID, data: ChatInput, + background_tasks: BackgroundTasks, ) -> ChatResponse: # First get the chat context - chat_context = prepare_chat_context( - developer_id=x_developer_id, - agent_id=data.agent_id, + chat_context: ChatContext = prepare_chat_context( + developer_id=developer.id, session_id=session_id, ) + assert isinstance(chat_context, ChatContext) # Merge the settings and prepare environment - request_settings = data.settings - chat_context.merge_settings(request_settings) - + chat_context.merge_settings(data) + settings: dict = chat_context.settings.model_dump() env: dict = chat_context.get_chat_environment() # Get the session history history: History = get_history( - developer_id=x_developer_id, + developer_id=developer.id, session_id=session_id, allowed_sources=["api_request", "api_response", "tool_response", "summarizer"], ) # Keep leaf nodes only relations = history.relations - past_entries = [ + past_messages = [ entry.model_dump() for entry in history.entries if entry.id not in {r.head for r in relations} ] - past_messages = render_template(past_entries, variables=env) + new_raw_messages = [msg.model_dump() for msg in data.messages] - messages = past_messages + [msg.model_dump() for msg in data.messages] + # Search matching docs + [query_embedding, *_] = await embed( + inputs=[ + f"{msg.get('name') or msg['role']}: {msg['content']}" + for msg in new_raw_messages + ], + join_inputs=True, + ) + query_text = new_raw_messages[-1]["content"] - # TODO: Implement the chat logic here - print(messages) + doc_references: list[DocReference] = search_docs_hybrid( + developer_id=developer.id, + owner_type="agent", + owner_id=chat_context.get_active_agent().id, + query=query_text, + query_embedding=query_embedding, + ) + + # Render the messages + env["docs"] = doc_references + new_messages = render_template(new_raw_messages, variables=env) + messages = past_messages + new_messages # Get the response from the model + model_response = await acompletion( + messages=messages, + **settings, + user=str(developer.id), + tags=developer.tags, + ) # Save the input and the response to the session history + new_entries = [CreateEntryRequest(**msg) for msg in new_messages] + background_tasks.add_task( + create_entries, + developer_id=developer.id, + session_id=session_id, + data=new_entries, + mark_session_as_updated=True, + ) # Return the response - raise NotImplementedError() + response_json = model_response.model_dump() + response_json.pop("id", None) + + chat_response: ChatResponse = ChatResponse( + **response_json, + id=uuid4(), + created_at=model_response.created, + jobs=[], + docs=doc_references, + ) + + return chat_response From 7ab4075dc209fc7119af6a0f40c52a2723ebc5e1 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 12 Aug 2024 14:44:24 -0400 Subject: [PATCH 036/110] fix(agents-api): Minor fix to fastapi dependency Signed-off-by: Diwank Tomer --- agents-api/agents_api/dependencies/developer_id.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agents-api/agents_api/dependencies/developer_id.py b/agents-api/agents_api/dependencies/developer_id.py index e9b51e850..af4aa04de 100644 --- a/agents-api/agents_api/dependencies/developer_id.py +++ b/agents-api/agents_api/dependencies/developer_id.py @@ -13,7 +13,7 @@ async def get_developer_id( x_developer_id: Annotated[UUID | None, Header()] = None, ) -> UUID: if skip_check_developer_headers: - return UUID("00000000-0000-0000-0000-000000000000") + return x_developer_id or UUID("00000000-0000-0000-0000-000000000000") if not x_developer_id: raise InvalidHeaderFormat("X-Developer-Id header required") @@ -33,7 +33,7 @@ async def get_developer_data( x_developer_id: Annotated[UUID | None, Header()] = None, ) -> Developer: if skip_check_developer_headers: - x_developer_id = UUID("00000000-0000-0000-0000-000000000000") + x_developer_id = x_developer_id or UUID("00000000-0000-0000-0000-000000000000") if not x_developer_id: raise InvalidHeaderFormat("X-Developer-Id header required") From 5f91ac38712a64c9b3c6ca2c1f308b0cfcffbb78 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 12 Aug 2024 17:26:53 -0400 Subject: [PATCH 037/110] feat(agents-api): Make chat route tests pass Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Chat.py | 8 +- agents-api/agents_api/autogen/Entries.py | 41 +------ .../agents_api/common/protocol/sessions.py | 6 +- .../agents_api/common/utils/template.py | 39 ++++++- .../models/docs/search_docs_hybrid.py | 3 + .../agents_api/routers/sessions/chat.py | 105 +++++++++++------- agents-api/poetry.lock | 6 +- agents-api/tests/fixtures.py | 37 +++++- agents-api/tests/test_agent_queries.py | 1 + agents-api/tests/test_agent_routes.py | 1 + agents-api/tests/test_chat_routes.py | 84 ++++++++++++++ agents-api/tests/test_docs_queries.py | 3 +- sdks/python/julep/api/__init__.py | 10 -- sdks/python/julep/api/types/__init__.py | 12 -- .../julep/api/types/chat_chat_output_chunk.py | 4 +- .../api/types/chat_multiple_chat_output.py | 4 +- .../api/types/chat_single_chat_output.py | 4 +- .../api/types/entries_chat_ml_message.py | 71 ------------ .../types/entries_chat_ml_message_content.py | 9 -- .../entries_chat_ml_message_content_item.py | 87 --------------- .../julep/api/types/entries_chat_ml_role.py | 2 +- sdks/python/poetry.lock | 6 +- sdks/ts/src/api/index.ts | 2 - .../ts/src/api/models/Chat_ChatOutputChunk.ts | 4 +- .../src/api/models/Chat_MultipleChatOutput.ts | 4 +- .../src/api/models/Chat_SingleChatOutput.ts | 4 +- .../src/api/models/Entries_ChatMLMessage.ts | 30 ----- sdks/ts/src/api/models/Entries_ChatMLRole.ts | 2 +- .../src/api/schemas/$Chat_ChatOutputChunk.ts | 2 +- .../api/schemas/$Chat_MultipleChatOutput.ts | 2 +- .../src/api/schemas/$Chat_SingleChatOutput.ts | 2 +- .../src/api/schemas/$Entries_ChatMLMessage.ts | 63 ----------- typespec/chat/models.tsp | 6 +- typespec/entries/models.tsp | 2 +- 34 files changed, 258 insertions(+), 408 deletions(-) create mode 100644 agents-api/tests/test_chat_routes.py delete mode 100644 sdks/python/julep/api/types/entries_chat_ml_message.py delete mode 100644 sdks/python/julep/api/types/entries_chat_ml_message_content.py delete mode 100644 sdks/python/julep/api/types/entries_chat_ml_message_content_item.py delete mode 100644 sdks/ts/src/api/models/Entries_ChatMLMessage.ts delete mode 100644 sdks/ts/src/api/schemas/$Entries_ChatMLMessage.ts diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 1f4f5a67a..2ecdaf2d3 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -10,7 +10,7 @@ from .Common import LogitBias from .Docs import DocReference -from .Entries import ChatMLMessage, InputChatMLMessage +from .Entries import InputChatMLMessage from .Tools import FunctionTool, NamedToolChoice @@ -90,7 +90,7 @@ class ChatOutputChunk(BaseChatOutput): model_config = ConfigDict( populate_by_name=True, ) - delta: ChatMLMessage + delta: InputChatMLMessage """ The message generated by the model """ @@ -166,7 +166,7 @@ class MultipleChatOutput(BaseChatOutput): model_config = ConfigDict( populate_by_name=True, ) - messages: list[ChatMLMessage] + messages: list[InputChatMLMessage] class OpenAISettings(BaseModel): @@ -199,7 +199,7 @@ class SingleChatOutput(BaseChatOutput): model_config = ConfigDict( populate_by_name=True, ) - message: ChatMLMessage + message: InputChatMLMessage class TokenLogProb(BaseTokenLogProb): diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index cfe3692f2..b9921daa4 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -17,7 +17,7 @@ class BaseEntry(BaseModel): ) role: Literal[ "user", - "agent", + "assistant", "system", "function", "function_response", @@ -67,43 +67,6 @@ class ChatMLImageContentPart(BaseModel): """ -class ChatMLMessage(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - role: Literal[ - "user", - "agent", - "system", - "function", - "function_response", - "function_call", - "auto", - ] - """ - The role of the message - """ - content: str | list[str] | list[ChatMLTextContentPart | ChatMLImageContentPart] - """ - The content parts of the message - """ - name: str | None = None - """ - Name - """ - tool_calls: Annotated[ - list[ChosenToolCall], Field([], json_schema_extra={"readOnly": True}) - ] - """ - Tool calls generated by the model. - """ - created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] - """ - When this resource was created as UTC date-time - """ - id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] - - class ChatMLTextContentPart(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -159,7 +122,7 @@ class InputChatMLMessage(BaseModel): ) role: Literal[ "user", - "agent", + "assistant", "system", "function", "function_response", diff --git a/agents-api/agents_api/common/protocol/sessions.py b/agents-api/agents_api/common/protocol/sessions.py index 73c71b365..80f9049d4 100644 --- a/agents-api/agents_api/common/protocol/sessions.py +++ b/agents-api/agents_api/common/protocol/sessions.py @@ -54,7 +54,7 @@ def get_active_agent(self) -> Agent: """ Get the active agent from the session data. """ - requested_agent: UUID | None = self.settings.agent + requested_agent: UUID | None = self.settings and self.settings.agent if requested_agent: assert requested_agent in [agent.id for agent in self.agents], ( @@ -67,7 +67,7 @@ def get_active_agent(self) -> Agent: return self.agents[0] def merge_settings(self, chat_input: ChatInput) -> ChatSettings: - request_settings = ChatSettings.model_validate(chat_input) + request_settings = chat_input.model_dump(exclude_unset=True) active_agent = self.get_active_agent() default_settings = active_agent.default_settings @@ -75,7 +75,7 @@ def merge_settings(self, chat_input: ChatInput) -> ChatSettings: **{ "model": active_agent.model, **default_settings.model_dump(), - **request_settings.model_dump(exclude_unset=True), + **request_settings, } ) diff --git a/agents-api/agents_api/common/utils/template.py b/agents-api/agents_api/common/utils/template.py index eabf6f307..d806ddb6d 100644 --- a/agents-api/agents_api/common/utils/template.py +++ b/agents-api/agents_api/common/utils/template.py @@ -40,6 +40,28 @@ async def render_template_string( return rendered +async def render_template_chatml( + messages: list[dict], variables: dict, check: bool = False +) -> list[dict]: + # Parse template + # FIXME: should template_strings contain a list of ChatMLTextContentPart? Should we handle it somehow? + templates = [jinja_env.from_string(msg["content"]) for msg in messages] + + # If check is required, get required vars from template and validate variables + if check: + for template in templates: + schema = to_json_schema(infer(template)) + validate(instance=variables, schema=schema) + + # Render + rendered = [ + ({**msg, "content": await template.render_async(**variables)}) + for template, msg in zip(templates, messages) + ] + + return rendered + + async def render_template_parts( template_strings: list[dict], variables: dict, check: bool = False ) -> list[dict]: @@ -73,7 +95,7 @@ async def render_template_parts( async def render_template( - template_string: str | list[dict], + input: str | list[dict], variables: dict, check: bool = False, skip_vars: list[str] | None = None, @@ -83,8 +105,15 @@ async def render_template( for name, val in variables.items() if not (skip_vars is not None and isinstance(name, str) and name in skip_vars) } - if isinstance(template_string, str): - return await render_template_string(template_string, variables, check) - elif isinstance(template_string, list): - return await render_template_parts(template_string, variables, check) + match input: + case str(): + future = render_template_string(input, variables, check) + + case [{"content": str()}, *_]: + future = render_template_chatml(input, variables, check) + + case _: + future = render_template_parts(input, variables, check) + + return await future diff --git a/agents-api/agents_api/models/docs/search_docs_hybrid.py b/agents-api/agents_api/models/docs/search_docs_hybrid.py index 52ae76277..0a9cd2815 100644 --- a/agents-api/agents_api/models/docs/search_docs_hybrid.py +++ b/agents-api/agents_api/models/docs/search_docs_hybrid.py @@ -18,6 +18,9 @@ def dbsf_normalize(scores: list[float]) -> list[float]: Scores scaled using minmax scaler with our custom feature range (extremes indicated as 3 standard deviations from the mean) """ + if len(scores) < 2: + return scores + sd = stdev(scores) if sd == 0: return scores diff --git a/agents-api/agents_api/routers/sessions/chat.py b/agents-api/agents_api/routers/sessions/chat.py index 225e25163..f0023cb93 100644 --- a/agents-api/agents_api/routers/sessions/chat.py +++ b/agents-api/agents_api/routers/sessions/chat.py @@ -7,14 +7,16 @@ from ...autogen.openapi_model import ( ChatInput, ChatResponse, + ChunkChatResponse, CreateEntryRequest, DocReference, History, + MessageChatResponse, ) -from ...clients.embed import embed -from ...clients.litellm import acompletion +from ...clients import embed, litellm from ...common.protocol.developers import Developer from ...common.protocol.sessions import ChatContext +from ...common.utils.datetime import utcnow from ...common.utils.template import render_template from ...dependencies.developer_id import get_developer_data from ...models.docs.search_docs_hybrid import search_docs_hybrid @@ -24,28 +26,14 @@ from .router import router -@router.post( - "/sessions/{session_id}/chat", - status_code=HTTP_201_CREATED, - tags=["sessions", "chat"], -) -async def chat( - developer: Annotated[Developer, Depends(get_developer_data)], +async def get_messages( + *, + developer: Developer, session_id: UUID, - data: ChatInput, - background_tasks: BackgroundTasks, -) -> ChatResponse: - # First get the chat context - chat_context: ChatContext = prepare_chat_context( - developer_id=developer.id, - session_id=session_id, - ) - assert isinstance(chat_context, ChatContext) - - # Merge the settings and prepare environment - chat_context.merge_settings(data) - settings: dict = chat_context.settings.model_dump() - env: dict = chat_context.get_chat_environment() + new_raw_messages: list[dict], + chat_context: ChatContext, +): + assert len(new_raw_messages) > 0 # Get the session history history: History = get_history( @@ -62,10 +50,8 @@ async def chat( if entry.id not in {r.head for r in relations} ] - new_raw_messages = [msg.model_dump() for msg in data.messages] - # Search matching docs - [query_embedding, *_] = await embed( + [query_embedding, *_] = await embed.embed( inputs=[ f"{msg.get('name') or msg['role']}: {msg['content']}" for msg in new_raw_messages @@ -82,13 +68,46 @@ async def chat( query_embedding=query_embedding, ) + return past_messages, doc_references + + +@router.post( + "/sessions/{session_id}/chat", + status_code=HTTP_201_CREATED, + tags=["sessions", "chat"], +) +async def chat( + developer: Annotated[Developer, Depends(get_developer_data)], + session_id: UUID, + data: ChatInput, + background_tasks: BackgroundTasks, +) -> ChatResponse: + # First get the chat context + chat_context: ChatContext = prepare_chat_context( + developer_id=developer.id, + session_id=session_id, + ) + + # Merge the settings and prepare environment + chat_context.merge_settings(data) + settings: dict = chat_context.settings.model_dump() + env: dict = chat_context.get_chat_environment() + new_raw_messages = [msg.model_dump() for msg in data.messages] + # Render the messages + past_messages, doc_references = await get_messages( + developer=developer, + session_id=session_id, + new_raw_messages=new_raw_messages, + chat_context=chat_context, + ) + env["docs"] = doc_references - new_messages = render_template(new_raw_messages, variables=env) + new_messages = await render_template(new_raw_messages, variables=env) messages = past_messages + new_messages # Get the response from the model - model_response = await acompletion( + model_response = await litellm.acompletion( messages=messages, **settings, user=str(developer.id), @@ -96,25 +115,27 @@ async def chat( ) # Save the input and the response to the session history - new_entries = [CreateEntryRequest(**msg) for msg in new_messages] - background_tasks.add_task( - create_entries, - developer_id=developer.id, - session_id=session_id, - data=new_entries, - mark_session_as_updated=True, - ) + if data.save: + new_entries = [ + CreateEntryRequest(**msg, source="api_request") for msg in new_messages + ] + background_tasks.add_task( + create_entries, + developer_id=developer.id, + session_id=session_id, + data=new_entries, + mark_session_as_updated=True, + ) # Return the response - response_json = model_response.model_dump() - response_json.pop("id", None) - - chat_response: ChatResponse = ChatResponse( - **response_json, + chat_response_class = ChunkChatResponse if data.stream else MessageChatResponse + chat_response: ChatResponse = chat_response_class( id=uuid4(), - created_at=model_response.created, + created_at=utcnow(), jobs=[], docs=doc_references, + usage=model_response.usage.model_dump(), + choices=[choice.model_dump() for choice in model_response.choices], ) return chat_response diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 4892624c7..bb528337c 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2904,13 +2904,13 @@ files = [ [[package]] name = "openai" -version = "1.40.3" +version = "1.40.5" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.3-py3-none-any.whl", hash = "sha256:09396cb6e2e15c921a5d872bf92841a60a9425da10dcd962b45fe7c4f48f8395"}, - {file = "openai-1.40.3.tar.gz", hash = "sha256:f2ffe907618240938c59d7ccc67dd01dc8c50be203c0077240db6758d2f02480"}, + {file = "openai-1.40.5-py3-none-any.whl", hash = "sha256:88ab3163573985c87fcf77958cdc51c0b89f843c80f3ac7d60e049940570ea00"}, + {file = "openai-1.40.5.tar.gz", hash = "sha256:0d27d120a287eb23dec2a98d101c6a2362a327861787d9fce2739aec9e2b9543"}, ] [package.dependencies] diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index f2b76e3e3..fafb351f0 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -1,7 +1,9 @@ +from unittest.mock import patch from uuid import uuid4 from cozo_migrate.api import apply, init from fastapi.testclient import TestClient +from litellm.types.utils import Choices, ModelResponse from pycozo import Client as CozoClient from temporalio.client import WorkflowHandle from ward import fixture @@ -18,6 +20,7 @@ from agents_api.env import api_key, api_key_header_name from agents_api.models.agent.create_agent import create_agent from agents_api.models.agent.delete_agent import delete_agent +from agents_api.models.developer.get_developer import get_developer from agents_api.models.docs.create_doc import create_doc from agents_api.models.docs.delete_doc import delete_doc from agents_api.models.execution.create_execution import create_execution @@ -31,6 +34,8 @@ from agents_api.models.user.delete_user import delete_user from agents_api.web import app +EMBEDDING_SIZE: int = 1024 + @fixture(scope="global") def cozo_client(migrations_dir: str = "./migrations"): @@ -65,6 +70,32 @@ def test_developer_id(cozo_client=cozo_client): ) +@fixture(scope="global") +def test_developer(cozo_client=cozo_client, developer_id=test_developer_id): + return get_developer( + developer_id=developer_id, + client=cozo_client, + ) + + +@fixture(scope="global") +def patch_embed_acompletion(): + mock_model_response = ModelResponse( + id="fake_id", + choices=[Choices(message={"role": "assistant", "content": "Hello, world!"})], + created=0, + object="text_completion", + ) + + with patch("agents_api.clients.embed.embed") as embed, patch( + "agents_api.clients.litellm.acompletion" + ) as acompletion: + embed.return_value = [[1.0] * EMBEDDING_SIZE] + acompletion.return_value = mock_model_response + + yield embed, acompletion + + @fixture(scope="global") def test_agent(cozo_client=cozo_client, developer_id=test_developer_id): agent = create_agent( @@ -232,10 +263,12 @@ def test_execution( yield execution - client.run(f""" + client.run( + f""" ?[execution_id] <- ["{str(execution.id)}"] :delete executions {{ execution_id }} - """) + """ + ) @fixture(scope="global") diff --git a/agents-api/tests/test_agent_queries.py b/agents-api/tests/test_agent_queries.py index b6d69c287..f074a768a 100644 --- a/agents-api/tests/test_agent_queries.py +++ b/agents-api/tests/test_agent_queries.py @@ -108,6 +108,7 @@ def _(client=cozo_client, developer_id=test_developer_id, agent=test_agent): data=UpdateAgentRequest( name="updated agent", about="updated agent about", + model="gpt-4o", default_settings={"temperature": 1.0}, metadata={"hello": "world"}, ), diff --git a/agents-api/tests/test_agent_routes.py b/agents-api/tests/test_agent_routes.py index 53951a855..353d6ab95 100644 --- a/agents-api/tests/test_agent_routes.py +++ b/agents-api/tests/test_agent_routes.py @@ -139,6 +139,7 @@ def _(make_request=make_request, agent=test_agent): name="updated agent", about="updated agent about", default_settings={"temperature": 1.0}, + model="gpt-4o", metadata={"hello": "world"}, ) diff --git a/agents-api/tests/test_chat_routes.py b/agents-api/tests/test_chat_routes.py new file mode 100644 index 000000000..72cd3158c --- /dev/null +++ b/agents-api/tests/test_chat_routes.py @@ -0,0 +1,84 @@ +# Tests for session queries + +from ward import test + +from agents_api.clients import embed, litellm +from agents_api.models.session.prepare_chat_context import prepare_chat_context +from agents_api.routers.sessions.chat import get_messages +from tests.fixtures import ( + cozo_client, + make_request, + patch_embed_acompletion, + test_agent, + test_developer, + test_developer_id, + test_session, + test_tool, + test_user, +) + + +@test("chat: check that patching libs works") +async def _( + _=patch_embed_acompletion, +): + assert (await litellm.acompletion(model="gpt-4o", messages=[])).id == "fake_id" + assert (await embed.embed())[0][0] == 1.0 + + +@test("chat: check that get_messages works") +async def _( + developer=test_developer, + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, + session=test_session, + tool=test_tool, + user=test_user, + mocks=patch_embed_acompletion, +): + (embed, _) = mocks + + chat_context = prepare_chat_context( + developer_id=developer_id, + session_id=session.id, + client=client, + ) + + session_id = session.id + + new_raw_messages = [{"role": "user", "content": "hello"}] + + past_messages, doc_references = await get_messages( + developer=developer, + session_id=session_id, + new_raw_messages=new_raw_messages, + chat_context=chat_context, + ) + + assert isinstance(past_messages, list) + assert isinstance(doc_references, list) + + # Check that embed was called at least once + embed.assert_called() + + +@test("chat: check that chat route calls both mocks") +async def _( + make_request=make_request, + session=test_session, + mocks=patch_embed_acompletion, +): + (embed, acompletion) = mocks + + response = make_request( + method="POST", + url=f"/sessions/{session.id}/chat", + json={"messages": [{"role": "user", "content": "hello"}]}, + ) + + response.raise_for_status() + + # Check that both mocks were called at least once + embed.assert_called() + acompletion.assert_called() diff --git a/agents-api/tests/test_docs_queries.py b/agents-api/tests/test_docs_queries.py index 5b9c28841..4743ea45d 100644 --- a/agents-api/tests/test_docs_queries.py +++ b/agents-api/tests/test_docs_queries.py @@ -10,6 +10,7 @@ from agents_api.models.docs.list_docs import list_docs from agents_api.models.docs.search_docs_by_embedding import search_docs_by_embedding from tests.fixtures import ( + EMBEDDING_SIZE, cozo_client, test_agent, test_developer_id, @@ -17,8 +18,6 @@ test_user, ) -EMBEDDING_SIZE: int = 1024 - @test("model: create docs") def _( diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index 61726cb46..c7371d4e1 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -75,11 +75,6 @@ EntriesBaseEntryContentItemItem_Text, EntriesBaseEntrySource, EntriesChatMlImageContentPart, - EntriesChatMlMessage, - EntriesChatMlMessageContent, - EntriesChatMlMessageContentItem, - EntriesChatMlMessageContentItem_ImageUrl, - EntriesChatMlMessageContentItem_Text, EntriesChatMlRole, EntriesChatMlTextContentPart, EntriesEntry, @@ -281,11 +276,6 @@ "EntriesBaseEntryContentItemItem_Text", "EntriesBaseEntrySource", "EntriesChatMlImageContentPart", - "EntriesChatMlMessage", - "EntriesChatMlMessageContent", - "EntriesChatMlMessageContentItem", - "EntriesChatMlMessageContentItem_ImageUrl", - "EntriesChatMlMessageContentItem_Text", "EntriesChatMlRole", "EntriesChatMlTextContentPart", "EntriesEntry", diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index 5f1b1b76a..3df1542cd 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -84,13 +84,6 @@ ) from .entries_base_entry_source import EntriesBaseEntrySource from .entries_chat_ml_image_content_part import EntriesChatMlImageContentPart -from .entries_chat_ml_message import EntriesChatMlMessage -from .entries_chat_ml_message_content import EntriesChatMlMessageContent -from .entries_chat_ml_message_content_item import ( - EntriesChatMlMessageContentItem, - EntriesChatMlMessageContentItem_ImageUrl, - EntriesChatMlMessageContentItem_Text, -) from .entries_chat_ml_role import EntriesChatMlRole from .entries_chat_ml_text_content_part import EntriesChatMlTextContentPart from .entries_entry import EntriesEntry @@ -323,11 +316,6 @@ "EntriesBaseEntryContentItemItem_Text", "EntriesBaseEntrySource", "EntriesChatMlImageContentPart", - "EntriesChatMlMessage", - "EntriesChatMlMessageContent", - "EntriesChatMlMessageContentItem", - "EntriesChatMlMessageContentItem_ImageUrl", - "EntriesChatMlMessageContentItem_Text", "EntriesChatMlRole", "EntriesChatMlTextContentPart", "EntriesEntry", diff --git a/sdks/python/julep/api/types/chat_chat_output_chunk.py b/sdks/python/julep/api/types/chat_chat_output_chunk.py index 6eb42c639..926787155 100644 --- a/sdks/python/julep/api/types/chat_chat_output_chunk.py +++ b/sdks/python/julep/api/types/chat_chat_output_chunk.py @@ -6,7 +6,7 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .chat_base_chat_output import ChatBaseChatOutput -from .entries_chat_ml_message import EntriesChatMlMessage +from .entries_input_chat_ml_message import EntriesInputChatMlMessage class ChatChatOutputChunk(ChatBaseChatOutput): @@ -14,7 +14,7 @@ class ChatChatOutputChunk(ChatBaseChatOutput): Streaming chat completion output """ - delta: EntriesChatMlMessage = pydantic_v1.Field() + delta: EntriesInputChatMlMessage = pydantic_v1.Field() """ The message generated by the model """ diff --git a/sdks/python/julep/api/types/chat_multiple_chat_output.py b/sdks/python/julep/api/types/chat_multiple_chat_output.py index ab41027eb..2fe0a50df 100644 --- a/sdks/python/julep/api/types/chat_multiple_chat_output.py +++ b/sdks/python/julep/api/types/chat_multiple_chat_output.py @@ -6,7 +6,7 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .chat_base_chat_output import ChatBaseChatOutput -from .entries_chat_ml_message import EntriesChatMlMessage +from .entries_input_chat_ml_message import EntriesInputChatMlMessage class ChatMultipleChatOutput(ChatBaseChatOutput): @@ -14,7 +14,7 @@ class ChatMultipleChatOutput(ChatBaseChatOutput): The output returned by the model. Note that, depending on the model provider, they might return more than one message. """ - messages: typing.List[EntriesChatMlMessage] + messages: typing.List[EntriesInputChatMlMessage] def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { diff --git a/sdks/python/julep/api/types/chat_single_chat_output.py b/sdks/python/julep/api/types/chat_single_chat_output.py index 236d7d566..51b5d99fe 100644 --- a/sdks/python/julep/api/types/chat_single_chat_output.py +++ b/sdks/python/julep/api/types/chat_single_chat_output.py @@ -6,7 +6,7 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .chat_base_chat_output import ChatBaseChatOutput -from .entries_chat_ml_message import EntriesChatMlMessage +from .entries_input_chat_ml_message import EntriesInputChatMlMessage class ChatSingleChatOutput(ChatBaseChatOutput): @@ -14,7 +14,7 @@ class ChatSingleChatOutput(ChatBaseChatOutput): The output returned by the model. Note that, depending on the model provider, they might return more than one message. """ - message: EntriesChatMlMessage + message: EntriesInputChatMlMessage def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { diff --git a/sdks/python/julep/api/types/entries_chat_ml_message.py b/sdks/python/julep/api/types/entries_chat_ml_message.py deleted file mode 100644 index f3c087f6b..000000000 --- a/sdks/python/julep/api/types/entries_chat_ml_message.py +++ /dev/null @@ -1,71 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .common_uuid import CommonUuid -from .entries_chat_ml_message_content import EntriesChatMlMessageContent -from .entries_chat_ml_role import EntriesChatMlRole -from .tools_chosen_tool_call import ToolsChosenToolCall - - -class EntriesChatMlMessage(pydantic_v1.BaseModel): - role: EntriesChatMlRole = pydantic_v1.Field() - """ - The role of the message - """ - - content: EntriesChatMlMessageContent = pydantic_v1.Field() - """ - The content parts of the message - """ - - name: typing.Optional[str] = pydantic_v1.Field(default=None) - """ - Name - """ - - tool_calls: typing.List[ToolsChosenToolCall] = pydantic_v1.Field() - """ - Tool calls generated by the model. - """ - - created_at: dt.datetime = pydantic_v1.Field() - """ - When this resource was created as UTC date-time - """ - - id: CommonUuid - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/entries_chat_ml_message_content.py b/sdks/python/julep/api/types/entries_chat_ml_message_content.py deleted file mode 100644 index 3b80f2242..000000000 --- a/sdks/python/julep/api/types/entries_chat_ml_message_content.py +++ /dev/null @@ -1,9 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -from .entries_chat_ml_message_content_item import EntriesChatMlMessageContentItem - -EntriesChatMlMessageContent = typing.Union[ - str, typing.List[str], typing.List[EntriesChatMlMessageContentItem] -] diff --git a/sdks/python/julep/api/types/entries_chat_ml_message_content_item.py b/sdks/python/julep/api/types/entries_chat_ml_message_content_item.py deleted file mode 100644 index 1af6f3b06..000000000 --- a/sdks/python/julep/api/types/entries_chat_ml_message_content_item.py +++ /dev/null @@ -1,87 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -from __future__ import annotations - -import datetime as dt -import typing - -from ..core.datetime_utils import serialize_datetime -from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .entries_image_url import EntriesImageUrl - - -class EntriesChatMlMessageContentItem_Text(pydantic_v1.BaseModel): - text: str - type: typing.Literal["text"] = "text" - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} - - -class EntriesChatMlMessageContentItem_ImageUrl(pydantic_v1.BaseModel): - image_url: EntriesImageUrl - type: typing.Literal["image_url"] = "image_url" - - def json(self, **kwargs: typing.Any) -> str: - kwargs_with_defaults: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - return super().json(**kwargs_with_defaults) - - def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: - kwargs_with_defaults_exclude_unset: typing.Any = { - "by_alias": True, - "exclude_unset": True, - **kwargs, - } - kwargs_with_defaults_exclude_none: typing.Any = { - "by_alias": True, - "exclude_none": True, - **kwargs, - } - - return deep_union_pydantic_dicts( - super().dict(**kwargs_with_defaults_exclude_unset), - super().dict(**kwargs_with_defaults_exclude_none), - ) - - class Config: - frozen = True - smart_union = True - extra = pydantic_v1.Extra.allow - json_encoders = {dt.datetime: serialize_datetime} - - -EntriesChatMlMessageContentItem = typing.Union[ - EntriesChatMlMessageContentItem_Text, EntriesChatMlMessageContentItem_ImageUrl -] diff --git a/sdks/python/julep/api/types/entries_chat_ml_role.py b/sdks/python/julep/api/types/entries_chat_ml_role.py index 9c61bb75a..0cc9a5d46 100644 --- a/sdks/python/julep/api/types/entries_chat_ml_role.py +++ b/sdks/python/julep/api/types/entries_chat_ml_role.py @@ -5,7 +5,7 @@ EntriesChatMlRole = typing.Union[ typing.Literal[ "user", - "agent", + "assistant", "system", "function", "function_response", diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index bef7da8bf..1240b9124 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -1751,13 +1751,13 @@ files = [ [[package]] name = "openai" -version = "1.40.3" +version = "1.40.5" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.3-py3-none-any.whl", hash = "sha256:09396cb6e2e15c921a5d872bf92841a60a9425da10dcd962b45fe7c4f48f8395"}, - {file = "openai-1.40.3.tar.gz", hash = "sha256:f2ffe907618240938c59d7ccc67dd01dc8c50be203c0077240db6758d2f02480"}, + {file = "openai-1.40.5-py3-none-any.whl", hash = "sha256:88ab3163573985c87fcf77958cdc51c0b89f843c80f3ac7d60e049940570ea00"}, + {file = "openai-1.40.5.tar.gz", hash = "sha256:0d27d120a287eb23dec2a98d101c6a2362a327861787d9fce2739aec9e2b9543"}, ] [package.dependencies] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index 0bd8c86e7..3aa08e1f6 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -64,7 +64,6 @@ export type { Docs_TextOnlyDocSearchRequest } from "./models/Docs_TextOnlyDocSea export type { Docs_VectorDocSearchRequest } from "./models/Docs_VectorDocSearchRequest"; export type { Entries_BaseEntry } from "./models/Entries_BaseEntry"; export type { Entries_ChatMLImageContentPart } from "./models/Entries_ChatMLImageContentPart"; -export type { Entries_ChatMLMessage } from "./models/Entries_ChatMLMessage"; export type { Entries_ChatMLRole } from "./models/Entries_ChatMLRole"; export type { Entries_ChatMLTextContentPart } from "./models/Entries_ChatMLTextContentPart"; export type { Entries_Entry } from "./models/Entries_Entry"; @@ -183,7 +182,6 @@ export { $Docs_TextOnlyDocSearchRequest } from "./schemas/$Docs_TextOnlyDocSearc export { $Docs_VectorDocSearchRequest } from "./schemas/$Docs_VectorDocSearchRequest"; export { $Entries_BaseEntry } from "./schemas/$Entries_BaseEntry"; export { $Entries_ChatMLImageContentPart } from "./schemas/$Entries_ChatMLImageContentPart"; -export { $Entries_ChatMLMessage } from "./schemas/$Entries_ChatMLMessage"; export { $Entries_ChatMLRole } from "./schemas/$Entries_ChatMLRole"; export { $Entries_ChatMLTextContentPart } from "./schemas/$Entries_ChatMLTextContentPart"; export { $Entries_Entry } from "./schemas/$Entries_Entry"; diff --git a/sdks/ts/src/api/models/Chat_ChatOutputChunk.ts b/sdks/ts/src/api/models/Chat_ChatOutputChunk.ts index 9d52a7fdc..a3bbfacc7 100644 --- a/sdks/ts/src/api/models/Chat_ChatOutputChunk.ts +++ b/sdks/ts/src/api/models/Chat_ChatOutputChunk.ts @@ -3,7 +3,7 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_BaseChatOutput } from "./Chat_BaseChatOutput"; -import type { Entries_ChatMLMessage } from "./Entries_ChatMLMessage"; +import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; /** * Streaming chat completion output */ @@ -11,5 +11,5 @@ export type Chat_ChatOutputChunk = Chat_BaseChatOutput & { /** * The message generated by the model */ - delta: Entries_ChatMLMessage; + delta: Entries_InputChatMLMessage; }; diff --git a/sdks/ts/src/api/models/Chat_MultipleChatOutput.ts b/sdks/ts/src/api/models/Chat_MultipleChatOutput.ts index 759edb0f5..b0eb182f6 100644 --- a/sdks/ts/src/api/models/Chat_MultipleChatOutput.ts +++ b/sdks/ts/src/api/models/Chat_MultipleChatOutput.ts @@ -3,10 +3,10 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_BaseChatOutput } from "./Chat_BaseChatOutput"; -import type { Entries_ChatMLMessage } from "./Entries_ChatMLMessage"; +import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; /** * The output returned by the model. Note that, depending on the model provider, they might return more than one message. */ export type Chat_MultipleChatOutput = Chat_BaseChatOutput & { - messages: Array; + messages: Array; }; diff --git a/sdks/ts/src/api/models/Chat_SingleChatOutput.ts b/sdks/ts/src/api/models/Chat_SingleChatOutput.ts index e4571c234..57b76490c 100644 --- a/sdks/ts/src/api/models/Chat_SingleChatOutput.ts +++ b/sdks/ts/src/api/models/Chat_SingleChatOutput.ts @@ -3,10 +3,10 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_BaseChatOutput } from "./Chat_BaseChatOutput"; -import type { Entries_ChatMLMessage } from "./Entries_ChatMLMessage"; +import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; /** * The output returned by the model. Note that, depending on the model provider, they might return more than one message. */ export type Chat_SingleChatOutput = Chat_BaseChatOutput & { - message: Entries_ChatMLMessage; + message: Entries_InputChatMLMessage; }; diff --git a/sdks/ts/src/api/models/Entries_ChatMLMessage.ts b/sdks/ts/src/api/models/Entries_ChatMLMessage.ts deleted file mode 100644 index 019cfb7db..000000000 --- a/sdks/ts/src/api/models/Entries_ChatMLMessage.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_uuid } from "./Common_uuid"; -import type { Entries_ChatMLRole } from "./Entries_ChatMLRole"; -import type { Tools_ChosenToolCall } from "./Tools_ChosenToolCall"; -export type Entries_ChatMLMessage = { - /** - * The role of the message - */ - role: Entries_ChatMLRole; - /** - * The content parts of the message - */ - content: string | Array; - /** - * Name - */ - name?: string; - /** - * Tool calls generated by the model. - */ - readonly tool_calls: Array; - /** - * When this resource was created as UTC date-time - */ - readonly created_at: string; - readonly id: Common_uuid; -}; diff --git a/sdks/ts/src/api/models/Entries_ChatMLRole.ts b/sdks/ts/src/api/models/Entries_ChatMLRole.ts index 0a4789cde..d0ad7e4da 100644 --- a/sdks/ts/src/api/models/Entries_ChatMLRole.ts +++ b/sdks/ts/src/api/models/Entries_ChatMLRole.ts @@ -7,7 +7,7 @@ */ export type Entries_ChatMLRole = | "user" - | "agent" + | "assistant" | "system" | "function" | "function_response" diff --git a/sdks/ts/src/api/schemas/$Chat_ChatOutputChunk.ts b/sdks/ts/src/api/schemas/$Chat_ChatOutputChunk.ts index 2ad1c1175..71efa1925 100644 --- a/sdks/ts/src/api/schemas/$Chat_ChatOutputChunk.ts +++ b/sdks/ts/src/api/schemas/$Chat_ChatOutputChunk.ts @@ -16,7 +16,7 @@ export const $Chat_ChatOutputChunk = { description: `The message generated by the model`, contains: [ { - type: "Entries_ChatMLMessage", + type: "Entries_InputChatMLMessage", }, ], isRequired: true, diff --git a/sdks/ts/src/api/schemas/$Chat_MultipleChatOutput.ts b/sdks/ts/src/api/schemas/$Chat_MultipleChatOutput.ts index 8dc1b3bb7..331290c5c 100644 --- a/sdks/ts/src/api/schemas/$Chat_MultipleChatOutput.ts +++ b/sdks/ts/src/api/schemas/$Chat_MultipleChatOutput.ts @@ -14,7 +14,7 @@ export const $Chat_MultipleChatOutput = { messages: { type: "array", contains: { - type: "Entries_ChatMLMessage", + type: "Entries_InputChatMLMessage", }, isRequired: true, }, diff --git a/sdks/ts/src/api/schemas/$Chat_SingleChatOutput.ts b/sdks/ts/src/api/schemas/$Chat_SingleChatOutput.ts index 1cd376f3f..75c9ddf74 100644 --- a/sdks/ts/src/api/schemas/$Chat_SingleChatOutput.ts +++ b/sdks/ts/src/api/schemas/$Chat_SingleChatOutput.ts @@ -12,7 +12,7 @@ export const $Chat_SingleChatOutput = { { properties: { message: { - type: "Entries_ChatMLMessage", + type: "Entries_InputChatMLMessage", isRequired: true, }, }, diff --git a/sdks/ts/src/api/schemas/$Entries_ChatMLMessage.ts b/sdks/ts/src/api/schemas/$Entries_ChatMLMessage.ts deleted file mode 100644 index a9a55cee2..000000000 --- a/sdks/ts/src/api/schemas/$Entries_ChatMLMessage.ts +++ /dev/null @@ -1,63 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Entries_ChatMLMessage = { - properties: { - role: { - type: "all-of", - description: `The role of the message`, - contains: [ - { - type: "Entries_ChatMLRole", - }, - ], - isRequired: true, - }, - content: { - type: "any-of", - description: `The content parts of the message`, - contains: [ - { - type: "string", - }, - { - type: "array", - contains: { - type: "string", - }, - }, - ], - isRequired: true, - }, - name: { - type: "string", - description: `Name`, - }, - tool_calls: { - type: "array", - contains: { - type: "Tools_ChosenToolCall", - }, - isReadOnly: true, - isRequired: true, - }, - created_at: { - type: "string", - description: `When this resource was created as UTC date-time`, - isReadOnly: true, - isRequired: true, - format: "date-time", - }, - id: { - type: "all-of", - contains: [ - { - type: "Common_uuid", - }, - ], - isReadOnly: true, - isRequired: true, - }, - }, -} as const; diff --git a/typespec/chat/models.tsp b/typespec/chat/models.tsp index 97f374413..819648d8b 100644 --- a/typespec/chat/models.tsp +++ b/typespec/chat/models.tsp @@ -193,12 +193,12 @@ model BaseChatOutput { /** The output returned by the model. Note that, depending on the model provider, they might return more than one message. */ model SingleChatOutput extends BaseChatOutput { - message: ChatMLMessage; + message: InputChatMLMessage; } /** The output returned by the model. Note that, depending on the model provider, they might return more than one message. */ model MultipleChatOutput extends BaseChatOutput { - messages: ChatMLMessage[]; + messages: InputChatMLMessage[]; } alias ChatOutput = SingleChatOutput | MultipleChatOutput; @@ -206,7 +206,7 @@ alias ChatOutput = SingleChatOutput | MultipleChatOutput; /** Streaming chat completion output */ model ChatOutputChunk extends BaseChatOutput { /** The message generated by the model */ - delta: ChatMLMessage; + delta: InputChatMLMessage; } model BaseChatResponse { diff --git a/typespec/entries/models.tsp b/typespec/entries/models.tsp index 0d8e604d9..f9050b4f4 100644 --- a/typespec/entries/models.tsp +++ b/typespec/entries/models.tsp @@ -22,7 +22,7 @@ enum ImageDetail { /** ChatML role (system|assistant|user|function_call|function|function_response|auto) */ enum ChatMLRole { user, - agent, + assistant, system, function, function_response, From dcc54c49280882066aa4ada432bb26a5fa25a40d Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 12 Aug 2024 21:25:44 -0400 Subject: [PATCH 038/110] fix(agents-api): Fixed chat route and tests Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Chat.py | 6 ++-- agents-api/poetry.lock | 30 +++++++++---------- agents-api/tests/test_chat_routes.py | 15 +++++++++- sdks/python/poetry.lock | 6 ++-- sdks/ts/src/api/models/Chat_BaseChatOutput.ts | 2 +- .../src/api/models/Chat_BaseChatResponse.ts | 2 +- .../src/api/models/Chat_BaseTokenLogProb.ts | 2 +- .../src/api/schemas/$Chat_BaseChatOutput.ts | 2 -- .../src/api/schemas/$Chat_BaseChatResponse.ts | 2 -- .../src/api/schemas/$Chat_BaseTokenLogProb.ts | 2 -- typespec/chat/models.tsp | 6 ++-- 11 files changed, 41 insertions(+), 34 deletions(-) diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 2ecdaf2d3..94832c4cb 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -23,7 +23,7 @@ class BaseChatOutput(BaseModel): """ The reason the model stopped generating tokens """ - logprobs: Annotated[LogProbResponse | None, Field(...)] + logprobs: LogProbResponse | None = None """ The log probabilities of tokens """ @@ -33,7 +33,7 @@ class BaseChatResponse(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - usage: Annotated[CompetionUsage | None, Field(...)] + usage: CompetionUsage | None = None """ Usage statistics for the completion request """ @@ -61,7 +61,7 @@ class BaseTokenLogProb(BaseModel): """ The log probability of the token """ - bytes: Annotated[list[int] | None, Field(...)] + bytes: list[int] | None = None class ChatInputData(BaseModel): diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index bb528337c..9e0dcabdf 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2139,19 +2139,19 @@ files = [ [[package]] name = "langchain" -version = "0.2.12" +version = "0.2.13" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.2.12-py3-none-any.whl", hash = "sha256:565d2f5df1c06815d1c684400218ec4ae5e1027887aad343226fad846c54e726"}, - {file = "langchain-0.2.12.tar.gz", hash = "sha256:fe7bd409c133017446fec54c38a5e7cb14f74e020090d7b5065374badf71e6d1"}, + {file = "langchain-0.2.13-py3-none-any.whl", hash = "sha256:80f21e48cdada424dd2af9bbf42234fe095744cf181b31eeb63d1da7479e2783"}, + {file = "langchain-0.2.13.tar.gz", hash = "sha256:947e96ac3153a46aa6a0d8207e5f8b6794084c397f60a01bbf4bba78e6838fee"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} -langchain-core = ">=0.2.27,<0.3.0" +langchain-core = ">=0.2.30,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0" langsmith = ">=0.1.17,<0.2.0" numpy = {version = ">=1,<2", markers = "python_version < \"3.12\""} @@ -2163,20 +2163,20 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" [[package]] name = "langchain-community" -version = "0.2.11" +version = "0.2.12" description = "Community contributed LangChain integrations." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_community-0.2.11-py3-none-any.whl", hash = "sha256:465c03ba1603975d141533424185e09546ecf09e379c93aee2671bdc9b325cda"}, - {file = "langchain_community-0.2.11.tar.gz", hash = "sha256:ede261ff8202f1433f004ee90baf89f371cee37cb1abfc16dd0f8392db10b23e"}, + {file = "langchain_community-0.2.12-py3-none-any.whl", hash = "sha256:50e74473dd2309bdef561760afbbf0c5ea17ed91fc4dfa0d52279dd16d6d34e0"}, + {file = "langchain_community-0.2.12.tar.gz", hash = "sha256:d671cfc6a4f3b65f49a2e59ab420d0164f109d0a56fc4b4996518205c63b8c7e"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" dataclasses-json = ">=0.5.7,<0.7" -langchain = ">=0.2.12,<0.3.0" -langchain-core = ">=0.2.27,<0.3.0" +langchain = ">=0.2.13,<0.3.0" +langchain-core = ">=0.2.30,<0.3.0" langsmith = ">=0.1.0,<0.2.0" numpy = {version = ">=1,<2", markers = "python_version < \"3.12\""} PyYAML = ">=5.3" @@ -2186,13 +2186,13 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.2.29" +version = "0.2.30" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.29-py3-none-any.whl", hash = "sha256:846c04a3bb72e409a9b928e0eb3ea1762e1473f2c4fb6df2596fbd7b3ab75973"}, - {file = "langchain_core-0.2.29.tar.gz", hash = "sha256:491324745a7afee5a7b285c3904edd9dd0c6efa7daf26b92fec6e84a2d2f5d10"}, + {file = "langchain_core-0.2.30-py3-none-any.whl", hash = "sha256:ea7eccb9566dd51b2b74bd292c4239d843a77cdba8ffae2b5edf7000d70d4194"}, + {file = "langchain_core-0.2.30.tar.gz", hash = "sha256:552ec586698140062cd299a83bad7e308f925b496d306b62529579c6fb122f7a"}, ] [package.dependencies] @@ -2904,13 +2904,13 @@ files = [ [[package]] name = "openai" -version = "1.40.5" +version = "1.40.6" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.5-py3-none-any.whl", hash = "sha256:88ab3163573985c87fcf77958cdc51c0b89f843c80f3ac7d60e049940570ea00"}, - {file = "openai-1.40.5.tar.gz", hash = "sha256:0d27d120a287eb23dec2a98d101c6a2362a327861787d9fce2739aec9e2b9543"}, + {file = "openai-1.40.6-py3-none-any.whl", hash = "sha256:b36372124a779381a420a34dd96f762baa748b6bdfaf83a6b9f2745f72ccc1c5"}, + {file = "openai-1.40.6.tar.gz", hash = "sha256:2239232bcb7f4bd4ce8e02544b5769618582411cf399816d96686d1b6c1e5c8d"}, ] [package.dependencies] diff --git a/agents-api/tests/test_chat_routes.py b/agents-api/tests/test_chat_routes.py index 72cd3158c..55d94b2a0 100644 --- a/agents-api/tests/test_chat_routes.py +++ b/agents-api/tests/test_chat_routes.py @@ -2,7 +2,9 @@ from ward import test +from agents_api.autogen.Sessions import CreateSessionRequest from agents_api.clients import embed, litellm +from agents_api.models.session.create_session import create_session from agents_api.models.session.prepare_chat_context import prepare_chat_context from agents_api.routers.sessions.chat import get_messages from tests.fixtures import ( @@ -66,9 +68,20 @@ async def _( @test("chat: check that chat route calls both mocks") async def _( make_request=make_request, - session=test_session, + developer_id=test_developer_id, + agent=test_agent, mocks=patch_embed_acompletion, + client=cozo_client, ): + session = create_session( + developer_id=developer_id, + data=CreateSessionRequest( + agent=agent.id, + situation="test session about", + ), + client=client, + ) + (embed, acompletion) = mocks response = make_request( diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 1240b9124..38e2c1b19 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -1751,13 +1751,13 @@ files = [ [[package]] name = "openai" -version = "1.40.5" +version = "1.40.6" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.5-py3-none-any.whl", hash = "sha256:88ab3163573985c87fcf77958cdc51c0b89f843c80f3ac7d60e049940570ea00"}, - {file = "openai-1.40.5.tar.gz", hash = "sha256:0d27d120a287eb23dec2a98d101c6a2362a327861787d9fce2739aec9e2b9543"}, + {file = "openai-1.40.6-py3-none-any.whl", hash = "sha256:b36372124a779381a420a34dd96f762baa748b6bdfaf83a6b9f2745f72ccc1c5"}, + {file = "openai-1.40.6.tar.gz", hash = "sha256:2239232bcb7f4bd4ce8e02544b5769618582411cf399816d96686d1b6c1e5c8d"}, ] [package.dependencies] diff --git a/sdks/ts/src/api/models/Chat_BaseChatOutput.ts b/sdks/ts/src/api/models/Chat_BaseChatOutput.ts index 6770dd5d5..91c56285f 100644 --- a/sdks/ts/src/api/models/Chat_BaseChatOutput.ts +++ b/sdks/ts/src/api/models/Chat_BaseChatOutput.ts @@ -13,5 +13,5 @@ export type Chat_BaseChatOutput = { /** * The log probabilities of tokens */ - logprobs: Chat_LogProbResponse | null; + logprobs?: Chat_LogProbResponse; }; diff --git a/sdks/ts/src/api/models/Chat_BaseChatResponse.ts b/sdks/ts/src/api/models/Chat_BaseChatResponse.ts index 781dd3838..078b8873f 100644 --- a/sdks/ts/src/api/models/Chat_BaseChatResponse.ts +++ b/sdks/ts/src/api/models/Chat_BaseChatResponse.ts @@ -9,7 +9,7 @@ export type Chat_BaseChatResponse = { /** * Usage statistics for the completion request */ - usage: Chat_CompetionUsage | null; + usage?: Chat_CompetionUsage; /** * Background job IDs that may have been spawned from this interaction. */ diff --git a/sdks/ts/src/api/models/Chat_BaseTokenLogProb.ts b/sdks/ts/src/api/models/Chat_BaseTokenLogProb.ts index 41b715757..dd9711b4d 100644 --- a/sdks/ts/src/api/models/Chat_BaseTokenLogProb.ts +++ b/sdks/ts/src/api/models/Chat_BaseTokenLogProb.ts @@ -8,5 +8,5 @@ export type Chat_BaseTokenLogProb = { * The log probability of the token */ logprob: number; - bytes: Array | null; + bytes?: Array; }; diff --git a/sdks/ts/src/api/schemas/$Chat_BaseChatOutput.ts b/sdks/ts/src/api/schemas/$Chat_BaseChatOutput.ts index 3053f6412..e5c25070d 100644 --- a/sdks/ts/src/api/schemas/$Chat_BaseChatOutput.ts +++ b/sdks/ts/src/api/schemas/$Chat_BaseChatOutput.ts @@ -27,8 +27,6 @@ export const $Chat_BaseChatOutput = { type: "Chat_LogProbResponse", }, ], - isRequired: true, - isNullable: true, }, }, } as const; diff --git a/sdks/ts/src/api/schemas/$Chat_BaseChatResponse.ts b/sdks/ts/src/api/schemas/$Chat_BaseChatResponse.ts index 57a56bc70..1ca3e7008 100644 --- a/sdks/ts/src/api/schemas/$Chat_BaseChatResponse.ts +++ b/sdks/ts/src/api/schemas/$Chat_BaseChatResponse.ts @@ -12,8 +12,6 @@ export const $Chat_BaseChatResponse = { type: "Chat_CompetionUsage", }, ], - isRequired: true, - isNullable: true, }, jobs: { type: "array", diff --git a/sdks/ts/src/api/schemas/$Chat_BaseTokenLogProb.ts b/sdks/ts/src/api/schemas/$Chat_BaseTokenLogProb.ts index e9cd03715..e7af2a307 100644 --- a/sdks/ts/src/api/schemas/$Chat_BaseTokenLogProb.ts +++ b/sdks/ts/src/api/schemas/$Chat_BaseTokenLogProb.ts @@ -20,8 +20,6 @@ export const $Chat_BaseTokenLogProb = { type: "number", format: "uint16", }, - isRequired: true, - isNullable: true, }, }, } as const; diff --git a/typespec/chat/models.tsp b/typespec/chat/models.tsp index 819648d8b..da7c170ca 100644 --- a/typespec/chat/models.tsp +++ b/typespec/chat/models.tsp @@ -169,7 +169,7 @@ model BaseTokenLogProb { /** The log probability of the token */ logprob: float32; - bytes: uint16[] | null; + bytes?: uint16[]; } model TokenLogProb extends BaseTokenLogProb { @@ -188,7 +188,7 @@ model BaseChatOutput { finish_reason: FinishReason; /** The log probabilities of tokens */ - logprobs: LogProbResponse | null; + logprobs?: LogProbResponse; } /** The output returned by the model. Note that, depending on the model provider, they might return more than one message. */ @@ -211,7 +211,7 @@ model ChatOutputChunk extends BaseChatOutput { model BaseChatResponse { /** Usage statistics for the completion request */ - usage: CompetionUsage | null; + usage?: CompetionUsage; /** Background job IDs that may have been spawned from this interaction. */ jobs: uuid[]; From 6279937e57c91822834c0eb9322301a491fb75ca Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 12 Aug 2024 21:39:11 -0400 Subject: [PATCH 039/110] feat(agents-api): Add /embed route Signed-off-by: Diwank Tomer --- .../agents_api/routers/docs/__init__.py | 1 + agents-api/agents_api/routers/docs/embed.py | 28 +++++++++++++++++++ agents-api/tests/test_docs_routes.py | 28 ++++++++++++++++++- 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 agents-api/agents_api/routers/docs/embed.py diff --git a/agents-api/agents_api/routers/docs/__init__.py b/agents-api/agents_api/routers/docs/__init__.py index 0d9fe8b5c..10195be77 100644 --- a/agents-api/agents_api/routers/docs/__init__.py +++ b/agents-api/agents_api/routers/docs/__init__.py @@ -1,6 +1,7 @@ # ruff: noqa: F401 from .create_doc import create_agent_doc, create_user_doc from .delete_doc import delete_agent_doc, delete_user_doc +from .embed import embed from .get_doc import get_doc from .list_docs import list_agent_docs, list_user_docs from .router import router diff --git a/agents-api/agents_api/routers/docs/embed.py b/agents-api/agents_api/routers/docs/embed.py new file mode 100644 index 000000000..1de99bfce --- /dev/null +++ b/agents-api/agents_api/routers/docs/embed.py @@ -0,0 +1,28 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 + +import agents_api.clients.embed as embedder + +from ...autogen.openapi_model import ( + EmbedQueryRequest, + EmbedQueryResponse, +) +from ...dependencies.developer_id import get_developer_id +from .router import router + + +@router.post("/embed", tags=["docs"]) +async def embed( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + data: EmbedQueryRequest, +) -> EmbedQueryResponse: + text_to_embed: str | list[str] = data.text + text_to_embed: list[str] = ( + [text_to_embed] if isinstance(text_to_embed, str) else text_to_embed + ) + + vectors = await embedder.embed(inputs=text_to_embed) + + return EmbedQueryResponse(vectors=vectors) diff --git a/agents-api/tests/test_docs_routes.py b/agents-api/tests/test_docs_routes.py index a2e699a47..05f095f49 100644 --- a/agents-api/tests/test_docs_routes.py +++ b/agents-api/tests/test_docs_routes.py @@ -1,6 +1,13 @@ from ward import test -from tests.fixtures import make_request, test_agent, test_doc, test_user, test_user_doc +from tests.fixtures import ( + make_request, + patch_embed_acompletion, + test_agent, + test_doc, + test_user, + test_user_doc, +) @test("route: create user doc") @@ -160,3 +167,22 @@ def _(make_request=make_request, user=test_user, doc=test_user_doc): # FIXME: This test is failing because the search is not returning the expected results # assert len(docs) >= 1 + + +@test("routes: embed route") +async def _( + make_request=make_request, + mocks=patch_embed_acompletion, +): + (embed, _) = mocks + + response = make_request( + method="POST", + url="/embed", + json={"text": "blah blah"}, + ) + + result = response.json() + assert "vectors" in result + + embed.assert_called() From 95dfa18a01fc6d918fff1420f1a9f54938b64dff Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Mon, 12 Aug 2024 21:45:04 +0300 Subject: [PATCH 040/110] fix: Fix generation --- .../activities/task_steps/__init__.py | 37 +++---------------- 1 file changed, 6 insertions(+), 31 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 9640dc6d3..58a8ddcfa 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -2,13 +2,11 @@ from typing import Literal from uuid import uuid4 -from openai.types.chat.chat_completion import ChatCompletion from simpleeval import simple_eval from temporalio import activity from ...autogen.openapi_model import ( CreateTransitionRequest, - ErrorWorkflowStep, EvaluateStep, IfElseWorkflowStep, InputChatMLMessage, @@ -17,6 +15,7 @@ UpdateExecutionRequest, YieldStep, ) +from ...clients.litellm import acompletion from ...clients.worker.types import ChatML from ...common.protocol.tasks import ( StepContext, @@ -30,28 +29,6 @@ update_execution as update_execution_query, ) -# from ...routers.sessions.protocol import Settings -# from ...routers.sessions.session import llm_generate - - -# TODO: remove stubs -class Settings: - def __init__(self, *args, **kwargs): - pass - - -def llm_generate(*args, **kwargs): - return ChatCompletion( - id="", - choices=[], - created=0, - model="", - object="chat.completion", - ) - - -# - @activity.defn async def prompt_step(context: StepContext) -> dict: @@ -79,16 +56,14 @@ async def prompt_step(context: StepContext) -> dict: for m in messages ] + settings: dict = context.definition.settings.model_dump() # Get settings and run llm - response: ChatCompletion = await llm_generate( - messages, - Settings( - model=context.definition.settings.model or "gpt-4-turbo", - response_format=None, - ), + response = await acompletion( + messages=messages, + **settings, ) - return response + return response.model_dump() @activity.defn From 9f8e6d6e4453f4fde745c78050a1ec273d1f1ac2 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Tue, 13 Aug 2024 10:10:09 -0400 Subject: [PATCH 041/110] feat(agents-api): Bump python version to 3.11 Signed-off-by: Diwank Tomer --- agents-api/.tool-versions | 2 +- agents-api/poetry.lock | 47 ++++----------------------------------- agents-api/pyproject.toml | 4 ++-- 3 files changed, 7 insertions(+), 46 deletions(-) diff --git a/agents-api/.tool-versions b/agents-api/.tool-versions index 47cd22e3c..8aa451a5c 100644 --- a/agents-api/.tool-versions +++ b/agents-api/.tool-versions @@ -1 +1 @@ -python 3.10.13 +python 3.11.9 diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 9e0dcabdf..ba83ef4a2 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -99,7 +99,6 @@ files = [ [package.dependencies] aiohappyeyeballs = ">=2.3.0" aiosignal = ">=1.1.2" -async-timeout = {version = ">=4.0,<5.0", markers = "python_version < \"3.11\""} attrs = ">=17.3.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" @@ -163,10 +162,8 @@ files = [ ] [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"] @@ -303,20 +300,6 @@ files = [ {file = "async_lru-2.0.4-py3-none-any.whl", hash = "sha256:ff02944ce3c288c5be660c42dbcca0742b32c3b279d6dceda655190240b99224"}, ] -[package.dependencies] -typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} - -[[package]] -name = "async-timeout" -version = "4.0.3" -description = "Timeout context manager for asyncio programs" -optional = false -python-versions = ">=3.7" -files = [ - {file = "async-timeout-4.0.3.tar.gz", hash = "sha256:4640d96be84d82d02ed59ea2b7105a0f7b33abe8703703cd0ab0bf87c427522f"}, - {file = "async_timeout-4.0.3-py3-none-any.whl", hash = "sha256:7405140ff1230c310e51dc27b3145b9092d659ce68ff733fb0cefe3ee42be028"}, -] - [[package]] name = "attrs" version = "24.2.0" @@ -426,8 +409,6 @@ mypy-extensions = ">=0.4.3" packaging = ">=22.0" pathspec = ">=0.9.0" platformdirs = ">=2" -tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} -typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""} [package.extras] colorama = ["colorama (>=0.4.3)"] @@ -840,9 +821,8 @@ inflect = ">=4.1.0,<6.0" isort = ">=4.3.21,<6.0" jinja2 = ">=2.10.1,<4.0" packaging = "*" -pydantic = {version = ">=1.9.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.10\" and python_version < \"3.11\""} +pydantic = {version = ">=1.10.0,<2.4.0 || >2.4.0,<3.0", extras = ["email"], markers = "python_version >= \"3.11\" and python_version < \"4.0\""} pyyaml = ">=6.0.1" -toml = {version = ">=0.10.0,<1.0.0", markers = "python_version < \"3.11\""} [package.extras] debug = ["PySnooper (>=0.4.1,<2.0.0)"] @@ -1009,20 +989,6 @@ django = ["dj-database-url", "dj-email-url", "django-cache-url"] lint = ["flake8 (==7.0.0)", "flake8-bugbear (==23.11.28)", "mypy (==1.8.0)", "pre-commit (>=3.6,<4.0)"] tests = ["environs[django]", "pytest"] -[[package]] -name = "exceptiongroup" -version = "1.2.2" -description = "Backport of PEP 654 (exception groups)" -optional = false -python-versions = ">=3.7" -files = [ - {file = "exceptiongroup-1.2.2-py3-none-any.whl", hash = "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b"}, - {file = "exceptiongroup-1.2.2.tar.gz", hash = "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"}, -] - -[package.extras] -test = ["pytest (>=6)"] - [[package]] name = "executing" version = "2.0.1" @@ -1563,7 +1529,6 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} decorator = "*" -exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} jedi = ">=0.16" matplotlib-inline = "*" pexpect = {version = ">4.3", markers = "sys_platform != \"win32\" and sys_platform != \"emscripten\""} @@ -2079,7 +2044,6 @@ jupyterlab-server = ">=2.27.1,<3" notebook-shim = ">=0.2" packaging = "*" setuptools = ">=40.1.0" -tomli = {version = ">=1.2.2", markers = "python_version < \"3.11\""} tornado = ">=6.2.0" traitlets = "*" @@ -2150,7 +2114,6 @@ files = [ [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" -async-timeout = {version = ">=4.0.0,<5.0.0", markers = "python_version < \"3.11\""} langchain-core = ">=0.2.30,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0" langsmith = ">=0.1.17,<0.2.0" @@ -3053,7 +3016,7 @@ files = [ ] [package.dependencies] -numpy = {version = ">=1.22.4", markers = "python_version < \"3.11\""} +numpy = {version = ">=1.23.2", markers = "python_version == \"3.11\""} python-dateutil = ">=2.8.2" pytz = ">=2020.1" tzdata = ">=2022.7" @@ -4501,7 +4464,6 @@ files = [ [package.dependencies] protobuf = ">=3.20" -python-dateutil = {version = ">=2.8.2,<3.0.0", markers = "python_version < \"3.11\""} types-protobuf = ">=3.20" typing-extensions = ">=4.2.0,<5.0.0" @@ -4955,7 +4917,6 @@ files = [ [package.dependencies] click = ">=7.0" h11 = ">=0.8" -typing-extensions = {version = ">=4.0", markers = "python_version < \"3.11\""} [package.extras] standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,!=0.15.0,!=0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] @@ -5187,5 +5148,5 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" -python-versions = ">=3.10,<3.11" -content-hash = "47fb59596e6067d2218064a57ca421d39a2420505082cef0d17945b120611115" +python-versions = ">=3.11,<3.12" +content-hash = "df48532534cbce05d6360e5c818a984efaa498b5d3f465a8669c0a1b05977bc4" diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index a401aaf48..a80350a5c 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -7,7 +7,7 @@ readme = "README.md" packages = [{include = "agents_api"}] [tool.poetry.dependencies] -python = ">=3.10,<3.11" +python = ">=3.11,<3.12" fastapi = "^0.110.1" pycozo = {extras = ["embedded"], version = "^0.7.6"} uvicorn = "^0.23.2" @@ -93,4 +93,4 @@ datamodel-codegen \ --target-python-version 3.10 \ --openapi-scopes schemas \ --keep-model-order \ - --disable-timestamp""" \ No newline at end of file + --disable-timestamp""" From 0f6e0612e8db28d695078840ec9fa2e0dc00ab1f Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Tue, 13 Aug 2024 22:27:45 +0300 Subject: [PATCH 042/110] chore: Refactor tasks routes and add unit tests --- agents-api/agents_api/routers/__init__.py | 1 + .../agents_api/routers/tasks/__init__.py | 10 +- .../agents_api/routers/tasks/create_task.py | 46 ++ .../routers/tasks/create_task_execution.py | 108 +++++ .../routers/tasks/get_execution_details.py | 28 ++ .../routers/tasks/get_task_details.py | 47 ++ .../tasks/list_execution_transitions.py | 69 +++ .../routers/tasks/list_task_executions.py | 30 ++ .../agents_api/routers/tasks/list_tasks.py | 45 ++ .../routers/tasks/patch_execution.py | 41 ++ agents-api/agents_api/routers/tasks/router.py | 3 + .../agents_api/routers/tasks/routers.py | 416 ------------------ .../routers/tasks/update_execution.py | 39 ++ agents-api/tests/test_task_routes.py | 196 +++++++++ 14 files changed, 662 insertions(+), 417 deletions(-) create mode 100644 agents-api/agents_api/routers/tasks/create_task.py create mode 100644 agents-api/agents_api/routers/tasks/create_task_execution.py create mode 100644 agents-api/agents_api/routers/tasks/get_execution_details.py create mode 100644 agents-api/agents_api/routers/tasks/get_task_details.py create mode 100644 agents-api/agents_api/routers/tasks/list_execution_transitions.py create mode 100644 agents-api/agents_api/routers/tasks/list_task_executions.py create mode 100644 agents-api/agents_api/routers/tasks/list_tasks.py create mode 100644 agents-api/agents_api/routers/tasks/patch_execution.py create mode 100644 agents-api/agents_api/routers/tasks/router.py delete mode 100644 agents-api/agents_api/routers/tasks/routers.py create mode 100644 agents-api/agents_api/routers/tasks/update_execution.py create mode 100644 agents-api/tests/test_task_routes.py diff --git a/agents-api/agents_api/routers/__init__.py b/agents-api/agents_api/routers/__init__.py index 04280e706..ac26a3e82 100644 --- a/agents-api/agents_api/routers/__init__.py +++ b/agents-api/agents_api/routers/__init__.py @@ -16,4 +16,5 @@ from .docs import router as docs_router from .jobs import router as jobs_router from .sessions import router as sessions_router +from .tasks import router as tasks_router from .users import router as users_router diff --git a/agents-api/agents_api/routers/tasks/__init__.py b/agents-api/agents_api/routers/tasks/__init__.py index fa07d0740..8d67171c0 100644 --- a/agents-api/agents_api/routers/tasks/__init__.py +++ b/agents-api/agents_api/routers/tasks/__init__.py @@ -1 +1,9 @@ -from .routers import router # noqa: F401 +from .create_task import create_task +from .create_task_execution import create_task_execution +from .get_execution_details import get_execution_details +from .get_task_details import get_task_details +from .list_task_executions import list_task_executions +from .list_tasks import list_tasks +from .patch_execution import patch_execution +from .router import router # noqa: F401 +from .update_execution import update_execution diff --git a/agents-api/agents_api/routers/tasks/create_task.py b/agents-api/agents_api/routers/tasks/create_task.py new file mode 100644 index 000000000..3c76663e4 --- /dev/null +++ b/agents-api/agents_api/routers/tasks/create_task.py @@ -0,0 +1,46 @@ +from typing import Annotated +from uuid import uuid4 + +import pandas as pd +from fastapi import Depends +from pydantic import UUID4 +from starlette.status import HTTP_201_CREATED + +from agents_api.autogen.openapi_model import ( + CreateTaskRequest, + ResourceCreatedResponse, +) +from agents_api.dependencies.developer_id import get_developer_id +from agents_api.models.task.create_task import create_task as create_task_query + +from .router import router + + +@router.post("/agents/{agent_id}/tasks", status_code=HTTP_201_CREATED, tags=["tasks"]) +async def create_task( + request: CreateTaskRequest, + agent_id: UUID4, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], +) -> ResourceCreatedResponse: + task_id = uuid4() + + # TODO: Do thorough validation of the task spec + + workflows = [ + {"name": "main", "steps": [w.model_dump() for w in request.main]}, + ] + [{"name": name, "steps": steps} for name, steps in request.model_extra.items()] + + resp: pd.DataFrame = create_task_query( + agent_id=agent_id, + task_id=task_id, + developer_id=x_developer_id, + name=request.name, + description=request.description, + input_schema=request.input_schema or {}, + tools_available=request.tools or [], + workflows=workflows, + ) + + return ResourceCreatedResponse( + id=resp["task_id"][0], created_at=resp["created_at"][0] + ) diff --git a/agents-api/agents_api/routers/tasks/create_task_execution.py b/agents-api/agents_api/routers/tasks/create_task_execution.py new file mode 100644 index 000000000..9f78de8ca --- /dev/null +++ b/agents-api/agents_api/routers/tasks/create_task_execution.py @@ -0,0 +1,108 @@ +import logging +from typing import Annotated +from uuid import uuid4 + +from fastapi import Depends, HTTPException, status +from jsonschema import validate +from jsonschema.exceptions import ValidationError +from pycozo.client import QueryException +from pydantic import UUID4 +from starlette.status import HTTP_201_CREATED + +from agents_api.autogen.openapi_model import ( + CreateExecutionRequest, + ResourceCreatedResponse, + UpdateExecutionRequest, +) +from agents_api.clients.temporal import run_task_execution_workflow +from agents_api.dependencies.developer_id import get_developer_id +from agents_api.models.execution.create_execution import ( + create_execution as create_execution_query, +) +from agents_api.models.execution.prepare_execution_input import prepare_execution_input +from agents_api.models.execution.update_execution import ( + update_execution as update_execution_query, +) +from agents_api.models.task.get_task import get_task as get_task_query + +from .router import router + +logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + + +@router.post( + "/tasks/{task_id}/executions", + status_code=HTTP_201_CREATED, + tags=["tasks"], +) +async def create_task_execution( + task_id: UUID4, + data: CreateExecutionRequest, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], +) -> ResourceCreatedResponse: + try: + task = [ + row.to_dict() + for _, row in get_task_query( + task_id=task_id, developer_id=x_developer_id + ).iterrows() + ][0] + + validate(data.input, task["input_schema"]) + except ValidationError: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid request arguments schema", + ) + except (IndexError, KeyError): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Task not found", + ) + except QueryException as e: + if e.code == "transact::assertion_failure": + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Task not found" + ) + + raise + + execution_id = uuid4() + execution_input = prepare_execution_input( + developer_id=x_developer_id, + task_id=task_id, + execution_id=execution_id, + ) + + try: + handle = await run_task_execution_workflow( + execution_input=execution_input, + job_id=uuid4(), + ) + except Exception as e: + logger.exception(e) + + update_execution_query( + developer_id=x_developer_id, + task_id=task_id, + execution_id=execution_id, + data=UpdateExecutionRequest(status="failed"), + ) + + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Task creation failed", + ) + + execution = create_execution_query( + developer_id=x_developer_id, + task_id=task_id, + execution_id=execution_id, + data=data, + workflow_hande=handle, + ) + + return ResourceCreatedResponse( + id=execution["execution_id"][0], created_at=execution["created_at"][0] + ) diff --git a/agents-api/agents_api/routers/tasks/get_execution_details.py b/agents-api/agents_api/routers/tasks/get_execution_details.py new file mode 100644 index 000000000..1377d7b22 --- /dev/null +++ b/agents-api/agents_api/routers/tasks/get_execution_details.py @@ -0,0 +1,28 @@ +from uuid import uuid4 + +from fastapi import HTTPException, status +from pydantic import UUID4 + +from agents_api.autogen.openapi_model import ( + Execution, +) +from agents_api.models.execution.get_execution import ( + get_execution as get_execution_query, +) + +from .router import router + + +@router.get("/executions/{execution_id}", tags=["executions"]) +async def get_execution_details(execution_id: UUID4) -> Execution: + try: + res = [ + row.to_dict() + for _, row in get_execution_query(execution_id=execution_id).iterrows() + ][0] + return Execution(**res) + except (IndexError, KeyError): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Execution not found", + ) diff --git a/agents-api/agents_api/routers/tasks/get_task_details.py b/agents-api/agents_api/routers/tasks/get_task_details.py new file mode 100644 index 000000000..dbf8bf7da --- /dev/null +++ b/agents-api/agents_api/routers/tasks/get_task_details.py @@ -0,0 +1,47 @@ +from typing import Annotated + +from fastapi import Depends, HTTPException, status +from pycozo.client import QueryException +from pydantic import UUID4 + +from agents_api.autogen.openapi_model import ( + Task, +) +from agents_api.dependencies.developer_id import get_developer_id +from agents_api.models.task.get_task import get_task as get_task_query + +from .router import router + + +@router.get("/agents/{agent_id}/tasks/{task_id}", tags=["tasks"]) +async def get_task_details( + task_id: UUID4, + agent_id: UUID4, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], +) -> Task: + try: + resp = [ + row.to_dict() + for _, row in get_task_query( + agent_id=agent_id, task_id=task_id, developer_id=x_developer_id + ).iterrows() + ][0] + + for workflow in resp["workflows"]: + if workflow["name"] == "main": + resp["main"] = workflow["steps"] + break + + return Task(**resp) + except (IndexError, KeyError): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Task not found", + ) + except QueryException as e: + if e.code == "transact::assertion_failure": + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Task not found" + ) + + raise diff --git a/agents-api/agents_api/routers/tasks/list_execution_transitions.py b/agents-api/agents_api/routers/tasks/list_execution_transitions.py new file mode 100644 index 000000000..36f61601e --- /dev/null +++ b/agents-api/agents_api/routers/tasks/list_execution_transitions.py @@ -0,0 +1,69 @@ +from pydantic import UUID4 + +from agents_api.autogen.openapi_model import ( + ListResponse, + Transition, +) +from agents_api.models.execution.list_execution_transitions import ( + list_execution_transitions as list_execution_transitions_query, +) + +from .router import router + + +@router.get("/executions/{execution_id}/transitions", tags=["executions"]) +async def list_execution_transitions( + execution_id: UUID4, + limit: int = 100, + offset: int = 0, +) -> ListResponse[Transition]: + res = list_execution_transitions_query( + execution_id=execution_id, limit=limit, offset=offset + ) + return ListResponse[Transition]( + items=[Transition(**row.to_dict()) for _, row in res.iterrows()] + ) + + +# @router.get("/executions/{execution_id}/transitions/{transition_id}", tags=["tasks"]) +# async def get_execution_transition( +# execution_id: UUID4, +# transition_id: UUID4, +# ) -> Transition: +# try: +# res = [ +# row.to_dict() +# for _, row in get_execution_transition_query( +# execution_id, transition_id +# ).iterrows() +# ][0] +# return Transition(**res) +# except (IndexError, KeyError): +# raise HTTPException( +# status_code=status.HTTP_404_NOT_FOUND, +# detail="Transition not found", +# ) + + +# TODO: Later; for resuming waiting transitions +# TODO: Ask for a task token to resume a waiting transition +# @router.put("/executions/{execution_id}/transitions/{transition_id}", tags=["tasks"]) +# async def update_execution_transition( +# execution_id: UUID4, +# transition_id: UUID4, +# request: Transition, +# ) -> ResourceUpdatedResponse: +# try: +# resp = update_execution_transition_query( +# execution_id, transition_id, **request.model_dump() +# ) + +# return ResourceUpdatedResponse( +# id=resp["transition_id"][0], +# updated_at=resp["updated_at"][0][0], +# ) +# except (IndexError, KeyError): +# raise HTTPException( +# status_code=status.HTTP_404_NOT_FOUND, +# detail="Transition not found", +# ) diff --git a/agents-api/agents_api/routers/tasks/list_task_executions.py b/agents-api/agents_api/routers/tasks/list_task_executions.py new file mode 100644 index 000000000..718d13a0d --- /dev/null +++ b/agents-api/agents_api/routers/tasks/list_task_executions.py @@ -0,0 +1,30 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 + +from agents_api.autogen.openapi_model import ( + Execution, + ListResponse, +) +from agents_api.dependencies.developer_id import get_developer_id +from agents_api.models.execution.list_executions import ( + list_executions as list_task_executions_query, +) + +from .router import router + + +@router.get("/tasks/{task_id}/executions", tags=["tasks"]) +async def list_task_executions( + task_id: UUID4, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + limit: int = 100, + offset: int = 0, +) -> ListResponse[Execution]: + res = list_task_executions_query( + task_id=task_id, developer_id=x_developer_id, limit=limit, offse=offset + ) + return ListResponse[Execution]( + items=[Execution(**row.to_dict()) for _, row in res.iterrows()] + ) diff --git a/agents-api/agents_api/routers/tasks/list_tasks.py b/agents-api/agents_api/routers/tasks/list_tasks.py new file mode 100644 index 000000000..771b9f38c --- /dev/null +++ b/agents-api/agents_api/routers/tasks/list_tasks.py @@ -0,0 +1,45 @@ +from typing import Annotated, Literal + +from fastapi import Depends +from pydantic import UUID4 + +from agents_api.autogen.openapi_model import ( + ListResponse, + Task, +) +from agents_api.dependencies.developer_id import get_developer_id +from agents_api.models.task.list_tasks import list_tasks as list_tasks_query + +from .router import router + + +@router.get("/agents/{agent_id}/tasks", tags=["tasks"]) +async def list_tasks( + agent_id: UUID4, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + limit: int = 100, + offset: int = 0, + sort_by: Literal["created_at", "updated_at"] = "created_at", + direction: Literal["asc", "desc"] = "desc", +) -> ListResponse[Task]: + query_results = list_tasks_query( + agent_id=agent_id, + developer_id=x_developer_id, + limit=limit, + offset=offset, + sort_by=sort_by, + direction=direction, + ) + + tasks = [] + for _, row in query_results.iterrows(): + row_dict = row.to_dict() + + for workflow in row_dict["workflows"]: + if workflow["name"] == "main": + row_dict["main"] = workflow["steps"] + break + + tasks.append(Task(**row_dict)) + + return ListResponse[Task](items=tasks) diff --git a/agents-api/agents_api/routers/tasks/patch_execution.py b/agents-api/agents_api/routers/tasks/patch_execution.py new file mode 100644 index 000000000..74ae3fc72 --- /dev/null +++ b/agents-api/agents_api/routers/tasks/patch_execution.py @@ -0,0 +1,41 @@ +from typing import Annotated + +from fastapi import Depends, HTTPException, status +from pydantic import UUID4 + +from agents_api.autogen.openapi_model import ( + Execution, + UpdateExecutionRequest, +) +from agents_api.dependencies.developer_id import get_developer_id +from agents_api.models.execution.update_execution import ( + update_execution as update_execution_query, +) + +from .router import router + + +# TODO: write PATCH query +@router.patch("/tasks/{task_id}/executions/{execution_id}", tags=["tasks"]) +async def patch_execution( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + task_id: UUID4, + execution_id: UUID4, + data: UpdateExecutionRequest, +) -> Execution: + try: + res = [ + row.to_dict() + for _, row in update_execution_query( + developer_id=x_developer_id, + task_id=task_id, + execution_id=execution_id, + data=data, + ).iterrows() + ][0] + return Execution(**res) + except (IndexError, KeyError): + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Execution not found", + ) diff --git a/agents-api/agents_api/routers/tasks/router.py b/agents-api/agents_api/routers/tasks/router.py new file mode 100644 index 000000000..af9233c56 --- /dev/null +++ b/agents-api/agents_api/routers/tasks/router.py @@ -0,0 +1,3 @@ +from fastapi import APIRouter + +router = APIRouter() diff --git a/agents-api/agents_api/routers/tasks/routers.py b/agents-api/agents_api/routers/tasks/routers.py deleted file mode 100644 index 857a139de..000000000 --- a/agents-api/agents_api/routers/tasks/routers.py +++ /dev/null @@ -1,416 +0,0 @@ -import logging -from typing import Annotated, Literal -from uuid import uuid4 - -import pandas as pd -from fastapi import APIRouter, Depends, HTTPException, status -from jsonschema import validate -from jsonschema.exceptions import ValidationError -from pycozo.client import QueryException -from pydantic import UUID4, BaseModel -from starlette.status import HTTP_201_CREATED - -from agents_api.autogen.openapi_model import ( - CreateExecutionRequest, - CreateTaskRequest, - Execution, - ResourceCreatedResponse, - ResumeExecutionRequest, - StopExecutionRequest, - # ResourceUpdatedResponse, - Task, - Transition, - UpdateExecutionRequest, -) -from agents_api.clients.temporal import get_client, run_task_execution_workflow -from agents_api.dependencies.developer_id import get_developer_id -from agents_api.models.execution.create_execution import ( - create_execution as create_execution_query, -) -from agents_api.models.execution.get_execution import ( - get_execution as get_execution_query, -) -from agents_api.models.execution.get_paused_execution_token import ( - get_paused_execution_token, -) -from agents_api.models.execution.get_temporal_workflow_data import ( - get_temporal_workflow_data, -) - -# from agents_api.models.execution.get_execution_transition import ( -# get_execution_transition as get_execution_transition_query, -# ) -from agents_api.models.execution.list_execution_transitions import ( - list_execution_transitions as list_execution_transitions_query, -) -from agents_api.models.execution.list_executions import ( - list_executions as list_executions_query, -) -from agents_api.models.execution.list_executions import ( - list_executions as list_task_executions_query, -) -from agents_api.models.execution.prepare_execution_input import prepare_execution_input -from agents_api.models.execution.update_execution import ( - update_execution as update_execution_query, -) - -# from agents_api.models.execution.update_execution_transition import ( -# update_execution_transition_query, -# ) -from agents_api.models.task.create_task import create_task as create_task_query -from agents_api.models.task.get_task import get_task as get_task_query -from agents_api.models.task.list_tasks import list_tasks as list_tasks_query - -logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) - - -class TaskList(BaseModel): - items: list[Task] - - -class ExecutionList(BaseModel): - items: list[Execution] - - -class ExecutionTransitionList(BaseModel): - items: list[Transition] - - -router = APIRouter() - - -@router.get("/agents/{agent_id}/tasks", tags=["tasks"]) -async def list_tasks( - agent_id: UUID4, - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], - limit: int = 100, - offset: int = 0, - sort_by: Literal["created_at", "updated_at"] = "created_at", - direction: Literal["asc", "desc"] = "desc", -) -> TaskList: - query_results = list_tasks_query( - agent_id=agent_id, - developer_id=x_developer_id, - limit=limit, - offset=offset, - sort_by=sort_by, - direction=direction, - ) - - items = [] - for _, row in query_results.iterrows(): - row_dict = row.to_dict() - - for workflow in row_dict["workflows"]: - if workflow["name"] == "main": - row_dict["main"] = workflow["steps"] - break - - items.append(Task(**row_dict)) - - return TaskList(items=items) - - -@router.post("/agents/{agent_id}/tasks", status_code=HTTP_201_CREATED, tags=["tasks"]) -async def create_task( - request: CreateTaskRequest, - agent_id: UUID4, - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], -) -> ResourceCreatedResponse: - task_id = uuid4() - - # TODO: Do thorough validation of the task spec - - workflows = [ - {"name": "main", "steps": [w.model_dump() for w in request.main]}, - ] + [{"name": name, "steps": steps} for name, steps in request.model_extra.items()] - - resp: pd.DataFrame = create_task_query( - agent_id=agent_id, - task_id=task_id, - developer_id=x_developer_id, - name=request.name, - description=request.description, - input_schema=request.input_schema or {}, - tools_available=request.tools or [], - workflows=workflows, - ) - - return ResourceCreatedResponse( - id=resp["task_id"][0], created_at=resp["created_at"][0] - ) - - -@router.get("/agents/{agent_id}/tasks/{task_id}", tags=["tasks"]) -async def get_task( - task_id: UUID4, - agent_id: UUID4, - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], -) -> Task: - try: - resp = [ - row.to_dict() - for _, row in get_task_query( - agent_id=agent_id, task_id=task_id, developer_id=x_developer_id - ).iterrows() - ][0] - - for workflow in resp["workflows"]: - if workflow["name"] == "main": - resp["main"] = workflow["steps"] - break - - return Task(**resp) - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Task not found", - ) - except QueryException as e: - if e.code == "transact::assertion_failure": - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Task not found" - ) - - raise - - -@router.post( - "/tasks/{task_id}/executions", - status_code=HTTP_201_CREATED, - tags=["tasks"], -) -async def create_task_execution( - task_id: UUID4, - data: CreateExecutionRequest, - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], -) -> ResourceCreatedResponse: - try: - task = [ - row.to_dict() - for _, row in get_task_query( - task_id=task_id, developer_id=x_developer_id - ).iterrows() - ][0] - - validate(data.input, task["input_schema"]) - except ValidationError: - raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail="Invalid request arguments schema", - ) - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Task not found", - ) - except QueryException as e: - if e.code == "transact::assertion_failure": - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Task not found" - ) - - raise - - execution_id = uuid4() - execution_input = prepare_execution_input( - developer_id=x_developer_id, - task_id=task_id, - execution_id=execution_id, - ) - - try: - handle = await run_task_execution_workflow( - execution_input=execution_input, - job_id=uuid4(), - ) - except Exception as e: - logger.exception(e) - - update_execution_query( - developer_id=x_developer_id, - task_id=task_id, - execution_id=execution_id, - data=UpdateExecutionRequest(status="failed"), - ) - - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Task creation failed", - ) - - execution = create_execution_query( - developer_id=x_developer_id, - task_id=task_id, - execution_id=execution_id, - data=data, - workflow_hande=handle, - ) - - return ResourceCreatedResponse( - id=execution["execution_id"][0], created_at=execution["created_at"][0] - ) - - -@router.get("/agents/{agent_id}/tasks/{task_id}/executions", tags=["tasks"]) -async def list_task_executions( - task_id: UUID4, - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], - limit: int = 100, - offset: int = 0, -) -> ExecutionList: - res = list_task_executions_query( - task_id=task_id, developer_id=x_developer_id, limit=limit, offse=offset - ) - return ExecutionList( - items=[Execution(**row.to_dict()) for _, row in res.iterrows()] - ) - - -@router.get("/executions/{execution_id}", tags=["executions"]) -async def get_execution(task_id: UUID4, execution_id: UUID4) -> Execution: - try: - res = [ - row.to_dict() - for _, row in get_execution_query(execution_id=execution_id).iterrows() - ][0] - return Execution(**res) - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Execution not found", - ) - - -# TODO: write PATCH query -@router.patch("/tasks/{task_id}/executions/{execution_id}", tags=["tasks"]) -async def patch_execution( - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], - task_id: UUID4, - execution_id: UUID4, - data: UpdateExecutionRequest, -) -> Execution: - try: - res = [ - row.to_dict() - for _, row in update_execution_query( - developer_id=x_developer_id, - task_id=task_id, - execution_id=execution_id, - data=data, - ).iterrows() - ][0] - return Execution(**res) - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Execution not found", - ) - - -@router.put("/executions/{execution_id}", tags=["executions"]) -async def put_execution( - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], - execution_id: UUID4, - data: ResumeExecutionRequest | StopExecutionRequest, -): - temporal_client = await get_client() - if isinstance(data, StopExecutionRequest): - handle = temporal_client.get_workflow_handle_for( - *get_temporal_workflow_data(execution_id=execution_id) - ) - await handle.cancel() - else: - token_data = get_paused_execution_token( - developer_id=x_developer_id, execution_id=execution_id - ) - handle = temporal_client.get_async_activity_handle(token_data["task_token"]) - await handle.complete(data.input) - - -@router.get("/tasks/{task_id}/executions", tags=["tasks"]) -async def list_execution( - x_developer_id: Annotated[UUID4, Depends(get_developer_id)], - task_id: UUID4, - limit: int = 100, - offset: int = 0, - sort_by: Literal["created_at", "updated_at"] = "created_at", - direction: Literal["asc", "desc"] = "desc", -) -> list[Execution]: - try: - res = [ - Execution(**row.to_dict()) - for _, row in list_executions_query( - developer_id=x_developer_id, - task_id=task_id, - limit=limit, - offset=offset, - sort_by=sort_by, - direction=direction, - ).iterrows() - ] - return res - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Execution not found", - ) - - -# @router.get("/executions/{execution_id}/transitions/{transition_id}", tags=["tasks"]) -# async def get_execution_transition( -# execution_id: UUID4, -# transition_id: UUID4, -# ) -> Transition: -# try: -# res = [ -# row.to_dict() -# for _, row in get_execution_transition_query( -# execution_id, transition_id -# ).iterrows() -# ][0] -# return Transition(**res) -# except (IndexError, KeyError): -# raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail="Transition not found", -# ) - - -# TODO: Later; for resuming waiting transitions -# TODO: Ask for a task token to resume a waiting transition -# @router.put("/executions/{execution_id}/transitions/{transition_id}", tags=["tasks"]) -# async def update_execution_transition( -# execution_id: UUID4, -# transition_id: UUID4, -# request: Transition, -# ) -> ResourceUpdatedResponse: -# try: -# resp = update_execution_transition_query( -# execution_id, transition_id, **request.model_dump() -# ) - -# return ResourceUpdatedResponse( -# id=resp["transition_id"][0], -# updated_at=resp["updated_at"][0][0], -# ) -# except (IndexError, KeyError): -# raise HTTPException( -# status_code=status.HTTP_404_NOT_FOUND, -# detail="Transition not found", -# ) - - -@router.get("/executions/{execution_id}/transitions", tags=["executions"]) -async def list_execution_transitions( - execution_id: UUID4, - limit: int = 100, - offset: int = 0, -) -> ExecutionTransitionList: - res = list_execution_transitions_query( - execution_id=execution_id, limit=limit, offset=offset - ) - return ExecutionTransitionList( - items=[Transition(**row.to_dict()) for _, row in res.iterrows()] - ) diff --git a/agents-api/agents_api/routers/tasks/update_execution.py b/agents-api/agents_api/routers/tasks/update_execution.py new file mode 100644 index 000000000..f47b0ddf3 --- /dev/null +++ b/agents-api/agents_api/routers/tasks/update_execution.py @@ -0,0 +1,39 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 + +from agents_api.autogen.openapi_model import ( + ResumeExecutionRequest, + StopExecutionRequest, +) +from agents_api.clients.temporal import get_client +from agents_api.dependencies.developer_id import get_developer_id +from agents_api.models.execution.get_paused_execution_token import ( + get_paused_execution_token, +) +from agents_api.models.execution.get_temporal_workflow_data import ( + get_temporal_workflow_data, +) + +from .router import router + + +@router.put("/executions/{execution_id}", tags=["executions"]) +async def update_execution( + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + execution_id: UUID4, + data: ResumeExecutionRequest | StopExecutionRequest, +): + temporal_client = await get_client() + if isinstance(data, StopExecutionRequest): + handle = temporal_client.get_workflow_handle_for( + *get_temporal_workflow_data(execution_id=execution_id) + ) + await handle.cancel() + else: + token_data = get_paused_execution_token( + developer_id=x_developer_id, execution_id=execution_id + ) + handle = temporal_client.get_async_activity_handle(token_data["task_token"]) + await handle.complete(data.input) diff --git a/agents-api/tests/test_task_routes.py b/agents-api/tests/test_task_routes.py new file mode 100644 index 000000000..fcae8e979 --- /dev/null +++ b/agents-api/tests/test_task_routes.py @@ -0,0 +1,196 @@ +# Tests for task routes +from uuid import uuid4 + +from ward import test + +from tests.fixtures import client, make_request, test_execution, test_task + + +@test("route: unauthorized should fail") +def _(client=client): + data = dict( + name="test user", + main={ + "kind_": "evaluate", + "evaluate": { + "additionalProp1": "value1", + } + }, + ) + + response = client.request( + method="POST", + url="/tasks", + data=data, + ) + + assert response.status_code == 403 + + +@test("route: create task") +def _(make_request=make_request): + data = dict( + name="test user", + main={ + "kind_": "evaluate", + "evaluate": { + "additionalProp1": "value1", + } + }, + ) + + response = make_request( + method="POST", + url="/tasks", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: create task execution") +def _(make_request=make_request): + task_id = str(uuid4()) + data = dict( + input={}, + metadata={}, + ) + + response = make_request( + method="POST", + url=f"/tasks/{task_id}/executions", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: get execution not exists") +def _(make_request=make_request): + execution_id = str(uuid4()) + + response = make_request( + method="GET", + url=f"/executions/{execution_id}", + ) + + assert response.status_code == 404 + + +@test("route: get execution exists") +def _(make_request=make_request): + execution_id = str(uuid4()) + + response = make_request( + method="GET", + url=f"/executions/{execution_id}", + ) + + assert response.status_code == 200 + + +@test("route: get task not exists") +def _(make_request=make_request): + data = dict( + name="test user", + main="test user about", + ) + + response = make_request( + method="POST", + url="/tasks", + json=data, + ) + + assert response.status_code == 201 + + +@test("route: get task exists") +def _(make_request=make_request): + data = dict( + name="test user", + main="test user about", + ) + + response = make_request( + method="POST", + url="/tasks", + json=data, + ) + + assert response.status_code == 201 + + +@test("model: list execution transitions") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/users", + ) + + assert response.status_code == 200 + response = response.json() + users = response["items"] + + assert isinstance(users, list) + assert len(users) > 0 + + +@test("model: list task executions") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/users", + ) + + assert response.status_code == 200 + response = response.json() + users = response["items"] + + assert isinstance(users, list) + assert len(users) > 0 + + +@test("model: list tasks") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/users", + ) + + assert response.status_code == 200 + response = response.json() + users = response["items"] + + assert isinstance(users, list) + assert len(users) > 0 + + +@test("model: patch execution") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/users", + ) + + assert response.status_code == 200 + response = response.json() + users = response["items"] + + assert isinstance(users, list) + assert len(users) > 0 + + +@test("model: update execution") +def _(make_request=make_request): + response = make_request( + method="GET", + url="/users", + ) + + assert response.status_code == 200 + response = response.json() + users = response["items"] + + assert isinstance(users, list) + assert len(users) > 0 From 83a02cf343de51c2af38f2e4d6d48a0b0db499c6 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Tue, 13 Aug 2024 15:01:19 -0400 Subject: [PATCH 043/110] feat: Fix type checks Signed-off-by: Diwank Tomer --- agents-api/agents_api/activities/__init__.py | 1 - .../agents_api/activities/co_density.py | 7 ++++--- agents-api/agents_api/activities/demo.py | 10 ---------- .../agents_api/activities/dialog_insights.py | 7 ++++--- .../agents_api/activities/embed_docs.py | 4 ++-- agents-api/agents_api/activities/mem_mgmt.py | 7 ++++--- .../agents_api/activities/mem_rating.py | 6 +++--- .../activities/relationship_summary.py | 6 +++--- .../activities/salient_questions.py | 6 +++--- .../agents_api/activities/summarization.py | 6 +++--- .../activities/task_steps/__init__.py | 1 - agents-api/agents_api/model_registry.py | 4 ++-- .../models/agent/create_or_update_agent.py | 2 +- .../migrations/migrate_1704699172_init.py | 2 +- .../migrate_1704699595_developers.py | 4 ++-- .../migrations/migrate_1709631202_metadata.py | 4 ++-- ...igrate_1712405369_simplify_instructions.py | 4 ++-- agents-api/pytype.toml | 19 +++++++------------ agents-api/tests/test_entry_queries.py | 2 +- agents-api/tests/test_execution_queries.py | 2 +- agents-api/tests/test_session_queries.py | 2 +- 21 files changed, 46 insertions(+), 60 deletions(-) delete mode 100644 agents-api/agents_api/activities/demo.py diff --git a/agents-api/agents_api/activities/__init__.py b/agents-api/agents_api/activities/__init__.py index a804127fc..49722a7d5 100644 --- a/agents-api/agents_api/activities/__init__.py +++ b/agents-api/agents_api/activities/__init__.py @@ -2,7 +2,6 @@ The `activities` module within the agents-api package is designed to facilitate various activities related to agent interactions. This includes handling memory management, generating insights from dialogues, summarizing relationships, and more. Each file within the module offers specific functionality: - `co_density.py`: Conducts cognitive density analysis to generate concise, entity-dense summaries. -- `demo.py`: Provides a simple demonstration of defining an activity with Temporal. - `dialog_insights.py`: Extracts insights from dialogues, identifying details that participants might find interesting. - `mem_mgmt.py`: Manages memory by updating and incorporating new personality information from dialogues. - `mem_rating.py`: Rates memories based on their poignancy and importance. diff --git a/agents-api/agents_api/activities/co_density.py b/agents-api/agents_api/activities/co_density.py index 8d276b401..408cc398a 100644 --- a/agents-api/agents_api/activities/co_density.py +++ b/agents-api/agents_api/activities/co_density.py @@ -3,7 +3,8 @@ from temporalio import activity -from ..clients.model import julep_client +from agents_api.clients import litellm + from .types import MemoryDensityTaskArgs @@ -56,14 +57,14 @@ def make_prompt(args: MemoryDensityTaskArgs): async def run_prompt( memory: str, - model: str = "julep-ai/samantha-1-turbo", + model: str = "gpt-4o", max_tokens: int = 400, temperature: float = 0.2, parser: Callable[[str], str] = lambda x: x, ) -> str: prompt = make_prompt(MemoryDensityTaskArgs(memory=memory)) - response = await julep_client.chat.completions.create( + response = await litellm.acompletion( model=model, messages=[ { diff --git a/agents-api/agents_api/activities/demo.py b/agents-api/agents_api/activities/demo.py deleted file mode 100644 index a0edcde3c..000000000 --- a/agents-api/agents_api/activities/demo.py +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python3 - -from temporalio import activity - - -@activity.defn -async def say_hello(name: str) -> str: - message = f"Hello, {name}!" - print(message) - return message diff --git a/agents-api/agents_api/activities/dialog_insights.py b/agents-api/agents_api/activities/dialog_insights.py index d6b10ae01..1d5adec39 100644 --- a/agents-api/agents_api/activities/dialog_insights.py +++ b/agents-api/agents_api/activities/dialog_insights.py @@ -3,7 +3,8 @@ from temporalio import activity -from ..clients.model import julep_client +from agents_api.clients import litellm + from .types import ChatML, DialogInsightsTaskArgs @@ -57,7 +58,7 @@ async def run_prompt( dialog: list[ChatML], person1: str, person2: str, - model: str = "julep-ai/samantha-1-turbo", + model: str = "gpt-4o", max_tokens: int = 400, temperature: float = 0.4, parser: Callable[[str], str] = lambda x: x, @@ -66,7 +67,7 @@ async def run_prompt( DialogInsightsTaskArgs(dialog=dialog, person1=person1, person2=person2) ) - response = await julep_client.chat.completions.create( + response = await litellm.acompletion( model=model, messages=[ { diff --git a/agents-api/agents_api/activities/embed_docs.py b/agents-api/agents_api/activities/embed_docs.py index b486c3af1..b8e01e65e 100644 --- a/agents-api/agents_api/activities/embed_docs.py +++ b/agents-api/agents_api/activities/embed_docs.py @@ -1,7 +1,7 @@ from pydantic import UUID4 from temporalio import activity -from agents_api.clients.embed import embed +from agents_api.clients import embed as embedder from agents_api.models.docs.embed_snippets import embed_snippets as embed_snippets_query snippet_embed_instruction = "Encode this passage for retrieval: " @@ -10,7 +10,7 @@ @activity.defn async def embed_docs(doc_id: UUID4, title: str, content: list[str]) -> None: indices, snippets = list(zip(*enumerate(content))) - embeddings = await embed( + embeddings = await embedder.embed( [ { "instruction": snippet_embed_instruction, diff --git a/agents-api/agents_api/activities/mem_mgmt.py b/agents-api/agents_api/activities/mem_mgmt.py index 4f661ca46..f368a0b0b 100644 --- a/agents-api/agents_api/activities/mem_mgmt.py +++ b/agents-api/agents_api/activities/mem_mgmt.py @@ -4,7 +4,8 @@ from temporalio import activity -from ..clients.model import julep_client +from agents_api.clients import litellm + from .types import ChatML, MemoryManagementTaskArgs example_previous_memory = """ @@ -120,7 +121,7 @@ async def run_prompt( dialog: list[ChatML], session_id: UUID, previous_memories: list[str] = [], - model: str = "julep-ai/samantha-1-turbo", + model: str = "gpt-4o", max_tokens: int = 400, temperature: float = 0.4, parser: Callable[[str], str] = lambda x: x, @@ -134,7 +135,7 @@ async def run_prompt( ) ) - response = await julep_client.chat.completions.create( + response = await litellm.acompletion( model=model, messages=[ { diff --git a/agents-api/agents_api/activities/mem_rating.py b/agents-api/agents_api/activities/mem_rating.py index bc35ac82d..222148f4c 100644 --- a/agents-api/agents_api/activities/mem_rating.py +++ b/agents-api/agents_api/activities/mem_rating.py @@ -3,7 +3,7 @@ from temporalio import activity -from ..clients.model import julep_client +from ..clients import litellm from .types import MemoryRatingTaskArgs @@ -40,14 +40,14 @@ def make_prompt(args: MemoryRatingTaskArgs): async def run_prompt( memory: str, - model: str = "julep-ai/samantha-1-turbo", + model: str = "gpt-4o", max_tokens: int = 400, temperature: float = 0.1, parser: Callable[[str], str] = lambda x: x, ) -> str: prompt = make_prompt(MemoryRatingTaskArgs(memory=memory)) - response = await julep_client.chat.completions.create( + response = await litellm.acompletion( model=model, messages=[ { diff --git a/agents-api/agents_api/activities/relationship_summary.py b/agents-api/agents_api/activities/relationship_summary.py index 5346040d3..997eaf40a 100644 --- a/agents-api/agents_api/activities/relationship_summary.py +++ b/agents-api/agents_api/activities/relationship_summary.py @@ -3,7 +3,7 @@ from temporalio import activity -from ..clients.model import julep_client +from ..clients import litellm from .types import RelationshipSummaryTaskArgs @@ -38,7 +38,7 @@ async def run_prompt( statements: list[str], person1: str, person2: str, - model: str = "julep-ai/samantha-1-turbo", + model: str = "gpt-4o", max_tokens: int = 400, temperature: float = 0.6, parser: Callable[[str], str] = lambda x: x, @@ -49,7 +49,7 @@ async def run_prompt( ) ) - response = await julep_client.chat.completions.create( + response = await litellm.acompletion( model=model, messages=[ { diff --git a/agents-api/agents_api/activities/salient_questions.py b/agents-api/agents_api/activities/salient_questions.py index 6a34409d6..0194e8c72 100644 --- a/agents-api/agents_api/activities/salient_questions.py +++ b/agents-api/agents_api/activities/salient_questions.py @@ -3,7 +3,7 @@ from temporalio import activity -from ..clients.model import julep_client +from ..clients import litellm from .types import SalientQuestionsTaskArgs @@ -33,14 +33,14 @@ def make_prompt(args: SalientQuestionsTaskArgs): async def run_prompt( statements: list[str], num: int = 3, - model: str = "julep-ai/samantha-1-turbo", + model: str = "gpt-4o", max_tokens: int = 400, temperature: float = 0.6, parser: Callable[[str], str] = lambda x: x, ) -> str: prompt = make_prompt(SalientQuestionsTaskArgs(statements=statements, num=num)) - response = await julep_client.chat.completions.create( + response = await litellm.acompletion( model=model, messages=[ { diff --git a/agents-api/agents_api/activities/summarization.py b/agents-api/agents_api/activities/summarization.py index 4d2b37f8c..dc365380d 100644 --- a/agents-api/agents_api/activities/summarization.py +++ b/agents-api/agents_api/activities/summarization.py @@ -18,7 +18,7 @@ from agents_api.rec_sum.summarize import summarize_messages from agents_api.rec_sum.trim import trim_messages -from ..clients.litellm import acompletion +from ..clients import litellm from ..env import summarization_model_name @@ -142,14 +142,14 @@ def make_prompt( async def run_prompt( dialog: list[Entry], previous_memories: list[str], - model: str = "julep-ai/samantha-1-turbo", + model: str = "gpt-4o", max_tokens: int = 400, temperature: float = 0.1, parser: Callable[[str], str] = lambda x: x, **kwargs, ) -> str: prompt = make_prompt(dialog, previous_memories, **kwargs) - response = await acompletion( + response = await litellm.acompletion( model=model, messages=[ { diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 58a8ddcfa..a9818d515 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -6,7 +6,6 @@ from temporalio import activity from ...autogen.openapi_model import ( - CreateTransitionRequest, EvaluateStep, IfElseWorkflowStep, InputChatMLMessage, diff --git a/agents-api/agents_api/model_registry.py b/agents-api/agents_api/model_registry.py index 8b7bce4f0..99ae66ea3 100644 --- a/agents-api/agents_api/model_registry.py +++ b/agents-api/agents_api/model_registry.py @@ -87,8 +87,8 @@ OPENAI_MODELS = {**GPT4_MODELS, **TURBO_MODELS, **GPT3_5_MODELS, **GPT3_MODELS} LOCAL_MODELS = { - "julep-ai/samantha-1-turbo": 32768, - "julep-ai/samantha-1-turbo-awq": 32768, + "gpt-4o": 32768, + "gpt-4o-awq": 32768, "TinyLlama/TinyLlama_v1.1": 2048, "casperhansen/llama-3-8b-instruct-awq": 8192, "julep-ai/Hermes-2-Theta-Llama-3-8B": 8192, diff --git a/agents-api/agents_api/models/agent/create_or_update_agent.py b/agents-api/agents_api/models/agent/create_or_update_agent.py index 8cba24d2b..fb80f6dd7 100644 --- a/agents-api/agents_api/models/agent/create_or_update_agent.py +++ b/agents-api/agents_api/models/agent/create_or_update_agent.py @@ -48,7 +48,7 @@ def create_or_update_agent( - name (str): The name of the agent. - about (str): A description of the agent. - instructions (list[str], optional): A list of instructions for using the agent. Defaults to an empty list. - - model (str, optional): The model identifier for the agent. Defaults to "julep-ai/samantha-1-turbo". + - model (str, optional): The model identifier for the agent. Defaults to "gpt-4o". - metadata (dict, optional): A dictionary of metadata for the agent. Defaults to an empty dict. - default_settings (dict, optional): A dictionary of default settings for the agent. Defaults to an empty dict. - client (CozoClient, optional): The CozoDB client instance to use for the query. Defaults to a preconfigured client instance. diff --git a/agents-api/migrations/migrate_1704699172_init.py b/agents-api/migrations/migrate_1704699172_init.py index 1f131c8a1..3a427ad48 100644 --- a/agents-api/migrations/migrate_1704699172_init.py +++ b/agents-api/migrations/migrate_1704699172_init.py @@ -19,7 +19,7 @@ def up(client): => name: String, about: String, - model: String default 'julep-ai/samantha-1-turbo', + model: String default 'gpt-4o', created_at: Float default now(), updated_at: Float default now(), } diff --git a/agents-api/migrations/migrate_1704699595_developers.py b/agents-api/migrations/migrate_1704699595_developers.py index e2c183520..d22edb393 100644 --- a/agents-api/migrations/migrate_1704699595_developers.py +++ b/agents-api/migrations/migrate_1704699595_developers.py @@ -29,7 +29,7 @@ def up(client): => name: String, about: String, - model: String default 'julep-ai/samantha-1-turbo', + model: String default 'gpt-4o', created_at: Float default now(), updated_at: Float default now(), } @@ -99,7 +99,7 @@ def down(client): => name: String, about: String, - model: String default 'julep-ai/samantha-1-turbo', + model: String default 'gpt-4o', created_at: Float default now(), updated_at: Float default now(), } diff --git a/agents-api/migrations/migrate_1709631202_metadata.py b/agents-api/migrations/migrate_1709631202_metadata.py index b5c220cb3..36c1c8ec4 100644 --- a/agents-api/migrations/migrate_1709631202_metadata.py +++ b/agents-api/migrations/migrate_1709631202_metadata.py @@ -22,7 +22,7 @@ => name: String, about: String, - model: String default 'julep-ai/samantha-1-turbo', + model: String default 'gpt-4o', created_at: Float default now(), updated_at: Float default now(), metadata: Json default {}, @@ -45,7 +45,7 @@ => name: String, about: String, - model: String default 'julep-ai/samantha-1-turbo', + model: String default 'gpt-4o', created_at: Float default now(), updated_at: Float default now(), } diff --git a/agents-api/migrations/migrate_1712405369_simplify_instructions.py b/agents-api/migrations/migrate_1712405369_simplify_instructions.py index ee3a87da1..b3f8a289a 100644 --- a/agents-api/migrations/migrate_1712405369_simplify_instructions.py +++ b/agents-api/migrations/migrate_1712405369_simplify_instructions.py @@ -24,7 +24,7 @@ name: String, about: String, instructions: [String] default [], - model: String default 'julep-ai/samantha-1-turbo', + model: String default 'gpt-4o', created_at: Float default now(), updated_at: Float default now(), metadata: Json default {}, @@ -47,7 +47,7 @@ => name: String, about: String, - model: String default 'julep-ai/samantha-1-turbo', + model: String default 'gpt-4o', created_at: Float default now(), updated_at: Float default now(), metadata: Json default {}, diff --git a/agents-api/pytype.toml b/agents-api/pytype.toml index 1b95217a6..edd07e7d4 100644 --- a/agents-api/pytype.toml +++ b/agents-api/pytype.toml @@ -2,15 +2,10 @@ [tool.pytype] -# Space-separated list of files or directories to exclude. -exclude = [ - '**/*_test.py', - '**/test_*.py', -] - # Space-separated list of files or directories to process. inputs = [ 'agents_api', + 'tests', ] # Keep going past errors to analyze as many files as possible. @@ -30,7 +25,7 @@ platform = 'linux' pythonpath = '.' # Python version (major.minor) of the target code. -python_version = '3.10' +python_version = '3.11' # Bind 'self' in methods with non-transparent decorators. This flag is temporary # and will be removed once this behavior is enabled by default. @@ -38,7 +33,7 @@ bind_decorated_methods = true # Don't allow None to match bool. This flag is temporary and will be removed # once this behavior is enabled by default. -none_is_not_bool = false +none_is_not_bool = true # Enable parameter count checks for overriding methods with renamed arguments. # This flag is temporary and will be removed once this behavior is enabled by @@ -64,20 +59,20 @@ require_override_decorator = false precise_return = true # Experimental: Solve unknown types to label with structural types. -protocols = false +protocols = true # Experimental: Only load submodules that are explicitly imported. strict_import = true # Experimental: Enable exhaustive checking of function parameter types. -strict_parameter_checks = false +strict_parameter_checks = true # Experimental: Emit errors for comparisons between incompatible primitive # types. -strict_primitive_comparisons = false +strict_primitive_comparisons = true # Experimental: Check that variables are defined in all possible code paths. -strict_undefined_checks = false +strict_undefined_checks = true # Experimental: FOR TESTING ONLY. Use pytype/rewrite/. use_rewrite = false diff --git a/agents-api/tests/test_entry_queries.py b/agents-api/tests/test_entry_queries.py index be8e6362e..6161ad94c 100644 --- a/agents-api/tests/test_entry_queries.py +++ b/agents-api/tests/test_entry_queries.py @@ -14,7 +14,7 @@ from agents_api.models.entry.list_entries import list_entries from tests.fixtures import cozo_client, test_developer_id, test_session -MODEL = "julep-ai/samantha-1-turbo" +MODEL = "gpt-4o" @test("model: create entry") diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index b2ef1a04e..c4dec7c5f 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -15,7 +15,7 @@ from agents_api.models.execution.list_executions import list_executions from tests.fixtures import cozo_client, test_developer_id, test_execution, test_task -MODEL = "julep-ai/samantha-1-turbo" +MODEL = "gpt-4o" @test("model: create execution") diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index 763e62bed..94b5bbbe4 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -21,7 +21,7 @@ test_user, ) -MODEL = "julep-ai/samantha-1-turbo" +MODEL = "gpt-4o" @test("model: create session") From 853da78f2d701d65a552a72e1cab6c8dfc2c5217 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Tue, 13 Aug 2024 16:35:44 -0400 Subject: [PATCH 044/110] fix(agents-api): Fix chat endpoint behavior Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Chat.py | 20 ++++-- .../models/docs/search_docs_by_embedding.py | 66 ++++++++++++------- .../models/docs/search_docs_by_text.py | 63 ++++++++++-------- .../models/docs/search_docs_hybrid.py | 9 +-- .../agents_api/routers/docs/search_docs.py | 6 +- .../agents_api/routers/sessions/chat.py | 31 ++++++--- agents-api/poetry.lock | 20 +++--- agents-api/tests/fixtures.py | 2 +- agents-api/tests/test_chat_routes.py | 41 ++++++++++++ agents-api/tests/test_docs_queries.py | 28 +++++++- sdks/python/julep/api/client.py | 28 ++++---- sdks/python/julep/api/reference.md | 6 +- .../julep/api/types/chat_competion_usage.py | 6 +- sdks/python/poetry.lock | 14 ++-- sdks/ts/src/api/models/Chat_ChatInput.ts | 8 +-- sdks/ts/src/api/models/Chat_CompetionUsage.ts | 6 +- sdks/ts/src/api/schemas/$Chat_ChatInput.ts | 9 ++- .../src/api/schemas/$Chat_CompetionUsage.ts | 3 - typespec/chat/models.tsp | 15 ++--- 19 files changed, 238 insertions(+), 143 deletions(-) diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 94832c4cb..4d157f7c5 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -114,15 +114,21 @@ class CompetionUsage(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - completion_tokens: Annotated[int, Field(json_schema_extra={"readOnly": True})] + completion_tokens: Annotated[ + int | None, Field(None, json_schema_extra={"readOnly": True}) + ] """ Number of tokens in the generated completion """ - prompt_tokens: Annotated[int, Field(json_schema_extra={"readOnly": True})] + prompt_tokens: Annotated[ + int | None, Field(None, json_schema_extra={"readOnly": True}) + ] """ Number of tokens in the prompt """ - total_tokens: Annotated[int, Field(json_schema_extra={"readOnly": True})] + total_tokens: Annotated[ + int | None, Field(None, json_schema_extra={"readOnly": True}) + ] """ Total number of tokens used in the request (prompt + completion) """ @@ -213,13 +219,13 @@ class ChatInput(ChatInputData): model_config = ConfigDict( populate_by_name=True, ) - recall: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] + remember: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] """ - Whether previous memories should be recalled or not (will be enabled in a future release) + DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release) """ - remember: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] + recall: bool = True """ - Whether this interaction should form new memories or not (will be enabled in a future release) + Whether previous memories and docs should be recalled or not """ save: bool = True """ diff --git a/agents-api/agents_api/models/docs/search_docs_by_embedding.py b/agents-api/agents_api/models/docs/search_docs_by_embedding.py index 0acbf8f6a..3f7114a23 100644 --- a/agents-api/agents_api/models/docs/search_docs_by_embedding.py +++ b/agents-api/agents_api/models/docs/search_docs_by_embedding.py @@ -41,8 +41,7 @@ def search_docs_by_embedding( *, developer_id: UUID, - owner_type: Literal["user", "agent"], - owner_id: UUID, + owners: list[tuple[Literal["user", "agent"], UUID]], query_embedding: list[float], k: int = 3, confidence: float = 0.7, @@ -65,25 +64,29 @@ def search_docs_by_embedding( assert len(query_embedding) == embedding_size assert sum(query_embedding) - owner_id = str(owner_id) + owners: list[list[str]] = [ + [owner_type, str(owner_id)] for owner_type, owner_id in owners + ] # Calculate the search radius based on confidence level radius: float = 1.0 - confidence # Construct the datalog query for searching document snippets interim_query = f""" + owners[owner_type, owner_id] <- $owners input[ + owner_type, owner_id, query_embedding, - ] <- [[ - to_uuid($owner_id), - vec($query_embedding), - ]] + ] := + owners[owner_type, owner_id_str], + owner_id = to_uuid(owner_id_str), + query_embedding = vec($query_embedding) candidate[doc_id] := - input[owner_id, _], + input[owner_type, owner_id, _], *docs {{ - owner_type: $owner_type, + owner_type, owner_id, doc_id }} @@ -125,7 +128,7 @@ def search_docs_by_embedding( index, distance, ] := - input[owner_id, query], + input[_, __, query], candidate[doc_id], ~snippets:embedding_space {{ doc_id, @@ -151,6 +154,8 @@ def search_docs_by_embedding( snippet_data = [index, content] ?[ + owner_type, + owner_id, doc_id, snippet_data, distance, @@ -175,6 +180,8 @@ def search_docs_by_embedding( :limit {k} :create _interim {{ + owner_type, + owner_id, doc_id, snippet_data, distance, @@ -186,18 +193,23 @@ def search_docs_by_embedding( collect_query = """ m[ doc_id, + owner_type, + owner_id, collect(snippet), distance, title, - ] := *_interim { - doc_id, - snippet_data, - distance, - title, - }, snippet = { - "index": snippet_data->0, - "content": snippet_data->1, - } + ] := + *_interim { + owner_type, + owner_id, + doc_id, + snippet_data, + distance, + title, + }, snippet = { + "index": snippet_data->0, + "content": snippet_data->1, + } ?[ id, @@ -208,17 +220,22 @@ def search_docs_by_embedding( title, ] := m[ id, + owner_type, + owner_id, snippets, distance, title, - ], owner_type = $owner_type, owner_id = $owner_id + ] """ queries = [ verify_developer_id_query(developer_id), - verify_developer_owns_resource_query( - developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} - ), + *[ + verify_developer_owns_resource_query( + developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} + ) + for owner_type, owner_id in owners + ], interim_query, collect_query, ] @@ -226,8 +243,7 @@ def search_docs_by_embedding( return ( queries, { - "owner_type": owner_type, - "owner_id": owner_id, + "owners": owners, "query_embedding": query_embedding, }, ) diff --git a/agents-api/agents_api/models/docs/search_docs_by_text.py b/agents-api/agents_api/models/docs/search_docs_by_text.py index 8befeb07d..a5e379f24 100644 --- a/agents-api/agents_api/models/docs/search_docs_by_text.py +++ b/agents-api/agents_api/models/docs/search_docs_by_text.py @@ -41,8 +41,7 @@ def search_docs_by_text( *, developer_id: UUID, - owner_type: Literal["user", "agent"], - owner_id: UUID, + owners: list[tuple[Literal["user", "agent"], UUID]], query: str, k: int = 3, ) -> tuple[list[str], dict]: @@ -50,28 +49,29 @@ def search_docs_by_text( Searches for document snippets in CozoDB by embedding query. Parameters: - - owner_type (Literal["user", "agent"]): The type of the owner of the documents. - - owner_id (UUID): The unique identifier of the owner. + - owners (list[tuple[Literal["user", "agent"], UUID]]): The type of the owner of the documents. - query (str): The query string. - k (int, optional): The number of nearest neighbors to retrieve. Defaults to 3. """ - owner_id = str(owner_id) + owners: list[list[str]] = [ + [owner_type, str(owner_id)] for owner_type, owner_id in owners + ] # Construct the datalog query for searching document snippets search_query = f""" + owners[owner_type, owner_id] <- $owners input[ + owner_type, owner_id, - query, - ] <- [[ - to_uuid($owner_id), - $query, - ]] + ] := + owners[owner_type, owner_id_str], + owner_id = to_uuid(owner_id_str) candidate[doc_id] := - input[owner_id, _], + input[owner_type, owner_id], *docs {{ - owner_type: $owner_type, + owner_type, owner_id, doc_id }} @@ -81,17 +81,16 @@ def search_docs_by_text( snippet_data, distance, ] := - input[owner_id, query], candidate[doc_id], ~snippets:lsh {{ doc_id, index, content | - query: query, + query: $query, k: {k}, }}, - distance = 10000000, # Very large distance to depict no distance + distance = 10000000, # Very large distance to depict no valid distance snippet_data = [index, content] search_result[ @@ -99,14 +98,13 @@ def search_docs_by_text( snippet_data, distance, ] := - input[owner_id, query], candidate[doc_id], ~snippets:fts {{ doc_id, index, content | - query: query, + query: $query, k: {k}, score_kind: 'tf_idf', bind_score: score, @@ -119,10 +117,12 @@ def search_docs_by_text( collect(snippet), distance, title, + owner_type, + owner_id, ] := candidate[doc_id], *docs {{ - owner_type: $owner_type, + owner_type, owner_id, doc_id, title, @@ -145,12 +145,16 @@ def search_docs_by_text( snippets, distance, title, - ] := m[ - id, - snippets, - distance, - title, - ], owner_type = $owner_type, owner_id = $owner_id + ] := + input[owner_type, owner_id], + m[ + id, + snippets, + distance, + title, + owner_type, + owner_id, + ] # Sort the results by distance to find the closest matches :sort distance @@ -159,13 +163,16 @@ def search_docs_by_text( queries = [ verify_developer_id_query(developer_id), - verify_developer_owns_resource_query( - developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} - ), + *[ + verify_developer_owns_resource_query( + developer_id, f"{owner_type}s", **{f"{owner_type}_id": owner_id} + ) + for owner_type, owner_id in owners + ], search_query, ] return ( queries, - {"owner_type": owner_type, "owner_id": owner_id, "query": query}, + {"owners": owners, "query": query}, ) diff --git a/agents-api/agents_api/models/docs/search_docs_hybrid.py b/agents-api/agents_api/models/docs/search_docs_hybrid.py index 0a9cd2815..03fb44037 100644 --- a/agents-api/agents_api/models/docs/search_docs_hybrid.py +++ b/agents-api/agents_api/models/docs/search_docs_hybrid.py @@ -95,8 +95,7 @@ def dbsf_fuse( def search_docs_hybrid( *, developer_id: UUID, - owner_type: Literal["user", "agent"], - owner_id: UUID, + owners: list[tuple[Literal["user", "agent"], UUID]], query: str, query_embedding: list[float], k: int = 3, @@ -107,8 +106,7 @@ def search_docs_hybrid( # TODO: We should probably parallelize these queries text_results = search_docs_by_text( developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, + owners=owners, query=query, k=2 * k, **text_search_options, @@ -116,8 +114,7 @@ def search_docs_hybrid( embedding_results = search_docs_by_embedding( developer_id=developer_id, - owner_type=owner_type, - owner_id=owner_id, + owners=owners, query_embedding=query_embedding, k=2 * k, **embed_search_options, diff --git a/agents-api/agents_api/routers/docs/search_docs.py b/agents-api/agents_api/routers/docs/search_docs.py index 0e5430a7a..ad19a3178 100644 --- a/agents-api/agents_api/routers/docs/search_docs.py +++ b/agents-api/agents_api/routers/docs/search_docs.py @@ -70,8 +70,7 @@ async def search_user_docs( start = time.time() docs = search_fn( developer_id=x_developer_id, - owner_type="user", - owner_id=user_id, + owners=[("user", user_id)], **params, ) @@ -98,8 +97,7 @@ async def search_agent_docs( start = time.time() docs = search_fn( developer_id=x_developer_id, - owner_type="agent", - owner_id=agent_id, + owners=[("agent", agent_id)], **params, ) diff --git a/agents-api/agents_api/routers/sessions/chat.py b/agents-api/agents_api/routers/sessions/chat.py index f0023cb93..afe7e3e2d 100644 --- a/agents-api/agents_api/routers/sessions/chat.py +++ b/agents-api/agents_api/routers/sessions/chat.py @@ -32,6 +32,7 @@ async def get_messages( session_id: UUID, new_raw_messages: list[dict], chat_context: ChatContext, + recall: bool, ): assert len(new_raw_messages) > 0 @@ -50,6 +51,9 @@ async def get_messages( if entry.id not in {r.head for r in relations} ] + if not recall: + return past_messages, [] + # Search matching docs [query_embedding, *_] = await embed.embed( inputs=[ @@ -60,10 +64,14 @@ async def get_messages( ) query_text = new_raw_messages[-1]["content"] + # List all the applicable owners to search docs from + active_agent_id = chat_context.get_active_agent().id + user_ids = [user.id for user in chat_context.users] + owners = [("user", user_id) for user_id in user_ids] + [("agent", active_agent_id)] + doc_references: list[DocReference] = search_docs_hybrid( developer_id=developer.id, - owner_type="agent", - owner_id=chat_context.get_active_agent().id, + owners=owners, query=query_text, query_embedding=query_embedding, ) @@ -79,7 +87,7 @@ async def get_messages( async def chat( developer: Annotated[Developer, Depends(get_developer_data)], session_id: UUID, - data: ChatInput, + input: ChatInput, background_tasks: BackgroundTasks, ) -> ChatResponse: # First get the chat context @@ -89,10 +97,10 @@ async def chat( ) # Merge the settings and prepare environment - chat_context.merge_settings(data) + chat_context.merge_settings(input) settings: dict = chat_context.settings.model_dump() env: dict = chat_context.get_chat_environment() - new_raw_messages = [msg.model_dump() for msg in data.messages] + new_raw_messages = [msg.model_dump() for msg in input.messages] # Render the messages past_messages, doc_references = await get_messages( @@ -100,22 +108,27 @@ async def chat( session_id=session_id, new_raw_messages=new_raw_messages, chat_context=chat_context, + recall=input.recall, ) env["docs"] = doc_references new_messages = await render_template(new_raw_messages, variables=env) messages = past_messages + new_messages + # Get the tools + tools = settings.get("tools") or chat_context.get_active_tools() + # Get the response from the model model_response = await litellm.acompletion( messages=messages, + tools=tools, + user=str(developer.id), # For tracking usage + tags=developer.tags, # For filtering models in litellm **settings, - user=str(developer.id), - tags=developer.tags, ) # Save the input and the response to the session history - if data.save: + if input.save: new_entries = [ CreateEntryRequest(**msg, source="api_request") for msg in new_messages ] @@ -128,7 +141,7 @@ async def chat( ) # Return the response - chat_response_class = ChunkChatResponse if data.stream else MessageChatResponse + chat_response_class = ChunkChatResponse if input.stream else MessageChatResponse chat_response: ChatResponse = chat_response_class( id=uuid4(), created_at=utcnow(), diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index ba83ef4a2..b8c0a42b7 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2255,13 +2255,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.7" +version = "1.43.9" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.7-py3-none-any.whl", hash = "sha256:88d9d8dcb4579839106941f1ce59143ab926af986a2206cce4bcda1ae153a78c"}, - {file = "litellm-1.43.7.tar.gz", hash = "sha256:b6ef8db0c7555d590957c37b228584efc5e9154b925ab0fffb112be26f1ab5ab"}, + {file = "litellm-1.43.9-py3-none-any.whl", hash = "sha256:54253281139e61f130b7e1a613a11f7a5ee896c2ee8536b0ca9a5ffbfce4c5f0"}, + {file = "litellm-1.43.9.tar.gz", hash = "sha256:c397a14c9b851f007f09c99e5a28606f7f122fdb4ae954931220f60e9edc6918"}, ] [package.dependencies] @@ -4219,18 +4219,18 @@ tornado = ["tornado (>=5)"] [[package]] name = "setuptools" -version = "72.1.0" +version = "72.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, - {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, + {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, + {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, ] [package.extras] core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -4290,13 +4290,13 @@ files = [ [[package]] name = "soupsieve" -version = "2.5" +version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] [[package]] diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index fafb351f0..1b3b1000a 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -78,7 +78,7 @@ def test_developer(cozo_client=cozo_client, developer_id=test_developer_id): ) -@fixture(scope="global") +@fixture(scope="test") def patch_embed_acompletion(): mock_model_response = ModelResponse( id="fake_id", diff --git a/agents-api/tests/test_chat_routes.py b/agents-api/tests/test_chat_routes.py index 55d94b2a0..ccf91c89e 100644 --- a/agents-api/tests/test_chat_routes.py +++ b/agents-api/tests/test_chat_routes.py @@ -28,6 +28,46 @@ async def _( assert (await embed.embed())[0][0] == 1.0 +@test("chat: check that non-recall get_messages works") +async def _( + developer=test_developer, + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, + session=test_session, + tool=test_tool, + user=test_user, + mocks=patch_embed_acompletion, +): + (embed, _) = mocks + + chat_context = prepare_chat_context( + developer_id=developer_id, + session_id=session.id, + client=client, + ) + + session_id = session.id + + new_raw_messages = [{"role": "user", "content": "hello"}] + + past_messages, doc_references = await get_messages( + developer=developer, + session_id=session_id, + new_raw_messages=new_raw_messages, + chat_context=chat_context, + recall=False, + ) + + assert isinstance(past_messages, list) + assert len(past_messages) >= 0 + assert isinstance(doc_references, list) + assert len(doc_references) == 0 + + # Check that embed was not called + embed.assert_not_called() + + @test("chat: check that get_messages works") async def _( developer=test_developer, @@ -56,6 +96,7 @@ async def _( session_id=session_id, new_raw_messages=new_raw_messages, chat_context=chat_context, + recall=True, ) assert isinstance(past_messages, list) diff --git a/agents-api/tests/test_docs_queries.py b/agents-api/tests/test_docs_queries.py index 4743ea45d..fcf7f9bd6 100644 --- a/agents-api/tests/test_docs_queries.py +++ b/agents-api/tests/test_docs_queries.py @@ -9,6 +9,7 @@ from agents_api.models.docs.get_doc import get_doc from agents_api.models.docs.list_docs import list_docs from agents_api.models.docs.search_docs_by_embedding import search_docs_by_embedding +from agents_api.models.docs.search_docs_by_text import search_docs_by_text from tests.fixtures import ( EMBEDDING_SIZE, cozo_client, @@ -82,7 +83,29 @@ def _( assert len(result) >= 1 -@test("model: search docs") +@test("model: search docs by text") +def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): + create_doc( + developer_id=developer_id, + owner_type="agent", + owner_id=agent.id, + data=CreateDocRequest( + title="Hello", content=["The world is a funny little thing"] + ), + client=client, + ) + + result = search_docs_by_text( + developer_id=developer_id, + owners=[("agent", agent.id)], + query="funny", + client=client, + ) + + assert len(result) >= 1 + + +@test("model: search docs by embedding") def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): doc = create_doc( developer_id=developer_id, @@ -106,8 +129,7 @@ def _(client=cozo_client, agent=test_agent, developer_id=test_developer_id): result = search_docs_by_embedding( developer_id=developer_id, - owner_type="agent", - owner_id=agent.id, + owners=[("agent", agent.id)], query_embedding=query_embedding, client=client, ) diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index 42c9f45a5..2030b74ce 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -2663,8 +2663,8 @@ def chat_route_generate( self, id: CommonUuid, *, - recall: bool, remember: bool, + recall: bool, save: bool, stream: bool, messages: typing.Sequence[EntriesInputChatMlMessage], @@ -2694,11 +2694,11 @@ def chat_route_generate( id : CommonUuid The session ID - recall : bool - Whether previous memories should be recalled or not (will be enabled in a future release) - remember : bool - Whether this interaction should form new memories or not (will be enabled in a future release) + DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release) + + recall : bool + Whether previous memories and docs should be recalled or not save : bool Whether this interaction should be stored in the session history or not @@ -2782,8 +2782,8 @@ def chat_route_generate( content="content", ) ], - recall=True, remember=True, + recall=True, save=True, stream=True, ) @@ -2792,8 +2792,8 @@ def chat_route_generate( f"sessions/{jsonable_encoder(id)}/chat", method="POST", json={ - "recall": recall, "remember": remember, + "recall": recall, "save": save, "model": model, "stream": stream, @@ -6542,8 +6542,8 @@ async def chat_route_generate( self, id: CommonUuid, *, - recall: bool, remember: bool, + recall: bool, save: bool, stream: bool, messages: typing.Sequence[EntriesInputChatMlMessage], @@ -6573,11 +6573,11 @@ async def chat_route_generate( id : CommonUuid The session ID - recall : bool - Whether previous memories should be recalled or not (will be enabled in a future release) - remember : bool - Whether this interaction should form new memories or not (will be enabled in a future release) + DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release) + + recall : bool + Whether previous memories and docs should be recalled or not save : bool Whether this interaction should be stored in the session history or not @@ -6666,8 +6666,8 @@ async def main() -> None: content="content", ) ], - recall=True, remember=True, + recall=True, save=True, stream=True, ) @@ -6679,8 +6679,8 @@ async def main() -> None: f"sessions/{jsonable_encoder(id)}/chat", method="POST", json={ - "recall": recall, "remember": remember, + "recall": recall, "save": save, "model": model, "stream": stream, diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index 5bf9bf81f..85fbaf94c 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -3790,8 +3790,8 @@ client.chat_route_generate( content="content", ) ], - recall=True, remember=True, + recall=True, save=True, stream=True, ) @@ -3818,7 +3818,7 @@ client.chat_route_generate(
    -**recall:** `bool` — Whether previous memories should be recalled or not (will be enabled in a future release) +**remember:** `bool` — DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release)
    @@ -3826,7 +3826,7 @@ client.chat_route_generate(
    -**remember:** `bool` — Whether this interaction should form new memories or not (will be enabled in a future release) +**recall:** `bool` — Whether previous memories and docs should be recalled or not
    diff --git a/sdks/python/julep/api/types/chat_competion_usage.py b/sdks/python/julep/api/types/chat_competion_usage.py index f6f798330..23d6b7b3c 100644 --- a/sdks/python/julep/api/types/chat_competion_usage.py +++ b/sdks/python/julep/api/types/chat_competion_usage.py @@ -12,17 +12,17 @@ class ChatCompetionUsage(pydantic_v1.BaseModel): Usage statistics for the completion request """ - completion_tokens: int = pydantic_v1.Field() + completion_tokens: typing.Optional[int] = pydantic_v1.Field(default=None) """ Number of tokens in the generated completion """ - prompt_tokens: int = pydantic_v1.Field() + prompt_tokens: typing.Optional[int] = pydantic_v1.Field(default=None) """ Number of tokens in the prompt """ - total_tokens: int = pydantic_v1.Field() + total_tokens: typing.Optional[int] = pydantic_v1.Field(default=None) """ Total number of tokens used in the request (prompt + completion) """ diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 38e2c1b19..060ffa16f 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -2865,18 +2865,18 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "72.1.0" +version = "72.2.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.1.0-py3-none-any.whl", hash = "sha256:5a03e1860cf56bb6ef48ce186b0e557fdba433237481a9a625176c2831be15d1"}, - {file = "setuptools-72.1.0.tar.gz", hash = "sha256:8d243eff56d095e5817f796ede6ae32941278f542e0f941867cc05ae52b162ec"}, + {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, + {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, ] [package.extras] core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] -doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] [[package]] @@ -2914,13 +2914,13 @@ files = [ [[package]] name = "soupsieve" -version = "2.5" +version = "2.6" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" files = [ - {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, - {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, + {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, + {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] [[package]] diff --git a/sdks/ts/src/api/models/Chat_ChatInput.ts b/sdks/ts/src/api/models/Chat_ChatInput.ts index 5de17283c..e03fa2914 100644 --- a/sdks/ts/src/api/models/Chat_ChatInput.ts +++ b/sdks/ts/src/api/models/Chat_ChatInput.ts @@ -9,13 +9,13 @@ import type { Common_logit_bias } from "./Common_logit_bias"; import type { Common_uuid } from "./Common_uuid"; export type Chat_ChatInput = Chat_ChatInputData & { /** - * Whether previous memories should be recalled or not (will be enabled in a future release) + * DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release) */ - readonly recall: boolean; + readonly remember: boolean; /** - * Whether this interaction should form new memories or not (will be enabled in a future release) + * Whether previous memories and docs should be recalled or not */ - readonly remember: boolean; + recall: boolean; /** * Whether this interaction should be stored in the session history or not */ diff --git a/sdks/ts/src/api/models/Chat_CompetionUsage.ts b/sdks/ts/src/api/models/Chat_CompetionUsage.ts index 089b474f1..8422d5f36 100644 --- a/sdks/ts/src/api/models/Chat_CompetionUsage.ts +++ b/sdks/ts/src/api/models/Chat_CompetionUsage.ts @@ -9,13 +9,13 @@ export type Chat_CompetionUsage = { /** * Number of tokens in the generated completion */ - readonly completion_tokens: number; + readonly completion_tokens?: number; /** * Number of tokens in the prompt */ - readonly prompt_tokens: number; + readonly prompt_tokens?: number; /** * Total number of tokens used in the request (prompt + completion) */ - readonly total_tokens: number; + readonly total_tokens?: number; }; diff --git a/sdks/ts/src/api/schemas/$Chat_ChatInput.ts b/sdks/ts/src/api/schemas/$Chat_ChatInput.ts index 635d83534..538b90e76 100644 --- a/sdks/ts/src/api/schemas/$Chat_ChatInput.ts +++ b/sdks/ts/src/api/schemas/$Chat_ChatInput.ts @@ -10,16 +10,15 @@ export const $Chat_ChatInput = { }, { properties: { - recall: { + remember: { type: "boolean", - description: `Whether previous memories should be recalled or not (will be enabled in a future release)`, + description: `DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release)`, isReadOnly: true, isRequired: true, }, - remember: { + recall: { type: "boolean", - description: `Whether this interaction should form new memories or not (will be enabled in a future release)`, - isReadOnly: true, + description: `Whether previous memories and docs should be recalled or not`, isRequired: true, }, save: { diff --git a/sdks/ts/src/api/schemas/$Chat_CompetionUsage.ts b/sdks/ts/src/api/schemas/$Chat_CompetionUsage.ts index 556114810..d8f34cb14 100644 --- a/sdks/ts/src/api/schemas/$Chat_CompetionUsage.ts +++ b/sdks/ts/src/api/schemas/$Chat_CompetionUsage.ts @@ -9,21 +9,18 @@ export const $Chat_CompetionUsage = { type: "number", description: `Number of tokens in the generated completion`, isReadOnly: true, - isRequired: true, format: "uint32", }, prompt_tokens: { type: "number", description: `Number of tokens in the prompt`, isReadOnly: true, - isRequired: true, format: "uint32", }, total_tokens: { type: "number", description: `Total number of tokens used in the request (prompt + completion)`, isReadOnly: true, - isRequired: true, format: "uint32", }, }, diff --git a/typespec/chat/models.tsp b/typespec/chat/models.tsp index da7c170ca..f52dae04c 100644 --- a/typespec/chat/models.tsp +++ b/typespec/chat/models.tsp @@ -34,14 +34,13 @@ enum FinishReason { /** Determines how the session accesses history and memories */ model MemoryAccessOptions { - /** Whether previous memories should be recalled or not (will be enabled in a future release) */ - @visibility("read") // DISABLED - recall: boolean = false; - - /** Whether this interaction should form new memories or not (will be enabled in a future release) */ + /** DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release) */ @visibility("read") // DISABLED remember: boolean = false; + /** Whether previous memories and docs should be recalled or not */ + recall: boolean = true; + /** Whether this interaction should be stored in the session history or not */ save: boolean = true; } @@ -134,15 +133,15 @@ model ChatSettings extends DefaultChatSettings { model CompetionUsage { /** Number of tokens in the generated completion */ @visibility("read") - completion_tokens: uint32; + completion_tokens?: uint32; /** Number of tokens in the prompt */ @visibility("read") - prompt_tokens: uint32; + prompt_tokens?: uint32; /** Total number of tokens used in the request (prompt + completion) */ @visibility("read") - total_tokens: uint32; + total_tokens?: uint32; } model ChatInputData { From afc35464b5ffb1f38c4e8af5d887bc49f3033b4f Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Tue, 13 Aug 2024 18:06:12 -0400 Subject: [PATCH 045/110] refactor(agents-api): Remove unnecessary stuff Signed-off-by: Diwank Tomer --- .../agents_api/activities/dialog_insights.py | 118 ------------------ .../activities/relationship_summary.py | 102 --------------- .../activities/salient_questions.py | 91 -------------- .../activities/task_steps/__init__.py | 6 +- agents-api/agents_api/activities/types.py | 98 ++------------- .../agents_api/routers/agents/create_agent.py | 1 - .../agents_api/routers/sessions/chat.py | 17 ++- .../agents_api/routers/users/delete_user.py | 1 - agents-api/agents_api/worker/__main__.py | 12 -- .../agents_api/workflows/dialog_insights.py | 21 ---- .../workflows/relationship_summary.py | 20 --- .../agents_api/workflows/salient_questions.py | 20 --- 12 files changed, 27 insertions(+), 480 deletions(-) delete mode 100644 agents-api/agents_api/activities/dialog_insights.py delete mode 100644 agents-api/agents_api/activities/relationship_summary.py delete mode 100644 agents-api/agents_api/activities/salient_questions.py delete mode 100644 agents-api/agents_api/workflows/dialog_insights.py delete mode 100644 agents-api/agents_api/workflows/relationship_summary.py delete mode 100644 agents-api/agents_api/workflows/salient_questions.py diff --git a/agents-api/agents_api/activities/dialog_insights.py b/agents-api/agents_api/activities/dialog_insights.py deleted file mode 100644 index 1d5adec39..000000000 --- a/agents-api/agents_api/activities/dialog_insights.py +++ /dev/null @@ -1,118 +0,0 @@ -from textwrap import dedent -from typing import Callable - -from temporalio import activity - -from agents_api.clients import litellm - -from .types import ChatML, DialogInsightsTaskArgs - - -def make_prompt( - args: DialogInsightsTaskArgs, - max_turns: int = 20, -): - # Unpack - dialog = args.dialog - person1 = args.person1 - person2 = args.person2 - - # Template - template = dedent( - """\ - [[Conversation]] - {dialog_context} - - --- - - Write down if there are any details from the conversation above that {person1} might have found interesting from {person2}'s perspective, in a full sentence. Write down point by point only the most important points. Answer must be in third person. - - Answer: " - """ - ).strip() - - # Filter dialog (keep only user and assistant sections) - dialog = [entry for entry in dialog if entry.role != "system"] - - # Truncate to max_turns - dialog = dialog[-max_turns:] - - # Prepare dialog context - dialog_context = "\n".join( - [ - f'{e.name or ("User" if e.role == "user" else "Assistant")}: {e.content}' - for e in dialog - ] - ) - - prompt = template.format( - dialog_context=dialog_context, - person1=person1, - person2=person2, - ) - - return prompt - - -async def run_prompt( - dialog: list[ChatML], - person1: str, - person2: str, - model: str = "gpt-4o", - max_tokens: int = 400, - temperature: float = 0.4, - parser: Callable[[str], str] = lambda x: x, -) -> str: - prompt = make_prompt( - DialogInsightsTaskArgs(dialog=dialog, person1=person1, person2=person2) - ) - - response = await litellm.acompletion( - model=model, - messages=[ - { - "content": prompt, - "role": "user", - } - ], - max_tokens=max_tokens, - temperature=temperature, - stop=["<", "<|"], - stream=False, - ) - - content = response.choices[0].message.content - - return parser(content.strip() if content is not None else "") - - -@activity.defn -async def dialog_insights(dialog: list[ChatML], person1: str, person2: str) -> None: - # session_id = UUID(session_id) - # entries = [ - # Entry(**row) - # for _, row in client.run( - # get_toplevel_entries_query(session_id=session_id) - # ).iterrows() - # ] - - # assert len(entries) > 0, "no need to summarize on empty entries list" - - await run_prompt(dialog, person1, person2) - - # new_entry = Entry( - # session_id=session_id, - # source="summarizer", - # role="system", - # name="information", - # content=response, - # timestamp=entries[-1].timestamp + 0.01, - # ) - - # client.run( - # entries_summarization_query( - # session_id=session_id, - # new_entry=new_entry, - # old_entry_ids=[e.id for e in entries], - # ) - # ) diff --git a/agents-api/agents_api/activities/relationship_summary.py b/agents-api/agents_api/activities/relationship_summary.py deleted file mode 100644 index 997eaf40a..000000000 --- a/agents-api/agents_api/activities/relationship_summary.py +++ /dev/null @@ -1,102 +0,0 @@ -from textwrap import dedent -from typing import Callable - -from temporalio import activity - -from ..clients import litellm -from .types import RelationshipSummaryTaskArgs - - -def make_prompt(args: RelationshipSummaryTaskArgs): - # Unpack - statements = args.statements - person1 = args.person1 - person2 = args.person2 - - # Template - template = dedent( - """\ - Statements: - - {statements_joined} - - Based on the statements above, summarize {person1} and {person2}'s relationship in a 2-3 sentences. What do they feel or know about each other? - - Answer: " - """ - ).strip() - - prompt = template.format( - statements_joined="\n- ".join(statements), - person1=person1, - person2=person2, - ) - - return prompt - - -async def run_prompt( - statements: list[str], - person1: str, - person2: str, - model: str = "gpt-4o", - max_tokens: int = 400, - temperature: float = 0.6, - parser: Callable[[str], str] = lambda x: x, -) -> str: - prompt = make_prompt( - RelationshipSummaryTaskArgs( - statements=statements, person1=person1, person2=person2 - ) - ) - - response = await litellm.acompletion( - model=model, - messages=[ - { - "content": prompt, - "role": "user", - } - ], - max_tokens=max_tokens, - temperature=temperature, - stop=["<", "<|"], - stream=False, - ) - - content = response.choices[0].message.content - - return parser(content.strip() if content is not None else "") - - -@activity.defn -async def relationship_summary( - statements: list[str], person1: str, person2: str -) -> None: - # session_id = UUID(session_id) - # entries = [ - # Entry(**row) - # for _, row in client.run( - # get_toplevel_entries_query(session_id=session_id) - # ).iterrows() - # ] - - # assert len(entries) > 0, "no need to summarize on empty entries list" - - await run_prompt(statements=statements, person1=person1, person2=person2) - - # new_entry = Entry( - # session_id=session_id, - # source="summarizer", - # role="system", - # name="information", - # content=response, - # timestamp=entries[-1].timestamp + 0.01, - # ) - - # client.run( - # entries_summarization_query( - # session_id=session_id, - # new_entry=new_entry, - # old_entry_ids=[e.id for e in entries], - # ) - # ) diff --git a/agents-api/agents_api/activities/salient_questions.py b/agents-api/agents_api/activities/salient_questions.py deleted file mode 100644 index 0194e8c72..000000000 --- a/agents-api/agents_api/activities/salient_questions.py +++ /dev/null @@ -1,91 +0,0 @@ -from textwrap import dedent -from typing import Callable - -from temporalio import activity - -from ..clients import litellm -from .types import SalientQuestionsTaskArgs - - -def make_prompt(args: SalientQuestionsTaskArgs): - # Unpack - statements = args.statements - num = args.num - - # Template - template = dedent( - """\ - Statements: - - {statements_joined} - - Given only the information above, what are the {num} most salient high-level questions we can answer about the subjects grounded in the statements? - - """ - ).strip() - - prompt = template.format( - statements_joined="\n- ".join(statements), - num=num, - ) - - return prompt - - -async def run_prompt( - statements: list[str], - num: int = 3, - model: str = "gpt-4o", - max_tokens: int = 400, - temperature: float = 0.6, - parser: Callable[[str], str] = lambda x: x, -) -> str: - prompt = make_prompt(SalientQuestionsTaskArgs(statements=statements, num=num)) - - response = await litellm.acompletion( - model=model, - messages=[ - { - "content": prompt, - "role": "user", - } - ], - max_tokens=max_tokens, - temperature=temperature, - stop=["<", "<|"], - stream=False, - ) - - content = response.choices[0].message.content - - return parser(content.strip() if content is not None else "") - - -@activity.defn -async def salient_questions(statements: list[str], num: int = 3) -> None: - # session_id = UUID(session_id) - # entries = [ - # Entry(**row) - # for _, row in client.run( - # get_toplevel_entries_query(session_id=session_id) - # ).iterrows() - # ] - - # assert len(entries) > 0, "no need to summarize on empty entries list" - - await run_prompt(statements=statements, num=num) - - # new_entry = Entry( - # session_id=session_id, - # source="summarizer", - # role="system", - # name="information", - # content=response, - # timestamp=entries[-1].timestamp + 0.01, - # ) - - # client.run( - # entries_summarization_query( - # session_id=session_id, - # new_entry=new_entry, - # old_entry_ids=[e.id for e in entries], - # ) - # ) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index a9818d515..494226a5b 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -14,7 +14,9 @@ UpdateExecutionRequest, YieldStep, ) -from ...clients.litellm import acompletion +from ...clients import ( + litellm, # We dont directly import `acompletion` so we can mock it +) from ...clients.worker.types import ChatML from ...common.protocol.tasks import ( StepContext, @@ -57,7 +59,7 @@ async def prompt_step(context: StepContext) -> dict: settings: dict = context.definition.settings.model_dump() # Get settings and run llm - response = await acompletion( + response = await litellm.acompletion( messages=messages, **settings, ) diff --git a/agents-api/agents_api/activities/types.py b/agents-api/agents_api/activities/types.py index 37fd8015d..f550b5c75 100644 --- a/agents-api/agents_api/activities/types.py +++ b/agents-api/agents_api/activities/types.py @@ -1,111 +1,27 @@ -from typing import Any, Callable, Literal, Optional, Protocol, TypedDict +from typing import Literal from uuid import UUID from pydantic import BaseModel +from ..autogen.openapi_model import InputChatMLMessage -class PromptModule(Protocol): - stop: list[str] - temperature: float - parser: Callable[[str], str] - make_prompt: Callable[..., str] - -class ChatML(BaseModel): - role: Literal["system", "user", "assistant"] - content: str - - name: Optional[str] = None - entry_id: Optional[UUID] = None - - processed: bool = False - parent_id: Optional[UUID] = None - session_id: Optional[UUID] = None - timestamp: Optional[float] = None - token_count: Optional[int] = None - - -class BaseTask(BaseModel): ... - - -class BaseTaskArgs(BaseModel): ... - - -class AddPrinciplesTaskArgs(BaseTaskArgs): - scores: dict[str, Any] - full: bool = False - name: Optional[str] = None - user_id: Optional[UUID] = None - character_id: Optional[UUID] = None - - -class AddPrinciplesTask(BaseTask): - name: Literal["add_principles.v1"] - args: AddPrinciplesTaskArgs - - -class MemoryManagementTaskArgs(BaseTaskArgs): +class MemoryManagementTaskArgs(BaseModel): session_id: UUID model: str - dialog: list[ChatML] + dialog: list[InputChatMLMessage] previous_memories: list[str] = [] -class MemoryManagementTask(BaseTask): +class MemoryManagementTask(BaseModel): name: Literal["memory_management.v1"] args: MemoryManagementTaskArgs -class MemoryDensityTaskArgs(BaseTaskArgs): - memory: str - - -class MemoryDensityTask(BaseTask): - name: Literal["memory_density.v1"] - args: MemoryDensityTaskArgs - - -class MemoryRatingTaskArgs(BaseTaskArgs): +class MemoryRatingTaskArgs(BaseModel): memory: str -class MemoryRatingTask(BaseTask): +class MemoryRatingTask(BaseModel): name: Literal["memory_rating.v1"] args: MemoryRatingTaskArgs - - -class DialogInsightsTaskArgs(BaseTaskArgs): - dialog: list[ChatML] - person1: str - person2: str - - -class DialogInsightsTask(BaseTask): - name: Literal["dialog_insights.v1"] - args: DialogInsightsTaskArgs - - -class RelationshipSummaryTaskArgs(BaseTaskArgs): - statements: list[str] - person1: str - person2: str - - -class RelationshipSummaryTask(BaseTask): - name: Literal["relationship_summary.v1"] - args: RelationshipSummaryTaskArgs - - -class SalientQuestionsTaskArgs(BaseTaskArgs): - statements: list[str] - num: int = 3 - - -class SalientQuestionsTask(BaseTask): - name: Literal["salient_questions.v1"] - args: SalientQuestionsTaskArgs - - -class CombinedTask(TypedDict): - name: str - args: dict[Any, Any] diff --git a/agents-api/agents_api/routers/agents/create_agent.py b/agents-api/agents_api/routers/agents/create_agent.py index 56e2eadf7..d1cac0d6b 100644 --- a/agents-api/agents_api/routers/agents/create_agent.py +++ b/agents-api/agents_api/routers/agents/create_agent.py @@ -19,7 +19,6 @@ async def create_agent( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], data: CreateAgentRequest, ) -> ResourceCreatedResponse: - print("create_agent", x_developer_id, data) agent = models.agent.create_agent( developer_id=x_developer_id, data=data, diff --git a/agents-api/agents_api/routers/sessions/chat.py b/agents-api/agents_api/routers/sessions/chat.py index afe7e3e2d..e6103c15e 100644 --- a/agents-api/agents_api/routers/sessions/chat.py +++ b/agents-api/agents_api/routers/sessions/chat.py @@ -118,6 +118,11 @@ async def chat( # Get the tools tools = settings.get("tools") or chat_context.get_active_tools() + # Truncate the messages if necessary + if chat_context.session.context_overflow == "truncate": + # messages = messages[-settings["max_tokens"] :] + raise NotImplementedError("Truncation is not yet implemented") + # Get the response from the model model_response = await litellm.acompletion( messages=messages, @@ -129,9 +134,12 @@ async def chat( # Save the input and the response to the session history if input.save: + # TODO: Count the number of tokens before saving it to the session + new_entries = [ CreateEntryRequest(**msg, source="api_request") for msg in new_messages ] + background_tasks.add_task( create_entries, developer_id=developer.id, @@ -140,12 +148,19 @@ async def chat( mark_session_as_updated=True, ) + # Adaptive context handling + jobs = [] + if chat_context.session.context_overflow == "adaptive": + # TODO: Start the adaptive context workflow + # jobs = [await start_adaptive_context_workflow] + raise NotImplementedError("Adaptive context is not yet implemented") + # Return the response chat_response_class = ChunkChatResponse if input.stream else MessageChatResponse chat_response: ChatResponse = chat_response_class( id=uuid4(), created_at=utcnow(), - jobs=[], + jobs=jobs, docs=doc_references, usage=model_response.usage.model_dump(), choices=[choice.model_dump() for choice in model_response.choices], diff --git a/agents-api/agents_api/routers/users/delete_user.py b/agents-api/agents_api/routers/users/delete_user.py index 3a63e42e9..fd1d02a94 100644 --- a/agents-api/agents_api/routers/users/delete_user.py +++ b/agents-api/agents_api/routers/users/delete_user.py @@ -14,5 +14,4 @@ async def delete_user( user_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)] ) -> ResourceDeletedResponse: - print(user_id) return delete_user_query(developer_id=x_developer_id, user_id=user_id) diff --git a/agents-api/agents_api/worker/__main__.py b/agents-api/agents_api/worker/__main__.py index 544a28b4d..b84ed7992 100644 --- a/agents-api/agents_api/worker/__main__.py +++ b/agents-api/agents_api/worker/__main__.py @@ -11,12 +11,9 @@ from temporalio.worker import Worker from ..activities.co_density import co_density -from ..activities.dialog_insights import dialog_insights from ..activities.embed_docs import embed_docs from ..activities.mem_mgmt import mem_mgmt from ..activities.mem_rating import mem_rating -from ..activities.relationship_summary import relationship_summary -from ..activities.salient_questions import salient_questions from ..activities.summarization import summarization from ..activities.task_steps import ( evaluate_step, @@ -35,12 +32,9 @@ temporal_task_queue, ) from ..workflows.co_density import CoDensityWorkflow -from ..workflows.dialog_insights import DialogInsightsWorkflow from ..workflows.embed_docs import EmbedDocsWorkflow from ..workflows.mem_mgmt import MemMgmtWorkflow from ..workflows.mem_rating import MemRatingWorkflow -from ..workflows.relationship_summary import RelationshipSummaryWorkflow -from ..workflows.salient_questions import SalientQuestionsWorkflow from ..workflows.summarization import SummarizationWorkflow from ..workflows.task_execution import TaskExecutionWorkflow from ..workflows.truncation import TruncationWorkflow @@ -88,11 +82,8 @@ async def main(): workflows=[ SummarizationWorkflow, CoDensityWorkflow, - DialogInsightsWorkflow, MemMgmtWorkflow, MemRatingWorkflow, - RelationshipSummaryWorkflow, - SalientQuestionsWorkflow, EmbedDocsWorkflow, TaskExecutionWorkflow, TruncationWorkflow, @@ -101,11 +92,8 @@ async def main(): *task_activities, summarization, co_density, - dialog_insights, mem_mgmt, mem_rating, - relationship_summary, - salient_questions, embed_docs, truncation, ], diff --git a/agents-api/agents_api/workflows/dialog_insights.py b/agents-api/agents_api/workflows/dialog_insights.py deleted file mode 100644 index d7e40395e..000000000 --- a/agents-api/agents_api/workflows/dialog_insights.py +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env python3 - - -from datetime import timedelta - -from temporalio import workflow - -with workflow.unsafe.imports_passed_through(): - from ..activities.dialog_insights import dialog_insights - from ..activities.types import ChatML - - -@workflow.defn -class DialogInsightsWorkflow: - @workflow.run - async def run(self, dialog: list[ChatML], person1: str, person2: str) -> None: - return await workflow.execute_activity( - dialog_insights, - [dialog, person1, person2], - schedule_to_close_timeout=timedelta(seconds=600), - ) diff --git a/agents-api/agents_api/workflows/relationship_summary.py b/agents-api/agents_api/workflows/relationship_summary.py deleted file mode 100644 index 0f2e5fb07..000000000 --- a/agents-api/agents_api/workflows/relationship_summary.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 - - -from datetime import timedelta - -from temporalio import workflow - -with workflow.unsafe.imports_passed_through(): - from ..activities.relationship_summary import relationship_summary - - -@workflow.defn -class RelationshipSummaryWorkflow: - @workflow.run - async def run(self, statements: list[str], person1: str, person2: str) -> None: - return await workflow.execute_activity( - relationship_summary, - [statements, person1, person2], - schedule_to_close_timeout=timedelta(seconds=600), - ) diff --git a/agents-api/agents_api/workflows/salient_questions.py b/agents-api/agents_api/workflows/salient_questions.py deleted file mode 100644 index 59f30dc37..000000000 --- a/agents-api/agents_api/workflows/salient_questions.py +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env python3 - - -from datetime import timedelta - -from temporalio import workflow - -with workflow.unsafe.imports_passed_through(): - from ..activities.salient_questions import salient_questions - - -@workflow.defn -class SalientQuestionsWorkflow: - @workflow.run - async def run(self, statements: list[str], num: int = 3) -> None: - return await workflow.execute_activity( - salient_questions, - [statements, num], - schedule_to_close_timeout=timedelta(seconds=600), - ) From be18ec65856de6819934dc4d921060d0675508d9 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Wed, 14 Aug 2024 12:29:24 -0400 Subject: [PATCH 046/110] feat(agents-api): Add fixtures for testing workflows Signed-off-by: Diwank Tomer --- .../agents_api/activities/co_density.py | 115 -------- .../agents_api/activities/embed_docs.py | 18 +- agents-api/agents_api/activities/mem_mgmt.py | 12 +- .../agents_api/activities/summarization.py | 250 ++++-------------- .../activities/task_steps/__init__.py | 5 +- .../agents_api/activities/truncation.py | 63 ++--- agents-api/agents_api/clients/temporal.py | 36 ++- agents-api/agents_api/clients/worker/types.py | 87 +----- .../agents_api/common/protocol/entries.py | 52 ++-- .../agents_api/models/docs/embed_snippets.py | 2 +- agents-api/agents_api/rec_sum/entities.py | 2 +- agents-api/agents_api/rec_sum/generate.py | 6 +- agents-api/agents_api/rec_sum/summarize.py | 2 +- agents-api/agents_api/rec_sum/trim.py | 2 +- agents-api/agents_api/worker/__main__.py | 85 +----- agents-api/agents_api/worker/worker.py | 68 +++++ agents-api/agents_api/workflows/co_density.py | 20 -- agents-api/agents_api/workflows/mem_mgmt.py | 7 +- agents-api/tests/fixtures.py | 27 ++ agents-api/tests/test_activities.py | 52 +++- 20 files changed, 333 insertions(+), 578 deletions(-) delete mode 100644 agents-api/agents_api/activities/co_density.py create mode 100644 agents-api/agents_api/worker/worker.py delete mode 100644 agents-api/agents_api/workflows/co_density.py diff --git a/agents-api/agents_api/activities/co_density.py b/agents-api/agents_api/activities/co_density.py deleted file mode 100644 index 408cc398a..000000000 --- a/agents-api/agents_api/activities/co_density.py +++ /dev/null @@ -1,115 +0,0 @@ -from textwrap import dedent -from typing import Callable - -from temporalio import activity - -from agents_api.clients import litellm - -from .types import MemoryDensityTaskArgs - - -def make_prompt(args: MemoryDensityTaskArgs): - # Unpack - memory = args.memory - - # Template - template = dedent( - """\ - [[Memory from a Dialog]] - {memory} - - [[Instruction]] - You will generate increasingly concise, entity-dense summaries of the above Memory. - - Repeat the following 2 steps 5 times. - - Step 1: Identify 1-3 informative Entities (";" delimited) from the Memory which are missing from the previously generated summary. - Step 2: Write a new, denser summary of identical length which covers every entity and detail from the previous summary plus the Missing Entities. - - A Missing Entity is: - - Relevant: to the main story. - - Specific: descriptive yet concise (5 words or fewer). - - Novel: not in the previous summary. - - Faithful: present in the Memory. - - Anywhere: located anywhere in the Memory. - - Guidelines: - - The first summary should be long (4-5 sentences, ~80 words) yet highly non-specific, containing little information beyond the entities marked as missing. Use overly verbose language and fillers (e.g., "this article discusses") to reach ~80 words. - - Make every word count: rewrite the previous summary to improve flow and make space for additional entities. - - Make space with fusion, compression, and removal of uninformative phrases like "the memory discusses." - - The summaries should become highly dense and concise yet self-contained, e.g., easily understood without the Memory. - - Missing entities can appear anywhere in the new summary. - - Never drop entities from the previous summary. If space cannot be made, add fewer new entities. - - Remember, use the exact same number of words for each summary. - - Answer in JSON. The JSON should be a list (length 5) of dictionaries whose keys are "Missing_Entities", "Denser_Summary" and "Density_Score" (between 1-10, higher is better). - - [[Result]] - ```json - """ - ).strip() - - prompt = template.format(memory=memory) - - return prompt - - -async def run_prompt( - memory: str, - model: str = "gpt-4o", - max_tokens: int = 400, - temperature: float = 0.2, - parser: Callable[[str], str] = lambda x: x, -) -> str: - prompt = make_prompt(MemoryDensityTaskArgs(memory=memory)) - - response = await litellm.acompletion( - model=model, - messages=[ - { - "content": prompt, - "role": "user", - } - ], - max_tokens=max_tokens, - temperature=temperature, - stop=["<", "<|"], - stream=False, - ) - - content = response.choices[0].message.content - - return parser(content.strip() if content is not None else "") - - -@activity.defn -async def co_density(memory: str) -> None: - # session_id = UUID(session_id) - # entries = [ - # Entry(**row) - # for _, row in client.run( - # get_toplevel_entries_query(session_id=session_id) - # ).iterrows() - # ] - - # assert len(entries) > 0, "no need to summarize on empty entries list" - - await run_prompt(memory=memory) - - # new_entry = Entry( - # session_id=session_id, - # source="summarizer", - # role="system", - # name="information", - # content=response, - # timestamp=entries[-1].timestamp + 0.01, - # ) - - # client.run( - # entries_summarization_query( - # session_id=session_id, - # new_entry=new_entry, - # old_entry_ids=[e.id for e in entries], - # ) - # ) diff --git a/agents-api/agents_api/activities/embed_docs.py b/agents-api/agents_api/activities/embed_docs.py index b8e01e65e..da7bb313f 100644 --- a/agents-api/agents_api/activities/embed_docs.py +++ b/agents-api/agents_api/activities/embed_docs.py @@ -1,27 +1,39 @@ -from pydantic import UUID4 +from uuid import UUID + from temporalio import activity from agents_api.clients import embed as embedder +from agents_api.clients.cozo import get_cozo_client from agents_api.models.docs.embed_snippets import embed_snippets as embed_snippets_query snippet_embed_instruction = "Encode this passage for retrieval: " @activity.defn -async def embed_docs(doc_id: UUID4, title: str, content: list[str]) -> None: +async def embed_docs( + developer_id: UUID, + doc_id: UUID, + title: str, + content: list[str], + include_title: bool = True, + cozo_client=None, +) -> None: indices, snippets = list(zip(*enumerate(content))) + embeddings = await embedder.embed( [ { "instruction": snippet_embed_instruction, - "text": title + "\n\n" + snippet, + "text": (title + "\n\n" + snippet) if include_title else snippet, } for snippet in snippets ] ) embed_snippets_query( + developer_id=developer_id, doc_id=doc_id, snippet_indices=indices, embeddings=embeddings, + client=cozo_client or get_cozo_client(), ) diff --git a/agents-api/agents_api/activities/mem_mgmt.py b/agents-api/agents_api/activities/mem_mgmt.py index f368a0b0b..ea4bb84d2 100644 --- a/agents-api/agents_api/activities/mem_mgmt.py +++ b/agents-api/agents_api/activities/mem_mgmt.py @@ -4,9 +4,9 @@ from temporalio import activity -from agents_api.clients import litellm - -from .types import ChatML, MemoryManagementTaskArgs +from ..autogen.openapi_model import InputChatMLMessage +from ..clients import litellm +from .types import MemoryManagementTaskArgs example_previous_memory = """ Speaker 1: Composes and listens to music. Likes to buy basketball shoes but doesn't wear them often. @@ -118,7 +118,7 @@ def make_prompt( async def run_prompt( - dialog: list[ChatML], + dialog: list[InputChatMLMessage], session_id: UUID, previous_memories: list[str] = [], model: str = "gpt-4o", @@ -156,7 +156,9 @@ async def run_prompt( @activity.defn async def mem_mgmt( - dialog: list[ChatML], session_id: UUID, previous_memories: list[str] = [] + dialog: list[InputChatMLMessage], + session_id: UUID, + previous_memories: list[str] = [], ) -> None: # session_id = UUID(session_id) # entries = [ diff --git a/agents-api/agents_api/activities/summarization.py b/agents-api/agents_api/activities/summarization.py index dc365380d..581dcdb00 100644 --- a/agents-api/agents_api/activities/summarization.py +++ b/agents-api/agents_api/activities/summarization.py @@ -8,8 +8,7 @@ import pandas as pd from temporalio import activity -from agents_api.common.protocol.entries import Entry - +# from agents_api.common.protocol.entries import Entry # from agents_api.models.entry.entries_summarization import ( # entries_summarization_query, # get_toplevel_entries_query, @@ -18,7 +17,6 @@ from agents_api.rec_sum.summarize import summarize_messages from agents_api.rec_sum.trim import trim_messages -from ..clients import litellm from ..env import summarization_model_name @@ -31,196 +29,60 @@ def get_toplevel_entries_query(*args, **kwargs): return pd.DataFrame() -# - -example_previous_memory = """ -Speaker 1: Composes and listens to music. Likes to buy basketball shoes but doesn't wear them often. -""".strip() - -example_dialog_context = """ -Speaker 1: Did you find a place to donate your shoes? -Speaker 2: I did! I was driving to the grocery store the other day, when I noticed a bin labeled "Donation for Shoes and Clothing." It was easier than I thought! How about you? Why do you have so many pairs of sandals? -Speaker 1: I don't understand myself! When I look them online I just have the urge to buy them, even when I know I don't need them. This addiction is getting worse and worse. -Speaker 2: I completely agree that buying shoes can become an addiction! Are there any ways you can make money from home while waiting for a job offer from a call center? -Speaker 1: Well I already got the job so I just need to learn using the software. When I was still searching for jobs, we actually do a yard sale to sell many of my random items that are never used and clearly aren't needed either. -Speaker 2: Congratulations on getting the job! I know it'll help you out so much. And of course, maybe I should turn to yard sales as well, for they can be a great way to make some extra cash! -Speaker 1: Do you have another job or do you compose music for a living? How does your shopping addiction go? -Speaker 2: As a matter of fact, I do have another job in addition to composing music. I'm actually a music teacher at a private school, and on the side, I compose music for friends and family. As far as my shopping addiction goes, it's getting better. I promised myself that I wouldn't buy myself any more shoes this year! -Speaker 1: Ah, I remember the time I promised myself the same thing on not buying random things anymore, never work so far. Good luck with yours! -Speaker 2: Thanks! I need the good luck wishes. I've been avoiding malls and shopping outlets. Maybe you can try the same! -Speaker 1: I can avoid them physically, but with my job enable me sitting in front of my computer for a long period of time, I already turn the shopping addiction into online-shopping addiction. lol. Wish me luck! -Speaker 2: Sure thing! You know, and speaking of spending time before a computer, I need to look up information about Precious Moments figurines. I'd still like to know what they are! -""".strip() - -example_updated_memory = """ -Speaker 1: -- Enjoys composing and listening to music. -- Recently got a job that requires the use of specialized software. -- Displays a shopping addiction, particularly for shoes, that has transitioned to online-shopping due to job nature. -- Previously attempted to mitigate shopping addiction without success. -- Had organized a yard sale to sell unused items when job searching. - -Speaker 2: -- Also enjoys buying shoes and admits to it being addictive. -- Works as a music teacher at a private school in addition to composing music. -- Takes active measures to control his shopping addiction, including avoiding malls. -- Is interested in Precious Moments figurines. -""".strip() - - -def make_prompt( - dialog: list[Entry], - previous_memories: list[str], - max_turns: int = 10, - num_sentences: int = 10, -): - # Template - template = dedent( - """\ - **Instructions** - You are an advanced AI language model with the ability to store and update a memory to keep track of key personality information for people. You will receive a memory and a dialogue between two people. - - Your goal is to update the memory by incorporating the new personality information for both participants while ensuring that the memory does not exceed {num_sentences} sentences. - - To successfully update the memory, follow these steps: - - 1. Carefully analyze the existing memory and extract the key personality information of the participants from it. - 2. Consider the dialogue provided to identify any new or changed personality traits of either participant that need to be incorporated into the memory. - 3. Combine the old and new personality information to create an updated representation of the participants' traits. - 4. Structure the updated memory in a clear and concise manner, ensuring that it does not exceed {num_sentences} sentences. - 5. Pay attention to the relevance and importance of the personality information, focusing on capturing the most significant aspects while maintaining the overall coherence of the memory. - - Remember, the memory should serve as a reference point to maintain continuity in the dialogue and help accurately set context in future conversations based on the personality traits of the participants. - - **Test Example** - [[Previous Memory]] - {example_previous_memory} - - [[Dialogue Context]] - {example_dialog_context} - - [[Updated Memory]] - {example_updated_memory} - - **Actual Run** - [[Previous Memory]] - {previous_memory} - - [[Dialogue Context]] - {dialog_context} - - [[Updated Memory]] - """ - ).strip() - - # Filter dialog (keep only user and assistant sections) - dialog = [entry for entry in dialog if entry.role != "system"] - - # Truncate to max_turns - dialog = dialog[-max_turns:] - - # Prepare dialog context - dialog_context = "\n".join( - [ - f'{e.name or ("User" if e.role == "user" else "Assistant")}: {e.content}' - for e in dialog - ] - ) - - prompt = template.format( - dialog_context=dialog_context, - previous_memory="\n".join(previous_memories), - num_sentences=num_sentences, - example_dialog_context=example_dialog_context, - example_previous_memory=example_previous_memory, - example_updated_memory=example_updated_memory, - ) - - return prompt - - -async def run_prompt( - dialog: list[Entry], - previous_memories: list[str], - model: str = "gpt-4o", - max_tokens: int = 400, - temperature: float = 0.1, - parser: Callable[[str], str] = lambda x: x, - **kwargs, -) -> str: - prompt = make_prompt(dialog, previous_memories, **kwargs) - response = await litellm.acompletion( - model=model, - messages=[ - { - "content": prompt, - "role": "user", - } - ], - max_tokens=max_tokens, - temperature=temperature, - stop=["<", "<|"], - stream=False, - ) - - content = response.choices[0].message.content - - return parser(content.strip() if content is not None else "") - - @activity.defn async def summarization(session_id: str) -> None: - session_id = UUID(session_id) - entries = [] - entities_entry_ids = [] - for _, row in get_toplevel_entries_query(session_id=session_id).iterrows(): - if row["role"] == "system" and row.get("name") == "entities": - entities_entry_ids.append(UUID(row["entry_id"], version=4)) - else: - entries.append(row) - - assert len(entries) > 0, "no need to summarize on empty entries list" - - summarized, entities = await asyncio.gather( - summarize_messages(entries, model=summarization_model_name), - get_entities(entries, model=summarization_model_name), - ) - trimmed_messages = await trim_messages(summarized, model=summarization_model_name) - ts_delta = (entries[1]["timestamp"] - entries[0]["timestamp"]) / 2 - new_entities_entry = Entry( - session_id=session_id, - source="summarizer", - role="system", - name="entities", - content=entities["content"], - timestamp=entries[0]["timestamp"] + ts_delta, - ) - - entries_summarization_query( - session_id=session_id, - new_entry=new_entities_entry, - old_entry_ids=entities_entry_ids, - ) - - trimmed_map = { - m["index"]: m["content"] for m in trimmed_messages if m.get("index") is not None - } - - for idx, msg in enumerate(summarized): - new_entry = Entry( - session_id=session_id, - source="summarizer", - role="system", - name="information", - content=trimmed_map.get(idx, msg["content"]), - timestamp=entries[-1]["timestamp"] + 0.01, - ) - - entries_summarization_query( - session_id=session_id, - new_entry=new_entry, - old_entry_ids=[ - UUID(entries[idx - 1]["entry_id"], version=4) - for idx in msg["summarizes"] - ], - ) + raise NotImplementedError() + # session_id = UUID(session_id) + # entries = [] + # entities_entry_ids = [] + # for _, row in get_toplevel_entries_query(session_id=session_id).iterrows(): + # if row["role"] == "system" and row.get("name") == "entities": + # entities_entry_ids.append(UUID(row["entry_id"], version=4)) + # else: + # entries.append(row) + + # assert len(entries) > 0, "no need to summarize on empty entries list" + + # summarized, entities = await asyncio.gather( + # summarize_messages(entries, model=summarization_model_name), + # get_entities(entries, model=summarization_model_name), + # ) + # trimmed_messages = await trim_messages(summarized, model=summarization_model_name) + # ts_delta = (entries[1]["timestamp"] - entries[0]["timestamp"]) / 2 + # new_entities_entry = Entry( + # session_id=session_id, + # source="summarizer", + # role="system", + # name="entities", + # content=entities["content"], + # timestamp=entries[0]["timestamp"] + ts_delta, + # ) + + # entries_summarization_query( + # session_id=session_id, + # new_entry=new_entities_entry, + # old_entry_ids=entities_entry_ids, + # ) + + # trimmed_map = { + # m["index"]: m["content"] for m in trimmed_messages if m.get("index") is not None + # } + + # for idx, msg in enumerate(summarized): + # new_entry = Entry( + # session_id=session_id, + # source="summarizer", + # role="system", + # name="information", + # content=trimmed_map.get(idx, msg["content"]), + # timestamp=entries[-1]["timestamp"] + 0.01, + # ) + + # entries_summarization_query( + # session_id=session_id, + # new_entry=new_entry, + # old_entry_ids=[ + # UUID(entries[idx - 1]["entry_id"], version=4) + # for idx in msg["summarizes"] + # ], + # ) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 494226a5b..13f1adcfe 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -17,7 +17,6 @@ from ...clients import ( litellm, # We dont directly import `acompletion` so we can mock it ) -from ...clients.worker.types import ChatML from ...common.protocol.tasks import ( StepContext, TransitionInfo, @@ -53,7 +52,9 @@ async def prompt_step(context: StepContext) -> dict: ) messages = [ - ChatML(role="user", content=m) if isinstance(m, str) else ChatML(**m) + InputChatMLMessage(role="user", content=m) + if isinstance(m, str) + else InputChatMLMessage(**m) for m in messages ] diff --git a/agents-api/agents_api/activities/truncation.py b/agents-api/agents_api/activities/truncation.py index 190190a79..353e4b570 100644 --- a/agents-api/agents_api/activities/truncation.py +++ b/agents-api/agents_api/activities/truncation.py @@ -2,10 +2,11 @@ from temporalio import activity -from agents_api.autogen.openapi_model import Role +# from agents_api.autogen.openapi_model import Role from agents_api.common.protocol.entries import Entry from agents_api.models.entry.delete_entries import delete_entries -from agents_api.models.entry.entries_summarization import get_toplevel_entries_query + +# from agents_api.models.entry.entries_summarization import get_toplevel_entries_query def get_extra_entries(messages: list[Entry], token_count_threshold: int) -> list[UUID]: @@ -14,40 +15,40 @@ def get_extra_entries(messages: list[Entry], token_count_threshold: int) -> list result: list[UUID] = [] token_cnt, offset = 0, 0 - if messages[0].role == Role.system: - token_cnt, offset = messages[0].token_count, 1 + # if messages[0].role == Role.system: + # token_cnt, offset = messages[0].token_count, 1 - for m in reversed(messages[offset:]): - token_cnt += m.token_count - if token_cnt < token_count_threshold: - continue - else: - result.append(m.id) + # for m in reversed(messages[offset:]): + # token_cnt += m.token_count + # if token_cnt < token_count_threshold: + # continue + # else: + # result.append(m.id) - return result + # return result @activity.defn async def truncation(session_id: str, token_count_threshold: int) -> None: session_id = UUID(session_id) - delete_entries( - get_extra_entries( - [ - Entry( - entry_id=row["entry_id"], - session_id=session_id, - source=row["source"], - role=Role(row["role"]), - name=row["name"], - content=row["content"], - created_at=row["created_at"], - timestamp=row["timestamp"], - ) - for _, row in get_toplevel_entries_query( - session_id=session_id - ).iterrows() - ], - token_count_threshold, - ), - ) + # delete_entries( + # get_extra_entries( + # [ + # Entry( + # entry_id=row["entry_id"], + # session_id=session_id, + # source=row["source"], + # role=Role(row["role"]), + # name=row["name"], + # content=row["content"], + # created_at=row["created_at"], + # timestamp=row["timestamp"], + # ) + # for _, row in get_toplevel_entries_query( + # session_id=session_id + # ).iterrows() + # ], + # token_count_threshold, + # ), + # ) diff --git a/agents-api/agents_api/clients/temporal.py b/agents-api/agents_api/clients/temporal.py index 7e45b50d7..72a5056c8 100644 --- a/agents-api/agents_api/clients/temporal.py +++ b/agents-api/agents_api/clients/temporal.py @@ -13,7 +13,11 @@ from ..worker.codec import pydantic_data_converter -async def get_client(): +async def get_client( + worker_url: str = temporal_worker_url, + namespace: str = temporal_namespace, + data_converter=pydantic_data_converter, +): tls_config = False if temporal_private_key and temporal_client_cert: @@ -23,15 +27,17 @@ async def get_client(): ) return await Client.connect( - temporal_worker_url, - namespace=temporal_namespace, + worker_url, + namespace=namespace, tls=tls_config, - data_converter=pydantic_data_converter, + data_converter=data_converter, ) -async def run_summarization_task(session_id: UUID, job_id: UUID): - client = await get_client() +async def run_summarization_task( + session_id: UUID, job_id: UUID, client: Client | None = None +): + client = client or (await get_client()) await client.execute_workflow( "SummarizationWorkflow", @@ -42,9 +48,13 @@ async def run_summarization_task(session_id: UUID, job_id: UUID): async def run_embed_docs_task( - doc_id: UUID, title: str, content: list[str], job_id: UUID + doc_id: UUID, + title: str, + content: list[str], + job_id: UUID, + client: Client | None = None, ): - client = await get_client() + client = client or (await get_client()) await client.execute_workflow( "EmbedDocsWorkflow", @@ -55,9 +65,12 @@ async def run_embed_docs_task( async def run_truncation_task( - token_count_threshold: int, session_id: UUID, job_id: UUID + token_count_threshold: int, + session_id: UUID, + job_id: UUID, + client: Client | None = None, ): - client = await get_client() + client = client or (await get_client()) await client.execute_workflow( "TruncationWorkflow", @@ -72,8 +85,9 @@ async def run_task_execution_workflow( job_id: UUID, start: tuple[str, int] = ("main", 0), previous_inputs: list[dict] = [], + client: Client | None = None, ): - client = await get_client() + client = client or (await get_client()) return await client.start_workflow( "TaskExecutionWorkflow", diff --git a/agents-api/agents_api/clients/worker/types.py b/agents-api/agents_api/clients/worker/types.py index 02b6add6c..3bf063083 100644 --- a/agents-api/agents_api/clients/worker/types.py +++ b/agents-api/agents_api/clients/worker/types.py @@ -1,108 +1,41 @@ -from typing import Callable, Literal, Optional, Protocol +from typing import Literal from uuid import UUID from pydantic import BaseModel from agents_api.autogen.openapi_model import ( - ChatMLImageContentPart, - ChatMLTextContentPart, + InputChatMLMessage, ) -class PromptModule(Protocol): - stop: list[str] - temperature: float - parser: Callable[[str], str] - make_prompt: Callable[..., str] - - -class ChatML(BaseModel): - role: Literal["system", "user", "assistant", "function_call"] - content: str | dict | list[ChatMLTextContentPart] | list[ChatMLImageContentPart] - - name: Optional[str] = None - entry_id: Optional[UUID] = None - - processed: bool = False - parent_id: Optional[UUID] = None - session_id: Optional[UUID] = None - timestamp: Optional[float] = None - token_count: Optional[int] = None - - -class BaseTask(BaseModel): ... - - -class BaseTaskArgs(BaseModel): ... - - -class MemoryManagementTaskArgs(BaseTaskArgs): +class MemoryManagementTaskArgs(BaseModel): session_id: UUID model: str - dialog: list[ChatML] + dialog: list[InputChatMLMessage] previous_memories: list[str] = [] -class MemoryManagementTask(BaseTask): +class MemoryManagementTask(BaseModel): name: Literal["memory_management.v1"] args: MemoryManagementTaskArgs -class MemoryDensityTaskArgs(BaseTaskArgs): +class MemoryDensityTaskArgs(BaseModel): memory: str -class MemoryDensityTask(BaseTask): +class MemoryDensityTask(BaseModel): name: Literal["memory_density.v1"] args: MemoryDensityTaskArgs -class MemoryRatingTaskArgs(BaseTaskArgs): +class MemoryRatingTaskArgs(BaseModel): memory: str -class MemoryRatingTask(BaseTask): +class MemoryRatingTask(BaseModel): name: Literal["memory_rating.v1"] args: MemoryRatingTaskArgs -class DialogInsightsTaskArgs(BaseTaskArgs): - dialog: list[ChatML] - person1: str - person2: str - - -class DialogInsightsTask(BaseTask): - name: Literal["dialog_insights.v1"] - args: DialogInsightsTaskArgs - - -class RelationshipSummaryTaskArgs(BaseTaskArgs): - statements: list[str] - person1: str - person2: str - - -class RelationshipSummaryTask(BaseTask): - name: Literal["relationship_summary.v1"] - args: RelationshipSummaryTaskArgs - - -class SalientQuestionsTaskArgs(BaseTaskArgs): - statements: list[str] - num: int = 3 - - -class SalientQuestionsTask(BaseTask): - name: Literal["salient_questions.v1"] - args: SalientQuestionsTaskArgs - - -CombinedTask = ( - MemoryManagementTask - | MemoryDensityTask - | MemoryRatingTask - | DialogInsightsTask - | RelationshipSummaryTask - | SalientQuestionsTask -) +CombinedTask = MemoryManagementTask | MemoryDensityTask | MemoryRatingTask diff --git a/agents-api/agents_api/common/protocol/entries.py b/agents-api/agents_api/common/protocol/entries.py index 6ef7f70f2..18d63f583 100644 --- a/agents-api/agents_api/common/protocol/entries.py +++ b/agents-api/agents_api/common/protocol/entries.py @@ -27,28 +27,30 @@ class Entry(BaseEntry): token_count: int tokenizer: str = Field(default="character_count") - @computed_field - @property - def token_count(self) -> int: - """Calculates the token count based on the content's character count. The tokenizer 'character_count' divides the length of the content by 3.5 to estimate the token count. Raises NotImplementedError for unknown tokenizers.""" - if self.tokenizer == "character_count": - content_length = 0 - if isinstance(self.content, str): - content_length = len(self.content) - elif isinstance(self.content, dict): - content_length = len(json.dumps(self.content)) - elif isinstance(self.content, list): - for part in self.content: - if isinstance(part, ChatMLTextContentPart): - content_length += len(part.text) - elif isinstance(part, ChatMLImageContentPart): - content_length += ( - LOW_IMAGE_TOKEN_COUNT - if part.image_url.detail == "low" - else HIGH_IMAGE_TOKEN_COUNT - ) - - # Divide the content length by 3.5 to estimate token count based on character count. - return int(content_length // 3.5) - - raise NotImplementedError(f"Unknown tokenizer: {self.tokenizer}") + # TODO: Replace this with a proper implementation. + + # @computed_field + # @property + # def token_count(self) -> int: + # """Calculates the token count based on the content's character count. The tokenizer 'character_count' divides the length of the content by 3.5 to estimate the token count. Raises NotImplementedError for unknown tokenizers.""" + # if self.tokenizer == "character_count": + # content_length = 0 + # if isinstance(self.content, str): + # content_length = len(self.content) + # elif isinstance(self.content, dict): + # content_length = len(json.dumps(self.content)) + # elif isinstance(self.content, list): + # for part in self.content: + # if isinstance(part, ChatMLTextContentPart): + # content_length += len(part.text) + # elif isinstance(part, ChatMLImageContentPart): + # content_length += ( + # LOW_IMAGE_TOKEN_COUNT + # if part.image_url.detail == "low" + # else HIGH_IMAGE_TOKEN_COUNT + # ) + + # # Divide the content length by 3.5 to estimate token count based on character count. + # return int(content_length // 3.5) + + # raise NotImplementedError(f"Unknown tokenizer: {self.tokenizer}") diff --git a/agents-api/agents_api/models/docs/embed_snippets.py b/agents-api/agents_api/models/docs/embed_snippets.py index 5dd4b4457..89750192a 100644 --- a/agents-api/agents_api/models/docs/embed_snippets.py +++ b/agents-api/agents_api/models/docs/embed_snippets.py @@ -37,7 +37,7 @@ def embed_snippets( *, developer_id: UUID, doc_id: UUID, - snippet_indices: list[int] | tuple[int], + snippet_indices: list[int] | tuple[int, ...], embeddings: list[list[float]], embedding_size: int = 1024, ) -> tuple[list[str], dict]: diff --git a/agents-api/agents_api/rec_sum/entities.py b/agents-api/agents_api/rec_sum/entities.py index 11346447c..9992063ff 100644 --- a/agents-api/agents_api/rec_sum/entities.py +++ b/agents-api/agents_api/rec_sum/entities.py @@ -57,7 +57,7 @@ def make_entities_prompt( @retry(stop=stop_after_attempt(2)) async def get_entities( chat_session, - model="gpt-4-turbo", + model="gpt-4o", stop=[" dict: - result = await acompletion( + result = await litellm.acompletion( model=model, messages=messages, **kwargs, diff --git a/agents-api/agents_api/rec_sum/summarize.py b/agents-api/agents_api/rec_sum/summarize.py index 97f39905b..f98f35094 100644 --- a/agents-api/agents_api/rec_sum/summarize.py +++ b/agents-api/agents_api/rec_sum/summarize.py @@ -44,7 +44,7 @@ def make_summarize_prompt(session, user="a user", assistant="gpt-4-turbo", **_): @retry(stop=stop_after_attempt(2)) async def summarize_messages( chat_session, - model="gpt-4-turbo", + model="gpt-4o", stop=[" None: - return await workflow.execute_activity( - co_density, - memory, - schedule_to_close_timeout=timedelta(seconds=600), - ) diff --git a/agents-api/agents_api/workflows/mem_mgmt.py b/agents-api/agents_api/workflows/mem_mgmt.py index 2db9f95da..31c973741 100644 --- a/agents-api/agents_api/workflows/mem_mgmt.py +++ b/agents-api/agents_api/workflows/mem_mgmt.py @@ -7,14 +7,17 @@ with workflow.unsafe.imports_passed_through(): from ..activities.mem_mgmt import mem_mgmt - from ..activities.types import ChatML + from ..autogen.openapi_model import InputChatMLMessage @workflow.defn class MemMgmtWorkflow: @workflow.run async def run( - self, dialog: list[ChatML], session_id: str, previous_memories: list[str] + self, + dialog: list[InputChatMLMessage], + session_id: str, + previous_memories: list[str], ) -> None: return await workflow.execute_activity( mem_mgmt, diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 1b3b1000a..d8acec61c 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -6,6 +6,7 @@ from litellm.types.utils import Choices, ModelResponse from pycozo import Client as CozoClient from temporalio.client import WorkflowHandle +from temporalio.testing import ActivityEnvironment, WorkflowEnvironment from ward import fixture from agents_api.autogen.openapi_model import ( @@ -33,6 +34,7 @@ from agents_api.models.user.create_user import create_user from agents_api.models.user.delete_user import delete_user from agents_api.web import app +from agents_api.worker.worker import create_worker EMBEDDING_SIZE: int = 1024 @@ -49,6 +51,31 @@ def cozo_client(migrations_dir: str = "./migrations"): return client +@fixture(scope="test") +def activity_environment(): + return ActivityEnvironment() + + +@fixture(scope="global") +async def workflow_environment(): + wf_env = await WorkflowEnvironment.start_local() + return wf_env + + +@fixture(scope="global") +async def temporal_client(wf_env=workflow_environment): + return wf_env.client + + +@fixture(scope="global") +async def temporal_worker(temporal_client=temporal_client): + worker = await create_worker(client=temporal_client) + + async with worker as running_worker: + yield running_worker + await running_worker.shutdown() + + @fixture(scope="global") def test_developer_id(cozo_client=cozo_client): developer_id = uuid4() diff --git a/agents-api/tests/test_activities.py b/agents-api/tests/test_activities.py index 2723911cc..a5e35760b 100644 --- a/agents-api/tests/test_activities.py +++ b/agents-api/tests/test_activities.py @@ -1,13 +1,59 @@ -# import time -# import uuid +from ward import test -# from ward import test +from agents_api.activities.embed_docs import embed_docs + +from .fixtures import ( + cozo_client, + patch_embed_acompletion, + temporal_client, + temporal_worker, + test_developer_id, + test_doc, + workflow_environment, +) # from agents_api.activities.truncation import get_extra_entries # from agents_api.autogen.openapi_model import Role # from agents_api.common.protocol.entries import Entry +@test("activity: embed_docs") +async def _( + cozo_client=cozo_client, + developer_id=test_developer_id, + doc=test_doc, + mocks=patch_embed_acompletion, +): + (embed, _) = mocks + + title = "title" + content = ["content 1"] + include_title = True + + await embed_docs( + developer_id=developer_id, + doc_id=doc.id, + title=title, + content=content, + include_title=include_title, + cozo_client=cozo_client, + ) + + embed.assert_called_once() + + +@test("activity: check that workflow environment and worker are started correctly") +async def _( + workflow_environment=workflow_environment, + worker=temporal_worker, + client=temporal_client, +): + async with workflow_environment as wf_env: + assert wf_env is not None + assert worker is not None + assert worker.is_running + + # @test("get extra entries, do not strip system message") # def _(): # session_ids = [uuid.uuid4()] * 3 From 3a38e70016ba6e08bcf8874c3841a79221550406 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Wed, 14 Aug 2024 13:46:16 -0400 Subject: [PATCH 047/110] feat(agents-api): Add some workflow tests Signed-off-by: Diwank Tomer --- .../activities/task_steps/__init__.py | 18 +----- .../agents_api/common/protocol/tasks.py | 2 +- .../execution/create_execution_transition.py | 57 +++++++++++++++++-- agents-api/tests/test_execution_queries.py | 28 +++++++++ agents-api/tests/test_task_routes.py | 14 ++--- 5 files changed, 91 insertions(+), 28 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 13f1adcfe..e3d3099bb 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -11,7 +11,6 @@ InputChatMLMessage, PromptStep, ToolCallStep, - UpdateExecutionRequest, YieldStep, ) from ...clients import ( @@ -25,9 +24,6 @@ from ...models.execution.create_execution_transition import ( create_execution_transition as create_execution_transition_query, ) -from ...models.execution.update_execution import ( - update_execution as update_execution_query, -) @activity.defn @@ -134,8 +130,7 @@ async def transition_step( "cancelled", ] = "awaiting_input", ): - print("Running transition step") - # raise NotImplementedError() + activity.heartbeat("Running transition step") # Get transition info transition_data = transition_info.model_dump(by_alias=False) @@ -150,16 +145,9 @@ async def transition_step( developer_id=context.developer_id, execution_id=context.execution.id, transition_id=uuid4(), - **transition_data, - ) - - update_execution_query( - developer_id=context.developer_id, + update_execution_status=True, task_id=context.task.id, - execution_id=context.execution.id, - data=UpdateExecutionRequest( - status=execution_status, - ), + **transition_data, ) # Raise if it's a waiting step diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index 850aa700a..1bd4241b2 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -25,7 +25,7 @@ class ExecutionInput(BaseModel): developer_id: UUID execution: Execution - task: TaskSpec + task: TaskSpecDef agent: Agent tools: list[Tool] arguments: dict[str, Any] diff --git a/agents-api/agents_api/models/execution/create_execution_transition.py b/agents-api/agents_api/models/execution/create_execution_transition.py index 06874016d..df538be79 100644 --- a/agents-api/agents_api/models/execution/create_execution_transition.py +++ b/agents-api/agents_api/models/execution/create_execution_transition.py @@ -5,7 +5,11 @@ from pycozo.client import QueryException from pydantic import ValidationError -from ...autogen.openapi_model import CreateTransitionRequest, Transition +from ...autogen.openapi_model import ( + CreateTransitionRequest, + Transition, + UpdateExecutionRequest, +) from ...common.utils.cozo import cozo_process_mutate_data from ..utils import ( cozo_query, @@ -15,6 +19,7 @@ verify_developer_owns_resource_query, wrap_in_class, ) +from .update_execution import update_execution valid_transitions = { # Start state @@ -29,6 +34,16 @@ "step": ["wait", "error", "step", "finish", "cancelled"], } +transition_to_execution_status = { + "init": "queued", + "wait": "awaiting_input", + "resume": "running", + "step": "running", + "finish": "succeeded", + "error": "failed", + "cancelled": "cancelled", +} + @rewrap_exceptions( { @@ -46,17 +61,22 @@ def create_execution_transition( *, developer_id: UUID, execution_id: UUID, - transition_id: UUID | None = None, data: CreateTransitionRequest, + # Only one of these needed + transition_id: UUID | None = None, task_token: str | None = None, + # Only required for updating the execution status as well + update_execution_status: bool = False, + task_id: UUID | None = None, ) -> tuple[list[str], dict]: transition_id = transition_id or uuid4() data.metadata = data.metadata or {} data.execution_id = execution_id + # Prepare the transition data transition_data = data.model_dump(exclude_unset=True) - columns, values = cozo_process_mutate_data( + columns, transition_values = cozo_process_mutate_data( { **transition_data, "task_token": task_token, @@ -87,8 +107,9 @@ def create_execution_transition( :assert some """ + # Prepare the insert query insert_query = f""" - ?[{columns}] <- $values + ?[{columns}] <- $transition_values :insert transitions {{ {columns} @@ -97,6 +118,29 @@ def create_execution_transition( :returning """ + validate_status_query, update_execution_query, update_execution_params = ( + "", + "", + {}, + ) + + if update_execution_status: + assert ( + task_id is not None + ), "task_id is required for updating the execution status" + + # Prepare the execution update query + [*_, validate_status_query, update_execution_query], update_execution_params = ( + update_execution.__wrapped__( + developer_id=developer_id, + task_id=task_id, + execution_id=execution_id, + data=UpdateExecutionRequest( + status=transition_to_execution_status[data.type] + ), + ) + ) + queries = [ verify_developer_id_query(developer_id), verify_developer_owns_resource_query( @@ -105,6 +149,8 @@ def create_execution_transition( execution_id=execution_id, parents=[("agents", "agent_id"), ("tasks", "task_id")], ), + validate_status_query, + update_execution_query, check_last_transition_query, insert_query, ] @@ -112,8 +158,9 @@ def create_execution_transition( return ( queries, { - "values": values, + "transition_values": transition_values, "next_type": data.type, "valid_transitions": valid_transitions, + **update_execution_params, }, ) diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index c4dec7c5f..af81f75ff 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -83,3 +83,31 @@ def _(client=cozo_client, developer_id=test_developer_id, execution=test_executi assert result is not None assert result.type == "step" assert result.output == {"result": "test"} + + +@test("model: create execution transition with execution update") +def _( + client=cozo_client, + developer_id=test_developer_id, + task=test_task, + execution=test_execution, +): + result = create_execution_transition( + developer_id=developer_id, + execution_id=execution.id, + data=CreateTransitionRequest( + **{ + "type": "step", + "output": {"result": "test"}, + "current": ["main", 0], + "next": None, + } + ), + task_id=task.id, + update_execution_status=True, + client=client, + ) + + assert result is not None + assert result.type == "step" + assert result.output == {"result": "test"} diff --git a/agents-api/tests/test_task_routes.py b/agents-api/tests/test_task_routes.py index fcae8e979..8f2eef7c6 100644 --- a/agents-api/tests/test_task_routes.py +++ b/agents-api/tests/test_task_routes.py @@ -3,24 +3,24 @@ from ward import test -from tests.fixtures import client, make_request, test_execution, test_task +from tests.fixtures import client, make_request, test_agent @test("route: unauthorized should fail") -def _(client=client): +def _(client=client, agent=test_agent): data = dict( name="test user", main={ "kind_": "evaluate", "evaluate": { "additionalProp1": "value1", - } + }, }, ) response = client.request( method="POST", - url="/tasks", + url=f"/agents/{str(agent.id)}/tasks", data=data, ) @@ -28,20 +28,20 @@ def _(client=client): @test("route: create task") -def _(make_request=make_request): +def _(make_request=make_request, agent=test_agent): data = dict( name="test user", main={ "kind_": "evaluate", "evaluate": { "additionalProp1": "value1", - } + }, }, ) response = make_request( method="POST", - url="/tasks", + url=f"/agents/{str(agent.id)}/tasks", json=data, ) From 82c8af251ea6e94c73cc6cf6ff410f10fc2dd6d9 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Wed, 14 Aug 2024 14:02:18 -0400 Subject: [PATCH 048/110] fix(agents-api): Minor fix to tests Signed-off-by: Diwank Tomer --- .../agents_api/models/entry/create_entries.py | 3 +- .../agents_api/routers/tasks/create_task.py | 24 ++++------------ agents-api/tests/test_task_routes.py | 28 +++++++++++-------- 3 files changed, 23 insertions(+), 32 deletions(-) diff --git a/agents-api/agents_api/models/entry/create_entries.py b/agents-api/agents_api/models/entry/create_entries.py index 68d644266..d2aa87f86 100644 --- a/agents-api/agents_api/models/entry/create_entries.py +++ b/agents-api/agents_api/models/entry/create_entries.py @@ -78,8 +78,7 @@ def create_entries( verify_developer_owns_resource_query( developer_id, "sessions", session_id=session_id ), - mark_session_as_updated - and mark_session_updated_query(developer_id, session_id), + mark_session_updated_query(developer_id, session_id) if mark_session_as_updated else "", create_query, ] diff --git a/agents-api/agents_api/routers/tasks/create_task.py b/agents-api/agents_api/routers/tasks/create_task.py index 3c76663e4..5f8c86e1f 100644 --- a/agents-api/agents_api/routers/tasks/create_task.py +++ b/agents-api/agents_api/routers/tasks/create_task.py @@ -1,7 +1,5 @@ from typing import Annotated -from uuid import uuid4 -import pandas as pd from fastapi import Depends from pydantic import UUID4 from starlette.status import HTTP_201_CREATED @@ -9,6 +7,7 @@ from agents_api.autogen.openapi_model import ( CreateTaskRequest, ResourceCreatedResponse, + Task, ) from agents_api.dependencies.developer_id import get_developer_id from agents_api.models.task.create_task import create_task as create_task_query @@ -18,29 +17,18 @@ @router.post("/agents/{agent_id}/tasks", status_code=HTTP_201_CREATED, tags=["tasks"]) async def create_task( - request: CreateTaskRequest, + data: CreateTaskRequest, agent_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> ResourceCreatedResponse: - task_id = uuid4() - # TODO: Do thorough validation of the task spec - workflows = [ - {"name": "main", "steps": [w.model_dump() for w in request.main]}, - ] + [{"name": name, "steps": steps} for name, steps in request.model_extra.items()] - - resp: pd.DataFrame = create_task_query( - agent_id=agent_id, - task_id=task_id, + task: Task = create_task_query( developer_id=x_developer_id, - name=request.name, - description=request.description, - input_schema=request.input_schema or {}, - tools_available=request.tools or [], - workflows=workflows, + agent_id=agent_id, + data=data, ) return ResourceCreatedResponse( - id=resp["task_id"][0], created_at=resp["created_at"][0] + id=task.id, created_at=task.created_at, jobs=[] ) diff --git a/agents-api/tests/test_task_routes.py b/agents-api/tests/test_task_routes.py index 8f2eef7c6..b790240c4 100644 --- a/agents-api/tests/test_task_routes.py +++ b/agents-api/tests/test_task_routes.py @@ -10,12 +10,14 @@ def _(client=client, agent=test_agent): data = dict( name="test user", - main={ - "kind_": "evaluate", - "evaluate": { - "additionalProp1": "value1", - }, - }, + main=[ + { + "kind_": "evaluate", + "evaluate": { + "additionalProp1": "value1", + }, + } + ], ) response = client.request( @@ -31,12 +33,14 @@ def _(client=client, agent=test_agent): def _(make_request=make_request, agent=test_agent): data = dict( name="test user", - main={ - "kind_": "evaluate", - "evaluate": { - "additionalProp1": "value1", - }, - }, + main=[ + { + "kind_": "evaluate", + "evaluate": { + "additionalProp1": "value1", + }, + } + ], ) response = make_request( From b1fc2a4c0cc1bd8c3ed7fb5a0a5a7260c37d673c Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Wed, 14 Aug 2024 15:59:33 -0400 Subject: [PATCH 049/110] fix(agents-api): Fix tests and fixtures Signed-off-by: Diwank Tomer --- .../agents_api/models/entry/create_entries.py | 4 +- .../models/session/patch_session.py | 2 +- .../models/session/update_session.py | 2 +- agents-api/agents_api/models/utils.py | 45 ++++++++++++++++--- .../agents_api/routers/tasks/create_task.py | 4 +- agents-api/agents_api/worker/worker.py | 3 ++ agents-api/tests/fixtures.py | 16 +++---- agents-api/tests/test_activities.py | 2 - agents-api/tests/test_entry_queries.py | 38 ++++++++++++++++ 9 files changed, 92 insertions(+), 24 deletions(-) diff --git a/agents-api/agents_api/models/entry/create_entries.py b/agents-api/agents_api/models/entry/create_entries.py index d2aa87f86..01193d395 100644 --- a/agents-api/agents_api/models/entry/create_entries.py +++ b/agents-api/agents_api/models/entry/create_entries.py @@ -78,7 +78,9 @@ def create_entries( verify_developer_owns_resource_query( developer_id, "sessions", session_id=session_id ), - mark_session_updated_query(developer_id, session_id) if mark_session_as_updated else "", + mark_session_updated_query(developer_id, session_id) + if mark_session_as_updated + else "", create_query, ] diff --git a/agents-api/agents_api/models/session/patch_session.py b/agents-api/agents_api/models/session/patch_session.py index 5aae245cc..f16a53f44 100644 --- a/agents-api/agents_api/models/session/patch_session.py +++ b/agents-api/agents_api/models/session/patch_session.py @@ -92,7 +92,7 @@ def patch_session( *sessions{{ {rest_fields}, metadata: md, @ "NOW" }}, - updated_at = [floor(now()), true], + updated_at = 'ASSERT', metadata = concat(md, $metadata), :put sessions {{ diff --git a/agents-api/agents_api/models/session/update_session.py b/agents-api/agents_api/models/session/update_session.py index b498650e8..5f7789f29 100644 --- a/agents-api/agents_api/models/session/update_session.py +++ b/agents-api/agents_api/models/session/update_session.py @@ -81,7 +81,7 @@ def update_session( *sessions{{ {rest_fields}, @ "NOW" }}, - updated_at = [floor(now()), true] + updated_at = 'ASSERT' :put sessions {{ {all_fields}, updated_at diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index 2939b2208..411349ad3 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -71,16 +71,42 @@ def mark_session_updated_query(developer_id: UUID | str, session_id: UUID | str) to_uuid("{str(session_id)}"), ]] - ?[developer_id, session_id, updated_at] := + ?[ + developer_id, + session_id, + situation, + summary, + created_at, + metadata, + render_templates, + token_budget, + context_overflow, + updated_at, + ] := input[developer_id, session_id], *sessions {{ session_id, + situation, + summary, + created_at, + metadata, + render_templates, + token_budget, + context_overflow, + @ 'NOW' }}, updated_at = [floor(now()), true] - :update sessions {{ + :put sessions {{ developer_id, session_id, + situation, + summary, + created_at, + metadata, + render_templates, + token_budget, + context_overflow, updated_at, }} """ @@ -148,8 +174,7 @@ def cozo_query_dec(func: Callable[P, tuple[str | list[Any], dict]]): and then run the query using the client, returning a DataFrame. """ - if debug: - from pprint import pprint + from pprint import pprint @wraps(func) def wrapper(*args: P.args, client=None, **kwargs: P.kwargs) -> pd.DataFrame: @@ -162,17 +187,23 @@ def wrapper(*args: P.args, client=None, **kwargs: P.kwargs) -> pd.DataFrame: query = "}\n\n{\n".join(queries) query = f"{{ {query} }}" + debug and print(query) debug and pprint( dict( - query=query, variables=variables, ) ) + # Run the query from ..clients.cozo import get_cozo_client - client = client or get_cozo_client() - result = client.run(query, variables) + try: + client = client or get_cozo_client() + result = client.run(query, variables) + + except Exception as e: + debug and print(repr(getattr(e, "__cause__", None) or e)) + raise # Need to fix the UUIDs in the result result = result.map(fix_uuid_if_present) diff --git a/agents-api/agents_api/routers/tasks/create_task.py b/agents-api/agents_api/routers/tasks/create_task.py index 5f8c86e1f..0e63210d2 100644 --- a/agents-api/agents_api/routers/tasks/create_task.py +++ b/agents-api/agents_api/routers/tasks/create_task.py @@ -29,6 +29,4 @@ async def create_task( data=data, ) - return ResourceCreatedResponse( - id=task.id, created_at=task.created_at, jobs=[] - ) + return ResourceCreatedResponse(id=task.id, created_at=task.created_at, jobs=[]) diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index 3b77d3fa8..ac045c5ab 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -1,3 +1,5 @@ +from datetime import timedelta + from temporalio.client import Client from temporalio.worker import Worker @@ -46,6 +48,7 @@ async def create_worker(client: Client | None = None): # Initialize the worker with the specified task queue, workflows, and activities worker = Worker( client, + graceful_shutdown_timeout=timedelta(seconds=30), task_queue=temporal_task_queue, workflows=[ SummarizationWorkflow, diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index d8acec61c..4fb53ffd7 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -59,21 +59,19 @@ def activity_environment(): @fixture(scope="global") async def workflow_environment(): wf_env = await WorkflowEnvironment.start_local() - return wf_env + yield wf_env + await wf_env.shutdown() @fixture(scope="global") -async def temporal_client(wf_env=workflow_environment): - return wf_env.client - - -@fixture(scope="global") -async def temporal_worker(temporal_client=temporal_client): - worker = await create_worker(client=temporal_client) +async def temporal_worker(wf_env=workflow_environment): + worker = await create_worker(client=wf_env.client) + # FIXME: This does not stop the worker properly + c = worker.shutdown() async with worker as running_worker: yield running_worker - await running_worker.shutdown() + await c @fixture(scope="global") diff --git a/agents-api/tests/test_activities.py b/agents-api/tests/test_activities.py index a5e35760b..29adb8110 100644 --- a/agents-api/tests/test_activities.py +++ b/agents-api/tests/test_activities.py @@ -5,7 +5,6 @@ from .fixtures import ( cozo_client, patch_embed_acompletion, - temporal_client, temporal_worker, test_developer_id, test_doc, @@ -46,7 +45,6 @@ async def _( async def _( workflow_environment=workflow_environment, worker=temporal_worker, - client=temporal_client, ): async with workflow_environment as wf_env: assert wf_env is not None diff --git a/agents-api/tests/test_entry_queries.py b/agents-api/tests/test_entry_queries.py index 6161ad94c..ce8b6a00d 100644 --- a/agents-api/tests/test_entry_queries.py +++ b/agents-api/tests/test_entry_queries.py @@ -5,6 +5,8 @@ # Tests for entry queries +import time + from ward import test from agents_api.autogen.openapi_model import CreateEntryRequest @@ -12,6 +14,7 @@ from agents_api.models.entry.delete_entries import delete_entries from agents_api.models.entry.get_history import get_history from agents_api.models.entry.list_entries import list_entries +from agents_api.models.session.get_session import get_session from tests.fixtures import cozo_client, test_developer_id, test_session MODEL = "gpt-4o" @@ -35,10 +38,45 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): developer_id=developer_id, session_id=session.id, data=[test_entry], + mark_session_as_updated=False, client=client, ) +@test("model: create entry, update session") +def _(client=cozo_client, developer_id=test_developer_id, session=test_session): + """ + Tests the addition of a new entry to the database. + Verifies that the entry can be successfully added using the create_entries function. + """ + + test_entry = CreateEntryRequest( + session_id=session.id, + role="user", + source="internal", + content="test entry content", + ) + + # FIXME: We should make sessions.updated_at also a updated_at_ms field to avoid this sleep + time.sleep(1) + + create_entries( + developer_id=developer_id, + session_id=session.id, + data=[test_entry], + mark_session_as_updated=True, + client=client, + ) + + updated_session = get_session( + developer_id=developer_id, + session_id=session.id, + client=client, + ) + + assert updated_session.updated_at > session.updated_at + + @test("model: get entries") def _(client=cozo_client, developer_id=test_developer_id, session=test_session): """ From 3ab775839ab1f7d678b293a2b5efb16aaeac8607 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Wed, 14 Aug 2024 18:54:42 -0400 Subject: [PATCH 050/110] refactor(agents-api): Move gather_messages to its own model Signed-off-by: Diwank Tomer --- agents-api/agents_api/models/chat/__init__.py | 22 +++++ .../agents_api/models/chat/gather_messages.py | 82 +++++++++++++++++ .../{session => chat}/get_cached_response.py | 0 .../{session => chat}/prepare_chat_context.py | 2 +- .../{session => chat}/set_cached_response.py | 0 .../agents_api/models/entry/create_entries.py | 6 +- .../agents_api/models/entry/get_history.py | 2 + .../agents_api/models/entry/list_entries.py | 2 + .../agents_api/models/session/__init__.py | 3 - agents-api/agents_api/models/utils.py | 4 + .../agents_api/routers/sessions/chat.py | 88 ++++--------------- agents-api/poetry.lock | 12 +-- 12 files changed, 137 insertions(+), 86 deletions(-) create mode 100644 agents-api/agents_api/models/chat/__init__.py create mode 100644 agents-api/agents_api/models/chat/gather_messages.py rename agents-api/agents_api/models/{session => chat}/get_cached_response.py (100%) rename agents-api/agents_api/models/{session => chat}/prepare_chat_context.py (98%) rename agents-api/agents_api/models/{session => chat}/set_cached_response.py (100%) diff --git a/agents-api/agents_api/models/chat/__init__.py b/agents-api/agents_api/models/chat/__init__.py new file mode 100644 index 000000000..428b72572 --- /dev/null +++ b/agents-api/agents_api/models/chat/__init__.py @@ -0,0 +1,22 @@ +""" +Module: agents_api/models/docs + +This module is responsible for managing document-related operations within the application, particularly for agents and possibly other entities. It serves as a core component of the document management system, enabling features such as document creation, listing, deletion, and embedding of snippets for enhanced search and retrieval capabilities. + +Main functionalities include: +- Creating new documents and associating them with agents or users. +- Listing documents based on various criteria, including ownership and metadata filters. +- Deleting documents by their unique identifiers. +- Embedding document snippets for retrieval purposes. + +The module interacts with other parts of the application, such as the agents and users modules, to provide a comprehensive document management system. Its role is crucial in enabling document search, retrieval, and management features within the context of agents and users. + +This documentation aims to provide clear, concise, and sufficient context for new developers or contributors to understand the module's role without needing to dive deep into the code immediately. +""" + +# ruff: noqa: F401, F403, F405 + +from .gather_messages import gather_messages +from .get_cached_response import get_cached_response +from .prepare_chat_context import prepare_chat_context +from .set_cached_response import set_cached_response diff --git a/agents-api/agents_api/models/chat/gather_messages.py b/agents-api/agents_api/models/chat/gather_messages.py new file mode 100644 index 000000000..2a3c0eca1 --- /dev/null +++ b/agents-api/agents_api/models/chat/gather_messages.py @@ -0,0 +1,82 @@ +from uuid import UUID + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError + +from agents_api.autogen.Chat import ChatInput + +from ...autogen.openapi_model import DocReference, History +from ...clients import embed +from ...common.protocol.developers import Developer +from ...common.protocol.sessions import ChatContext +from ..docs.search_docs_hybrid import search_docs_hybrid +from ..entry.get_history import get_history +from ..utils import ( + partialclass, + rewrap_exceptions, +) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@beartype +async def gather_messages( + *, + developer: Developer, + session_id: UUID, + chat_context: ChatContext, + chat_input: ChatInput, +): + new_raw_messages = [msg.model_dump() for msg in chat_input.messages] + recall = chat_input.recall + + assert len(new_raw_messages) > 0 + + # Get the session history + history: History = get_history( + developer_id=developer.id, + session_id=session_id, + allowed_sources=["api_request", "api_response", "tool_response", "summarizer"], + ) + + # Keep leaf nodes only + relations = history.relations + past_messages = [ + entry.model_dump() + for entry in history.entries + if entry.id not in {r.head for r in relations} + ] + + if not recall: + return past_messages, [] + + # Search matching docs + [query_embedding, *_] = await embed.embed( + inputs=[ + f"{msg.get('name') or msg['role']}: {msg['content']}" + for msg in new_raw_messages + ], + join_inputs=True, + ) + query_text = new_raw_messages[-1]["content"] + + # List all the applicable owners to search docs from + active_agent_id = chat_context.get_active_agent().id + user_ids = [user.id for user in chat_context.users] + owners = [("user", user_id) for user_id in user_ids] + [("agent", active_agent_id)] + + doc_references: list[DocReference] = search_docs_hybrid( + developer_id=developer.id, + owners=owners, + query=query_text, + query_embedding=query_embedding, + ) + + return past_messages, doc_references diff --git a/agents-api/agents_api/models/session/get_cached_response.py b/agents-api/agents_api/models/chat/get_cached_response.py similarity index 100% rename from agents-api/agents_api/models/session/get_cached_response.py rename to agents-api/agents_api/models/chat/get_cached_response.py diff --git a/agents-api/agents_api/models/session/prepare_chat_context.py b/agents-api/agents_api/models/chat/prepare_chat_context.py similarity index 98% rename from agents-api/agents_api/models/session/prepare_chat_context.py rename to agents-api/agents_api/models/chat/prepare_chat_context.py index 83e6c6f8b..0e076bc20 100644 --- a/agents-api/agents_api/models/session/prepare_chat_context.py +++ b/agents-api/agents_api/models/chat/prepare_chat_context.py @@ -7,6 +7,7 @@ from ...autogen.openapi_model import make_session from ...common.protocol.sessions import ChatContext +from ..session.prepare_session_data import prepare_session_data from ..utils import ( cozo_query, fix_uuid_if_present, @@ -16,7 +17,6 @@ verify_developer_owns_resource_query, wrap_in_class, ) -from .prepare_session_data import prepare_session_data @rewrap_exceptions( diff --git a/agents-api/agents_api/models/session/set_cached_response.py b/agents-api/agents_api/models/chat/set_cached_response.py similarity index 100% rename from agents-api/agents_api/models/session/set_cached_response.py rename to agents-api/agents_api/models/chat/set_cached_response.py diff --git a/agents-api/agents_api/models/entry/create_entries.py b/agents-api/agents_api/models/entry/create_entries.py index 01193d395..31c8b4d01 100644 --- a/agents-api/agents_api/models/entry/create_entries.py +++ b/agents-api/agents_api/models/entry/create_entries.py @@ -1,4 +1,3 @@ -import json from uuid import UUID, uuid4 from beartype import beartype @@ -34,6 +33,7 @@ "id": UUID(d.pop("entry_id")), **d, }, + _kind="inserted", ) @cozo_query @beartype @@ -55,10 +55,6 @@ def create_entries( item["entry_id"] = item.pop("id", None) or str(uuid4()) item["created_at"] = (item.get("created_at") or utcnow()).timestamp() - if not item.get("token_count"): - item["token_count"] = len(json.dumps(item)) // 3.5 - item["tokenizer"] = "character_count" - cols, rows = cozo_process_mutate_data(data_dicts) # Construct a datalog query to insert the processed entries into the 'cozodb' database. diff --git a/agents-api/agents_api/models/entry/get_history.py b/agents-api/agents_api/models/entry/get_history.py index 49eb7b929..fed658ea5 100644 --- a/agents-api/agents_api/models/entry/get_history.py +++ b/agents-api/agents_api/models/entry/get_history.py @@ -62,6 +62,7 @@ def get_history( content, source, token_count, + tokenizer, created_at, timestamp, }, @@ -75,6 +76,7 @@ def get_history( "content": content, "source": source, "token_count": token_count, + "tokenizer": tokenizer, "created_at": created_at, "timestamp": timestamp } diff --git a/agents-api/agents_api/models/entry/list_entries.py b/agents-api/agents_api/models/entry/list_entries.py index 0c47d9a74..da2341c4c 100644 --- a/agents-api/agents_api/models/entry/list_entries.py +++ b/agents-api/agents_api/models/entry/list_entries.py @@ -65,6 +65,7 @@ def list_entries( content, source, token_count, + tokenizer, created_at, timestamp, ] := *entries {{ @@ -75,6 +76,7 @@ def list_entries( content, source, token_count, + tokenizer, created_at, timestamp, }}, diff --git a/agents-api/agents_api/models/session/__init__.py b/agents-api/agents_api/models/session/__init__.py index b4092611f..bc5f7fbb4 100644 --- a/agents-api/agents_api/models/session/__init__.py +++ b/agents-api/agents_api/models/session/__init__.py @@ -14,11 +14,8 @@ from .create_or_update_session import create_or_update_session from .create_session import create_session from .delete_session import delete_session -from .get_cached_response import get_cached_response from .get_session import get_session from .list_sessions import list_sessions from .patch_session import patch_session -from .prepare_chat_context import prepare_chat_context from .prepare_session_data import prepare_session_data -from .set_cached_response import set_cached_response from .update_session import update_session diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index 411349ad3..ee2ba3fdd 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -232,6 +232,7 @@ def wrap_in_class( cls: Type[ModelT] | Callable[..., ModelT], one: bool = False, transform: Callable[[dict], dict] | None = None, + _kind: str | None = None, ): def decorator(func: Callable[P, pd.DataFrame]): @wraps(func) @@ -239,6 +240,9 @@ def wrapper(*args: P.args, **kwargs: P.kwargs) -> ModelT | list[ModelT]: df = func(*args, **kwargs) # Convert df to list of dicts + if _kind: + df = df[df["_kind"] == _kind] + data = df.to_dict(orient="records") nonlocal transform diff --git a/agents-api/agents_api/routers/sessions/chat.py b/agents-api/agents_api/routers/sessions/chat.py index e6103c15e..8d0355de2 100644 --- a/agents-api/agents_api/routers/sessions/chat.py +++ b/agents-api/agents_api/routers/sessions/chat.py @@ -9,76 +9,20 @@ ChatResponse, ChunkChatResponse, CreateEntryRequest, - DocReference, - History, MessageChatResponse, ) -from ...clients import embed, litellm +from ...clients import litellm from ...common.protocol.developers import Developer from ...common.protocol.sessions import ChatContext from ...common.utils.datetime import utcnow from ...common.utils.template import render_template from ...dependencies.developer_id import get_developer_data -from ...models.docs.search_docs_hybrid import search_docs_hybrid +from ...models.chat.gather_messages import gather_messages +from ...models.chat.prepare_chat_context import prepare_chat_context from ...models.entry.create_entries import create_entries -from ...models.entry.get_history import get_history -from ...models.session.prepare_chat_context import prepare_chat_context from .router import router -async def get_messages( - *, - developer: Developer, - session_id: UUID, - new_raw_messages: list[dict], - chat_context: ChatContext, - recall: bool, -): - assert len(new_raw_messages) > 0 - - # Get the session history - history: History = get_history( - developer_id=developer.id, - session_id=session_id, - allowed_sources=["api_request", "api_response", "tool_response", "summarizer"], - ) - - # Keep leaf nodes only - relations = history.relations - past_messages = [ - entry.model_dump() - for entry in history.entries - if entry.id not in {r.head for r in relations} - ] - - if not recall: - return past_messages, [] - - # Search matching docs - [query_embedding, *_] = await embed.embed( - inputs=[ - f"{msg.get('name') or msg['role']}: {msg['content']}" - for msg in new_raw_messages - ], - join_inputs=True, - ) - query_text = new_raw_messages[-1]["content"] - - # List all the applicable owners to search docs from - active_agent_id = chat_context.get_active_agent().id - user_ids = [user.id for user in chat_context.users] - owners = [("user", user_id) for user_id in user_ids] + [("agent", active_agent_id)] - - doc_references: list[DocReference] = search_docs_hybrid( - developer_id=developer.id, - owners=owners, - query=query_text, - query_embedding=query_embedding, - ) - - return past_messages, doc_references - - @router.post( "/sessions/{session_id}/chat", status_code=HTTP_201_CREATED, @@ -87,7 +31,7 @@ async def get_messages( async def chat( developer: Annotated[Developer, Depends(get_developer_data)], session_id: UUID, - input: ChatInput, + chat_input: ChatInput, background_tasks: BackgroundTasks, ) -> ChatResponse: # First get the chat context @@ -97,18 +41,17 @@ async def chat( ) # Merge the settings and prepare environment - chat_context.merge_settings(input) + chat_context.merge_settings(chat_input) settings: dict = chat_context.settings.model_dump() env: dict = chat_context.get_chat_environment() - new_raw_messages = [msg.model_dump() for msg in input.messages] + new_raw_messages = [msg.model_dump() for msg in chat_input.messages] # Render the messages - past_messages, doc_references = await get_messages( + past_messages, doc_references = await gather_messages( developer=developer, session_id=session_id, - new_raw_messages=new_raw_messages, chat_context=chat_context, - recall=input.recall, + chat_input=chat_input, ) env["docs"] = doc_references @@ -118,7 +61,7 @@ async def chat( # Get the tools tools = settings.get("tools") or chat_context.get_active_tools() - # Truncate the messages if necessary + # TODO: Truncate the messages if necessary if chat_context.session.context_overflow == "truncate": # messages = messages[-settings["max_tokens"] :] raise NotImplementedError("Truncation is not yet implemented") @@ -133,11 +76,12 @@ async def chat( ) # Save the input and the response to the session history - if input.save: - # TODO: Count the number of tokens before saving it to the session - + if chat_input.save: new_entries = [ - CreateEntryRequest(**msg, source="api_request") for msg in new_messages + CreateEntryRequest.from_model_input( + model=settings["model"], **msg, source="api_request" + ) + for msg in new_messages ] background_tasks.add_task( @@ -156,7 +100,9 @@ async def chat( raise NotImplementedError("Adaptive context is not yet implemented") # Return the response - chat_response_class = ChunkChatResponse if input.stream else MessageChatResponse + chat_response_class = ( + ChunkChatResponse if chat_input.stream else MessageChatResponse + ) chat_response: ChatResponse = chat_response_class( id=uuid4(), created_at=utcnow(), diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index b8c0a42b7..6a29c9f4a 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2149,13 +2149,13 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.2.30" +version = "0.2.31" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.30-py3-none-any.whl", hash = "sha256:ea7eccb9566dd51b2b74bd292c4239d843a77cdba8ffae2b5edf7000d70d4194"}, - {file = "langchain_core-0.2.30.tar.gz", hash = "sha256:552ec586698140062cd299a83bad7e308f925b496d306b62529579c6fb122f7a"}, + {file = "langchain_core-0.2.31-py3-none-any.whl", hash = "sha256:b4daf5ddc23c0c3d8c5fd1a6c118f95fb5d0f96067b43f2c5935e1cd572e4374"}, + {file = "langchain_core-0.2.31.tar.gz", hash = "sha256:afb2089d4c10842d2477dc5cfa9ae9feb415c1421c6ef9aa608fea879ee41769"}, ] [package.dependencies] @@ -2255,13 +2255,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.9" +version = "1.43.12" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.9-py3-none-any.whl", hash = "sha256:54253281139e61f130b7e1a613a11f7a5ee896c2ee8536b0ca9a5ffbfce4c5f0"}, - {file = "litellm-1.43.9.tar.gz", hash = "sha256:c397a14c9b851f007f09c99e5a28606f7f122fdb4ae954931220f60e9edc6918"}, + {file = "litellm-1.43.12-py3-none-any.whl", hash = "sha256:f2c5f498a079df6eb8448ac41704367a389ea679a22e195c79b7963ede5cc462"}, + {file = "litellm-1.43.12.tar.gz", hash = "sha256:719eca58904942465dfd827e9d8f317112996ef481db71f9562f5263a553c74a"}, ] [package.dependencies] From 313a3120df3d782724b1b998973b14332c87bba4 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Wed, 14 Aug 2024 18:55:08 -0400 Subject: [PATCH 051/110] feat(agents-api): Add token count support using litellm Signed-off-by: Diwank Tomer --- .../agents_api/activities/summarization.py | 10 ---- .../agents_api/activities/truncation.py | 7 +-- agents-api/agents_api/autogen/Entries.py | 4 +- .../agents_api/autogen/openapi_model.py | 59 ++++++++++++++++++- .../agents_api/common/protocol/entries.py | 56 ------------------ .../agents_api/routers/tasks/__init__.py | 3 +- .../routers/tasks/get_execution_details.py | 2 - agents-api/tests/test_chat_routes.py | 44 ++++++++++---- agents-api/tests/test_entry_queries.py | 32 +++++----- agents-api/tests/test_session_queries.py | 22 ------- agents-api/tests/test_task_routes.py | 1 + .../julep/api/types/entries_base_entry.py | 4 +- sdks/python/poetry.lock | 16 ++--- sdks/ts/src/api/models/Entries_BaseEntry.ts | 4 +- sdks/ts/src/api/schemas/$Entries_BaseEntry.ts | 2 + typespec/entries/models.tsp | 4 +- 16 files changed, 127 insertions(+), 143 deletions(-) delete mode 100644 agents-api/agents_api/common/protocol/entries.py diff --git a/agents-api/agents_api/activities/summarization.py b/agents-api/agents_api/activities/summarization.py index 581dcdb00..8a45927ee 100644 --- a/agents-api/agents_api/activities/summarization.py +++ b/agents-api/agents_api/activities/summarization.py @@ -1,23 +1,13 @@ #!/usr/bin/env python3 -import asyncio -from textwrap import dedent -from typing import Callable -from uuid import UUID import pandas as pd from temporalio import activity -# from agents_api.common.protocol.entries import Entry # from agents_api.models.entry.entries_summarization import ( # entries_summarization_query, # get_toplevel_entries_query, # ) -from agents_api.rec_sum.entities import get_entities -from agents_api.rec_sum.summarize import summarize_messages -from agents_api.rec_sum.trim import trim_messages - -from ..env import summarization_model_name # TODO: remove stubs diff --git a/agents-api/agents_api/activities/truncation.py b/agents-api/agents_api/activities/truncation.py index 353e4b570..7f381ac0f 100644 --- a/agents-api/agents_api/activities/truncation.py +++ b/agents-api/agents_api/activities/truncation.py @@ -2,9 +2,7 @@ from temporalio import activity -# from agents_api.autogen.openapi_model import Role -from agents_api.common.protocol.entries import Entry -from agents_api.models.entry.delete_entries import delete_entries +from agents_api.autogen.openapi_model import Entry # from agents_api.models.entry.entries_summarization import get_toplevel_entries_query @@ -13,8 +11,7 @@ def get_extra_entries(messages: list[Entry], token_count_threshold: int) -> list if not len(messages): return messages - result: list[UUID] = [] - token_cnt, offset = 0, 0 + _token_cnt, _offset = 0, 0 # if messages[0].role == Role.system: # token_cnt, offset = messages[0].token_count, 1 diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index b9921daa4..00070bd71 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -45,8 +45,8 @@ class BaseEntry(BaseModel): source: Literal[ "api_request", "api_response", "tool_response", "internal", "summarizer", "meta" ] - tokenizer: str | None = None - token_count: int | None = None + tokenizer: str + token_count: int timestamp: Annotated[float, Field(ge=0.0)] """ This is the time that this event refers to. diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index 16a57ac31..24640c46d 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -1,7 +1,9 @@ # ruff: noqa: F401, F403, F405 -from typing import Annotated, Generic, TypeVar +from typing import Annotated, Generic, Self, Type, TypeVar from uuid import UUID +from litellm.utils import _select_tokenizer as select_tokenizer +from litellm.utils import token_counter from pydantic import AwareDatetime, Field from pydantic_partial import create_partial_model @@ -34,7 +36,34 @@ "metadata", ) -ChatMLRole = BaseEntry.model_fields["role"].annotation +ChatMLRole = Literal[ + "user", + "assistant", + "system", + "function", + "function_response", + "function_call", + "auto", +] + +ChatMLContent = ( + list[ChatMLTextContentPart | ChatMLImageContentPart] + | Tool + | ChosenToolCall + | str + | ToolResponse + | list[ + list[ChatMLTextContentPart | ChatMLImageContentPart] + | Tool + | ChosenToolCall + | str + | ToolResponse + ] +) + +ChatMLSource = Literal[ + "api_request", "api_response", "tool_response", "internal", "summarizer", "meta" +] class CreateEntryRequest(BaseEntry): @@ -42,6 +71,32 @@ class CreateEntryRequest(BaseEntry): float, Field(ge=0.0, default_factory=lambda: utcnow().timestamp()) ] + @classmethod + def from_model_input( + cls: Type[Self], + model: str, + *, + role: ChatMLRole, + content: ChatMLContent, + name: str | None = None, + source: ChatMLSource, + **kwargs: dict, + ) -> Self: + tokenizer: dict = select_tokenizer(model=model) + token_count = token_counter( + model=model, messages=[{"role": role, "content": content, "name": name}] + ) + + return cls( + role=role, + content=content, + name=name, + source=source, + tokenizer=tokenizer["type"], + token_count=token_count, + **kwargs, + ) + def make_session( *, diff --git a/agents-api/agents_api/common/protocol/entries.py b/agents-api/agents_api/common/protocol/entries.py deleted file mode 100644 index 18d63f583..000000000 --- a/agents-api/agents_api/common/protocol/entries.py +++ /dev/null @@ -1,56 +0,0 @@ -import json -from typing import Literal -from uuid import UUID - -from pydantic import Field, computed_field - -from ...autogen.openapi_model import ( - ChatMLImageContentPart, - ChatMLTextContentPart, -) -from ...autogen.openapi_model import ( - Entry as BaseEntry, -) - -EntrySource = Literal["api_request", "api_response", "internal", "summarizer"] -Tokenizer = Literal["character_count"] - - -LOW_IMAGE_TOKEN_COUNT = 85 -HIGH_IMAGE_TOKEN_COUNT = 85 + 4 * 170 - - -class Entry(BaseEntry): - """Represents an entry in the system, encapsulating all necessary details such as ID, session ID, source, role, and content among others.""" - - session_id: UUID - token_count: int - tokenizer: str = Field(default="character_count") - - # TODO: Replace this with a proper implementation. - - # @computed_field - # @property - # def token_count(self) -> int: - # """Calculates the token count based on the content's character count. The tokenizer 'character_count' divides the length of the content by 3.5 to estimate the token count. Raises NotImplementedError for unknown tokenizers.""" - # if self.tokenizer == "character_count": - # content_length = 0 - # if isinstance(self.content, str): - # content_length = len(self.content) - # elif isinstance(self.content, dict): - # content_length = len(json.dumps(self.content)) - # elif isinstance(self.content, list): - # for part in self.content: - # if isinstance(part, ChatMLTextContentPart): - # content_length += len(part.text) - # elif isinstance(part, ChatMLImageContentPart): - # content_length += ( - # LOW_IMAGE_TOKEN_COUNT - # if part.image_url.detail == "low" - # else HIGH_IMAGE_TOKEN_COUNT - # ) - - # # Divide the content length by 3.5 to estimate token count based on character count. - # return int(content_length // 3.5) - - # raise NotImplementedError(f"Unknown tokenizer: {self.tokenizer}") diff --git a/agents-api/agents_api/routers/tasks/__init__.py b/agents-api/agents_api/routers/tasks/__init__.py index 8d67171c0..66621b34c 100644 --- a/agents-api/agents_api/routers/tasks/__init__.py +++ b/agents-api/agents_api/routers/tasks/__init__.py @@ -1,3 +1,4 @@ +# ruff: noqa: F401, F403, F405 from .create_task import create_task from .create_task_execution import create_task_execution from .get_execution_details import get_execution_details @@ -5,5 +6,5 @@ from .list_task_executions import list_task_executions from .list_tasks import list_tasks from .patch_execution import patch_execution -from .router import router # noqa: F401 +from .router import router from .update_execution import update_execution diff --git a/agents-api/agents_api/routers/tasks/get_execution_details.py b/agents-api/agents_api/routers/tasks/get_execution_details.py index 1377d7b22..6a9a01caa 100644 --- a/agents-api/agents_api/routers/tasks/get_execution_details.py +++ b/agents-api/agents_api/routers/tasks/get_execution_details.py @@ -1,5 +1,3 @@ -from uuid import uuid4 - from fastapi import HTTPException, status from pydantic import UUID4 diff --git a/agents-api/tests/test_chat_routes.py b/agents-api/tests/test_chat_routes.py index ccf91c89e..1e8065d9d 100644 --- a/agents-api/tests/test_chat_routes.py +++ b/agents-api/tests/test_chat_routes.py @@ -2,11 +2,12 @@ from ward import test -from agents_api.autogen.Sessions import CreateSessionRequest +from agents_api.autogen.openapi_model import ChatInput, CreateSessionRequest from agents_api.clients import embed, litellm +from agents_api.common.protocol.sessions import ChatContext +from agents_api.models.chat.gather_messages import gather_messages +from agents_api.models.chat.prepare_chat_context import prepare_chat_context from agents_api.models.session.create_session import create_session -from agents_api.models.session.prepare_chat_context import prepare_chat_context -from agents_api.routers.sessions.chat import get_messages from tests.fixtures import ( cozo_client, make_request, @@ -28,7 +29,7 @@ async def _( assert (await embed.embed())[0][0] == 1.0 -@test("chat: check that non-recall get_messages works") +@test("chat: check that non-recall gather_messages works") async def _( developer=test_developer, client=cozo_client, @@ -49,14 +50,13 @@ async def _( session_id = session.id - new_raw_messages = [{"role": "user", "content": "hello"}] + messages = [{"role": "user", "content": "hello"}] - past_messages, doc_references = await get_messages( + past_messages, doc_references = await gather_messages( developer=developer, session_id=session_id, - new_raw_messages=new_raw_messages, chat_context=chat_context, - recall=False, + chat_input=ChatInput(messages=messages, recall=False), ) assert isinstance(past_messages, list) @@ -68,7 +68,7 @@ async def _( embed.assert_not_called() -@test("chat: check that get_messages works") +@test("chat: check that gather_messages works") async def _( developer=test_developer, client=cozo_client, @@ -89,14 +89,13 @@ async def _( session_id = session.id - new_raw_messages = [{"role": "user", "content": "hello"}] + messages = [{"role": "user", "content": "hello"}] - past_messages, doc_references = await get_messages( + past_messages, doc_references = await gather_messages( developer=developer, session_id=session_id, - new_raw_messages=new_raw_messages, chat_context=chat_context, - recall=True, + chat_input=ChatInput(messages=messages, recall=True), ) assert isinstance(past_messages, list) @@ -136,3 +135,22 @@ async def _( # Check that both mocks were called at least once embed.assert_called() acompletion.assert_called() + + +@test("model: prepare chat context") +def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, + session=test_session, + tool=test_tool, + user=test_user, +): + context = prepare_chat_context( + developer_id=developer_id, + session_id=session.id, + client=client, + ) + + assert isinstance(context, ChatContext) + assert len(context.toolsets) > 0 diff --git a/agents-api/tests/test_entry_queries.py b/agents-api/tests/test_entry_queries.py index ce8b6a00d..c6b7150b6 100644 --- a/agents-api/tests/test_entry_queries.py +++ b/agents-api/tests/test_entry_queries.py @@ -27,8 +27,8 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): Verifies that the entry can be successfully added using the create_entries function. """ - test_entry = CreateEntryRequest( - session_id=session.id, + test_entry = CreateEntryRequest.from_model_input( + model=MODEL, role="user", source="internal", content="test entry content", @@ -50,8 +50,8 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): Verifies that the entry can be successfully added using the create_entries function. """ - test_entry = CreateEntryRequest( - session_id=session.id, + test_entry = CreateEntryRequest.from_model_input( + model=MODEL, role="user", source="internal", content="test entry content", @@ -84,15 +84,15 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): Verifies that entries matching specific criteria can be successfully retrieved. """ - test_entry = CreateEntryRequest( - session_id=session.id, + test_entry = CreateEntryRequest.from_model_input( + model=MODEL, role="user", source="api_request", content="test entry content", ) - internal_entry = CreateEntryRequest( - session_id=session.id, + internal_entry = CreateEntryRequest.from_model_input( + model=MODEL, role="user", content="test entry content", source="internal", @@ -122,15 +122,15 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): Verifies that entries matching specific criteria can be successfully retrieved. """ - test_entry = CreateEntryRequest( - session_id=session.id, + test_entry = CreateEntryRequest.from_model_input( + model=MODEL, role="user", source="api_request", content="test entry content", ) - internal_entry = CreateEntryRequest( - session_id=session.id, + internal_entry = CreateEntryRequest.from_model_input( + model=MODEL, role="user", content="test entry content", source="internal", @@ -161,15 +161,15 @@ def _(client=cozo_client, developer_id=test_developer_id, session=test_session): Verifies that entries can be successfully deleted using the delete_entries function. """ - test_entry = CreateEntryRequest( - session_id=session.id, + test_entry = CreateEntryRequest.from_model_input( + model=MODEL, role="user", source="api_request", content="test entry content", ) - internal_entry = CreateEntryRequest( - session_id=session.id, + internal_entry = CreateEntryRequest.from_model_input( + model=MODEL, role="user", content="internal entry content", source="internal", diff --git a/agents-api/tests/test_session_queries.py b/agents-api/tests/test_session_queries.py index 94b5bbbe4..7eae8485f 100644 --- a/agents-api/tests/test_session_queries.py +++ b/agents-api/tests/test_session_queries.py @@ -5,19 +5,16 @@ from agents_api.autogen.openapi_model import CreateOrUpdateSessionRequest, Session from agents_api.autogen.Sessions import CreateSessionRequest -from agents_api.common.protocol.sessions import ChatContext from agents_api.models.session.create_or_update_session import create_or_update_session from agents_api.models.session.create_session import create_session from agents_api.models.session.delete_session import delete_session from agents_api.models.session.get_session import get_session from agents_api.models.session.list_sessions import list_sessions -from agents_api.models.session.prepare_chat_context import prepare_chat_context from tests.fixtures import ( cozo_client, test_agent, test_developer_id, test_session, - test_tool, test_user, ) @@ -146,22 +143,3 @@ def _( assert result is not None assert isinstance(result, Session) assert result.id == session_id - - -@test("model: prepare chat context") -def _( - client=cozo_client, - developer_id=test_developer_id, - agent=test_agent, - session=test_session, - tool=test_tool, - user=test_user, -): - context = prepare_chat_context( - developer_id=developer_id, - session_id=session.id, - client=client, - ) - - assert isinstance(context, ChatContext) - assert len(context.toolsets) > 0 diff --git a/agents-api/tests/test_task_routes.py b/agents-api/tests/test_task_routes.py index b790240c4..80aae9e7f 100644 --- a/agents-api/tests/test_task_routes.py +++ b/agents-api/tests/test_task_routes.py @@ -1,4 +1,5 @@ # Tests for task routes + from uuid import uuid4 from ward import test diff --git a/sdks/python/julep/api/types/entries_base_entry.py b/sdks/python/julep/api/types/entries_base_entry.py index cd9a8158f..a12c99c1c 100644 --- a/sdks/python/julep/api/types/entries_base_entry.py +++ b/sdks/python/julep/api/types/entries_base_entry.py @@ -15,8 +15,8 @@ class EntriesBaseEntry(pydantic_v1.BaseModel): name: typing.Optional[str] = None content: EntriesBaseEntryContent source: EntriesBaseEntrySource - tokenizer: typing.Optional[str] = None - token_count: typing.Optional[int] = None + tokenizer: str + token_count: int timestamp: float = pydantic_v1.Field() """ This is the time that this event refers to. diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 060ffa16f..fe5a1519d 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -848,21 +848,21 @@ test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "p [[package]] name = "importlib-resources" -version = "6.4.0" +version = "6.4.1" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.4.0-py3-none-any.whl", hash = "sha256:50d10f043df931902d4194ea07ec57960f66a80449ff867bfe782b4c486ba78c"}, - {file = "importlib_resources-6.4.0.tar.gz", hash = "sha256:cdb2b453b8046ca4e3798eb1d84f3cce1446a0e8e7b5ef4efb600f19fc398145"}, + {file = "importlib_resources-6.4.1-py3-none-any.whl", hash = "sha256:8fbee7ba7376ca7c47ce8d31b96b93d8787349845f01ebdbee5ef90409035234"}, + {file = "importlib_resources-6.4.1.tar.gz", hash = "sha256:5ede8acf5d752abda46fb6922a4a6ab782b6d904dfd362bf2d8b857eee1759d9"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] -testing = ["jaraco.test (>=5.4)", "pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] [[package]] name = "ipykernel" @@ -2309,13 +2309,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyright" -version = "1.1.375" +version = "1.1.376" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.375-py3-none-any.whl", hash = "sha256:4c5e27eddeaee8b41cc3120736a1dda6ae120edf8523bb2446b6073a52f286e3"}, - {file = "pyright-1.1.375.tar.gz", hash = "sha256:7765557b0d6782b2fadabff455da2014476404c9e9214f49977a4e49dec19a0f"}, + {file = "pyright-1.1.376-py3-none-any.whl", hash = "sha256:0f2473b12c15c46b3207f0eec224c3cea2bdc07cd45dd4a037687cbbca0fbeff"}, + {file = "pyright-1.1.376.tar.gz", hash = "sha256:bffd63b197cd0810395bb3245c06b01f95a85ddf6bfa0e5644ed69c841e954dd"}, ] [package.dependencies] diff --git a/sdks/ts/src/api/models/Entries_BaseEntry.ts b/sdks/ts/src/api/models/Entries_BaseEntry.ts index e77df13c3..d397d851e 100644 --- a/sdks/ts/src/api/models/Entries_BaseEntry.ts +++ b/sdks/ts/src/api/models/Entries_BaseEntry.ts @@ -17,8 +17,8 @@ export type Entries_BaseEntry = { | "internal" | "summarizer" | "meta"; - tokenizer?: string; - token_count?: number; + tokenizer: string; + token_count: number; /** * This is the time that this event refers to. */ diff --git a/sdks/ts/src/api/schemas/$Entries_BaseEntry.ts b/sdks/ts/src/api/schemas/$Entries_BaseEntry.ts index 6aad5206a..bcdb7122e 100644 --- a/sdks/ts/src/api/schemas/$Entries_BaseEntry.ts +++ b/sdks/ts/src/api/schemas/$Entries_BaseEntry.ts @@ -37,9 +37,11 @@ export const $Entries_BaseEntry = { }, tokenizer: { type: "string", + isRequired: true, }, token_count: { type: "number", + isRequired: true, format: "uint16", }, timestamp: { diff --git a/typespec/entries/models.tsp b/typespec/entries/models.tsp index f9050b4f4..fba6803df 100644 --- a/typespec/entries/models.tsp +++ b/typespec/entries/models.tsp @@ -91,8 +91,8 @@ model BaseEntry { content: EntryContent | EntryContent[]; source: entrySource; - tokenizer?: string; - token_count?: uint16; + tokenizer: string; + token_count: uint16; /** This is the time that this event refers to. */ @minValue(0) From 152c29a9089120abf8985a8ce22d9745d704c0b6 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Wed, 14 Aug 2024 19:49:49 -0400 Subject: [PATCH 052/110] feat(agents-api): Minor modifier to models Signed-off-by: Diwank Tomer --- agents-api/agents_api/models/agent/create_agent.py | 5 ++++- agents-api/agents_api/models/agent/delete_agent.py | 1 + agents-api/agents_api/models/agent/patch_agent.py | 1 + agents-api/agents_api/models/agent/update_agent.py | 1 + agents-api/agents_api/models/docs/delete_doc.py | 1 + agents-api/agents_api/models/docs/embed_snippets.py | 1 + agents-api/agents_api/models/entry/create_entries.py | 2 +- agents-api/agents_api/models/entry/delete_entries.py | 1 + agents-api/agents_api/models/execution/create_execution.py | 1 + .../models/execution/create_execution_transition.py | 5 ++++- agents-api/agents_api/models/execution/update_execution.py | 1 + agents-api/agents_api/models/session/create_session.py | 1 + agents-api/agents_api/models/session/delete_session.py | 1 + agents-api/agents_api/models/session/patch_session.py | 1 + agents-api/agents_api/models/session/update_session.py | 1 + agents-api/agents_api/models/task/create_task.py | 2 +- agents-api/agents_api/models/task/delete_task.py | 1 + agents-api/agents_api/models/task/patch_task.py | 1 + agents-api/agents_api/models/tools/create_tools.py | 1 + agents-api/agents_api/models/tools/delete_tool.py | 1 + agents-api/agents_api/models/tools/patch_tool.py | 1 + agents-api/agents_api/models/tools/update_tool.py | 1 + agents-api/agents_api/models/user/create_user.py | 7 ++++++- agents-api/agents_api/models/user/delete_user.py | 1 + agents-api/agents_api/models/user/patch_user.py | 1 + agents-api/agents_api/models/user/update_user.py | 1 + 26 files changed, 37 insertions(+), 5 deletions(-) diff --git a/agents-api/agents_api/models/agent/create_agent.py b/agents-api/agents_api/models/agent/create_agent.py index 6b649afbb..a4b408c78 100644 --- a/agents-api/agents_api/models/agent/create_agent.py +++ b/agents-api/agents_api/models/agent/create_agent.py @@ -34,7 +34,10 @@ } ) @wrap_in_class( - Agent, one=True, transform=lambda d: {"id": UUID(d.pop("agent_id")), **d} + Agent, + one=True, + transform=lambda d: {"id": UUID(d.pop("agent_id")), **d}, + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/agent/delete_agent.py b/agents-api/agents_api/models/agent/delete_agent.py index 27e3ecc1c..e1efd7333 100644 --- a/agents-api/agents_api/models/agent/delete_agent.py +++ b/agents-api/agents_api/models/agent/delete_agent.py @@ -41,6 +41,7 @@ "deleted_at": utcnow(), "jobs": [], }, + _kind="deleted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/agent/patch_agent.py b/agents-api/agents_api/models/agent/patch_agent.py index 3a374f91a..87f5db046 100644 --- a/agents-api/agents_api/models/agent/patch_agent.py +++ b/agents-api/agents_api/models/agent/patch_agent.py @@ -29,6 +29,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["agent_id"], "jobs": [], **d}, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/agent/update_agent.py b/agents-api/agents_api/models/agent/update_agent.py index 9cd8f04d9..95116e1a2 100644 --- a/agents-api/agents_api/models/agent/update_agent.py +++ b/agents-api/agents_api/models/agent/update_agent.py @@ -28,6 +28,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["agent_id"], "jobs": [], **d}, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/docs/delete_doc.py b/agents-api/agents_api/models/docs/delete_doc.py index 9dcaf0f33..e7a02f3d9 100644 --- a/agents-api/agents_api/models/docs/delete_doc.py +++ b/agents-api/agents_api/models/docs/delete_doc.py @@ -32,6 +32,7 @@ "deleted_at": utcnow(), "jobs": [], }, + _kind="deleted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/docs/embed_snippets.py b/agents-api/agents_api/models/docs/embed_snippets.py index 89750192a..64bf8e130 100644 --- a/agents-api/agents_api/models/docs/embed_snippets.py +++ b/agents-api/agents_api/models/docs/embed_snippets.py @@ -30,6 +30,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["doc_id"], "updated_at": utcnow(), "jobs": []}, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/entry/create_entries.py b/agents-api/agents_api/models/entry/create_entries.py index 31c8b4d01..e71a81c7a 100644 --- a/agents-api/agents_api/models/entry/create_entries.py +++ b/agents-api/agents_api/models/entry/create_entries.py @@ -90,7 +90,7 @@ def create_entries( TypeError: partialclass(HTTPException, status_code=400), } ) -@wrap_in_class(Relation) +@wrap_in_class(Relation, _kind="inserted") @cozo_query @beartype def add_entry_relations( diff --git a/agents-api/agents_api/models/entry/delete_entries.py b/agents-api/agents_api/models/entry/delete_entries.py index f64bfbf73..5bf34c721 100644 --- a/agents-api/agents_api/models/entry/delete_entries.py +++ b/agents-api/agents_api/models/entry/delete_entries.py @@ -34,6 +34,7 @@ "deleted_at": utcnow(), "jobs": [], }, + _kind="deleted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/execution/create_execution.py b/agents-api/agents_api/models/execution/create_execution.py index 4e85a1db7..155d18b16 100644 --- a/agents-api/agents_api/models/execution/create_execution.py +++ b/agents-api/agents_api/models/execution/create_execution.py @@ -31,6 +31,7 @@ Execution, one=True, transform=lambda d: {"id": d["execution_id"], **d}, + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/execution/create_execution_transition.py b/agents-api/agents_api/models/execution/create_execution_transition.py index df538be79..701ffd3cc 100644 --- a/agents-api/agents_api/models/execution/create_execution_transition.py +++ b/agents-api/agents_api/models/execution/create_execution_transition.py @@ -53,7 +53,10 @@ } ) @wrap_in_class( - Transition, transform=lambda d: {"id": d["transition_id"], **d}, one=True + Transition, + transform=lambda d: {"id": d["transition_id"], **d}, + one=True, + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/execution/update_execution.py b/agents-api/agents_api/models/execution/update_execution.py index 1424c4ef2..4386b9502 100644 --- a/agents-api/agents_api/models/execution/update_execution.py +++ b/agents-api/agents_api/models/execution/update_execution.py @@ -31,6 +31,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["execution_id"], "jobs": [], **d}, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/session/create_session.py b/agents-api/agents_api/models/session/create_session.py index aab7fddac..7bb0576c9 100644 --- a/agents-api/agents_api/models/session/create_session.py +++ b/agents-api/agents_api/models/session/create_session.py @@ -36,6 +36,7 @@ "updated_at": (d.pop("updated_at")[0]), **d, }, + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/session/delete_session.py b/agents-api/agents_api/models/session/delete_session.py index e6b0037ca..71f153fb1 100644 --- a/agents-api/agents_api/models/session/delete_session.py +++ b/agents-api/agents_api/models/session/delete_session.py @@ -34,6 +34,7 @@ "deleted_at": utcnow(), "jobs": [], }, + _kind="deleted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/session/patch_session.py b/agents-api/agents_api/models/session/patch_session.py index f16a53f44..131d82bec 100644 --- a/agents-api/agents_api/models/session/patch_session.py +++ b/agents-api/agents_api/models/session/patch_session.py @@ -46,6 +46,7 @@ "jobs": [], **d, }, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/session/update_session.py b/agents-api/agents_api/models/session/update_session.py index 5f7789f29..c4296634a 100644 --- a/agents-api/agents_api/models/session/update_session.py +++ b/agents-api/agents_api/models/session/update_session.py @@ -44,6 +44,7 @@ "jobs": [], **d, }, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/task/create_task.py b/agents-api/agents_api/models/task/create_task.py index 17f991e7b..61f0bca72 100644 --- a/agents-api/agents_api/models/task/create_task.py +++ b/agents-api/agents_api/models/task/create_task.py @@ -32,7 +32,7 @@ TypeError: partialclass(HTTPException, status_code=400), } ) -@wrap_in_class(spec_to_task, one=True) +@wrap_in_class(spec_to_task, one=True, _kind="inserted") @cozo_query @beartype def create_task( diff --git a/agents-api/agents_api/models/task/delete_task.py b/agents-api/agents_api/models/task/delete_task.py index 28c3defb3..1eb780057 100644 --- a/agents-api/agents_api/models/task/delete_task.py +++ b/agents-api/agents_api/models/task/delete_task.py @@ -33,6 +33,7 @@ "deleted_at": utcnow(), **d, }, + _kind="deleted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/task/patch_task.py b/agents-api/agents_api/models/task/patch_task.py index 93d57f32f..4be73c025 100644 --- a/agents-api/agents_api/models/task/patch_task.py +++ b/agents-api/agents_api/models/task/patch_task.py @@ -39,6 +39,7 @@ "updated_at": d["updated_at_ms"][0] / 1000, **d, }, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/tools/create_tools.py b/agents-api/agents_api/models/tools/create_tools.py index 597268863..0c034d4d2 100644 --- a/agents-api/agents_api/models/tools/create_tools.py +++ b/agents-api/agents_api/models/tools/create_tools.py @@ -32,6 +32,7 @@ d["type"]: d.pop("spec"), **d, }, + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/tools/delete_tool.py b/agents-api/agents_api/models/tools/delete_tool.py index ad6a9d4f5..e6d00498a 100644 --- a/agents-api/agents_api/models/tools/delete_tool.py +++ b/agents-api/agents_api/models/tools/delete_tool.py @@ -28,6 +28,7 @@ ResourceDeletedResponse, one=True, transform=lambda d: {"id": d["tool_id"], "deleted_at": utcnow(), "jobs": [], **d}, + _kind="deleted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/tools/patch_tool.py b/agents-api/agents_api/models/tools/patch_tool.py index acdbcf0b4..68bea3ee5 100644 --- a/agents-api/agents_api/models/tools/patch_tool.py +++ b/agents-api/agents_api/models/tools/patch_tool.py @@ -28,6 +28,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["tool_id"], "jobs": [], **d}, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/tools/update_tool.py b/agents-api/agents_api/models/tools/update_tool.py index 1376aba37..b8a395a94 100644 --- a/agents-api/agents_api/models/tools/update_tool.py +++ b/agents-api/agents_api/models/tools/update_tool.py @@ -31,6 +31,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["tool_id"], "jobs": [], **d}, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/user/create_user.py b/agents-api/agents_api/models/user/create_user.py index f9c5af19e..4115b3326 100644 --- a/agents-api/agents_api/models/user/create_user.py +++ b/agents-api/agents_api/models/user/create_user.py @@ -32,7 +32,12 @@ TypeError: partialclass(HTTPException, status_code=400), } ) -@wrap_in_class(User, one=True, transform=lambda d: {"id": UUID(d.pop("user_id")), **d}) +@wrap_in_class( + User, + one=True, + transform=lambda d: {"id": UUID(d.pop("user_id")), **d}, + _kind="inserted", +) @cozo_query @beartype def create_user( diff --git a/agents-api/agents_api/models/user/delete_user.py b/agents-api/agents_api/models/user/delete_user.py index 04f96e630..b9ebc2db7 100644 --- a/agents-api/agents_api/models/user/delete_user.py +++ b/agents-api/agents_api/models/user/delete_user.py @@ -36,6 +36,7 @@ "deleted_at": utcnow(), "jobs": [], }, + _kind="deleted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/user/patch_user.py b/agents-api/agents_api/models/user/patch_user.py index fdad01fe7..89fe4db33 100644 --- a/agents-api/agents_api/models/user/patch_user.py +++ b/agents-api/agents_api/models/user/patch_user.py @@ -31,6 +31,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["user_id"], "jobs": [], **d}, + _kind="replaced", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/user/update_user.py b/agents-api/agents_api/models/user/update_user.py index 7132e13a0..88f24e6e0 100644 --- a/agents-api/agents_api/models/user/update_user.py +++ b/agents-api/agents_api/models/user/update_user.py @@ -28,6 +28,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["user_id"], "jobs": [], **d}, + _kind="replaced", ) @cozo_query @beartype From e97b6fb093af41fe2e78dab433594a77220d9eed Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Wed, 14 Aug 2024 20:56:49 -0400 Subject: [PATCH 053/110] fix(agents-api): Minor fixes Signed-off-by: Diwank Tomer --- agents-api/agents_api/activities/__init__.py | 10 +--- agents-api/agents_api/clients/temporal.py | 46 ------------------- .../agents_api/routers/docs/create_doc.py | 46 ++++++++++++++++++- agents-api/poetry.lock | 14 +++--- agents-api/tests/fixtures.py | 12 +++++ agents-api/tests/test_activities.py | 24 +++++----- agents-api/tests/test_docs_routes.py | 8 ++-- sdks/python/poetry.lock | 6 +-- 8 files changed, 84 insertions(+), 82 deletions(-) diff --git a/agents-api/agents_api/activities/__init__.py b/agents-api/agents_api/activities/__init__.py index 49722a7d5..c641ab7b9 100644 --- a/agents-api/agents_api/activities/__init__.py +++ b/agents-api/agents_api/activities/__init__.py @@ -1,13 +1,5 @@ """ -The `activities` module within the agents-api package is designed to facilitate various activities related to agent interactions. This includes handling memory management, generating insights from dialogues, summarizing relationships, and more. Each file within the module offers specific functionality: - -- `co_density.py`: Conducts cognitive density analysis to generate concise, entity-dense summaries. -- `dialog_insights.py`: Extracts insights from dialogues, identifying details that participants might find interesting. -- `mem_mgmt.py`: Manages memory by updating and incorporating new personality information from dialogues. -- `mem_rating.py`: Rates memories based on their poignancy and importance. -- `relationship_summary.py`: Summarizes the relationship between individuals based on provided statements. -- `salient_questions.py`: Identifies salient questions from a set of statements. -- `summarization.py`: Summarizes dialogues and updates memory based on the conversation context. +The `activities` module within the agents-api package is designed to facilitate various activities related to agent interactions. This includes handling memory management, generating insights from dialogues, summarizing relationships, and more. This module plays a crucial role in enhancing the capabilities of agents by providing them with the tools to understand and process information more effectively. """ diff --git a/agents-api/agents_api/clients/temporal.py b/agents-api/agents_api/clients/temporal.py index 72a5056c8..29ceedded 100644 --- a/agents-api/agents_api/clients/temporal.py +++ b/agents-api/agents_api/clients/temporal.py @@ -34,52 +34,6 @@ async def get_client( ) -async def run_summarization_task( - session_id: UUID, job_id: UUID, client: Client | None = None -): - client = client or (await get_client()) - - await client.execute_workflow( - "SummarizationWorkflow", - args=[str(session_id)], - task_queue="memory-task-queue", - id=str(job_id), - ) - - -async def run_embed_docs_task( - doc_id: UUID, - title: str, - content: list[str], - job_id: UUID, - client: Client | None = None, -): - client = client or (await get_client()) - - await client.execute_workflow( - "EmbedDocsWorkflow", - args=[str(doc_id), title, content], - task_queue="memory-task-queue", - id=str(job_id), - ) - - -async def run_truncation_task( - token_count_threshold: int, - session_id: UUID, - job_id: UUID, - client: Client | None = None, -): - client = client or (await get_client()) - - await client.execute_workflow( - "TruncationWorkflow", - args=[str(session_id), token_count_threshold], - task_queue="memory-task-queue", - id=str(job_id), - ) - - async def run_task_execution_workflow( execution_input: ExecutionInput, job_id: UUID, diff --git a/agents-api/agents_api/routers/docs/create_doc.py b/agents-api/agents_api/routers/docs/create_doc.py index 0b43fe8eb..645f82964 100644 --- a/agents-api/agents_api/routers/docs/create_doc.py +++ b/agents-api/agents_api/routers/docs/create_doc.py @@ -1,15 +1,35 @@ from typing import Annotated +from uuid import UUID, uuid4 from fastapi import Depends from pydantic import UUID4 from starlette.status import HTTP_201_CREATED +from temporalio.client import Client as TemporalClient from ...autogen.openapi_model import CreateDocRequest, ResourceCreatedResponse +from ...clients import temporal from ...dependencies.developer_id import get_developer_id from ...models.docs.create_doc import create_doc as create_doc_query from .router import router +async def run_embed_docs_task( + doc_id: UUID, + title: str, + content: list[str], + job_id: UUID, + client: TemporalClient | None = None, +): + client = client or (await temporal.get_client()) + + await client.execute_workflow( + "EmbedDocsWorkflow", + args=[str(doc_id), title, content], + task_queue="memory-task-queue", + id=str(job_id), + ) + + @router.post("/users/{user_id}/docs", status_code=HTTP_201_CREATED, tags=["docs"]) async def create_user_doc( user_id: UUID4, @@ -23,7 +43,18 @@ async def create_user_doc( data=data, ) - return ResourceCreatedResponse(id=doc.id, created_at=doc.created_at, jobs=[]) + embed_job_id = uuid4() + + await run_embed_docs_task( + doc_id=doc.id, + title=doc.title, + content=doc.content, + job_id=embed_job_id, + ) + + return ResourceCreatedResponse( + id=doc.id, created_at=doc.created_at, jobs=[embed_job_id] + ) @router.post("/agents/{agent_id}/docs", status_code=HTTP_201_CREATED, tags=["docs"]) @@ -39,4 +70,15 @@ async def create_agent_doc( data=data, ) - return ResourceCreatedResponse(id=doc.id, created_at=doc.created_at, jobs=[]) + embed_job_id = uuid4() + + await run_embed_docs_task( + doc_id=doc.id, + title=doc.title, + content=doc.content, + job_id=embed_job_id, + ) + + return ResourceCreatedResponse( + id=doc.id, created_at=doc.created_at, jobs=[embed_job_id] + ) diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 6a29c9f4a..e4edb74b7 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2103,18 +2103,18 @@ files = [ [[package]] name = "langchain" -version = "0.2.13" +version = "0.2.14" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain-0.2.13-py3-none-any.whl", hash = "sha256:80f21e48cdada424dd2af9bbf42234fe095744cf181b31eeb63d1da7479e2783"}, - {file = "langchain-0.2.13.tar.gz", hash = "sha256:947e96ac3153a46aa6a0d8207e5f8b6794084c397f60a01bbf4bba78e6838fee"}, + {file = "langchain-0.2.14-py3-none-any.whl", hash = "sha256:eed76194ee7d9c081037a3df7868d4de90e0410b51fc1ca933a8379e464bf40c"}, + {file = "langchain-0.2.14.tar.gz", hash = "sha256:dc2aa5a58882054fb5d043c39ab8332ebd055f88f17839da68e1c7fd0a4fefe2"}, ] [package.dependencies] aiohttp = ">=3.8.3,<4.0.0" -langchain-core = ">=0.2.30,<0.3.0" +langchain-core = ">=0.2.32,<0.3.0" langchain-text-splitters = ">=0.2.0,<0.3.0" langsmith = ">=0.1.17,<0.2.0" numpy = {version = ">=1,<2", markers = "python_version < \"3.12\""} @@ -2149,13 +2149,13 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.2.31" +version = "0.2.32" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.31-py3-none-any.whl", hash = "sha256:b4daf5ddc23c0c3d8c5fd1a6c118f95fb5d0f96067b43f2c5935e1cd572e4374"}, - {file = "langchain_core-0.2.31.tar.gz", hash = "sha256:afb2089d4c10842d2477dc5cfa9ae9feb415c1421c6ef9aa608fea879ee41769"}, + {file = "langchain_core-0.2.32-py3-none-any.whl", hash = "sha256:1f5584cf0034909e35ea17010a847d4079417e0ddcb5a9eb3fbb2bd55f3268c0"}, + {file = "langchain_core-0.2.32.tar.gz", hash = "sha256:d82cdc350bbbe74261330d87056b7d9f1fb567828e9e03f708d23a48b941819e"}, ] [package.dependencies] diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 4fb53ffd7..437c95083 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -74,6 +74,18 @@ async def temporal_worker(wf_env=workflow_environment): await c +@fixture(scope="test") +def patch_temporal_get_client( + wf_env=workflow_environment, + temporal_worker=temporal_worker, +): + mock_client = wf_env.client + + with patch("agents_api.clients.temporal.get_client") as get_client: + get_client.return_value = mock_client + yield get_client + + @fixture(scope="global") def test_developer_id(cozo_client=cozo_client): developer_id = uuid4() diff --git a/agents-api/tests/test_activities.py b/agents-api/tests/test_activities.py index 29adb8110..ef9d4151b 100644 --- a/agents-api/tests/test_activities.py +++ b/agents-api/tests/test_activities.py @@ -16,7 +16,18 @@ # from agents_api.common.protocol.entries import Entry -@test("activity: embed_docs") +@test("activity: check that workflow environment and worker are started correctly") +async def _( + workflow_environment=workflow_environment, + worker=temporal_worker, +): + async with workflow_environment as wf_env: + assert wf_env is not None + assert worker is not None + assert worker.is_running + + +@test("activity: call direct embed_docs") async def _( cozo_client=cozo_client, developer_id=test_developer_id, @@ -41,17 +52,6 @@ async def _( embed.assert_called_once() -@test("activity: check that workflow environment and worker are started correctly") -async def _( - workflow_environment=workflow_environment, - worker=temporal_worker, -): - async with workflow_environment as wf_env: - assert wf_env is not None - assert worker is not None - assert worker.is_running - - # @test("get extra entries, do not strip system message") # def _(): # session_ids = [uuid.uuid4()] * 3 diff --git a/agents-api/tests/test_docs_routes.py b/agents-api/tests/test_docs_routes.py index 05f095f49..67e38a50e 100644 --- a/agents-api/tests/test_docs_routes.py +++ b/agents-api/tests/test_docs_routes.py @@ -25,6 +25,9 @@ def _(make_request=make_request, user=test_user): assert response.status_code == 201 + result = response.json() + assert len(result["jobs"]) > 0 + @test("route: create agent doc") def _(make_request=make_request, agent=test_agent): @@ -41,9 +44,8 @@ def _(make_request=make_request, agent=test_agent): assert response.status_code == 201 - # FIXME: Should create a job to process the document - # result = response.json() - # assert len(result["jobs"]) > 0 + result = response.json() + assert len(result["jobs"]) > 0 @test("route: delete doc") diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index fe5a1519d..bfef1a91f 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -848,13 +848,13 @@ test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "p [[package]] name = "importlib-resources" -version = "6.4.1" +version = "6.4.2" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.4.1-py3-none-any.whl", hash = "sha256:8fbee7ba7376ca7c47ce8d31b96b93d8787349845f01ebdbee5ef90409035234"}, - {file = "importlib_resources-6.4.1.tar.gz", hash = "sha256:5ede8acf5d752abda46fb6922a4a6ab782b6d904dfd362bf2d8b857eee1759d9"}, + {file = "importlib_resources-6.4.2-py3-none-any.whl", hash = "sha256:8bba8c54a8a3afaa1419910845fa26ebd706dc716dd208d9b158b4b6966f5c5c"}, + {file = "importlib_resources-6.4.2.tar.gz", hash = "sha256:6cbfbefc449cc6e2095dd184691b7a12a04f40bc75dd4c55d31c34f174cdf57a"}, ] [package.dependencies] From adb2d9f4e7d3a768be546d16f22bf003f85ad11a Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Thu, 15 Aug 2024 15:54:28 +0300 Subject: [PATCH 054/110] fix: Fix tasks routes and tests --- .../execution/prepare_execution_input.py | 2 +- .../routers/tasks/create_task_execution.py | 17 +-- .../routers/tasks/get_execution_details.py | 11 +- .../routers/tasks/get_task_details.py | 41 ++++--- .../tasks/list_execution_transitions.py | 16 ++- .../routers/tasks/list_task_executions.py | 17 ++- .../agents_api/routers/tasks/list_tasks.py | 6 +- .../routers/tasks/patch_execution.py | 25 ++--- agents-api/tests/fixtures.py | 32 ++++++ agents-api/tests/test_task_routes.py | 103 ++++++++---------- 10 files changed, 140 insertions(+), 130 deletions(-) diff --git a/agents-api/agents_api/models/execution/prepare_execution_input.py b/agents-api/agents_api/models/execution/prepare_execution_input.py index c1a767740..2a07d4d3f 100644 --- a/agents-api/agents_api/models/execution/prepare_execution_input.py +++ b/agents-api/agents_api/models/execution/prepare_execution_input.py @@ -65,7 +65,7 @@ def prepare_execution_input( ) # Remove the outer curly braces - task_query = task_query.strip()[1:-1] + task_query = task_query[-1].strip() task_fields = ( "id", diff --git a/agents-api/agents_api/routers/tasks/create_task_execution.py b/agents-api/agents_api/routers/tasks/create_task_execution.py index 9f78de8ca..6f2c0bc3a 100644 --- a/agents-api/agents_api/routers/tasks/create_task_execution.py +++ b/agents-api/agents_api/routers/tasks/create_task_execution.py @@ -42,24 +42,17 @@ async def create_task_execution( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> ResourceCreatedResponse: try: - task = [ - row.to_dict() - for _, row in get_task_query( - task_id=task_id, developer_id=x_developer_id - ).iterrows() - ][0] + task = get_task_query( + task_id=task_id, developer_id=x_developer_id + ) + task_data = task.model_dump() - validate(data.input, task["input_schema"]) + validate(data.input, task_data["input_schema"]) except ValidationError: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid request arguments schema", ) - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Task not found", - ) except QueryException as e: if e.code == "transact::assertion_failure": raise HTTPException( diff --git a/agents-api/agents_api/routers/tasks/get_execution_details.py b/agents-api/agents_api/routers/tasks/get_execution_details.py index 6a9a01caa..e6f87b8af 100644 --- a/agents-api/agents_api/routers/tasks/get_execution_details.py +++ b/agents-api/agents_api/routers/tasks/get_execution_details.py @@ -14,13 +14,8 @@ @router.get("/executions/{execution_id}", tags=["executions"]) async def get_execution_details(execution_id: UUID4) -> Execution: try: - res = [ - row.to_dict() - for _, row in get_execution_query(execution_id=execution_id).iterrows() - ][0] - return Execution(**res) - except (IndexError, KeyError): + return get_execution_query(execution_id=execution_id) + except AssertionError: raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Execution not found", + status_code=status.HTTP_404_NOT_FOUND, detail="Execution not found" ) diff --git a/agents-api/agents_api/routers/tasks/get_task_details.py b/agents-api/agents_api/routers/tasks/get_task_details.py index dbf8bf7da..0890d5aeb 100644 --- a/agents-api/agents_api/routers/tasks/get_task_details.py +++ b/agents-api/agents_api/routers/tasks/get_task_details.py @@ -13,35 +13,32 @@ from .router import router -@router.get("/agents/{agent_id}/tasks/{task_id}", tags=["tasks"]) +@router.get("/tasks/{task_id}", tags=["tasks"]) async def get_task_details( task_id: UUID4, - agent_id: UUID4, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> Task: + not_found = HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Task not found" + ) + try: - resp = [ - row.to_dict() - for _, row in get_task_query( - agent_id=agent_id, task_id=task_id, developer_id=x_developer_id - ).iterrows() - ][0] - - for workflow in resp["workflows"]: - if workflow["name"] == "main": - resp["main"] = workflow["steps"] - break - - return Task(**resp) - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Task not found", + task = get_task_query( + developer_id=x_developer_id, task_id=task_id ) + task_data = task.model_dump() + except AssertionError: + raise not_found except QueryException as e: if e.code == "transact::assertion_failure": - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, detail="Task not found" - ) + raise not_found raise + + for workflow in task_data.get("workflows", []): + if workflow["name"] == "main": + task_data["main"] = workflow.get("steps", []) + break + + return Task(**task_data) + diff --git a/agents-api/agents_api/routers/tasks/list_execution_transitions.py b/agents-api/agents_api/routers/tasks/list_execution_transitions.py index 36f61601e..98b7f1cd7 100644 --- a/agents-api/agents_api/routers/tasks/list_execution_transitions.py +++ b/agents-api/agents_api/routers/tasks/list_execution_transitions.py @@ -1,3 +1,5 @@ +from typing import Literal + from pydantic import UUID4 from agents_api.autogen.openapi_model import ( @@ -16,13 +18,17 @@ async def list_execution_transitions( execution_id: UUID4, limit: int = 100, offset: int = 0, + sort_by: Literal["created_at", "updated_at"] = "created_at", + direction: Literal["asc", "desc"] = "desc", ) -> ListResponse[Transition]: - res = list_execution_transitions_query( - execution_id=execution_id, limit=limit, offset=offset - ) - return ListResponse[Transition]( - items=[Transition(**row.to_dict()) for _, row in res.iterrows()] + transitions = list_execution_transitions_query( + execution_id=execution_id, + limit=limit, + offset=offset, + sort_by=sort_by, + direction=direction, ) + return ListResponse[Transition](items=transitions) # @router.get("/executions/{execution_id}/transitions/{transition_id}", tags=["tasks"]) diff --git a/agents-api/agents_api/routers/tasks/list_task_executions.py b/agents-api/agents_api/routers/tasks/list_task_executions.py index 718d13a0d..24a541c7b 100644 --- a/agents-api/agents_api/routers/tasks/list_task_executions.py +++ b/agents-api/agents_api/routers/tasks/list_task_executions.py @@ -1,4 +1,4 @@ -from typing import Annotated +from typing import Annotated, Literal from fastapi import Depends from pydantic import UUID4 @@ -21,10 +21,15 @@ async def list_task_executions( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], limit: int = 100, offset: int = 0, + sort_by: Literal["created_at", "updated_at"] = "created_at", + direction: Literal["asc", "desc"] = "desc", ) -> ListResponse[Execution]: - res = list_task_executions_query( - task_id=task_id, developer_id=x_developer_id, limit=limit, offse=offset - ) - return ListResponse[Execution]( - items=[Execution(**row.to_dict()) for _, row in res.iterrows()] + executions = list_task_executions_query( + task_id=task_id, + developer_id=x_developer_id, + limit=limit, + offse=offset, + sort_by=sort_by, + direction=direction, ) + return ListResponse[Execution](items=executions) diff --git a/agents-api/agents_api/routers/tasks/list_tasks.py b/agents-api/agents_api/routers/tasks/list_tasks.py index 771b9f38c..5066fcc96 100644 --- a/agents-api/agents_api/routers/tasks/list_tasks.py +++ b/agents-api/agents_api/routers/tasks/list_tasks.py @@ -32,10 +32,10 @@ async def list_tasks( ) tasks = [] - for _, row in query_results.iterrows(): - row_dict = row.to_dict() + for row in query_results: + row_dict = row.model_dump() - for workflow in row_dict["workflows"]: + for workflow in row_dict.get("workflows", []): if workflow["name"] == "main": row_dict["main"] = workflow["steps"] break diff --git a/agents-api/agents_api/routers/tasks/patch_execution.py b/agents-api/agents_api/routers/tasks/patch_execution.py index 74ae3fc72..56c38fc38 100644 --- a/agents-api/agents_api/routers/tasks/patch_execution.py +++ b/agents-api/agents_api/routers/tasks/patch_execution.py @@ -5,6 +5,7 @@ from agents_api.autogen.openapi_model import ( Execution, + ResourceUpdatedResponse, UpdateExecutionRequest, ) from agents_api.dependencies.developer_id import get_developer_id @@ -22,20 +23,10 @@ async def patch_execution( task_id: UUID4, execution_id: UUID4, data: UpdateExecutionRequest, -) -> Execution: - try: - res = [ - row.to_dict() - for _, row in update_execution_query( - developer_id=x_developer_id, - task_id=task_id, - execution_id=execution_id, - data=data, - ).iterrows() - ][0] - return Execution(**res) - except (IndexError, KeyError): - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail="Execution not found", - ) +) -> ResourceUpdatedResponse: + return update_execution_query( + developer_id=x_developer_id, + task_id=task_id, + execution_id=execution_id, + data=data, + ) diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 437c95083..e8dfff790 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -16,6 +16,7 @@ CreateSessionRequest, CreateTaskRequest, CreateToolRequest, + CreateTransitionRequest, CreateUserRequest, ) from agents_api.env import api_key, api_key_header_name @@ -25,6 +26,9 @@ from agents_api.models.docs.create_doc import create_doc from agents_api.models.docs.delete_doc import delete_doc from agents_api.models.execution.create_execution import create_execution +from agents_api.models.execution.create_execution_transition import ( + create_execution_transition, +) from agents_api.models.session.create_session import create_session from agents_api.models.session.delete_session import delete_session from agents_api.models.task.create_task import create_task @@ -308,6 +312,34 @@ def test_execution( ) +@fixture(scope="global") +def test_transition( + client=cozo_client, + developer_id=test_developer_id, + execution=test_execution, +): + transition = create_execution_transition( + developer_id=developer_id, + execution_id=execution.id, + data=CreateTransitionRequest( + type="step", + output={}, + current=[], + next=[], + ), + client=client, + ) + + yield transition + + client.run( + f""" + ?[transition_id] <- ["{str(transition.id)}"] + :delete transitions {{ transition_id }} + """ + ) + + @fixture(scope="global") def test_tool( client=cozo_client, diff --git a/agents-api/tests/test_task_routes.py b/agents-api/tests/test_task_routes.py index 80aae9e7f..ce75348bb 100644 --- a/agents-api/tests/test_task_routes.py +++ b/agents-api/tests/test_task_routes.py @@ -4,7 +4,14 @@ from ward import test -from tests.fixtures import client, make_request, test_agent +from tests.fixtures import ( + client, + make_request, + test_agent, + test_execution, + test_task, + test_transition, +) @test("route: unauthorized should fail") @@ -54,8 +61,7 @@ def _(make_request=make_request, agent=test_agent): @test("route: create task execution") -def _(make_request=make_request): - task_id = str(uuid4()) +def _(make_request=make_request, task=test_task): data = dict( input={}, metadata={}, @@ -63,7 +69,7 @@ def _(make_request=make_request): response = make_request( method="POST", - url=f"/tasks/{task_id}/executions", + url=f"/tasks/{str(task.id)}/executions", json=data, ) @@ -83,12 +89,10 @@ def _(make_request=make_request): @test("route: get execution exists") -def _(make_request=make_request): - execution_id = str(uuid4()) - +def _(make_request=make_request, execution=test_execution): response = make_request( method="GET", - url=f"/executions/{execution_id}", + url=f"/executions/{str(execution.id)}", ) assert response.status_code == 200 @@ -96,106 +100,93 @@ def _(make_request=make_request): @test("route: get task not exists") def _(make_request=make_request): - data = dict( - name="test user", - main="test user about", - ) + task_id = str(uuid4()) response = make_request( - method="POST", - url="/tasks", - json=data, + method="GET", + url=f"/tasks/{task_id}", ) - assert response.status_code == 201 + assert response.status_code == 404 @test("route: get task exists") -def _(make_request=make_request): - data = dict( - name="test user", - main="test user about", - ) - +def _(make_request=make_request, task=test_task): response = make_request( - method="POST", - url="/tasks", - json=data, + method="GET", + url=f"/tasks/{str(task.id)}", ) - assert response.status_code == 201 + assert response.status_code == 200 @test("model: list execution transitions") -def _(make_request=make_request): +def _(make_request=make_request, transition=test_transition): response = make_request( method="GET", - url="/users", + url=f"/executions/{str(transition.execution_id)}/transitions", ) assert response.status_code == 200 response = response.json() - users = response["items"] + transitions = response["items"] - assert isinstance(users, list) - assert len(users) > 0 + assert isinstance(transitions, list) + assert len(transitions) > 0 @test("model: list task executions") -def _(make_request=make_request): +def _(make_request=make_request, execution=test_execution): response = make_request( method="GET", - url="/users", + url=f"/tasks/{str(execution.task_id)}/executions", ) assert response.status_code == 200 response = response.json() - users = response["items"] + executions = response["items"] - assert isinstance(users, list) - assert len(users) > 0 + assert isinstance(executions, list) + assert len(executions) > 0 @test("model: list tasks") -def _(make_request=make_request): +def _(make_request=make_request, agent=test_agent): response = make_request( method="GET", - url="/users", + url=f"/agents/{str(agent.id)}/tasks", ) assert response.status_code == 200 response = response.json() - users = response["items"] + tasks = response["items"] - assert isinstance(users, list) - assert len(users) > 0 + assert isinstance(tasks, list) + assert len(tasks) > 0 @test("model: patch execution") -def _(make_request=make_request): +def _(make_request=make_request, execution=test_execution): + data = dict( + status="running", + ) + response = make_request( - method="GET", - url="/users", + method="PATCH", + url=f"/tasks/{str(execution.task_id)}/executions/{str(execution.id)}", + json=data, ) assert response.status_code == 200 - response = response.json() - users = response["items"] - - assert isinstance(users, list) - assert len(users) > 0 + execution_id = response.json()["id"] -@test("model: update execution") -def _(make_request=make_request): response = make_request( method="GET", - url="/users", + url=f"/executions/{execution_id}", ) assert response.status_code == 200 - response = response.json() - users = response["items"] + execution = response.json() - assert isinstance(users, list) - assert len(users) > 0 + assert execution["status"] == "running" From 93a5fda46d4b7ddb7b1bb64f6f2699a3f6a07885 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Fri, 16 Aug 2024 22:14:59 +0300 Subject: [PATCH 055/110] fix: Split create execution query --- .../models/execution/create_execution.py | 23 +----- .../execution/create_temporal_lookup.py | 70 +++++++++++++++++++ 2 files changed, 71 insertions(+), 22 deletions(-) create mode 100644 agents-api/agents_api/models/execution/create_temporal_lookup.py diff --git a/agents-api/agents_api/models/execution/create_execution.py b/agents-api/agents_api/models/execution/create_execution.py index 155d18b16..c564c343d 100644 --- a/agents-api/agents_api/models/execution/create_execution.py +++ b/agents-api/agents_api/models/execution/create_execution.py @@ -5,7 +5,6 @@ from fastapi import HTTPException from pycozo.client import QueryException from pydantic import ValidationError -from temporalio.client import WorkflowHandle from ...autogen.openapi_model import CreateExecutionRequest, Execution from ...common.utils.cozo import cozo_process_mutate_data @@ -41,7 +40,6 @@ def create_execution( task_id: UUID, execution_id: UUID | None = None, data: Annotated[CreateExecutionRequest | dict, dict_like(CreateExecutionRequest)], - workflow_handle: WorkflowHandle, ) -> tuple[list[str], dict]: execution_id = execution_id or uuid4() @@ -56,24 +54,6 @@ def create_execution( data["metadata"] = data.get("metadata", {}) execution_data = data - temporal_columns, temporal_values = cozo_process_mutate_data( - { - "execution_id": execution_id, - "id": workflow_handle.id, - "run_id": workflow_handle.run_id, - "first_execution_run_id": workflow_handle.first_execution_run_id, - "result_run_id": workflow_handle.result_run_id, - } - ) - - temporal_executions_lookup_query = f""" - ?[{temporal_columns}] <- $temporal_values - - :insert temporal_executions_lookup {{ - {temporal_columns} - }} - """ - columns, values = cozo_process_mutate_data( { **execution_data, @@ -100,8 +80,7 @@ def create_execution( task_id=task_id, parents=[("agents", "agent_id")], ), - temporal_executions_lookup_query, insert_query, ] - return (queries, {"values": values, "temporal_values": temporal_values}) + return (queries, {"values": values}) diff --git a/agents-api/agents_api/models/execution/create_temporal_lookup.py b/agents-api/agents_api/models/execution/create_temporal_lookup.py new file mode 100644 index 000000000..1867b64b9 --- /dev/null +++ b/agents-api/agents_api/models/execution/create_temporal_lookup.py @@ -0,0 +1,70 @@ +from uuid import UUID, uuid4 + +from beartype import beartype +from fastapi import HTTPException +from pycozo.client import QueryException +from pydantic import ValidationError +from temporalio.client import WorkflowHandle + +from ...common.utils.cozo import cozo_process_mutate_data +from ..utils import ( + cozo_query, + partialclass, + rewrap_exceptions, + verify_developer_id_query, + verify_developer_owns_resource_query, +) + + +@rewrap_exceptions( + { + QueryException: partialclass(HTTPException, status_code=400), + ValidationError: partialclass(HTTPException, status_code=400), + TypeError: partialclass(HTTPException, status_code=400), + } +) +@cozo_query +@beartype +def create_temporal_lookup( + *, + developer_id: UUID, + task_id: UUID, + execution_id: UUID | None = None, + workflow_handle: WorkflowHandle, +) -> tuple[list[str], dict]: + execution_id = execution_id or uuid4() + + developer_id = str(developer_id) + task_id = str(task_id) + execution_id = str(execution_id) + + temporal_columns, temporal_values = cozo_process_mutate_data( + { + "execution_id": execution_id, + "id": workflow_handle.id, + "run_id": workflow_handle.run_id, + "first_execution_run_id": workflow_handle.first_execution_run_id, + "result_run_id": workflow_handle.result_run_id, + } + ) + + temporal_executions_lookup_query = f""" + ?[{temporal_columns}] <- $temporal_values + + :insert temporal_executions_lookup {{ + {temporal_columns} + }} + """ + + queries = [ + verify_developer_id_query(developer_id), + verify_developer_owns_resource_query( + developer_id, + "tasks", + task_id=task_id, + parents=[("agents", "agent_id")], + ), + temporal_executions_lookup_query, + ] + + return (queries, {"temporal_values": temporal_values}) From cceae91858edc22bb67bcba96ca17ad5d2daa272 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Fri, 16 Aug 2024 22:15:47 +0300 Subject: [PATCH 056/110] fix: Apply various small fixes --- .../execution/prepare_execution_input.py | 3 ++- .../agents_api/routers/tasks/__init__.py | 1 + .../routers/tasks/create_task_execution.py | 21 ++++++++++++------- .../routers/tasks/get_task_details.py | 5 +---- .../routers/tasks/list_task_executions.py | 2 +- agents-api/tests/fixtures.py | 10 +++++++-- agents-api/tests/test_execution_queries.py | 6 ++++++ agents-api/tests/test_task_routes.py | 2 +- 8 files changed, 34 insertions(+), 16 deletions(-) diff --git a/agents-api/agents_api/models/execution/prepare_execution_input.py b/agents-api/agents_api/models/execution/prepare_execution_input.py index 2a07d4d3f..8ba687bca 100644 --- a/agents-api/agents_api/models/execution/prepare_execution_input.py +++ b/agents-api/agents_api/models/execution/prepare_execution_input.py @@ -156,7 +156,7 @@ def prepare_execution_input( *_execution {{ {', '.join(execution_fields)} }}, execution = {{ {make_cozo_json_query(execution_fields)} }} - ?[developer_id, execution, task, agent, user, session, tools] := + ?[developer_id, execution, task, agent, user, session, tools, arguments] := developer_id = to_uuid($developer_id), agent_json[agent], @@ -167,6 +167,7 @@ def prepare_execution_input( # TODO: Enable these later user = null, session = null, + arguments = {{}}, """ queries = [ diff --git a/agents-api/agents_api/routers/tasks/__init__.py b/agents-api/agents_api/routers/tasks/__init__.py index 66621b34c..94cd1d8cd 100644 --- a/agents-api/agents_api/routers/tasks/__init__.py +++ b/agents-api/agents_api/routers/tasks/__init__.py @@ -3,6 +3,7 @@ from .create_task_execution import create_task_execution from .get_execution_details import get_execution_details from .get_task_details import get_task_details +from .list_execution_transitions import list_execution_transitions from .list_task_executions import list_task_executions from .list_tasks import list_tasks from .patch_execution import patch_execution diff --git a/agents-api/agents_api/routers/tasks/create_task_execution.py b/agents-api/agents_api/routers/tasks/create_task_execution.py index 6f2c0bc3a..5c40d4927 100644 --- a/agents-api/agents_api/routers/tasks/create_task_execution.py +++ b/agents-api/agents_api/routers/tasks/create_task_execution.py @@ -19,6 +19,7 @@ from agents_api.models.execution.create_execution import ( create_execution as create_execution_query, ) +from agents_api.models.execution.create_temporal_lookup import create_temporal_lookup from agents_api.models.execution.prepare_execution_input import prepare_execution_input from agents_api.models.execution.update_execution import ( update_execution as update_execution_query, @@ -42,9 +43,7 @@ async def create_task_execution( x_developer_id: Annotated[UUID4, Depends(get_developer_id)], ) -> ResourceCreatedResponse: try: - task = get_task_query( - task_id=task_id, developer_id=x_developer_id - ) + task = get_task_query(task_id=task_id, developer_id=x_developer_id) task_data = task.model_dump() validate(data.input, task_data["input_schema"]) @@ -62,6 +61,13 @@ async def create_task_execution( raise execution_id = uuid4() + execution = create_execution_query( + developer_id=x_developer_id, + task_id=task_id, + execution_id=execution_id, + data=data, + ) + execution_input = prepare_execution_input( developer_id=x_developer_id, task_id=task_id, @@ -88,14 +94,15 @@ async def create_task_execution( detail="Task creation failed", ) - execution = create_execution_query( + create_temporal_lookup( developer_id=x_developer_id, task_id=task_id, execution_id=execution_id, - data=data, - workflow_hande=handle, + workflow_handle=handle, ) return ResourceCreatedResponse( - id=execution["execution_id"][0], created_at=execution["created_at"][0] + id=execution.id, + created_at=execution.created_at, + jobs=[], ) diff --git a/agents-api/agents_api/routers/tasks/get_task_details.py b/agents-api/agents_api/routers/tasks/get_task_details.py index 0890d5aeb..bcfbeedc1 100644 --- a/agents-api/agents_api/routers/tasks/get_task_details.py +++ b/agents-api/agents_api/routers/tasks/get_task_details.py @@ -23,9 +23,7 @@ async def get_task_details( ) try: - task = get_task_query( - developer_id=x_developer_id, task_id=task_id - ) + task = get_task_query(developer_id=x_developer_id, task_id=task_id) task_data = task.model_dump() except AssertionError: raise not_found @@ -41,4 +39,3 @@ async def get_task_details( break return Task(**task_data) - diff --git a/agents-api/agents_api/routers/tasks/list_task_executions.py b/agents-api/agents_api/routers/tasks/list_task_executions.py index 24a541c7b..27ea782c4 100644 --- a/agents-api/agents_api/routers/tasks/list_task_executions.py +++ b/agents-api/agents_api/routers/tasks/list_task_executions.py @@ -28,7 +28,7 @@ async def list_task_executions( task_id=task_id, developer_id=x_developer_id, limit=limit, - offse=offset, + offset=offset, sort_by=sort_by, direction=direction, ) diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index e8dfff790..a1936d5f6 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -29,6 +29,7 @@ from agents_api.models.execution.create_execution_transition import ( create_execution_transition, ) +from agents_api.models.execution.create_temporal_lookup import create_temporal_lookup from agents_api.models.session.create_session import create_session from agents_api.models.session.delete_session import delete_session from agents_api.models.task.create_task import create_task @@ -298,6 +299,11 @@ def test_execution( developer_id=developer_id, task_id=task.id, data=CreateExecutionRequest(input={"test": "test"}), + client=client, + ) + create_temporal_lookup( + developer_id=developer_id, + task_id=task.id, workflow_handle=workflow_handle, client=client, ) @@ -324,8 +330,8 @@ def test_transition( data=CreateTransitionRequest( type="step", output={}, - current=[], - next=[], + current=["main", 0], + next=["wf1", 1], ), client=client, ) diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index af81f75ff..13bc809d5 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -11,6 +11,7 @@ from agents_api.models.execution.create_execution_transition import ( create_execution_transition, ) +from agents_api.models.execution.create_temporal_lookup import create_temporal_lookup from agents_api.models.execution.get_execution import get_execution from agents_api.models.execution.list_executions import list_executions from tests.fixtures import cozo_client, test_developer_id, test_execution, test_task @@ -29,6 +30,11 @@ def _(client=cozo_client, developer_id=test_developer_id, task=test_task): developer_id=developer_id, task_id=task.id, data=CreateExecutionRequest(input={"test": "test"}), + client=client, + ) + create_temporal_lookup( + developer_id=developer_id, + task_id=task.id, workflow_handle=workflow_handle, client=client, ) diff --git a/agents-api/tests/test_task_routes.py b/agents-api/tests/test_task_routes.py index ce75348bb..68a77c578 100644 --- a/agents-api/tests/test_task_routes.py +++ b/agents-api/tests/test_task_routes.py @@ -107,7 +107,7 @@ def _(make_request=make_request): url=f"/tasks/{task_id}", ) - assert response.status_code == 404 + assert response.status_code == 400 @test("route: get task exists") From 0610f86361d422a8a19c479c27c8709f67f973cf Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Thu, 15 Aug 2024 17:12:45 -0400 Subject: [PATCH 057/110] fix(agents-api): Minor fixes Signed-off-by: Diwank Tomer --- .../agents_api/activities/embed_docs.py | 6 +-- agents-api/agents_api/env.py | 1 + .../agents_api/models/agent/patch_agent.py | 2 +- .../agents_api/models/agent/update_agent.py | 2 +- .../agents_api/models/docs/embed_snippets.py | 2 +- .../models/execution/update_execution.py | 2 +- .../models/session/patch_session.py | 2 +- .../models/session/update_session.py | 2 +- .../agents_api/models/task/patch_task.py | 2 +- .../agents_api/models/tools/patch_tool.py | 2 +- .../agents_api/models/tools/update_tool.py | 2 +- .../agents_api/models/user/patch_user.py | 2 +- .../agents_api/models/user/update_user.py | 2 +- .../agents_api/routers/docs/create_doc.py | 25 ++++++++-- agents-api/agents_api/worker/__main__.py | 4 +- agents-api/agents_api/worker/worker.py | 49 +++++++++---------- agents-api/tests/fixtures.py | 31 ++++++------ agents-api/tests/test_activities.py | 7 +-- agents-api/tests/test_docs_routes.py | 11 +++-- 19 files changed, 85 insertions(+), 71 deletions(-) diff --git a/agents-api/agents_api/activities/embed_docs.py b/agents-api/agents_api/activities/embed_docs.py index da7bb313f..1000f456d 100644 --- a/agents-api/agents_api/activities/embed_docs.py +++ b/agents-api/agents_api/activities/embed_docs.py @@ -2,9 +2,9 @@ from temporalio import activity -from agents_api.clients import embed as embedder -from agents_api.clients.cozo import get_cozo_client -from agents_api.models.docs.embed_snippets import embed_snippets as embed_snippets_query +from ..clients import embed as embedder +from ..clients.cozo import get_cozo_client +from ..models.docs.embed_snippets import embed_snippets as embed_snippets_query snippet_embed_instruction = "Encode this passage for retrieval: " diff --git a/agents-api/agents_api/env.py b/agents-api/agents_api/env.py index 227103f6d..863a19b4f 100644 --- a/agents-api/agents_api/env.py +++ b/agents-api/agents_api/env.py @@ -15,6 +15,7 @@ # Debug # ----- debug: bool = env.bool("AGENTS_API_DEBUG", default=False) +testing: bool = env.bool("AGENTS_API_TESTING", default=False) sentry_dsn: str = env.str("SENTRY_DSN", default=None) diff --git a/agents-api/agents_api/models/agent/patch_agent.py b/agents-api/agents_api/models/agent/patch_agent.py index 87f5db046..336e33a91 100644 --- a/agents-api/agents_api/models/agent/patch_agent.py +++ b/agents-api/agents_api/models/agent/patch_agent.py @@ -29,7 +29,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["agent_id"], "jobs": [], **d}, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/agent/update_agent.py b/agents-api/agents_api/models/agent/update_agent.py index 95116e1a2..4dd489333 100644 --- a/agents-api/agents_api/models/agent/update_agent.py +++ b/agents-api/agents_api/models/agent/update_agent.py @@ -28,7 +28,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["agent_id"], "jobs": [], **d}, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/docs/embed_snippets.py b/agents-api/agents_api/models/docs/embed_snippets.py index 64bf8e130..a701ef1be 100644 --- a/agents-api/agents_api/models/docs/embed_snippets.py +++ b/agents-api/agents_api/models/docs/embed_snippets.py @@ -30,7 +30,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["doc_id"], "updated_at": utcnow(), "jobs": []}, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/execution/update_execution.py b/agents-api/agents_api/models/execution/update_execution.py index 4386b9502..59de6da14 100644 --- a/agents-api/agents_api/models/execution/update_execution.py +++ b/agents-api/agents_api/models/execution/update_execution.py @@ -31,7 +31,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["execution_id"], "jobs": [], **d}, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/session/patch_session.py b/agents-api/agents_api/models/session/patch_session.py index 131d82bec..feafcc679 100644 --- a/agents-api/agents_api/models/session/patch_session.py +++ b/agents-api/agents_api/models/session/patch_session.py @@ -46,7 +46,7 @@ "jobs": [], **d, }, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/session/update_session.py b/agents-api/agents_api/models/session/update_session.py index c4296634a..14b989de1 100644 --- a/agents-api/agents_api/models/session/update_session.py +++ b/agents-api/agents_api/models/session/update_session.py @@ -44,7 +44,7 @@ "jobs": [], **d, }, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/task/patch_task.py b/agents-api/agents_api/models/task/patch_task.py index 4be73c025..dc32f83d2 100644 --- a/agents-api/agents_api/models/task/patch_task.py +++ b/agents-api/agents_api/models/task/patch_task.py @@ -39,7 +39,7 @@ "updated_at": d["updated_at_ms"][0] / 1000, **d, }, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/tools/patch_tool.py b/agents-api/agents_api/models/tools/patch_tool.py index 68bea3ee5..0b2777030 100644 --- a/agents-api/agents_api/models/tools/patch_tool.py +++ b/agents-api/agents_api/models/tools/patch_tool.py @@ -28,7 +28,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["tool_id"], "jobs": [], **d}, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/tools/update_tool.py b/agents-api/agents_api/models/tools/update_tool.py index b8a395a94..3e91b7562 100644 --- a/agents-api/agents_api/models/tools/update_tool.py +++ b/agents-api/agents_api/models/tools/update_tool.py @@ -31,7 +31,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["tool_id"], "jobs": [], **d}, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/user/patch_user.py b/agents-api/agents_api/models/user/patch_user.py index 89fe4db33..d8dfeb2ad 100644 --- a/agents-api/agents_api/models/user/patch_user.py +++ b/agents-api/agents_api/models/user/patch_user.py @@ -31,7 +31,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["user_id"], "jobs": [], **d}, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/models/user/update_user.py b/agents-api/agents_api/models/user/update_user.py index 88f24e6e0..8411ecc3e 100644 --- a/agents-api/agents_api/models/user/update_user.py +++ b/agents-api/agents_api/models/user/update_user.py @@ -28,7 +28,7 @@ ResourceUpdatedResponse, one=True, transform=lambda d: {"id": d["user_id"], "jobs": [], **d}, - _kind="replaced", + _kind="inserted", ) @cozo_query @beartype diff --git a/agents-api/agents_api/routers/docs/create_doc.py b/agents-api/agents_api/routers/docs/create_doc.py index 645f82964..548ee29d5 100644 --- a/agents-api/agents_api/routers/docs/create_doc.py +++ b/agents-api/agents_api/routers/docs/create_doc.py @@ -1,7 +1,7 @@ from typing import Annotated from uuid import UUID, uuid4 -from fastapi import Depends +from fastapi import BackgroundTasks, Depends from pydantic import UUID4 from starlette.status import HTTP_201_CREATED from temporalio.client import Client as TemporalClient @@ -9,6 +9,7 @@ from ...autogen.openapi_model import CreateDocRequest, ResourceCreatedResponse from ...clients import temporal from ...dependencies.developer_id import get_developer_id +from ...env import temporal_task_queue, testing from ...models.docs.create_doc import create_doc as create_doc_query from .router import router @@ -18,23 +19,36 @@ async def run_embed_docs_task( title: str, content: list[str], job_id: UUID, + background_tasks: BackgroundTasks, client: TemporalClient | None = None, ): + from ...workflows.embed_docs import EmbedDocsWorkflow + client = client or (await temporal.get_client()) - await client.execute_workflow( - "EmbedDocsWorkflow", + # TODO: Remove this conditional once we have a way to run workflows in + # a test environment. + if testing: + return None + + handle = await client.start_workflow( + EmbedDocsWorkflow.run, args=[str(doc_id), title, content], - task_queue="memory-task-queue", + task_queue=temporal_task_queue, id=str(job_id), ) + background_tasks.add_task(handle.result) + + return handle + @router.post("/users/{user_id}/docs", status_code=HTTP_201_CREATED, tags=["docs"]) async def create_user_doc( user_id: UUID4, data: CreateDocRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + background_tasks: BackgroundTasks, ) -> ResourceCreatedResponse: doc = create_doc_query( developer_id=x_developer_id, @@ -50,6 +64,7 @@ async def create_user_doc( title=doc.title, content=doc.content, job_id=embed_job_id, + background_tasks=background_tasks, ) return ResourceCreatedResponse( @@ -62,6 +77,7 @@ async def create_agent_doc( agent_id: UUID4, data: CreateDocRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + background_tasks: BackgroundTasks, ) -> ResourceCreatedResponse: doc = create_doc_query( developer_id=x_developer_id, @@ -77,6 +93,7 @@ async def create_agent_doc( title=doc.title, content=doc.content, job_id=embed_job_id, + background_tasks=background_tasks, ) return ResourceCreatedResponse( diff --git a/agents-api/agents_api/worker/__main__.py b/agents-api/agents_api/worker/__main__.py index fd54a1b4f..72f9ccf14 100644 --- a/agents-api/agents_api/worker/__main__.py +++ b/agents-api/agents_api/worker/__main__.py @@ -7,6 +7,7 @@ import asyncio +from ..clients import temporal from .worker import create_worker @@ -16,7 +17,8 @@ async def main(): then starts the worker to listen for tasks on the configured task queue. """ - worker = await create_worker() + client = await temporal.get_client() + worker = create_worker(client) # Start the worker to listen for and process tasks await worker.run() diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index ac045c5ab..042f75275 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -3,38 +3,35 @@ from temporalio.client import Client from temporalio.worker import Worker -from ..activities.embed_docs import embed_docs -from ..activities.mem_mgmt import mem_mgmt -from ..activities.mem_rating import mem_rating -from ..activities.summarization import summarization -from ..activities.task_steps import ( - evaluate_step, - if_else_step, - prompt_step, - tool_call_step, - transition_step, - yield_step, -) -from ..activities.truncation import truncation -from ..clients.temporal import get_client -from ..env import ( - temporal_task_queue, -) -from ..workflows.embed_docs import EmbedDocsWorkflow -from ..workflows.mem_mgmt import MemMgmtWorkflow -from ..workflows.mem_rating import MemRatingWorkflow -from ..workflows.summarization import SummarizationWorkflow -from ..workflows.task_execution import TaskExecutionWorkflow -from ..workflows.truncation import TruncationWorkflow - -async def create_worker(client: Client | None = None): +def create_worker(client: Client): """ Initializes the Temporal client and worker with TLS configuration (if provided), then create a worker to listen for tasks on the configured task queue. """ - client = client or await get_client() + from ..activities.embed_docs import embed_docs + from ..activities.mem_mgmt import mem_mgmt + from ..activities.mem_rating import mem_rating + from ..activities.summarization import summarization + from ..activities.task_steps import ( + evaluate_step, + if_else_step, + prompt_step, + tool_call_step, + transition_step, + yield_step, + ) + from ..activities.truncation import truncation + from ..env import ( + temporal_task_queue, + ) + from ..workflows.embed_docs import EmbedDocsWorkflow + from ..workflows.mem_mgmt import MemMgmtWorkflow + from ..workflows.mem_rating import MemRatingWorkflow + from ..workflows.summarization import SummarizationWorkflow + from ..workflows.task_execution import TaskExecutionWorkflow + from ..workflows.truncation import TruncationWorkflow task_activities = [ prompt_step, diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index a1936d5f6..3afbd3195 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -1,3 +1,4 @@ +import asyncio from unittest.mock import patch from uuid import uuid4 @@ -39,6 +40,7 @@ from agents_api.models.user.create_user import create_user from agents_api.models.user.delete_user import delete_user from agents_api.web import app +# from agents_api.worker.worker import create_worker from agents_api.worker.worker import create_worker EMBEDDING_SIZE: int = 1024 @@ -62,29 +64,26 @@ def activity_environment(): @fixture(scope="global") -async def workflow_environment(): - wf_env = await WorkflowEnvironment.start_local() - yield wf_env - await wf_env.shutdown() +async def temporal_worker(): + async with (await WorkflowEnvironment.start_local()) as env: + worker = create_worker(client=env.client) + worker_task = asyncio.create_task(worker.run()) + yield worker -@fixture(scope="global") -async def temporal_worker(wf_env=workflow_environment): - worker = await create_worker(client=wf_env.client) - - # FIXME: This does not stop the worker properly - c = worker.shutdown() - async with worker as running_worker: - yield running_worker - await c - + kill_signal = worker.shutdown() + worker_task.cancel() + await asyncio.wait( + [kill_signal, worker_task], + return_when=asyncio.FIRST_COMPLETED, + ) + @fixture(scope="test") def patch_temporal_get_client( - wf_env=workflow_environment, temporal_worker=temporal_worker, ): - mock_client = wf_env.client + mock_client = temporal_worker.client with patch("agents_api.clients.temporal.get_client") as get_client: get_client.return_value = mock_client diff --git a/agents-api/tests/test_activities.py b/agents-api/tests/test_activities.py index ef9d4151b..6f128a874 100644 --- a/agents-api/tests/test_activities.py +++ b/agents-api/tests/test_activities.py @@ -8,7 +8,6 @@ temporal_worker, test_developer_id, test_doc, - workflow_environment, ) # from agents_api.activities.truncation import get_extra_entries @@ -18,13 +17,9 @@ @test("activity: check that workflow environment and worker are started correctly") async def _( - workflow_environment=workflow_environment, worker=temporal_worker, ): - async with workflow_environment as wf_env: - assert wf_env is not None - assert worker is not None - assert worker.is_running + assert worker.is_running @test("activity: call direct embed_docs") diff --git a/agents-api/tests/test_docs_routes.py b/agents-api/tests/test_docs_routes.py index 67e38a50e..88a39937d 100644 --- a/agents-api/tests/test_docs_routes.py +++ b/agents-api/tests/test_docs_routes.py @@ -3,6 +3,7 @@ from tests.fixtures import ( make_request, patch_embed_acompletion, + patch_temporal_get_client, test_agent, test_doc, test_user, @@ -11,7 +12,7 @@ @test("route: create user doc") -def _(make_request=make_request, user=test_user): +def _(make_request=make_request, user=test_user, get_client=patch_temporal_get_client): data = dict( title="Test User Doc", content=["This is a test user document."], @@ -23,6 +24,7 @@ def _(make_request=make_request, user=test_user): json=data, ) + get_client.assert_called() assert response.status_code == 201 result = response.json() @@ -30,7 +32,7 @@ def _(make_request=make_request, user=test_user): @test("route: create agent doc") -def _(make_request=make_request, agent=test_agent): +def _(make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client): data = dict( title="Test Agent Doc", content=["This is a test agent document."], @@ -42,6 +44,7 @@ def _(make_request=make_request, agent=test_agent): json=data, ) + get_client.assert_called() assert response.status_code == 201 result = response.json() @@ -49,7 +52,7 @@ def _(make_request=make_request, agent=test_agent): @test("route: delete doc") -def _(make_request=make_request, agent=test_agent): +def _(make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client): data = dict( title="Test Agent Doc", content=["This is a test agent document."], @@ -78,7 +81,7 @@ def _(make_request=make_request, agent=test_agent): @test("route: get doc") -def _(make_request=make_request, agent=test_agent): +def _(make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client): data = dict( title="Test Agent Doc", content=["This is a test agent document."], From 939569bc1e4224985589bc0740836b46526e46d8 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Thu, 15 Aug 2024 22:16:40 -0400 Subject: [PATCH 058/110] wip Signed-off-by: Diwank Tomer --- .../activities/task_steps/__init__.py | 51 +++---- agents-api/agents_api/autogen/Executions.py | 17 ++- .../agents_api/autogen/openapi_model.py | 125 ++++++++++-------- agents-api/agents_api/clients/litellm.py | 3 +- .../agents_api/common/protocol/sessions.py | 34 +++++ .../agents_api/common/protocol/tasks.py | 52 ++++++-- .../models/chat/prepare_chat_context.py | 3 +- .../execution/create_execution_transition.py | 60 +++++---- .../execution/get_execution_transition.py | 27 ++-- .../execution/list_execution_transitions.py | 30 +++-- .../execution/prepare_execution_input.py | 2 +- .../models/execution/update_execution.py | 13 +- .../agents_api/models/session/get_session.py | 2 +- .../models/session/list_sessions.py | 2 +- .../models/session/prepare_session_data.py | 3 +- .../routers/tasks/patch_execution.py | 3 +- .../agents_api/workflows/task_execution.py | 5 +- agents-api/poetry.lock | 12 +- agents-api/tests/fixtures.py | 5 +- agents-api/tests/test_docs_routes.py | 12 +- agents-api/tests/test_execution_queries.py | 22 ++- sdks/python/julep/api/__init__.py | 2 + sdks/python/julep/api/types/__init__.py | 2 + .../julep/api/types/executions_transition.py | 5 +- .../api/types/executions_transition_target.py | 44 ++++++ sdks/python/poetry.lock | 6 +- sdks/ts/src/api/index.ts | 2 + .../src/api/models/Executions_Transition.ts | 5 +- .../api/models/Executions_TransitionTarget.ts | 9 ++ .../src/api/schemas/$Executions_Transition.ts | 20 +-- .../schemas/$Executions_TransitionTarget.ts | 17 +++ typespec/executions/models.tsp | 5 +- 32 files changed, 392 insertions(+), 208 deletions(-) create mode 100644 sdks/python/julep/api/types/executions_transition_target.py create mode 100644 sdks/ts/src/api/models/Executions_TransitionTarget.ts create mode 100644 sdks/ts/src/api/schemas/$Executions_TransitionTarget.ts diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index e3d3099bb..f4cef8d3b 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -1,11 +1,10 @@ import asyncio -from typing import Literal -from uuid import uuid4 from simpleeval import simple_eval from temporalio import activity from ...autogen.openapi_model import ( + CreateTransitionRequest, EvaluateStep, IfElseWorkflowStep, InputChatMLMessage, @@ -18,7 +17,7 @@ ) from ...common.protocol.tasks import ( StepContext, - TransitionInfo, + StepOutcome, ) from ...common.utils.template import render_template from ...models.execution.create_execution_transition import ( @@ -27,9 +26,7 @@ @activity.defn -async def prompt_step(context: StepContext) -> dict: - assert isinstance(context.definition, PromptStep) - +async def prompt_step(context: StepContext[PromptStep]) -> StepOutcome: # Get context data context_data: dict = context.model_dump() @@ -39,6 +36,7 @@ async def prompt_step(context: StepContext) -> dict: if isinstance(context.definition.prompt, str) else context.definition.prompt ) + template_messages: list[InputChatMLMessage] = prompt messages = await asyncio.gather( *[ @@ -61,7 +59,10 @@ async def prompt_step(context: StepContext) -> dict: **settings, ) - return response.model_dump() + return StepOutcome( + output=response.model_dump(), + next=None, + ) @activity.defn @@ -103,10 +104,9 @@ async def tool_call_step(context: StepContext) -> dict: @activity.defn -async def if_else_step(context: StepContext) -> dict: - assert isinstance(context.definition, IfElseWorkflowStep) - +async def if_else_step(context: StepContext[IfElseWorkflowStep]) -> dict: context_data: dict = context.model_dump() + next_workflow = ( context.definition.then if simple_eval(context.definition.if_, names=context_data) @@ -118,38 +118,27 @@ async def if_else_step(context: StepContext) -> dict: @activity.defn async def transition_step( - context: StepContext, - transition_info: TransitionInfo, - execution_status: Literal[ - "queued", - "starting", - "running", - "awaiting_input", - "succeeded", - "failed", - "cancelled", - ] = "awaiting_input", + context: StepContext[None], + transition_info: CreateTransitionRequest, ): - activity.heartbeat("Running transition step") - - # Get transition info - transition_data = transition_info.model_dump(by_alias=False) + need_to_wait = transition_info.type == "wait" # Get task token if it's a waiting step - if transition_info.type == "awaiting_input": + if need_to_wait: task_token = activity.info().task_token - transition_data["__task_token"] = task_token + transition_info.task_token = task_token # Create transition + activity.heartbeat("Creating transition in db") create_execution_transition_query( developer_id=context.developer_id, execution_id=context.execution.id, - transition_id=uuid4(), - update_execution_status=True, task_id=context.task.id, - **transition_data, + data=transition_info, + update_execution_status=True, ) # Raise if it's a waiting step - if execution_status == "awaiting_input": + if need_to_wait: + activity.heartbeat("Starting to wait") activity.raise_complete_async() diff --git a/agents-api/agents_api/autogen/Executions.py b/agents-api/agents_api/autogen/Executions.py index f1b66897d..fbafb54f1 100644 --- a/agents-api/agents_api/autogen/Executions.py +++ b/agents-api/agents_api/autogen/Executions.py @@ -84,8 +84,10 @@ class Transition(BaseModel): ] execution_id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] output: Annotated[dict[str, Any], Field(json_schema_extra={"readOnly": True})] - current: Annotated[list, Field(json_schema_extra={"readOnly": True})] - next: Annotated[list | None, Field(json_schema_extra={"readOnly": True})] + current: Annotated[TransitionTarget, Field(json_schema_extra={"readOnly": True})] + next: Annotated[ + TransitionTarget | None, Field(json_schema_extra={"readOnly": True}) + ] id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] metadata: dict[str, Any] | None = None created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] @@ -98,6 +100,17 @@ class Transition(BaseModel): """ +class TransitionTarget(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + workflow: Annotated[str, Field(pattern="^[^\\W0-9]\\w*$")] + """ + Valid python identifier names + """ + step: int + + class UpdateExecutionRequest(BaseModel): model_config = ConfigDict( populate_by_name=True, diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index 24640c46d..df0d44342 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -20,31 +20,31 @@ from .Tools import * from .Users import * +# Generic models +# -------------- + +DataT = TypeVar("DataT", bound=BaseModel) + + +class ListResponse(BaseModel, Generic[DataT]): + items: list[DataT] + + +# Aliases +# ------- + CreateToolRequest = UpdateToolRequest CreateOrUpdateAgentRequest = UpdateAgentRequest CreateOrUpdateUserRequest = UpdateUserRequest CreateOrUpdateSessionRequest = CreateSessionRequest CreateOrUpdateTaskRequest = CreateTaskRequest +ChatResponse = ChunkChatResponse | MessageChatResponse -CreateTransitionRequest = create_partial_model( - Transition, - # The following fields are optional - "id", - "execution_id", - "created_at", - "updated_at", - "metadata", -) -ChatMLRole = Literal[ - "user", - "assistant", - "system", - "function", - "function_response", - "function_call", - "auto", -] +# Custom types (not generated correctly) +# -------------------------------------- + +# TODO: Remove these when auto-population is fixed ChatMLContent = ( list[ChatMLTextContentPart | ChatMLImageContentPart] @@ -61,9 +61,53 @@ ] ) +ChatMLRole = Literal[ + "user", + "assistant", + "system", + "function", + "function_response", + "function_call", + "auto", +] +assert BaseEntry.model_fields["role"].annotation == ChatMLRole + ChatMLSource = Literal[ "api_request", "api_response", "tool_response", "internal", "summarizer", "meta" ] +assert BaseEntry.model_fields["source"].annotation == ChatMLSource + + +ExecutionStatus = Literal[ + "queued", + "starting", + "running", + "awaiting_input", + "succeeded", + "failed", + "cancelled", +] +assert Execution.model_fields["status"].annotation == ExecutionStatus + + +TransitionType = Literal["finish", "wait", "resume", "error", "step", "cancelled"] +assert Transition.model_fields["type"].annotation == TransitionType + + +# Create models +# ------------- + +CreateTransitionRequest = create_partial_model( + Transition, + # + # The following fields are optional + "id", + "execution_id", + "created_at", + "updated_at", + "metadata", +) +CreateTransitionRequest.model_rebuild() class CreateEntryRequest(BaseEntry): @@ -98,35 +142,8 @@ def from_model_input( ) -def make_session( - *, - agents: list[UUID], - users: list[UUID], - **data: dict, -) -> Session: - """ - Create a new session object. - """ - cls, participants = None, {} - - match (len(agents), len(users)): - case (0, _): - raise ValueError("At least one agent must be provided.") - case (1, 0): - cls = SingleAgentNoUserSession - participants = {"agent": agents[0]} - case (1, 1): - cls = SingleAgentSingleUserSession - participants = {"agent": agents[0], "user": users[0]} - case (1, u) if u > 1: - cls = SingleAgentMultiUserSession - participants = {"agent": agents[0], "users": users} - case _: - cls = MultiAgentMultiUserSession - participants = {"agents": agents, "users": users} - - return cls(**{**data, **participants}) - +# Task related models +# ------------------- WorkflowStep = ( PromptStep @@ -157,7 +174,9 @@ class TaskSpec(_Task): model_config = ConfigDict(extra="ignore") workflows: list[Workflow] - main: list[WorkflowStep] | None = None + + # Remove main field from the model + main: None = None class TaskSpecDef(TaskSpec): @@ -213,13 +232,3 @@ class UpdateTaskRequest(_UpdateTaskRequest): "extra": "allow", } ) - - -DataT = TypeVar("DataT", bound=BaseModel) - - -class ListResponse(BaseModel, Generic[DataT]): - items: list[DataT] - - -ChatResponse = ChunkChatResponse | MessageChatResponse diff --git a/agents-api/agents_api/clients/litellm.py b/agents-api/agents_api/clients/litellm.py index d1ae7018c..5c3a2e28e 100644 --- a/agents-api/agents_api/clients/litellm.py +++ b/agents-api/agents_api/clients/litellm.py @@ -1,6 +1,7 @@ from functools import wraps from litellm import acompletion as _acompletion +from litellm.utils import CustomStreamWrapper, ModelResponse from ..env import litellm_master_key, litellm_url @@ -8,7 +9,7 @@ @wraps(_acompletion) -async def acompletion(*, model: str, **kwargs): +async def acompletion(*, model: str, **kwargs) -> ModelResponse | CustomStreamWrapper: return await _acompletion( model=f"openai/{model}", # This is here because litellm proxy expects this format **kwargs, diff --git a/agents-api/agents_api/common/protocol/sessions.py b/agents-api/agents_api/common/protocol/sessions.py index 80f9049d4..d4db30a7d 100644 --- a/agents-api/agents_api/common/protocol/sessions.py +++ b/agents-api/agents_api/common/protocol/sessions.py @@ -11,7 +11,11 @@ Agent, ChatInput, ChatSettings, + MultiAgentMultiUserSession, Session, + SingleAgentMultiUserSession, + SingleAgentNoUserSession, + SingleAgentSingleUserSession, Tool, User, ) @@ -107,3 +111,33 @@ def get_chat_environment(self) -> dict[str, dict | list[dict]]: "settings": self.settings.model_dump(), "tools": [tool.model_dump() for tool in tools], } + + +def make_session( + *, + agents: list[UUID], + users: list[UUID], + **data: dict, +) -> Session: + """ + Create a new session object. + """ + cls, participants = None, {} + + match (len(agents), len(users)): + case (0, _): + raise ValueError("At least one agent must be provided.") + case (1, 0): + cls = SingleAgentNoUserSession + participants = {"agent": agents[0]} + case (1, 1): + cls = SingleAgentSingleUserSession + participants = {"agent": agents[0], "user": users[0]} + case (1, u) if u > 1: + cls = SingleAgentMultiUserSession + participants = {"agent": agents[0], "users": users} + case _: + cls = MultiAgentMultiUserSession + participants = {"agents": agents, "users": users} + + return cls(**{**data, **participants}) diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index 1bd4241b2..88a5dd3fd 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -1,7 +1,7 @@ -from typing import Annotated, Any, List, Tuple +from typing import Any, Generic, TypeVar from uuid import UUID -from pydantic import BaseModel, Field +from pydantic import BaseModel from ...autogen.openapi_model import ( Agent, @@ -15,6 +15,7 @@ TaskSpecDef, TaskToolDef, Tool, + TransitionTarget, UpdateTaskRequest, User, Workflow, @@ -22,6 +23,35 @@ ) +valid_transitions = { + # Start state + "init": ["wait", "error", "step", "cancelled"], + # End states + "finish": [], + "error": [], + "cancelled": [], + # Intermediate states + "wait": ["resume", "error", "cancelled"], + "resume": ["wait", "error", "step", "finish", "cancelled"], + "step": ["wait", "error", "step", "finish", "cancelled"], +} + +valid_previous_statuses = { + "running": ["queued", "starting", "awaiting_input"], + "cancelled": ["queued", "starting", "awaiting_input", "running"], +} + +transition_to_execution_status = { + "init": "queued", + "wait": "awaiting_input", + "resume": "running", + "step": "running", + "finish": "succeeded", + "error": "failed", + "cancelled": "cancelled", +} + + class ExecutionInput(BaseModel): developer_id: UUID execution: Execution @@ -29,13 +59,19 @@ class ExecutionInput(BaseModel): agent: Agent tools: list[Tool] arguments: dict[str, Any] + + # Not used at the moment user: User | None = None session: Session | None = None -class StepContext(ExecutionInput): - definition: WorkflowStep +WorkflowStepType = TypeVar("WorkflowStepType", bound=WorkflowStep) + + +class StepContext(ExecutionInput, Generic[WorkflowStepType]): + definition: WorkflowStepType | None inputs: list[dict[str, Any]] + current: TransitionTarget def model_dump(self, *args, **kwargs) -> dict[str, Any]: dump = super().model_dump(*args, **kwargs) @@ -46,11 +82,9 @@ def model_dump(self, *args, **kwargs) -> dict[str, Any]: return dump -class TransitionInfo(BaseModel): - from_: Tuple[str, int] - to: List[str | int] | None = None - type: Annotated[str, Field(pattern="^(finish|wait|error|step)$")] - outputs: dict[str, Any] | None = None +class StepOutcome(BaseModel): + output: dict[str, Any] + next: TransitionTarget | None = None def task_to_spec( diff --git a/agents-api/agents_api/models/chat/prepare_chat_context.py b/agents-api/agents_api/models/chat/prepare_chat_context.py index 0e076bc20..5ca93b8c0 100644 --- a/agents-api/agents_api/models/chat/prepare_chat_context.py +++ b/agents-api/agents_api/models/chat/prepare_chat_context.py @@ -5,8 +5,7 @@ from pycozo.client import QueryException from pydantic import ValidationError -from ...autogen.openapi_model import make_session -from ...common.protocol.sessions import ChatContext +from ...common.protocol.sessions import ChatContext, make_session from ..session.prepare_session_data import prepare_session_data from ..utils import ( cozo_query, diff --git a/agents-api/agents_api/models/execution/create_execution_transition.py b/agents-api/agents_api/models/execution/create_execution_transition.py index 701ffd3cc..c49a0929f 100644 --- a/agents-api/agents_api/models/execution/create_execution_transition.py +++ b/agents-api/agents_api/models/execution/create_execution_transition.py @@ -10,6 +10,7 @@ Transition, UpdateExecutionRequest, ) +from ...common.protocol.tasks import transition_to_execution_status, valid_transitions from ...common.utils.cozo import cozo_process_mutate_data from ..utils import ( cozo_query, @@ -21,28 +22,22 @@ ) from .update_execution import update_execution -valid_transitions = { - # Start state - "init": ["wait", "error", "step", "cancelled"], - # End states - "finish": [], - "error": [], - "cancelled": [], - # Intermediate states - "wait": ["resume", "error", "cancelled"], - "resume": ["wait", "error", "step", "finish", "cancelled"], - "step": ["wait", "error", "step", "finish", "cancelled"], -} - -transition_to_execution_status = { - "init": "queued", - "wait": "awaiting_input", - "resume": "running", - "step": "running", - "finish": "succeeded", - "error": "failed", - "cancelled": "cancelled", -} + +def validate_transition_targets(data: CreateTransitionRequest) -> None: + # Make sure the current/next targets are valid + if data.type in ("finish", "error", "cancelled"): + assert data.next is None, "Next target must be None for finish/error/cancelled" + + if data.type in ("wait", "init"): + assert data.next is None, "Next target must be None for wait/init" + + if data.type in ("resume", "step"): + assert data.next is not None, "Next target must be provided for resume/step" + + if data.next.workflow == data.current.workflow: + assert ( + data.next.step > data.current.step + ), "Next step must be greater than current" @rewrap_exceptions( @@ -54,7 +49,12 @@ ) @wrap_in_class( Transition, - transform=lambda d: {"id": d["transition_id"], **d}, + transform=lambda d: { + **d, + "id": d["transition_id"], + "current": {"workflow": d["current"][0], "step": d["current"][1]}, + "next": d["next"] and {"workflow": d["next"][0], "step": d["next"][1]}, + }, one=True, _kind="inserted", ) @@ -78,7 +78,19 @@ def create_execution_transition( data.execution_id = execution_id # Prepare the transition data - transition_data = data.model_dump(exclude_unset=True) + transition_data = data.model_dump(exclude_unset=True, exclude={"id"}) + + # Parse the current and next targets + validate_transition_targets(data) + current_target = transition_data.pop("current") + next_target = transition_data.pop("next") + + transition_data["current"] = (current_target["workflow"], current_target["step"]) + transition_data["next"] = next_target and ( + next_target["workflow"], + next_target["step"], + ) + columns, transition_values = cozo_process_mutate_data( { **transition_data, diff --git a/agents-api/agents_api/models/execution/get_execution_transition.py b/agents-api/agents_api/models/execution/get_execution_transition.py index 23ce46d40..39d30278c 100644 --- a/agents-api/agents_api/models/execution/get_execution_transition.py +++ b/agents-api/agents_api/models/execution/get_execution_transition.py @@ -37,9 +37,6 @@ def get_execution_transition( transition_id or task_token ), "At least one of `transition_id` or `task_token` must be provided." - fields = [k for k in Transition.model_fields.keys() if k != "id"] - fields_str = ", ".join(fields) - if transition_id: transition_id = str(transition_id) filter = "id = to_uuid($transition_id)" @@ -47,12 +44,24 @@ def get_execution_transition( else: filter = "task_token = $task_token" - get_query = f""" - ?[id, {fields_str}] := - *transitions {{ - transition_id: id, - {fields_str} - }}, + get_query = """ + ?[id, type, current, next, output, metadata, updated_at, created_at] := + *transitions { + transition_id: id, + type, + current: current_tuple, + next: next_tuple, + output, + metadata, + updated_at, + created_at, + }, + current = {"state": current_tuple->0, "step": current_tuple->1}, + next = if( + isnull(next_tuple), + null, + {"state": next_tuple->0, "step": next_tuple->1}, + ), """ get_query += filter diff --git a/agents-api/agents_api/models/execution/list_execution_transitions.py b/agents-api/agents_api/models/execution/list_execution_transitions.py index a36135f2d..9b103acf0 100644 --- a/agents-api/agents_api/models/execution/list_execution_transitions.py +++ b/agents-api/agents_api/models/execution/list_execution_transitions.py @@ -31,17 +31,25 @@ def list_execution_transitions( sort = f"{'-' if direction == 'desc' else ''}{sort_by}" query = f""" - ?[id, execution_id, type, current, next, output, metadata, updated_at, created_at] := *transitions {{ - execution_id, - transition_id: id, - type, - current, - next, - output, - metadata, - updated_at, - created_at, - }}, execution_id = to_uuid($execution_id) + ?[id, execution_id, type, current, next, output, metadata, updated_at, created_at] := + *transitions {{ + execution_id, + transition_id: id, + type, + current: current_tuple, + next: next_tuple, + output, + metadata, + updated_at, + created_at, + }}, + current = {{"state": current_tuple->0, "step": current_tuple->1}}, + next = if( + isnull(next_tuple), + null, + {{"state": next_tuple->0, "step": next_tuple->1}}, + ), + execution_id = to_uuid($execution_id) :limit $limit :offset $offset diff --git a/agents-api/agents_api/models/execution/prepare_execution_input.py b/agents-api/agents_api/models/execution/prepare_execution_input.py index 8ba687bca..39485b90f 100644 --- a/agents-api/agents_api/models/execution/prepare_execution_input.py +++ b/agents-api/agents_api/models/execution/prepare_execution_input.py @@ -29,7 +29,7 @@ } ) @wrap_in_class(ExecutionInput, one=True) -@cozo_query(debug=True) +@cozo_query @beartype def prepare_execution_input( *, diff --git a/agents-api/agents_api/models/execution/update_execution.py b/agents-api/agents_api/models/execution/update_execution.py index 59de6da14..a75c6eff4 100644 --- a/agents-api/agents_api/models/execution/update_execution.py +++ b/agents-api/agents_api/models/execution/update_execution.py @@ -9,6 +9,7 @@ ResourceUpdatedResponse, UpdateExecutionRequest, ) +from ...common.protocol.tasks import valid_previous_statuses as valid_previous_statuses_map from ...common.utils.cozo import cozo_process_mutate_data from ..utils import ( cozo_query, @@ -46,17 +47,7 @@ def update_execution( task_id = str(task_id) execution_id = str(execution_id) - valid_previous_statuses = [] - match data.status: - case "running": - valid_previous_statuses = ["queued", "starting", "awaiting_input"] - case "cancelled": - valid_previous_statuses = [ - "queued", - "starting", - "awaiting_input", - "running", - ] + valid_previous_statuses = valid_previous_statuses_map[data.status] execution_data = data.model_dump(exclude_none=True) diff --git a/agents-api/agents_api/models/session/get_session.py b/agents-api/agents_api/models/session/get_session.py index 42dafc30d..8e7d6adb5 100644 --- a/agents-api/agents_api/models/session/get_session.py +++ b/agents-api/agents_api/models/session/get_session.py @@ -5,7 +5,7 @@ from pycozo.client import QueryException from pydantic import ValidationError -from ...autogen.openapi_model import make_session +from ...common.protocol.sessions import make_session from ..utils import ( cozo_query, partialclass, diff --git a/agents-api/agents_api/models/session/list_sessions.py b/agents-api/agents_api/models/session/list_sessions.py index 70ac23a3b..dba36ab98 100644 --- a/agents-api/agents_api/models/session/list_sessions.py +++ b/agents-api/agents_api/models/session/list_sessions.py @@ -8,7 +8,7 @@ from pycozo.client import QueryException from pydantic import ValidationError -from ...autogen.openapi_model import make_session +from ...common.protocol.sessions import make_session from ...common.utils import json from ..utils import ( cozo_query, diff --git a/agents-api/agents_api/models/session/prepare_session_data.py b/agents-api/agents_api/models/session/prepare_session_data.py index 414f29007..754b0538c 100644 --- a/agents-api/agents_api/models/session/prepare_session_data.py +++ b/agents-api/agents_api/models/session/prepare_session_data.py @@ -5,8 +5,7 @@ from pycozo.client import QueryException from pydantic import ValidationError -from ...autogen.openapi_model import make_session -from ...common.protocol.sessions import SessionData +from ...common.protocol.sessions import SessionData, make_session from ..utils import ( cozo_query, partialclass, diff --git a/agents-api/agents_api/routers/tasks/patch_execution.py b/agents-api/agents_api/routers/tasks/patch_execution.py index 56c38fc38..0bd0b01ba 100644 --- a/agents-api/agents_api/routers/tasks/patch_execution.py +++ b/agents-api/agents_api/routers/tasks/patch_execution.py @@ -1,10 +1,9 @@ from typing import Annotated -from fastapi import Depends, HTTPException, status +from fastapi import Depends from pydantic import UUID4 from agents_api.autogen.openapi_model import ( - Execution, ResourceUpdatedResponse, UpdateExecutionRequest, ) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 1b1d94bd3..495b62f62 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -25,7 +25,6 @@ from ..common.protocol.tasks import ( ExecutionInput, StepContext, - TransitionInfo, ) @@ -35,10 +34,10 @@ class TaskExecutionWorkflow: async def run( self, execution_input: ExecutionInput, - start: tuple[str, int] = ("main", 0), + current: tuple[str, int] = ("main", 0), previous_inputs: list[dict] = [], ) -> None: - wf_name, step_idx = start + wf_name, step_idx = current workflow_map = {wf.name: wf.steps for wf in execution_input.task.workflows} current_workflow = workflow_map[wf_name] previous_inputs = previous_inputs or [execution_input.arguments] diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index e4edb74b7..aaaada66e 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2255,13 +2255,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.12" +version = "1.43.13" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.12-py3-none-any.whl", hash = "sha256:f2c5f498a079df6eb8448ac41704367a389ea679a22e195c79b7963ede5cc462"}, - {file = "litellm-1.43.12.tar.gz", hash = "sha256:719eca58904942465dfd827e9d8f317112996ef481db71f9562f5263a553c74a"}, + {file = "litellm-1.43.13-py3-none-any.whl", hash = "sha256:47c27c1c1b394d6098c68eec637008b07a254dadc4b82206b1a9f960621a8776"}, + {file = "litellm-1.43.13.tar.gz", hash = "sha256:b0273cbed3f7a35f197c98d92b1a13038b430e5e78d30db7d94d8237a3b98641"}, ] [package.dependencies] @@ -2867,13 +2867,13 @@ files = [ [[package]] name = "openai" -version = "1.40.6" +version = "1.40.8" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.6-py3-none-any.whl", hash = "sha256:b36372124a779381a420a34dd96f762baa748b6bdfaf83a6b9f2745f72ccc1c5"}, - {file = "openai-1.40.6.tar.gz", hash = "sha256:2239232bcb7f4bd4ce8e02544b5769618582411cf399816d96686d1b6c1e5c8d"}, + {file = "openai-1.40.8-py3-none-any.whl", hash = "sha256:3ed4ddad48e0dde059c9b4d3dc240e47781beca2811e52ba449ddc4a471a2fd4"}, + {file = "openai-1.40.8.tar.gz", hash = "sha256:e225f830b946378e214c5b2cfa8df28ba2aeb7e9d44f738cb2a926fd971f5bc0"}, ] [package.dependencies] diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 3afbd3195..56b9dc7f6 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -40,6 +40,7 @@ from agents_api.models.user.create_user import create_user from agents_api.models.user.delete_user import delete_user from agents_api.web import app + # from agents_api.worker.worker import create_worker from agents_api.worker.worker import create_worker @@ -65,7 +66,7 @@ def activity_environment(): @fixture(scope="global") async def temporal_worker(): - async with (await WorkflowEnvironment.start_local()) as env: + async with await WorkflowEnvironment.start_local() as env: worker = create_worker(client=env.client) worker_task = asyncio.create_task(worker.run()) @@ -77,7 +78,7 @@ async def temporal_worker(): [kill_signal, worker_task], return_when=asyncio.FIRST_COMPLETED, ) - + @fixture(scope="test") def patch_temporal_get_client( diff --git a/agents-api/tests/test_docs_routes.py b/agents-api/tests/test_docs_routes.py index 88a39937d..a33e1d19e 100644 --- a/agents-api/tests/test_docs_routes.py +++ b/agents-api/tests/test_docs_routes.py @@ -32,7 +32,9 @@ def _(make_request=make_request, user=test_user, get_client=patch_temporal_get_c @test("route: create agent doc") -def _(make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client): +def _( + make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client +): data = dict( title="Test Agent Doc", content=["This is a test agent document."], @@ -52,7 +54,9 @@ def _(make_request=make_request, agent=test_agent, get_client=patch_temporal_get @test("route: delete doc") -def _(make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client): +def _( + make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client +): data = dict( title="Test Agent Doc", content=["This is a test agent document."], @@ -81,7 +85,9 @@ def _(make_request=make_request, agent=test_agent, get_client=patch_temporal_get @test("route: get doc") -def _(make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client): +def _( + make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client +): data = dict( title="Test Agent Doc", content=["This is a test agent document."], diff --git a/agents-api/tests/test_execution_queries.py b/agents-api/tests/test_execution_queries.py index 13bc809d5..70fef5bb8 100644 --- a/agents-api/tests/test_execution_queries.py +++ b/agents-api/tests/test_execution_queries.py @@ -76,12 +76,10 @@ def _(client=cozo_client, developer_id=test_developer_id, execution=test_executi developer_id=developer_id, execution_id=execution.id, data=CreateTransitionRequest( - **{ - "type": "step", - "output": {"result": "test"}, - "current": ["main", 0], - "next": None, - } + type="step", + output={"result": "test"}, + current={"workflow": "main", "step": 0}, + next={"workflow": "main", "step": 1}, ), client=client, ) @@ -102,12 +100,10 @@ def _( developer_id=developer_id, execution_id=execution.id, data=CreateTransitionRequest( - **{ - "type": "step", - "output": {"result": "test"}, - "current": ["main", 0], - "next": None, - } + type="cancelled", + output={"result": "test"}, + current={"workflow": "main", "step": 0}, + next=None, ), task_id=task.id, update_execution_status=True, @@ -115,5 +111,5 @@ def _( ) assert result is not None - assert result.type == "step" + assert result.type == "cancelled" assert result.output == {"result": "test"} diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index c7371d4e1..69e133f76 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -96,6 +96,7 @@ ExecutionsResumeExecutionRequest, ExecutionsStopExecutionRequest, ExecutionsTransition, + ExecutionsTransitionTarget, ExecutionsTransitionType, ExecutionsUpdateExecutionRequest, ExecutionsUpdateExecutionRequest_Cancelled, @@ -297,6 +298,7 @@ "ExecutionsResumeExecutionRequest", "ExecutionsStopExecutionRequest", "ExecutionsTransition", + "ExecutionsTransitionTarget", "ExecutionsTransitionType", "ExecutionsUpdateExecutionRequest", "ExecutionsUpdateExecutionRequest_Cancelled", diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index 3df1542cd..575f35db9 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -115,6 +115,7 @@ from .executions_resume_execution_request import ExecutionsResumeExecutionRequest from .executions_stop_execution_request import ExecutionsStopExecutionRequest from .executions_transition import ExecutionsTransition +from .executions_transition_target import ExecutionsTransitionTarget from .executions_transition_type import ExecutionsTransitionType from .executions_update_execution_request import ( ExecutionsUpdateExecutionRequest, @@ -337,6 +338,7 @@ "ExecutionsResumeExecutionRequest", "ExecutionsStopExecutionRequest", "ExecutionsTransition", + "ExecutionsTransitionTarget", "ExecutionsTransitionType", "ExecutionsUpdateExecutionRequest", "ExecutionsUpdateExecutionRequest_Cancelled", diff --git a/sdks/python/julep/api/types/executions_transition.py b/sdks/python/julep/api/types/executions_transition.py index c0982cfcc..901591f5e 100644 --- a/sdks/python/julep/api/types/executions_transition.py +++ b/sdks/python/julep/api/types/executions_transition.py @@ -6,6 +6,7 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_uuid import CommonUuid +from .executions_transition_target import ExecutionsTransitionTarget from .executions_transition_type import ExecutionsTransitionType @@ -13,8 +14,8 @@ class ExecutionsTransition(pydantic_v1.BaseModel): type: ExecutionsTransitionType execution_id: CommonUuid output: typing.Dict[str, typing.Any] - current: typing.List[typing.Any] - next: typing.Optional[typing.List[typing.Any]] = None + current: ExecutionsTransitionTarget + next: typing.Optional[ExecutionsTransitionTarget] = None id: CommonUuid metadata: typing.Optional[typing.Dict[str, typing.Any]] = None created_at: dt.datetime = pydantic_v1.Field() diff --git a/sdks/python/julep/api/types/executions_transition_target.py b/sdks/python/julep/api/types/executions_transition_target.py new file mode 100644 index 000000000..30b348941 --- /dev/null +++ b/sdks/python/julep/api/types/executions_transition_target.py @@ -0,0 +1,44 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_valid_python_identifier import CommonValidPythonIdentifier + + +class ExecutionsTransitionTarget(pydantic_v1.BaseModel): + workflow: CommonValidPythonIdentifier + step: int + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index bfef1a91f..9b612ca30 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -1751,13 +1751,13 @@ files = [ [[package]] name = "openai" -version = "1.40.6" +version = "1.40.8" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.6-py3-none-any.whl", hash = "sha256:b36372124a779381a420a34dd96f762baa748b6bdfaf83a6b9f2745f72ccc1c5"}, - {file = "openai-1.40.6.tar.gz", hash = "sha256:2239232bcb7f4bd4ce8e02544b5769618582411cf399816d96686d1b6c1e5c8d"}, + {file = "openai-1.40.8-py3-none-any.whl", hash = "sha256:3ed4ddad48e0dde059c9b4d3dc240e47781beca2811e52ba449ddc4a471a2fd4"}, + {file = "openai-1.40.8.tar.gz", hash = "sha256:e225f830b946378e214c5b2cfa8df28ba2aeb7e9d44f738cb2a926fd971f5bc0"}, ] [package.dependencies] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index 3aa08e1f6..d92686f43 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -78,6 +78,7 @@ export type { Executions_ResumeExecutionRequest } from "./models/Executions_Resu export type { Executions_StopExecutionRequest } from "./models/Executions_StopExecutionRequest"; export type { Executions_TaskTokenResumeExecutionRequest } from "./models/Executions_TaskTokenResumeExecutionRequest"; export type { Executions_Transition } from "./models/Executions_Transition"; +export type { Executions_TransitionTarget } from "./models/Executions_TransitionTarget"; export type { Executions_UpdateExecutionRequest } from "./models/Executions_UpdateExecutionRequest"; export type { Jobs_JobState } from "./models/Jobs_JobState"; export type { Jobs_JobStatus } from "./models/Jobs_JobStatus"; @@ -196,6 +197,7 @@ export { $Executions_ResumeExecutionRequest } from "./schemas/$Executions_Resume export { $Executions_StopExecutionRequest } from "./schemas/$Executions_StopExecutionRequest"; export { $Executions_TaskTokenResumeExecutionRequest } from "./schemas/$Executions_TaskTokenResumeExecutionRequest"; export { $Executions_Transition } from "./schemas/$Executions_Transition"; +export { $Executions_TransitionTarget } from "./schemas/$Executions_TransitionTarget"; export { $Executions_UpdateExecutionRequest } from "./schemas/$Executions_UpdateExecutionRequest"; export { $Jobs_JobState } from "./schemas/$Jobs_JobState"; export { $Jobs_JobStatus } from "./schemas/$Jobs_JobStatus"; diff --git a/sdks/ts/src/api/models/Executions_Transition.ts b/sdks/ts/src/api/models/Executions_Transition.ts index edc8756d9..8e8ae1f90 100644 --- a/sdks/ts/src/api/models/Executions_Transition.ts +++ b/sdks/ts/src/api/models/Executions_Transition.ts @@ -3,12 +3,13 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_uuid } from "./Common_uuid"; +import type { Executions_TransitionTarget } from "./Executions_TransitionTarget"; export type Executions_Transition = { readonly type: "finish" | "wait" | "resume" | "error" | "step" | "cancelled"; readonly execution_id: Common_uuid; readonly output: Record; - readonly current: Array; - readonly next: Array | null; + readonly current: Executions_TransitionTarget; + readonly next: Executions_TransitionTarget | null; readonly id: Common_uuid; metadata?: Record; /** diff --git a/sdks/ts/src/api/models/Executions_TransitionTarget.ts b/sdks/ts/src/api/models/Executions_TransitionTarget.ts new file mode 100644 index 000000000..804d89dcd --- /dev/null +++ b/sdks/ts/src/api/models/Executions_TransitionTarget.ts @@ -0,0 +1,9 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_validPythonIdentifier } from "./Common_validPythonIdentifier"; +export type Executions_TransitionTarget = { + workflow: Common_validPythonIdentifier; + step: number; +}; diff --git a/sdks/ts/src/api/schemas/$Executions_Transition.ts b/sdks/ts/src/api/schemas/$Executions_Transition.ts index 223abfb13..e427b3c24 100644 --- a/sdks/ts/src/api/schemas/$Executions_Transition.ts +++ b/sdks/ts/src/api/schemas/$Executions_Transition.ts @@ -28,18 +28,22 @@ export const $Executions_Transition = { isRequired: true, }, current: { - type: "array", - contains: { - properties: {}, - }, + type: "all-of", + contains: [ + { + type: "Executions_TransitionTarget", + }, + ], isReadOnly: true, isRequired: true, }, next: { - type: "array", - contains: { - properties: {}, - }, + type: "all-of", + contains: [ + { + type: "Executions_TransitionTarget", + }, + ], isReadOnly: true, isRequired: true, isNullable: true, diff --git a/sdks/ts/src/api/schemas/$Executions_TransitionTarget.ts b/sdks/ts/src/api/schemas/$Executions_TransitionTarget.ts new file mode 100644 index 000000000..d1ead50a5 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Executions_TransitionTarget.ts @@ -0,0 +1,17 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Executions_TransitionTarget = { + properties: { + workflow: { + type: "Common_validPythonIdentifier", + isRequired: true, + }, + step: { + type: "number", + isRequired: true, + format: "uint16", + }, + }, +} as const; diff --git a/typespec/executions/models.tsp b/typespec/executions/models.tsp index dd5378b30..961c8389a 100644 --- a/typespec/executions/models.tsp +++ b/typespec/executions/models.tsp @@ -108,7 +108,10 @@ alias TransitionType = ( | "cancelled" ); -alias TransitionTarget = [validPythonIdentifier, uint16]; +model TransitionTarget { + workflow: validPythonIdentifier; + step: uint16; +} model Transition { @visibility("read") From abb6e9ab4d4ccc639813e1e98160bdd85ad11bfc Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 01:54:42 -0400 Subject: [PATCH 059/110] wip Signed-off-by: Diwank Tomer --- .../agents_api/activities/embed_docs.py | 2 + agents-api/agents_api/activities/mem_mgmt.py | 2 + .../agents_api/activities/mem_rating.py | 2 + .../agents_api/activities/summarization.py | 2 + .../activities/task_steps/__init__.py | 154 +----------- .../activities/task_steps/evaluate_step.py | 22 ++ .../activities/task_steps/if_else_step.py | 25 ++ .../activities/task_steps/prompt_step.py | 60 +++++ .../task_steps/raise_complete_async.py | 8 + .../activities/task_steps/tool_call_step.py | 24 ++ .../activities/task_steps/transition_step.py | 36 +++ .../agents_api/activities/task_steps/utils.py | 11 + .../activities/task_steps/yield_step.py | 29 +++ .../agents_api/activities/truncation.py | 2 + agents-api/agents_api/clients/temporal.py | 13 +- .../agents_api/common/protocol/tasks.py | 26 +- .../agents_api/workflows/task_execution.py | 229 +++++++++++------- 17 files changed, 405 insertions(+), 242 deletions(-) create mode 100644 agents-api/agents_api/activities/task_steps/evaluate_step.py create mode 100644 agents-api/agents_api/activities/task_steps/if_else_step.py create mode 100644 agents-api/agents_api/activities/task_steps/prompt_step.py create mode 100644 agents-api/agents_api/activities/task_steps/raise_complete_async.py create mode 100644 agents-api/agents_api/activities/task_steps/tool_call_step.py create mode 100644 agents-api/agents_api/activities/task_steps/transition_step.py create mode 100644 agents-api/agents_api/activities/task_steps/utils.py create mode 100644 agents-api/agents_api/activities/task_steps/yield_step.py diff --git a/agents-api/agents_api/activities/embed_docs.py b/agents-api/agents_api/activities/embed_docs.py index 1000f456d..7198a7e54 100644 --- a/agents-api/agents_api/activities/embed_docs.py +++ b/agents-api/agents_api/activities/embed_docs.py @@ -1,5 +1,6 @@ from uuid import UUID +from beartype import beartype from temporalio import activity from ..clients import embed as embedder @@ -10,6 +11,7 @@ @activity.defn +@beartype async def embed_docs( developer_id: UUID, doc_id: UUID, diff --git a/agents-api/agents_api/activities/mem_mgmt.py b/agents-api/agents_api/activities/mem_mgmt.py index ea4bb84d2..7cd4a7d6b 100644 --- a/agents-api/agents_api/activities/mem_mgmt.py +++ b/agents-api/agents_api/activities/mem_mgmt.py @@ -2,6 +2,7 @@ from typing import Callable from uuid import UUID +from beartype import beartype from temporalio import activity from ..autogen.openapi_model import InputChatMLMessage @@ -155,6 +156,7 @@ async def run_prompt( @activity.defn +@beartype async def mem_mgmt( dialog: list[InputChatMLMessage], session_id: UUID, diff --git a/agents-api/agents_api/activities/mem_rating.py b/agents-api/agents_api/activities/mem_rating.py index 222148f4c..c681acbc3 100644 --- a/agents-api/agents_api/activities/mem_rating.py +++ b/agents-api/agents_api/activities/mem_rating.py @@ -1,6 +1,7 @@ from textwrap import dedent from typing import Callable +from beartype import beartype from temporalio import activity from ..clients import litellm @@ -67,6 +68,7 @@ async def run_prompt( @activity.defn +@beartype async def mem_rating(memory: str) -> None: # session_id = UUID(session_id) # entries = [ diff --git a/agents-api/agents_api/activities/summarization.py b/agents-api/agents_api/activities/summarization.py index 8a45927ee..662554181 100644 --- a/agents-api/agents_api/activities/summarization.py +++ b/agents-api/agents_api/activities/summarization.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 +from beartype import beartype import pandas as pd from temporalio import activity @@ -20,6 +21,7 @@ def get_toplevel_entries_query(*args, **kwargs): @activity.defn +@beartype async def summarization(session_id: str) -> None: raise NotImplementedError() # session_id = UUID(session_id) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index f4cef8d3b..28932c02e 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -1,144 +1,10 @@ -import asyncio - -from simpleeval import simple_eval -from temporalio import activity - -from ...autogen.openapi_model import ( - CreateTransitionRequest, - EvaluateStep, - IfElseWorkflowStep, - InputChatMLMessage, - PromptStep, - ToolCallStep, - YieldStep, -) -from ...clients import ( - litellm, # We dont directly import `acompletion` so we can mock it -) -from ...common.protocol.tasks import ( - StepContext, - StepOutcome, -) -from ...common.utils.template import render_template -from ...models.execution.create_execution_transition import ( - create_execution_transition as create_execution_transition_query, -) - - -@activity.defn -async def prompt_step(context: StepContext[PromptStep]) -> StepOutcome: - # Get context data - context_data: dict = context.model_dump() - - # Render template messages - prompt = ( - [InputChatMLMessage(content=context.definition.prompt)] - if isinstance(context.definition.prompt, str) - else context.definition.prompt - ) - - template_messages: list[InputChatMLMessage] = prompt - messages = await asyncio.gather( - *[ - render_template(msg.content, context_data, skip_vars=["developer_id"]) - for msg in template_messages - ] - ) - - messages = [ - InputChatMLMessage(role="user", content=m) - if isinstance(m, str) - else InputChatMLMessage(**m) - for m in messages - ] - - settings: dict = context.definition.settings.model_dump() - # Get settings and run llm - response = await litellm.acompletion( - messages=messages, - **settings, - ) - - return StepOutcome( - output=response.model_dump(), - next=None, - ) - - -@activity.defn -async def evaluate_step(context: StepContext) -> dict: - assert isinstance(context.definition, EvaluateStep) - - names = {} - for i in context.inputs: - names.update(i) - - return { - "result": { - k: simple_eval(v, names=names) - for k, v in context.definition.evaluate.items() - } - } - - -@activity.defn -async def yield_step(context: StepContext) -> dict: - if not isinstance(context.definition, YieldStep): - return {} - - # TODO: implement - - return {"test": "result"} - - -@activity.defn -async def tool_call_step(context: StepContext) -> dict: - assert isinstance(context.definition, ToolCallStep) - - context.definition.tool_id - context.definition.arguments - # get tool by id - # call tool - - return {} - - -@activity.defn -async def if_else_step(context: StepContext[IfElseWorkflowStep]) -> dict: - context_data: dict = context.model_dump() - - next_workflow = ( - context.definition.then - if simple_eval(context.definition.if_, names=context_data) - else context.definition.else_ - ) - - return {"goto_workflow": next_workflow} - - -@activity.defn -async def transition_step( - context: StepContext[None], - transition_info: CreateTransitionRequest, -): - need_to_wait = transition_info.type == "wait" - - # Get task token if it's a waiting step - if need_to_wait: - task_token = activity.info().task_token - transition_info.task_token = task_token - - # Create transition - activity.heartbeat("Creating transition in db") - create_execution_transition_query( - developer_id=context.developer_id, - execution_id=context.execution.id, - task_id=context.task.id, - data=transition_info, - update_execution_status=True, - ) - - # Raise if it's a waiting step - if need_to_wait: - activity.heartbeat("Starting to wait") - activity.raise_complete_async() +# ruff: noqa: F401, F403, F405 + +from .evaluate_step import evaluate_step +from .if_else_step import if_else_step +from .prompt_step import prompt_step +from .raise_complete_async import raise_complete_async +from .tool_call_step import tool_call_step +from .transition_step import transition_step +from .wait_for_input_step import wait_for_input_step +from .yield_step import yield_step \ No newline at end of file diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py new file mode 100644 index 000000000..6f6630f4a --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -0,0 +1,22 @@ +from typing import Any + +from beartype import beartype +from temporalio import activity + +from ...activities.task_steps.utils import simple_eval_dict +from ...autogen.openapi_model import EvaluateStep +from ...common.protocol.tasks import ( + StepContext, + StepOutcome, +) + + +@activity.defn +@beartype +async def evaluate_step( + context: StepContext[EvaluateStep], +) -> StepOutcome[dict[str, Any]]: + exprs = context.definition.arguments + output = simple_eval_dict(exprs, values=context.model_dump()) + + return StepOutcome(output=output) diff --git a/agents-api/agents_api/activities/task_steps/if_else_step.py b/agents-api/agents_api/activities/task_steps/if_else_step.py new file mode 100644 index 000000000..e179b05a6 --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/if_else_step.py @@ -0,0 +1,25 @@ +from beartype import beartype +from simpleeval import simple_eval +from temporalio import activity + +from ...autogen.openapi_model import ( + IfElseWorkflowStep, +) +from ...common.protocol.tasks import ( + StepContext, +) + + +@activity.defn +@beartype +async def if_else_step(context: StepContext[IfElseWorkflowStep]) -> dict: + raise NotImplementedError() + # context_data: dict = context.model_dump() + + # next_workflow = ( + # context.definition.then + # if simple_eval(context.definition.if_, names=context_data) + # else context.definition.else_ + # ) + + # return {"goto_workflow": next_workflow} \ No newline at end of file diff --git a/agents-api/agents_api/activities/task_steps/prompt_step.py b/agents-api/agents_api/activities/task_steps/prompt_step.py new file mode 100644 index 000000000..90ab975d3 --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/prompt_step.py @@ -0,0 +1,60 @@ +import asyncio + +from beartype import beartype +from temporalio import activity + +from ...autogen.openapi_model import ( + InputChatMLMessage, + PromptStep, +) +from ...clients import ( + litellm, # We dont directly import `acompletion` so we can mock it +) +from ...common.protocol.tasks import ( + StepContext, + StepOutcome, +) +from ...common.utils.template import render_template + + +@activity.defn +@beartype +async def prompt_step(context: StepContext[PromptStep]) -> StepOutcome: + # Get context data + context_data: dict = context.model_dump() + + # Render template messages + prompt = ( + [InputChatMLMessage(content=context.definition.prompt)] + if isinstance(context.definition.prompt, str) + else context.definition.prompt + ) + + template_messages: list[InputChatMLMessage] = prompt + messages = await asyncio.gather( + *[ + render_template(msg.content, context_data, skip_vars=["developer_id"]) + for msg in template_messages + ] + ) + + messages = [ + ( + InputChatMLMessage(role="user", content=m) + if isinstance(m, str) + else InputChatMLMessage(**m) + ) + for m in messages + ] + + settings: dict = context.definition.settings.model_dump() + # Get settings and run llm + response = await litellm.acompletion( + messages=messages, + **settings, + ) + + return StepOutcome( + output=response.model_dump(), + next=None, + ) diff --git a/agents-api/agents_api/activities/task_steps/raise_complete_async.py b/agents-api/agents_api/activities/task_steps/raise_complete_async.py new file mode 100644 index 000000000..ca1200a87 --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/raise_complete_async.py @@ -0,0 +1,8 @@ +from temporalio import activity + + +@activity.defn +async def raise_complete_async() -> None: + + activity.heartbeat("Starting to wait") + activity.raise_complete_async() diff --git a/agents-api/agents_api/activities/task_steps/tool_call_step.py b/agents-api/agents_api/activities/task_steps/tool_call_step.py new file mode 100644 index 000000000..0b12cad97 --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/tool_call_step.py @@ -0,0 +1,24 @@ +from beartype import beartype +from temporalio import activity + +from ...autogen.openapi_model import ( + ToolCallStep, +) +from ...common.protocol.tasks import ( + StepContext, +) + + + +@activity.defn +@beartype +async def tool_call_step(context: StepContext) -> dict: + raise NotImplementedError() + # assert isinstance(context.definition, ToolCallStep) + + # context.definition.tool_id + # context.definition.arguments + # # get tool by id + # # call tool + + # return {} \ No newline at end of file diff --git a/agents-api/agents_api/activities/task_steps/transition_step.py b/agents-api/agents_api/activities/task_steps/transition_step.py new file mode 100644 index 000000000..b70428df8 --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/transition_step.py @@ -0,0 +1,36 @@ +from beartype import beartype +from temporalio import activity + +from ...autogen.openapi_model import ( + CreateTransitionRequest, +) +from ...common.protocol.tasks import ( + StepContext, +) +from ...models.execution.create_execution_transition import ( + create_execution_transition as create_execution_transition_query, +) + + +@activity.defn +@beartype +async def transition_step( + context: StepContext[None], + transition_info: CreateTransitionRequest, +) -> None: + need_to_wait = transition_info.type == "wait" + + # Get task token if it's a waiting step + if need_to_wait: + task_token = activity.info().task_token + transition_info.task_token = task_token + + # Create transition + activity.heartbeat("Creating transition in db") + create_execution_transition_query( + developer_id=context.developer_id, + execution_id=context.execution.id, + task_id=context.task.id, + data=transition_info, + update_execution_status=True, + ) \ No newline at end of file diff --git a/agents-api/agents_api/activities/task_steps/utils.py b/agents-api/agents_api/activities/task_steps/utils.py new file mode 100644 index 000000000..e3d953a4a --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/utils.py @@ -0,0 +1,11 @@ +from typing import Any + +from beartype import beartype +from simpleeval import simple_eval + + +@beartype +def simple_eval_dict( + exprs: dict[str, str], *, values: dict[str, Any] +) -> dict[str, Any]: + return {k: simple_eval(v, names=values) for k, v in exprs.items()} diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py new file mode 100644 index 000000000..0710c99e6 --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -0,0 +1,29 @@ +from typing import Any +from agents_api.autogen.Executions import TransitionTarget +from beartype import beartype +from temporalio import activity + +from ...autogen.openapi_model import ( + YieldStep, +) +from ...common.protocol.tasks import ( + StepContext, + StepOutcome, +) + +from .utils import simple_eval_dict + +@activity.defn +@beartype +async def yield_step(context: StepContext[YieldStep]) -> StepOutcome[dict[str, Any]]: + workflow = context.definition.workflow + exprs = context.definition.arguments + arguments = simple_eval_dict(exprs, values=context.model_dump()) + + transition_target = TransitionTarget( + workflow=workflow, + step=0, + ) + + return StepOutcome(output=arguments, transition_to=("step", transition_target)) + diff --git a/agents-api/agents_api/activities/truncation.py b/agents-api/agents_api/activities/truncation.py index 7f381ac0f..d0ce919f0 100644 --- a/agents-api/agents_api/activities/truncation.py +++ b/agents-api/agents_api/activities/truncation.py @@ -1,5 +1,6 @@ from uuid import UUID +from beartype import beartype from temporalio import activity from agents_api.autogen.openapi_model import Entry @@ -26,6 +27,7 @@ def get_extra_entries(messages: list[Entry], token_count_threshold: int) -> list @activity.defn +@beartype async def truncation(session_id: str, token_count_threshold: int) -> None: session_id = UUID(session_id) diff --git a/agents-api/agents_api/clients/temporal.py b/agents-api/agents_api/clients/temporal.py index 29ceedded..2130c3947 100644 --- a/agents-api/agents_api/clients/temporal.py +++ b/agents-api/agents_api/clients/temporal.py @@ -2,14 +2,14 @@ from temporalio.client import Client, TLSConfig -from agents_api.env import ( +from ..autogen.openapi_model import TransitionTarget +from ..common.protocol.tasks import ExecutionInput +from ..env import ( temporal_client_cert, temporal_namespace, temporal_private_key, temporal_worker_url, ) - -from ..common.protocol.tasks import ExecutionInput from ..worker.codec import pydantic_data_converter @@ -35,16 +35,19 @@ async def get_client( async def run_task_execution_workflow( + *, execution_input: ExecutionInput, job_id: UUID, - start: tuple[str, int] = ("main", 0), + start: TransitionTarget = TransitionTarget(workflow="main", step=0), previous_inputs: list[dict] = [], client: Client | None = None, ): + from ..workflows.task_execution import TaskExecutionWorkflow + client = client or (await get_client()) return await client.start_workflow( - "TaskExecutionWorkflow", + TaskExecutionWorkflow.run, args=[execution_input, start, previous_inputs], task_queue="memory-task-queue", id=str(job_id), diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index 88a5dd3fd..5e273d657 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -1,7 +1,7 @@ from typing import Any, Generic, TypeVar from uuid import UUID -from pydantic import BaseModel +from pydantic import BaseModel, computed_field from ...autogen.openapi_model import ( Agent, @@ -16,6 +16,7 @@ TaskToolDef, Tool, TransitionTarget, + TransitionType, UpdateTaskRequest, User, Workflow, @@ -73,18 +74,29 @@ class StepContext(ExecutionInput, Generic[WorkflowStepType]): inputs: list[dict[str, Any]] current: TransitionTarget + @computed_field + @property + def outputs(self) -> list[dict[str, Any]]: + return self.inputs[1:] + + @computed_field + @property + def current_input(self) -> dict[str, Any]: + return self.inputs[-1] + def model_dump(self, *args, **kwargs) -> dict[str, Any]: dump = super().model_dump(*args, **kwargs) - - dump["_"] = self.inputs[-1] - dump["outputs"] = self.inputs[1:] + dump["_"] = self.current_input return dump -class StepOutcome(BaseModel): - output: dict[str, Any] - next: TransitionTarget | None = None +OutcomeType = TypeVar("OutcomeType", bound=BaseModel) + + +class StepOutcome(BaseModel, Generic[OutcomeType]): + output: OutcomeType | None + transition_to: tuple[TransitionType, TransitionTarget] | None = None def task_to_spec( diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 495b62f62..be639697f 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -3,6 +3,8 @@ from datetime import timedelta +from agents_api.autogen.Executions import TransitionTarget +from agents_api.autogen.openapi_model import CreateTransitionRequest from temporalio import workflow with workflow.unsafe.imports_passed_through(): @@ -12,7 +14,9 @@ prompt_step, tool_call_step, transition_step, + yield_step, ) + from ..autogen.openapi_model import ( ErrorWorkflowStep, EvaluateStep, @@ -22,28 +26,40 @@ WaitForInputStep, YieldStep, ) + from ..common.protocol.tasks import ( ExecutionInput, StepContext, + StepOutcome, ) +STEP_TO_ACTIVITY = { + PromptStep: prompt_step, + EvaluateStep: evaluate_step, + ToolCallStep: tool_call_step, + IfElseWorkflowStep: if_else_step, + YieldStep: yield_step, +} + + @workflow.defn class TaskExecutionWorkflow: @workflow.run async def run( self, execution_input: ExecutionInput, - current: tuple[str, int] = ("main", 0), + start: TransitionTarget = TransitionTarget(workflow="main", step=0), previous_inputs: list[dict] = [], ) -> None: - wf_name, step_idx = current - workflow_map = {wf.name: wf.steps for wf in execution_input.task.workflows} - current_workflow = workflow_map[wf_name] previous_inputs = previous_inputs or [execution_input.arguments] - step = current_workflow[step_idx] + workflow_map = {wf.name: wf.steps for wf in execution_input.task.workflows} + + current_workflow = workflow_map[start.workflow] + step = current_workflow[start.step] + step_type = type(step) - context = StepContext( + context = StepContext[step_type]( developer_id=execution_input.developer_id, execution=execution_input.execution, task=execution_input.task, @@ -56,91 +72,132 @@ async def run( inputs=previous_inputs, ) - should_wait, is_error = False, False - # Run the step - match step: - case PromptStep(): - outputs = await workflow.execute_activity( - prompt_step, - context, - schedule_to_close_timeout=timedelta(seconds=600), - ) - - # TODO: ChatCompletion does not have tool_calls - # if outputs.tool_calls is not None: - # should_wait = True + next = None + outcome = None + activity = STEP_TO_ACTIVITY.get(step_type) + final_output = None + is_last = False - case EvaluateStep(): - outputs = await workflow.execute_activity( - evaluate_step, - context, - schedule_to_close_timeout=timedelta(seconds=600), - ) - case YieldStep(): - outputs = await workflow.execute_child_workflow( - TaskExecutionWorkflow.run, - args=[execution_input, (step.workflow, 0), previous_inputs], - ) - case ToolCallStep(): - outputs = await workflow.execute_activity( - tool_call_step, - context, - schedule_to_close_timeout=timedelta(seconds=600), + if activity: + outcome = await workflow.execute_activity( + activity, + context, + schedule_to_close_timeout=timedelta(seconds=600), + ) + + match step, outcome: + case YieldStep(), StepOutcome(output=output, transition_to=(transition_type, next)): + transition_request = CreateTransitionRequest( + type=transition_type, + current=start, + next=next, + output=output, ) - case ErrorWorkflowStep(): - is_error = True - case IfElseWorkflowStep(): - outputs = await workflow.execute_activity( - if_else_step, - context, + + await workflow.execute_activity( + transition_step, + args=[context, transition_request], schedule_to_close_timeout=timedelta(seconds=600), ) - workflow_step = YieldStep(**outputs["goto_workflow"]) - outputs = await workflow.execute_child_workflow( + yield_outcome: StepOutcome = await workflow.execute_child_workflow( TaskExecutionWorkflow.run, - args=[ - execution_input, - (workflow_step.workflow, 0), - previous_inputs, - ], + args=[execution_input, next, [output]], ) - case WaitForInputStep(): - should_wait = True - - is_last = step_idx + 1 == len(current_workflow) - # Transition type - transition_type = ( - "awaiting_input" - if should_wait - else ("finish" if is_last else ("error" if is_error else "step")) - ) - - # Transition to the next step - transition_info = TransitionInfo( - from_=(wf_name, step_idx), - to=None if (is_last or should_wait) else (wf_name, step_idx + 1), - type=transition_type, - ) - await workflow.execute_activity( - transition_step, - args=[ - context, - transition_info, - "failed" if is_error else "awaiting_input", - ], - schedule_to_close_timeout=timedelta(seconds=600), - ) - - # FIXME: this is just a demo, we should handle the end of the workflow properly - # ----- - - # End if the last step - if is_last: - return outputs - - # Otherwise, recurse to the next step - workflow.continue_as_new( - execution_input, (wf_name, step_idx + 1), previous_inputs + [outputs] - ) + final_output = yield_outcome.output + + case _: + raise NotImplementedError() + + is_last = start.step + 1 == len(current_workflow) + + ################## + + # should_wait, is_error = False, False + # # Run the step + # match step: + # case PromptStep(): + # outputs = await workflow.execute_activity( + # prompt_step, + # context, + # schedule_to_close_timeout=timedelta(seconds=600), + # ) + + # # TODO: ChatCompletion does not have tool_calls + # # if outputs.tool_calls is not None: + # # should_wait = True + + # case EvaluateStep(): + # outputs = await workflow.execute_activity( + # evaluate_step, + # context, + # schedule_to_close_timeout=timedelta(seconds=600), + # ) + # case YieldStep(): + # outputs = await workflow.execute_child_workflow( + # TaskExecutionWorkflow.run, + # args=[execution_input, (step.workflow, 0), previous_inputs], + # ) + # case ToolCallStep(): + # outputs = await workflow.execute_activity( + # tool_call_step, + # context, + # schedule_to_close_timeout=timedelta(seconds=600), + # ) + # case ErrorWorkflowStep(): + # is_error = True + # case IfElseWorkflowStep(): + # outputs = await workflow.execute_activity( + # if_else_step, + # context, + # schedule_to_close_timeout=timedelta(seconds=600), + # ) + # workflow_step = YieldStep(**outputs["goto_workflow"]) + + # outputs = await workflow.execute_child_workflow( + # TaskExecutionWorkflow.run, + # args=[ + # execution_input, + # (workflow_step.workflow, 0), + # previous_inputs, + # ], + # ) + # case WaitForInputStep(): + # should_wait = True + + # # Transition type + # transition_type = ( + # "awaiting_input" + # if should_wait + # else ("finish" if is_last else ("error" if is_error else "step")) + # ) + + # # Transition to the next step + # transition_info = TransitionInfo( + # from_=(wf_name, step_idx), + # to=None if (is_last or should_wait) else (wf_name, step_idx + 1), + # type=transition_type, + # ) + + # await workflow.execute_activity( + # transition_step, + # args=[ + # context, + # transition_info, + # "failed" if is_error else "awaiting_input", + # ], + # schedule_to_close_timeout=timedelta(seconds=600), + # ) + + # # FIXME: this is just a demo, we should handle the end of the workflow properly + # # ----- + + # # End if the last step + # if is_last: + # return outputs + + # # Otherwise, recurse to the next step + # workflow.continue_as_new( + # execution_input, (wf_name, step_idx + 1), previous_inputs + [outputs] + # ) From ebd364067f8b6145d03eb6e3e2c6739d3825d36d Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 13:12:44 -0400 Subject: [PATCH 060/110] wip Signed-off-by: Diwank Tomer --- .../agents_api/activities/summarization.py | 2 +- .../activities/task_steps/__init__.py | 3 +- .../activities/task_steps/if_else_step.py | 3 +- .../task_steps/raise_complete_async.py | 1 - .../activities/task_steps/tool_call_step.py | 6 +- .../activities/task_steps/transition_step.py | 2 +- .../activities/task_steps/yield_step.py | 9 +- .../agents_api/common/protocol/tasks.py | 24 ++- .../models/execution/update_execution.py | 4 +- .../agents_api/workflows/task_execution.py | 171 +++++++++--------- 10 files changed, 121 insertions(+), 104 deletions(-) diff --git a/agents-api/agents_api/activities/summarization.py b/agents-api/agents_api/activities/summarization.py index 662554181..90a89fde5 100644 --- a/agents-api/agents_api/activities/summarization.py +++ b/agents-api/agents_api/activities/summarization.py @@ -1,8 +1,8 @@ #!/usr/bin/env python3 -from beartype import beartype import pandas as pd +from beartype import beartype from temporalio import activity # from agents_api.models.entry.entries_summarization import ( diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 28932c02e..828fe5e31 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -6,5 +6,4 @@ from .raise_complete_async import raise_complete_async from .tool_call_step import tool_call_step from .transition_step import transition_step -from .wait_for_input_step import wait_for_input_step -from .yield_step import yield_step \ No newline at end of file +from .yield_step import yield_step diff --git a/agents-api/agents_api/activities/task_steps/if_else_step.py b/agents-api/agents_api/activities/task_steps/if_else_step.py index e179b05a6..363af2ffe 100644 --- a/agents-api/agents_api/activities/task_steps/if_else_step.py +++ b/agents-api/agents_api/activities/task_steps/if_else_step.py @@ -1,5 +1,4 @@ from beartype import beartype -from simpleeval import simple_eval from temporalio import activity from ...autogen.openapi_model import ( @@ -22,4 +21,4 @@ async def if_else_step(context: StepContext[IfElseWorkflowStep]) -> dict: # else context.definition.else_ # ) - # return {"goto_workflow": next_workflow} \ No newline at end of file + # return {"goto_workflow": next_workflow} diff --git a/agents-api/agents_api/activities/task_steps/raise_complete_async.py b/agents-api/agents_api/activities/task_steps/raise_complete_async.py index ca1200a87..57aae94c4 100644 --- a/agents-api/agents_api/activities/task_steps/raise_complete_async.py +++ b/agents-api/agents_api/activities/task_steps/raise_complete_async.py @@ -3,6 +3,5 @@ @activity.defn async def raise_complete_async() -> None: - activity.heartbeat("Starting to wait") activity.raise_complete_async() diff --git a/agents-api/agents_api/activities/task_steps/tool_call_step.py b/agents-api/agents_api/activities/task_steps/tool_call_step.py index 0b12cad97..05bbe946b 100644 --- a/agents-api/agents_api/activities/task_steps/tool_call_step.py +++ b/agents-api/agents_api/activities/task_steps/tool_call_step.py @@ -1,15 +1,11 @@ from beartype import beartype from temporalio import activity -from ...autogen.openapi_model import ( - ToolCallStep, -) from ...common.protocol.tasks import ( StepContext, ) - @activity.defn @beartype async def tool_call_step(context: StepContext) -> dict: @@ -21,4 +17,4 @@ async def tool_call_step(context: StepContext) -> dict: # # get tool by id # # call tool - # return {} \ No newline at end of file + # return {} diff --git a/agents-api/agents_api/activities/task_steps/transition_step.py b/agents-api/agents_api/activities/task_steps/transition_step.py index b70428df8..e83acd59a 100644 --- a/agents-api/agents_api/activities/task_steps/transition_step.py +++ b/agents-api/agents_api/activities/task_steps/transition_step.py @@ -33,4 +33,4 @@ async def transition_step( task_id=context.task.id, data=transition_info, update_execution_status=True, - ) \ No newline at end of file + ) diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index 0710c99e6..1dca81168 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -1,8 +1,10 @@ from typing import Any -from agents_api.autogen.Executions import TransitionTarget + from beartype import beartype from temporalio import activity +from agents_api.autogen.Executions import TransitionTarget + from ...autogen.openapi_model import ( YieldStep, ) @@ -10,9 +12,9 @@ StepContext, StepOutcome, ) - from .utils import simple_eval_dict + @activity.defn @beartype async def yield_step(context: StepContext[YieldStep]) -> StepOutcome[dict[str, Any]]: @@ -20,10 +22,9 @@ async def yield_step(context: StepContext[YieldStep]) -> StepOutcome[dict[str, A exprs = context.definition.arguments arguments = simple_eval_dict(exprs, values=context.model_dump()) - transition_target = TransitionTarget( + transition_target = TransitionTarget( workflow=workflow, step=0, ) return StepOutcome(output=arguments, transition_to=("step", transition_target)) - diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index 5e273d657..30e5185bc 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -23,7 +23,6 @@ WorkflowStep, ) - valid_transitions = { # Start state "init": ["wait", "error", "step", "cancelled"], @@ -69,10 +68,10 @@ class ExecutionInput(BaseModel): WorkflowStepType = TypeVar("WorkflowStepType", bound=WorkflowStep) -class StepContext(ExecutionInput, Generic[WorkflowStepType]): - definition: WorkflowStepType | None +class StepContext(BaseModel, Generic[WorkflowStepType]): + execution_input: ExecutionInput inputs: list[dict[str, Any]] - current: TransitionTarget + cursor: TransitionTarget @computed_field @property @@ -84,6 +83,23 @@ def outputs(self) -> list[dict[str, Any]]: def current_input(self) -> dict[str, Any]: return self.inputs[-1] + @computed_field + @property + def current_workflow(self) -> Workflow: + workflows: list[Workflow] = self.execution_input.task.workflows + return next(wf for wf in workflows if wf.name == self.cursor.workflow) + + @computed_field + @property + def current_step(self) -> WorkflowStepType: + step = self.current_workflow[self.cursor.step] + return step + + @computed_field + @property + def is_last_step(self) -> bool: + return (self.cursor.step + 1) == len(self.current_workflow) + def model_dump(self, *args, **kwargs) -> dict[str, Any]: dump = super().model_dump(*args, **kwargs) dump["_"] = self.current_input diff --git a/agents-api/agents_api/models/execution/update_execution.py b/agents-api/agents_api/models/execution/update_execution.py index a75c6eff4..98aa71872 100644 --- a/agents-api/agents_api/models/execution/update_execution.py +++ b/agents-api/agents_api/models/execution/update_execution.py @@ -9,7 +9,9 @@ ResourceUpdatedResponse, UpdateExecutionRequest, ) -from ...common.protocol.tasks import valid_previous_statuses as valid_previous_statuses_map +from ...common.protocol.tasks import ( + valid_previous_statuses as valid_previous_statuses_map, +) from ...common.utils.cozo import cozo_process_mutate_data from ..utils import ( cozo_query, diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index be639697f..6215a6416 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -3,10 +3,11 @@ from datetime import timedelta -from agents_api.autogen.Executions import TransitionTarget -from agents_api.autogen.openapi_model import CreateTransitionRequest from temporalio import workflow +from agents_api.autogen.Executions import TransitionTarget +from agents_api.autogen.openapi_model import CreateTransitionRequest, TransitionType + with workflow.unsafe.imports_passed_through(): from ..activities.task_steps import ( evaluate_step, @@ -16,21 +17,22 @@ transition_step, yield_step, ) - from ..autogen.openapi_model import ( ErrorWorkflowStep, EvaluateStep, IfElseWorkflowStep, PromptStep, ToolCallStep, - WaitForInputStep, + # WaitForInputStep, + # WorkflowStep, YieldStep, ) - from ..common.protocol.tasks import ( ExecutionInput, + # OutcomeType, StepContext, StepOutcome, + # Workflow, ) @@ -53,45 +55,87 @@ async def run( previous_inputs: list[dict] = [], ) -> None: previous_inputs = previous_inputs or [execution_input.arguments] - workflow_map = {wf.name: wf.steps for wf in execution_input.task.workflows} - - current_workflow = workflow_map[start.workflow] - step = current_workflow[start.step] - step_type = type(step) - - context = StepContext[step_type]( - developer_id=execution_input.developer_id, - execution=execution_input.execution, - task=execution_input.task, - agent=execution_input.agent, - user=execution_input.user, - session=execution_input.session, - tools=execution_input.tools, - arguments=execution_input.arguments, - definition=step, + + context = StepContext( + execution_input=execution_input, inputs=previous_inputs, + current=start, ) - next = None - outcome = None - activity = STEP_TO_ACTIVITY.get(step_type) - final_output = None - is_last = False + step_type = type(context.current_step) - if activity: + # 1. First execute the current step's activity if applicable + if activity := STEP_TO_ACTIVITY.get(step_type): outcome = await workflow.execute_activity( activity, context, schedule_to_close_timeout=timedelta(seconds=600), ) - match step, outcome: - case YieldStep(), StepOutcome(output=output, transition_to=(transition_type, next)): + # 2. Then, based on the outcome and step type, decide what to do next + # (By default, exit if last otherwise transition 'step' to the next step) + final_output = None + transition_type: TransitionType + next_target: TransitionTarget | None + metadata: dict = {"step_type": step_type.__name__} + + if context.is_last_step: + transition_type = "finish" + next_target = None + + else: + transition_type = "step" + next_target = TransitionTarget( + workflow=context.cursor.workflow, step=context.cursor.step + 1 + ) + + # 3. Orchestrate the step + match context.current_step, outcome: + case EvaluateStep(), StepOutcome(output=output): + final_output = output transition_request = CreateTransitionRequest( type=transition_type, - current=start, + current=context.cursor, + next=next_target, + output=final_output, + metadata=metadata, + ) + + await workflow.execute_activity( + transition_step, + args=[context, transition_request], + schedule_to_close_timeout=timedelta(seconds=600), + ) + + case ErrorWorkflowStep(error=error), _: + final_output = dict(error=error) + transition_type = "error" + transition_request = CreateTransitionRequest( + type=transition_type, + current=context.cursor, + next=None, + output=final_output, + metadata=metadata, + ) + + await workflow.execute_activity( + transition_step, + args=[context, transition_request], + schedule_to_close_timeout=timedelta(seconds=600), + ) + + raise Exception(f"Error raised by ErrorWorkflowStep: {error}") + + case YieldStep(), StepOutcome( + output=output, transition_to=(transition_type, next) + ): + final_output = output + transition_request = CreateTransitionRequest( + type=transition_type, + current=context.cursor, next=next, - output=output, + output=final_output, + metadata=metadata, ) await workflow.execute_activity( @@ -110,7 +154,15 @@ async def run( case _: raise NotImplementedError() - is_last = start.step + 1 == len(current_workflow) + # 4. Closing + # End if the last step + if context.is_last_step: + return final_output + + # Otherwise, recurse to the next step + workflow.continue_as_new( + execution_input, next_target, previous_inputs + [final_output] + ) ################## @@ -123,30 +175,18 @@ async def run( # context, # schedule_to_close_timeout=timedelta(seconds=600), # ) - + # # # TODO: ChatCompletion does not have tool_calls # # if outputs.tool_calls is not None: # # should_wait = True - # case EvaluateStep(): - # outputs = await workflow.execute_activity( - # evaluate_step, - # context, - # schedule_to_close_timeout=timedelta(seconds=600), - # ) - # case YieldStep(): - # outputs = await workflow.execute_child_workflow( - # TaskExecutionWorkflow.run, - # args=[execution_input, (step.workflow, 0), previous_inputs], - # ) # case ToolCallStep(): # outputs = await workflow.execute_activity( # tool_call_step, # context, # schedule_to_close_timeout=timedelta(seconds=600), # ) - # case ErrorWorkflowStep(): - # is_error = True + # case IfElseWorkflowStep(): # outputs = await workflow.execute_activity( # if_else_step, @@ -154,7 +194,7 @@ async def run( # schedule_to_close_timeout=timedelta(seconds=600), # ) # workflow_step = YieldStep(**outputs["goto_workflow"]) - + # # outputs = await workflow.execute_child_workflow( # TaskExecutionWorkflow.run, # args=[ @@ -163,41 +203,6 @@ async def run( # previous_inputs, # ], # ) + # case WaitForInputStep(): # should_wait = True - - # # Transition type - # transition_type = ( - # "awaiting_input" - # if should_wait - # else ("finish" if is_last else ("error" if is_error else "step")) - # ) - - # # Transition to the next step - # transition_info = TransitionInfo( - # from_=(wf_name, step_idx), - # to=None if (is_last or should_wait) else (wf_name, step_idx + 1), - # type=transition_type, - # ) - - # await workflow.execute_activity( - # transition_step, - # args=[ - # context, - # transition_info, - # "failed" if is_error else "awaiting_input", - # ], - # schedule_to_close_timeout=timedelta(seconds=600), - # ) - - # # FIXME: this is just a demo, we should handle the end of the workflow properly - # # ----- - - # # End if the last step - # if is_last: - # return outputs - - # # Otherwise, recurse to the next step - # workflow.continue_as_new( - # execution_input, (wf_name, step_idx + 1), previous_inputs + [outputs] - # ) From 00edef794f1570466f18d6ddbb0c42fcdd98fd16 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 15:29:51 -0400 Subject: [PATCH 061/110] wip Signed-off-by: Diwank Tomer --- .../agents_api/activities/embed_docs.py | 29 +++----- .../activities/task_steps/transition_step.py | 2 +- .../activities/task_steps/yield_step.py | 12 +++- .../agents_api/activities/truncation.py | 2 + agents-api/agents_api/activities/types.py | 9 +++ .../agents_api/autogen/openapi_model.py | 2 +- .../agents_api/common/protocol/sessions.py | 12 +++- .../agents_api/models/entry/delete_entries.py | 5 +- .../agents_api/routers/docs/create_doc.py | 15 +++- agents-api/agents_api/workflows/embed_docs.py | 7 +- .../agents_api/workflows/task_execution.py | 8 +-- agents-api/pytype.toml | 68 +++++++------------ agents-api/tests/fixtures.py | 5 +- agents-api/tests/test_activities.py | 14 ++-- agents-api/tests/test_chat_routes.py | 2 +- 15 files changed, 101 insertions(+), 91 deletions(-) diff --git a/agents-api/agents_api/activities/embed_docs.py b/agents-api/agents_api/activities/embed_docs.py index 7198a7e54..9d606a041 100644 --- a/agents-api/agents_api/activities/embed_docs.py +++ b/agents-api/agents_api/activities/embed_docs.py @@ -1,40 +1,31 @@ -from uuid import UUID - from beartype import beartype from temporalio import activity from ..clients import embed as embedder from ..clients.cozo import get_cozo_client from ..models.docs.embed_snippets import embed_snippets as embed_snippets_query - -snippet_embed_instruction = "Encode this passage for retrieval: " +from .types import EmbedDocsPayload @activity.defn @beartype -async def embed_docs( - developer_id: UUID, - doc_id: UUID, - title: str, - content: list[str], - include_title: bool = True, - cozo_client=None, -) -> None: - indices, snippets = list(zip(*enumerate(content))) +async def embed_docs(payload: EmbedDocsPayload, cozo_client=None) -> None: + indices, snippets = list(zip(*enumerate(payload.content))) + embed_instruction: str = payload.embed_instruction or "" + title: str = payload.title or "" embeddings = await embedder.embed( [ - { - "instruction": snippet_embed_instruction, - "text": (title + "\n\n" + snippet) if include_title else snippet, - } + ( + embed_instruction + (title + "\n\n" + snippet) if title else snippet + ).strip() for snippet in snippets ] ) embed_snippets_query( - developer_id=developer_id, - doc_id=doc_id, + developer_id=payload.developer_id, + doc_id=payload.doc_id, snippet_indices=indices, embeddings=embeddings, client=cozo_client or get_cozo_client(), diff --git a/agents-api/agents_api/activities/task_steps/transition_step.py b/agents-api/agents_api/activities/task_steps/transition_step.py index e83acd59a..b06c19657 100644 --- a/agents-api/agents_api/activities/task_steps/transition_step.py +++ b/agents-api/agents_api/activities/task_steps/transition_step.py @@ -15,7 +15,7 @@ @activity.defn @beartype async def transition_step( - context: StepContext[None], + context: StepContext, transition_info: CreateTransitionRequest, ) -> None: need_to_wait = transition_info.type == "wait" diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index 1dca81168..f94c8715d 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -18,10 +18,18 @@ @activity.defn @beartype async def yield_step(context: StepContext[YieldStep]) -> StepOutcome[dict[str, Any]]: - workflow = context.definition.workflow - exprs = context.definition.arguments + all_workflows = context.execution_input.task.workflows + workflow = context.current_step.workflow + + assert workflow in [ + wf.name for wf in all_workflows + ], f"Workflow {workflow} not found in task" + + # Evaluate the expressions in the arguments + exprs = context.current_step.arguments arguments = simple_eval_dict(exprs, values=context.model_dump()) + # Transition to the first step of that workflow transition_target = TransitionTarget( workflow=workflow, step=0, diff --git a/agents-api/agents_api/activities/truncation.py b/agents-api/agents_api/activities/truncation.py index d0ce919f0..06f33d8b6 100644 --- a/agents-api/agents_api/activities/truncation.py +++ b/agents-api/agents_api/activities/truncation.py @@ -9,6 +9,8 @@ def get_extra_entries(messages: list[Entry], token_count_threshold: int) -> list[UUID]: + raise NotImplementedError() + if not len(messages): return messages diff --git a/agents-api/agents_api/activities/types.py b/agents-api/agents_api/activities/types.py index f550b5c75..c2af67936 100644 --- a/agents-api/agents_api/activities/types.py +++ b/agents-api/agents_api/activities/types.py @@ -25,3 +25,12 @@ class MemoryRatingTaskArgs(BaseModel): class MemoryRatingTask(BaseModel): name: Literal["memory_rating.v1"] args: MemoryRatingTaskArgs + + +class EmbedDocsPayload(BaseModel): + developer_id: UUID + doc_id: UUID + content: list[str] + embed_instruction: str | None + title: str | None = None + include_title: bool = False # Need to be a separate parameter for the activity diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index df0d44342..fdaadd30a 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -1,5 +1,5 @@ # ruff: noqa: F401, F403, F405 -from typing import Annotated, Generic, Self, Type, TypeVar +from typing import Annotated, Generic, Literal, Self, Type, TypeVar from uuid import UUID from litellm.utils import _select_tokenizer as select_tokenizer diff --git a/agents-api/agents_api/common/protocol/sessions.py b/agents-api/agents_api/common/protocol/sessions.py index d4db30a7d..1e98f7f12 100644 --- a/agents-api/agents_api/common/protocol/sessions.py +++ b/agents-api/agents_api/common/protocol/sessions.py @@ -73,12 +73,16 @@ def get_active_agent(self) -> Agent: def merge_settings(self, chat_input: ChatInput) -> ChatSettings: request_settings = chat_input.model_dump(exclude_unset=True) active_agent = self.get_active_agent() - default_settings = active_agent.default_settings + + default_settings: AgentDefaultSettings | None = active_agent.default_settings + default_settings: dict = ( + default_settings and default_settings.model_dump() or {} + ) self.settings = settings = ChatSettings( **{ "model": active_agent.model, - **default_settings.model_dump(), + **default_settings, **request_settings, } ) @@ -102,13 +106,15 @@ def get_chat_environment(self) -> dict[str, dict | list[dict]]: """ current_agent = self.get_active_agent() tools = self.get_active_tools() + settings: ChatSettings | None = self.settings + settings: dict = settings and settings.model_dump() or {} return { "session": self.session.model_dump(), "agents": [agent.model_dump() for agent in self.agents], "current_agent": current_agent.model_dump(), "users": [user.model_dump() for user in self.users], - "settings": self.settings.model_dump(), + "settings": settings, "tools": [tool.model_dump() for tool in tools], } diff --git a/agents-api/agents_api/models/entry/delete_entries.py b/agents-api/agents_api/models/entry/delete_entries.py index 5bf34c721..1d7cf0386 100644 --- a/agents-api/agents_api/models/entry/delete_entries.py +++ b/agents-api/agents_api/models/entry/delete_entries.py @@ -81,8 +81,9 @@ def delete_entries_for_session( verify_developer_owns_resource_query( developer_id, "sessions", session_id=session_id ), - mark_session_as_updated - and mark_session_updated_query(developer_id, session_id), + mark_session_updated_query(developer_id, session_id) + if mark_session_as_updated + else "", delete_query, ] diff --git a/agents-api/agents_api/routers/docs/create_doc.py b/agents-api/agents_api/routers/docs/create_doc.py index 548ee29d5..c7e5c6bd9 100644 --- a/agents-api/agents_api/routers/docs/create_doc.py +++ b/agents-api/agents_api/routers/docs/create_doc.py @@ -6,6 +6,7 @@ from starlette.status import HTTP_201_CREATED from temporalio.client import Client as TemporalClient +from ...activities.types import EmbedDocsPayload from ...autogen.openapi_model import CreateDocRequest, ResourceCreatedResponse from ...clients import temporal from ...dependencies.developer_id import get_developer_id @@ -15,6 +16,8 @@ async def run_embed_docs_task( + *, + developer_id: UUID, doc_id: UUID, title: str, content: list[str], @@ -31,9 +34,17 @@ async def run_embed_docs_task( if testing: return None + embed_payload = EmbedDocsPayload( + developer_id=developer_id, + doc_id=doc_id, + content=content, + title=title, + embed_instruction=None, + ) + handle = await client.start_workflow( EmbedDocsWorkflow.run, - args=[str(doc_id), title, content], + embed_payload, task_queue=temporal_task_queue, id=str(job_id), ) @@ -60,6 +71,7 @@ async def create_user_doc( embed_job_id = uuid4() await run_embed_docs_task( + developer_id=x_developer_id, doc_id=doc.id, title=doc.title, content=doc.content, @@ -89,6 +101,7 @@ async def create_agent_doc( embed_job_id = uuid4() await run_embed_docs_task( + developer_id=x_developer_id, doc_id=doc.id, title=doc.title, content=doc.content, diff --git a/agents-api/agents_api/workflows/embed_docs.py b/agents-api/agents_api/workflows/embed_docs.py index e52921ed8..62e0e65ae 100644 --- a/agents-api/agents_api/workflows/embed_docs.py +++ b/agents-api/agents_api/workflows/embed_docs.py @@ -7,14 +7,15 @@ with workflow.unsafe.imports_passed_through(): from ..activities.embed_docs import embed_docs + from ..activities.types import EmbedDocsPayload @workflow.defn class EmbedDocsWorkflow: @workflow.run - async def run(self, doc_id: str, title: str, content: list[str]) -> None: - return await workflow.execute_activity( + async def run(self, embed_payload: EmbedDocsPayload) -> None: + await workflow.execute_activity( embed_docs, - args=[doc_id, title, content], + embed_payload, schedule_to_close_timeout=timedelta(seconds=600), ) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 6215a6416..9fb74c4c0 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -5,9 +5,6 @@ from temporalio import workflow -from agents_api.autogen.Executions import TransitionTarget -from agents_api.autogen.openapi_model import CreateTransitionRequest, TransitionType - with workflow.unsafe.imports_passed_through(): from ..activities.task_steps import ( evaluate_step, @@ -18,11 +15,14 @@ yield_step, ) from ..autogen.openapi_model import ( + CreateTransitionRequest, ErrorWorkflowStep, EvaluateStep, IfElseWorkflowStep, PromptStep, ToolCallStep, + TransitionTarget, + TransitionType, # WaitForInputStep, # WorkflowStep, YieldStep, @@ -156,7 +156,7 @@ async def run( # 4. Closing # End if the last step - if context.is_last_step: + if transition_type in ("finish", "cancelled"): return final_output # Otherwise, recurse to the next step diff --git a/agents-api/pytype.toml b/agents-api/pytype.toml index edd07e7d4..2371cea58 100644 --- a/agents-api/pytype.toml +++ b/agents-api/pytype.toml @@ -2,10 +2,13 @@ [tool.pytype] +# Space-separated list of files or directories to exclude. +exclude = [ +] + # Space-separated list of files or directories to process. inputs = [ - 'agents_api', - 'tests', + '.', ] # Keep going past errors to analyze as many files as possible. @@ -27,60 +30,35 @@ pythonpath = '.' # Python version (major.minor) of the target code. python_version = '3.11' -# Bind 'self' in methods with non-transparent decorators. This flag is temporary -# and will be removed once this behavior is enabled by default. -bind_decorated_methods = true - # Don't allow None to match bool. This flag is temporary and will be removed # once this behavior is enabled by default. none_is_not_bool = true -# Enable parameter count checks for overriding methods with renamed arguments. -# This flag is temporary and will be removed once this behavior is enabled by -# default. -overriding_renamed_parameter_count_checks = true - # Variables initialized as None retain their None binding. This flag is # temporary and will be removed once this behavior is enabled by default. strict_none_binding = true -# Support the third-party fiddle library. This flag is temporary and will be -# removed once this behavior is enabled by default. -use_fiddle_overlay = true +# Space-separated list of error names to ignore. +disable = [ + 'pyi-error', +] + +# -------------- +# Optional flags +# -------------- + +# Bind 'self' in methods with non-transparent decorators. This flag is temporary +# and will be removed once this behavior is enabled by default. +bind_decorated_methods = false + +# Enable parameter count checks for overriding methods with renamed arguments. +# This flag is temporary and will be removed once this behavior is enabled by +# default. +overriding_renamed_parameter_count_checks = false # Opt-in: Do not allow Any as a return type. no_return_any = false # Opt-in: Require decoration with @typing.override when overriding a method or # nested class attribute of a parent class. -require_override_decorator = false - -# Experimental: Infer precise return types even for invalid function calls. -precise_return = true - -# Experimental: Solve unknown types to label with structural types. -protocols = true - -# Experimental: Only load submodules that are explicitly imported. -strict_import = true - -# Experimental: Enable exhaustive checking of function parameter types. -strict_parameter_checks = true - -# Experimental: Emit errors for comparisons between incompatible primitive -# types. -strict_primitive_comparisons = true - -# Experimental: Check that variables are defined in all possible code paths. -strict_undefined_checks = true - -# Experimental: FOR TESTING ONLY. Use pytype/rewrite/. -use_rewrite = false - -# Space-separated list of error names to ignore. -disable = [ - 'pyi-error', -] - -# Don't report errors. -report_errors = true +require_override_decorator = false \ No newline at end of file diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 56b9dc7f6..211f9dc35 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -74,10 +74,7 @@ async def temporal_worker(): kill_signal = worker.shutdown() worker_task.cancel() - await asyncio.wait( - [kill_signal, worker_task], - return_when=asyncio.FIRST_COMPLETED, - ) + await asyncio.gather(kill_signal, worker_task) @fixture(scope="test") diff --git a/agents-api/tests/test_activities.py b/agents-api/tests/test_activities.py index 6f128a874..0433ac661 100644 --- a/agents-api/tests/test_activities.py +++ b/agents-api/tests/test_activities.py @@ -1,6 +1,7 @@ from ward import test from agents_api.activities.embed_docs import embed_docs +from agents_api.activities.types import EmbedDocsPayload from .fixtures import ( cozo_client, @@ -36,11 +37,14 @@ async def _( include_title = True await embed_docs( - developer_id=developer_id, - doc_id=doc.id, - title=title, - content=content, - include_title=include_title, + EmbedDocsPayload( + developer_id=developer_id, + doc_id=doc.id, + title=title, + content=content, + include_title=include_title, + embed_instruction=None, + ), cozo_client=cozo_client, ) diff --git a/agents-api/tests/test_chat_routes.py b/agents-api/tests/test_chat_routes.py index 1e8065d9d..674703bfe 100644 --- a/agents-api/tests/test_chat_routes.py +++ b/agents-api/tests/test_chat_routes.py @@ -26,7 +26,7 @@ async def _( _=patch_embed_acompletion, ): assert (await litellm.acompletion(model="gpt-4o", messages=[])).id == "fake_id" - assert (await embed.embed())[0][0] == 1.0 + assert (await embed.embed())[0][0] == 1.0 # pytype: disable=missing-parameter @test("chat: check that non-recall gather_messages works") From a4e49a0cb40aaa54acde89e3dd386b75934364cb Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 16:14:32 -0400 Subject: [PATCH 062/110] wip Signed-off-by: Diwank Tomer --- agents-api/agents_api/activities/truncation.py | 1 + agents-api/agents_api/env.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/agents-api/agents_api/activities/truncation.py b/agents-api/agents_api/activities/truncation.py index 06f33d8b6..642cc3c5b 100644 --- a/agents-api/agents_api/activities/truncation.py +++ b/agents-api/agents_api/activities/truncation.py @@ -31,6 +31,7 @@ def get_extra_entries(messages: list[Entry], token_count_threshold: int) -> list @activity.defn @beartype async def truncation(session_id: str, token_count_threshold: int) -> None: + print(session_id, token_count_threshold) session_id = UUID(session_id) # delete_entries( diff --git a/agents-api/agents_api/env.py b/agents-api/agents_api/env.py index 863a19b4f..38ba2101c 100644 --- a/agents-api/agents_api/env.py +++ b/agents-api/agents_api/env.py @@ -66,7 +66,7 @@ temporal_client_cert: str = env.str("TEMPORAL_CLIENT_CERT", default=None) temporal_private_key: str = env.str("TEMPORAL_PRIVATE_KEY", default=None) temporal_endpoint = env.str("TEMPORAL_ENDPOINT", default="localhost:7233") -temporal_task_queue = env.str("TEMPORAL_TASK_QUEUE", default="memory-task-queue") +temporal_task_queue = env.str("TEMPORAL_TASK_QUEUE", default="julep-task-queue") # Consolidate environment variables From be7471b771061fb07b0ebdd9cb0f0d522204e74b Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 16:20:50 -0400 Subject: [PATCH 063/110] wip Signed-off-by: Diwank Tomer --- agents-api/agents_api/clients/temporal.py | 3 ++- agents-api/pyproject.toml | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/agents-api/agents_api/clients/temporal.py b/agents-api/agents_api/clients/temporal.py index 2130c3947..61f53c71c 100644 --- a/agents-api/agents_api/clients/temporal.py +++ b/agents-api/agents_api/clients/temporal.py @@ -8,6 +8,7 @@ temporal_client_cert, temporal_namespace, temporal_private_key, + temporal_task_queue, temporal_worker_url, ) from ..worker.codec import pydantic_data_converter @@ -49,6 +50,6 @@ async def run_task_execution_workflow( return await client.start_workflow( TaskExecutionWorkflow.run, args=[execution_input, start, previous_inputs], - task_queue="memory-task-queue", + task_queue=temporal_task_queue, id=str(job_id), ) diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index a80350a5c..88068f962 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -63,7 +63,6 @@ check = [ "format", "typecheck", ] -test = "ward" codegen = """ datamodel-codegen \ --input ../openapi.yaml \ @@ -94,3 +93,7 @@ datamodel-codegen \ --openapi-scopes schemas \ --keep-model-order \ --disable-timestamp""" + +[tool.poe.tasks.test] +env = { AGENTS_API_TESTING = "true" } +cmd = "ward test" \ No newline at end of file From cc43f387a9ddb4484b9ef1ce52e7a301371d6338 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 18:39:00 -0400 Subject: [PATCH 064/110] fix(agents-api): Fix update_execution model Signed-off-by: Diwank Tomer --- agents-api/agents_api/activities/demo.py | 6 ++++++ agents-api/agents_api/activities/truncation.py | 1 - agents-api/agents_api/common/protocol/tasks.py | 1 + .../models/execution/create_execution_transition.py | 7 +++++-- agents-api/agents_api/models/execution/update_execution.py | 6 +++--- 5 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 agents-api/agents_api/activities/demo.py diff --git a/agents-api/agents_api/activities/demo.py b/agents-api/agents_api/activities/demo.py new file mode 100644 index 000000000..c883050c1 --- /dev/null +++ b/agents-api/agents_api/activities/demo.py @@ -0,0 +1,6 @@ +from temporalio import activity + + +@activity.defn +async def demo_activity(a: int, b: int) -> int: + return a + b diff --git a/agents-api/agents_api/activities/truncation.py b/agents-api/agents_api/activities/truncation.py index 642cc3c5b..06f33d8b6 100644 --- a/agents-api/agents_api/activities/truncation.py +++ b/agents-api/agents_api/activities/truncation.py @@ -31,7 +31,6 @@ def get_extra_entries(messages: list[Entry], token_count_threshold: int) -> list @activity.defn @beartype async def truncation(session_id: str, token_count_threshold: int) -> None: - print(session_id, token_count_threshold) session_id = UUID(session_id) # delete_entries( diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index 30e5185bc..61466dfdb 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -23,6 +23,7 @@ WorkflowStep, ) +### NOTE: Here, "init" is NOT a real state, but a placeholder for the start state of the state machine valid_transitions = { # Start state "init": ["wait", "error", "step", "cancelled"], diff --git a/agents-api/agents_api/models/execution/create_execution_transition.py b/agents-api/agents_api/models/execution/create_execution_transition.py index c49a0929f..88f29da0b 100644 --- a/agents-api/agents_api/models/execution/create_execution_transition.py +++ b/agents-api/agents_api/models/execution/create_execution_transition.py @@ -113,13 +113,16 @@ def create_execution_transition( }}, type_created_at = [type, -created_at] - ?[last_type] := + matched[collect(last_type)] := last_transition_type[data], last_type_data = first(data), last_type = if(is_null(last_type_data), "init", last_type_data), valid_transition[last_type, $next_type] - :assert some + ?[valid] := + matched[prev_transitions], + found = length(prev_transitions), + valid = assert(found > 0, "Invalid transition"), """ # Prepare the insert query diff --git a/agents-api/agents_api/models/execution/update_execution.py b/agents-api/agents_api/models/execution/update_execution.py index 98aa71872..d939715bd 100644 --- a/agents-api/agents_api/models/execution/update_execution.py +++ b/agents-api/agents_api/models/execution/update_execution.py @@ -49,9 +49,9 @@ def update_execution( task_id = str(task_id) execution_id = str(execution_id) - valid_previous_statuses = valid_previous_statuses_map[data.status] + valid_previous_statuses: list[str] | None = valid_previous_statuses_map.get(data.status, None) - execution_data = data.model_dump(exclude_none=True) + execution_data: dict = data.model_dump(exclude_none=True) columns, values = cozo_process_mutate_data( { @@ -93,7 +93,7 @@ def update_execution( task_id=task_id, parents=[("agents", "agent_id")], ), - validate_status_query, + validate_status_query if valid_previous_statuses is not None else "", update_query, ] From a22a65e1f397d3b02c38f4d3d2eac97079e6768c Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 18:40:36 -0400 Subject: [PATCH 065/110] feat(agents-api): Add demo workflow for testing Signed-off-by: Diwank Tomer --- agents-api/agents_api/worker/worker.py | 4 ++++ agents-api/agents_api/workflows/demo.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 agents-api/agents_api/workflows/demo.py diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index 042f75275..d3821c902 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -10,6 +10,7 @@ def create_worker(client: Client): then create a worker to listen for tasks on the configured task queue. """ + from ..activities.demo import demo_activity from ..activities.embed_docs import embed_docs from ..activities.mem_mgmt import mem_mgmt from ..activities.mem_rating import mem_rating @@ -26,6 +27,7 @@ def create_worker(client: Client): from ..env import ( temporal_task_queue, ) + from ..workflows.demo import DemoWorkflow from ..workflows.embed_docs import EmbedDocsWorkflow from ..workflows.mem_mgmt import MemMgmtWorkflow from ..workflows.mem_rating import MemRatingWorkflow @@ -48,6 +50,7 @@ def create_worker(client: Client): graceful_shutdown_timeout=timedelta(seconds=30), task_queue=temporal_task_queue, workflows=[ + DemoWorkflow, SummarizationWorkflow, MemMgmtWorkflow, MemRatingWorkflow, @@ -57,6 +60,7 @@ def create_worker(client: Client): ], activities=[ *task_activities, + demo_activity, summarization, mem_mgmt, mem_rating, diff --git a/agents-api/agents_api/workflows/demo.py b/agents-api/agents_api/workflows/demo.py new file mode 100644 index 000000000..61ad9d4a8 --- /dev/null +++ b/agents-api/agents_api/workflows/demo.py @@ -0,0 +1,17 @@ +from datetime import timedelta + +from temporalio import workflow + +with workflow.unsafe.imports_passed_through(): + from ..activities.demo import demo_activity + + +@workflow.defn +class DemoWorkflow: + @workflow.run + async def run(self, a: int, b: int) -> int: + return await workflow.execute_activity( + demo_activity, + args=[a, b], + start_to_close_timeout=timedelta(seconds=30), + ) From 2d3effe76713dc85930c792b39fbb2876d4c209e Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 18:41:54 -0400 Subject: [PATCH 066/110] fix(agents-api): Use a contextmanager instead of fixture for creating test temporal envs Signed-off-by: Diwank Tomer --- .../models/execution/update_execution.py | 4 +- agents-api/tests/fixtures.py | 38 +---- agents-api/tests/test_activities.py | 108 +++---------- agents-api/tests/test_docs_routes.py | 148 +++++++++--------- agents-api/tests/test_task_routes.py | 16 +- agents-api/tests/utils.py | 44 ++++++ 6 files changed, 149 insertions(+), 209 deletions(-) create mode 100644 agents-api/tests/utils.py diff --git a/agents-api/agents_api/models/execution/update_execution.py b/agents-api/agents_api/models/execution/update_execution.py index d939715bd..f3c1e528e 100644 --- a/agents-api/agents_api/models/execution/update_execution.py +++ b/agents-api/agents_api/models/execution/update_execution.py @@ -49,7 +49,9 @@ def update_execution( task_id = str(task_id) execution_id = str(execution_id) - valid_previous_statuses: list[str] | None = valid_previous_statuses_map.get(data.status, None) + valid_previous_statuses: list[str] | None = valid_previous_statuses_map.get( + data.status, None + ) execution_data: dict = data.model_dump(exclude_none=True) diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 211f9dc35..323369921 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -1,4 +1,3 @@ -import asyncio from unittest.mock import patch from uuid import uuid4 @@ -7,7 +6,6 @@ from litellm.types.utils import Choices, ModelResponse from pycozo import Client as CozoClient from temporalio.client import WorkflowHandle -from temporalio.testing import ActivityEnvironment, WorkflowEnvironment from ward import fixture from agents_api.autogen.openapi_model import ( @@ -41,9 +39,6 @@ from agents_api.models.user.delete_user import delete_user from agents_api.web import app -# from agents_api.worker.worker import create_worker -from agents_api.worker.worker import create_worker - EMBEDDING_SIZE: int = 1024 @@ -59,35 +54,6 @@ def cozo_client(migrations_dir: str = "./migrations"): return client -@fixture(scope="test") -def activity_environment(): - return ActivityEnvironment() - - -@fixture(scope="global") -async def temporal_worker(): - async with await WorkflowEnvironment.start_local() as env: - worker = create_worker(client=env.client) - worker_task = asyncio.create_task(worker.run()) - - yield worker - - kill_signal = worker.shutdown() - worker_task.cancel() - await asyncio.gather(kill_signal, worker_task) - - -@fixture(scope="test") -def patch_temporal_get_client( - temporal_worker=temporal_worker, -): - mock_client = temporal_worker.client - - with patch("agents_api.clients.temporal.get_client") as get_client: - get_client.return_value = mock_client - yield get_client - - @fixture(scope="global") def test_developer_id(cozo_client=cozo_client): developer_id = uuid4() @@ -327,8 +293,8 @@ def test_transition( data=CreateTransitionRequest( type="step", output={}, - current=["main", 0], - next=["wf1", 1], + current={"workflow": "main", "step": 0}, + next={"workflow": "wf1", "step": 1}, ), client=client, ) diff --git a/agents-api/tests/test_activities.py b/agents-api/tests/test_activities.py index 0433ac661..d658019d0 100644 --- a/agents-api/tests/test_activities.py +++ b/agents-api/tests/test_activities.py @@ -1,28 +1,26 @@ +from uuid import uuid4 + from ward import test from agents_api.activities.embed_docs import embed_docs from agents_api.activities.types import EmbedDocsPayload +from agents_api.clients import temporal +from agents_api.env import temporal_task_queue +from agents_api.workflows.demo import DemoWorkflow from .fixtures import ( cozo_client, patch_embed_acompletion, - temporal_worker, test_developer_id, test_doc, ) +from .utils import patch_testing_temporal # from agents_api.activities.truncation import get_extra_entries # from agents_api.autogen.openapi_model import Role # from agents_api.common.protocol.entries import Entry -@test("activity: check that workflow environment and worker are started correctly") -async def _( - worker=temporal_worker, -): - assert worker.is_running - - @test("activity: call direct embed_docs") async def _( cozo_client=cozo_client, @@ -45,91 +43,23 @@ async def _( include_title=include_title, embed_instruction=None, ), - cozo_client=cozo_client, + cozo_client, ) embed.assert_called_once() -# @test("get extra entries, do not strip system message") -# def _(): -# session_ids = [uuid.uuid4()] * 3 -# entry_ids = [uuid.uuid4()] * 3 -# now = time.time() -# messages = [ -# Entry( -# entry_id=entry_ids[0], -# session_id=session_ids[0], -# role=Role.system, -# content="content 1", -# created_at=now, -# timestamp=now, -# ), -# Entry( -# entry_id=entry_ids[1], -# session_id=session_ids[1], -# role=Role.assistant, -# content="content 2", -# created_at=now, -# timestamp=now, -# ), -# Entry( -# entry_id=entry_ids[2], -# session_id=session_ids[2], -# role=Role.user, -# content="content 3", -# created_at=now, -# timestamp=now, -# ), -# ] - -# threshold = sum([m.token_count for m in messages]) - 1 -# result = get_extra_entries(messages, threshold) - -# assert result == [messages[1].id] - - -# @test("get extra entries") -# def _(): -# session_ids = [uuid.uuid4()] * 3 -# entry_ids = [uuid.uuid4()] * 3 -# now = time.time() -# messages = [ -# Entry( -# entry_id=entry_ids[0], -# session_id=session_ids[0], -# role=Role.user, -# content="content 1", -# created_at=now, -# timestamp=now, -# ), -# Entry( -# entry_id=entry_ids[1], -# session_id=session_ids[1], -# role=Role.assistant, -# content="content 2", -# created_at=now, -# timestamp=now, -# ), -# Entry( -# entry_id=entry_ids[2], -# session_id=session_ids[2], -# role=Role.user, -# content="content 3", -# created_at=now, -# timestamp=now, -# ), -# ] - -# threshold = sum([m.token_count for m in messages]) - 1 -# result = get_extra_entries(messages, threshold) - -# assert result == [messages[0].id] - +@test("activity: call demo workflow via temporal client") +async def _(): + async with patch_testing_temporal() as (_, mock_get_client): + client = await temporal.get_client() -# @test("get extra entries, no change if empty") -# def _(): -# messages = [] -# result = get_extra_entries(messages, 1) + result = await client.execute_workflow( + DemoWorkflow.run, + args=[1, 2], + id=str(uuid4()), + task_queue=temporal_task_queue, + ) -# assert result == [] + assert result == 3 + mock_get_client.assert_called_once() diff --git a/agents-api/tests/test_docs_routes.py b/agents-api/tests/test_docs_routes.py index a33e1d19e..168211fc6 100644 --- a/agents-api/tests/test_docs_routes.py +++ b/agents-api/tests/test_docs_routes.py @@ -1,111 +1,107 @@ from ward import test -from tests.fixtures import ( +from .fixtures import ( make_request, patch_embed_acompletion, - patch_temporal_get_client, test_agent, test_doc, test_user, test_user_doc, ) +from .utils import patch_testing_temporal @test("route: create user doc") -def _(make_request=make_request, user=test_user, get_client=patch_temporal_get_client): - data = dict( - title="Test User Doc", - content=["This is a test user document."], - ) +async def _(make_request=make_request, user=test_user): + async with patch_testing_temporal(): + data = dict( + title="Test User Doc", + content=["This is a test user document."], + ) - response = make_request( - method="POST", - url=f"/users/{user.id}/docs", - json=data, - ) + response = make_request( + method="POST", + url=f"/users/{user.id}/docs", + json=data, + ) - get_client.assert_called() - assert response.status_code == 201 + assert response.status_code == 201 - result = response.json() - assert len(result["jobs"]) > 0 + result = response.json() + assert len(result["jobs"]) > 0 @test("route: create agent doc") -def _( - make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client -): - data = dict( - title="Test Agent Doc", - content=["This is a test agent document."], - ) +async def _(make_request=make_request, agent=test_agent): + async with patch_testing_temporal(): + data = dict( + title="Test Agent Doc", + content=["This is a test agent document."], + ) - response = make_request( - method="POST", - url=f"/agents/{agent.id}/docs", - json=data, - ) + response = make_request( + method="POST", + url=f"/agents/{agent.id}/docs", + json=data, + ) - get_client.assert_called() - assert response.status_code == 201 + assert response.status_code == 201 - result = response.json() - assert len(result["jobs"]) > 0 + result = response.json() + assert len(result["jobs"]) > 0 @test("route: delete doc") -def _( - make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client -): - data = dict( - title="Test Agent Doc", - content=["This is a test agent document."], - ) +async def _(make_request=make_request, agent=test_agent): + async with patch_testing_temporal(): + data = dict( + title="Test Agent Doc", + content=["This is a test agent document."], + ) - response = make_request( - method="POST", - url=f"/agents/{agent.id}/docs", - json=data, - ) - doc_id = response.json()["id"] + response = make_request( + method="POST", + url=f"/agents/{agent.id}/docs", + json=data, + ) + doc_id = response.json()["id"] - response = make_request( - method="DELETE", - url=f"/agents/{agent.id}/docs/{doc_id}", - ) + response = make_request( + method="DELETE", + url=f"/agents/{agent.id}/docs/{doc_id}", + ) - assert response.status_code == 202 + assert response.status_code == 202 - response = make_request( - method="GET", - url=f"/docs/{doc_id}", - ) + response = make_request( + method="GET", + url=f"/docs/{doc_id}", + ) - assert response.status_code == 404 + assert response.status_code == 404 @test("route: get doc") -def _( - make_request=make_request, agent=test_agent, get_client=patch_temporal_get_client -): - data = dict( - title="Test Agent Doc", - content=["This is a test agent document."], - ) - - response = make_request( - method="POST", - url=f"/agents/{agent.id}/docs", - json=data, - ) - doc_id = response.json()["id"] - - response = make_request( - method="GET", - url=f"/docs/{doc_id}", - ) - - assert response.status_code == 200 +async def _(make_request=make_request, agent=test_agent): + async with patch_testing_temporal(): + data = dict( + title="Test Agent Doc", + content=["This is a test agent document."], + ) + + response = make_request( + method="POST", + url=f"/agents/{agent.id}/docs", + json=data, + ) + doc_id = response.json()["id"] + + response = make_request( + method="GET", + url=f"/docs/{doc_id}", + ) + + assert response.status_code == 200 @test("route: list user docs") diff --git a/agents-api/tests/test_task_routes.py b/agents-api/tests/test_task_routes.py index 68a77c578..d2c5bbec1 100644 --- a/agents-api/tests/test_task_routes.py +++ b/agents-api/tests/test_task_routes.py @@ -4,7 +4,7 @@ from ward import test -from tests.fixtures import ( +from .fixtures import ( client, make_request, test_agent, @@ -12,6 +12,7 @@ test_task, test_transition, ) +from .utils import patch_testing_temporal @test("route: unauthorized should fail") @@ -61,17 +62,18 @@ def _(make_request=make_request, agent=test_agent): @test("route: create task execution") -def _(make_request=make_request, task=test_task): +async def _(make_request=make_request, task=test_task): data = dict( input={}, metadata={}, ) - response = make_request( - method="POST", - url=f"/tasks/{str(task.id)}/executions", - json=data, - ) + async with patch_testing_temporal(): + response = make_request( + method="POST", + url=f"/tasks/{str(task.id)}/executions", + json=data, + ) assert response.status_code == 201 diff --git a/agents-api/tests/utils.py b/agents-api/tests/utils.py new file mode 100644 index 000000000..d1be73a49 --- /dev/null +++ b/agents-api/tests/utils.py @@ -0,0 +1,44 @@ +import asyncio +import logging +from contextlib import asynccontextmanager +from unittest.mock import patch + +from temporalio.testing import WorkflowEnvironment + +from agents_api.worker.codec import pydantic_data_converter +from agents_api.worker.worker import create_worker + +EMBEDDING_SIZE: int = 1024 + + +@asynccontextmanager +async def patch_testing_temporal(): + # Set log level to ERROR to avoid spamming the console + logger = logging.getLogger("temporalio") + previous_log_level = logger.getEffectiveLevel() + + logger.setLevel(logging.ERROR) + + # Start a local Temporal environment + async with await WorkflowEnvironment.start_local() as env: + # Set the correct codec + env.client._config["data_converter"] = pydantic_data_converter + + # Create a worker with our workflows and start it + worker = create_worker(client=env.client) + asyncio.create_task(worker.run()) + + # Mock the Temporal client + mock_client = worker.client + + with patch("agents_api.clients.temporal.get_client") as mock_get_client: + mock_get_client.return_value = mock_client + + # Yield the worker and the mock client <--- + yield worker, mock_get_client + + # Shutdown the worker + await worker.shutdown() + + # Reset log level + logger.setLevel(previous_log_level) From 655d222d49b083cd3e89a8b037cdd7b6533d9edf Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 18:55:34 -0400 Subject: [PATCH 067/110] wip Signed-off-by: Diwank Tomer --- agents-api/tests/test_task_queries.py | 3 ++- agents-api/tests/test_task_routes.py | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/agents-api/tests/test_task_queries.py b/agents-api/tests/test_task_queries.py index 06c7c969b..cd9b5b612 100644 --- a/agents-api/tests/test_task_queries.py +++ b/agents-api/tests/test_task_queries.py @@ -15,7 +15,8 @@ from agents_api.models.task.get_task import get_task from agents_api.models.task.list_tasks import list_tasks from agents_api.models.task.update_task import update_task -from tests.fixtures import cozo_client, test_agent, test_developer_id, test_task + +from .fixtures import cozo_client, test_agent, test_developer_id, test_task @test("model: create task") diff --git a/agents-api/tests/test_task_routes.py b/agents-api/tests/test_task_routes.py index d2c5bbec1..092a8a0f1 100644 --- a/agents-api/tests/test_task_routes.py +++ b/agents-api/tests/test_task_routes.py @@ -122,11 +122,12 @@ def _(make_request=make_request, task=test_task): assert response.status_code == 200 +# FIXME: This test is failing @test("model: list execution transitions") -def _(make_request=make_request, transition=test_transition): +def _(make_request=make_request, execution=test_execution, transition=test_transition): response = make_request( method="GET", - url=f"/executions/{str(transition.execution_id)}/transitions", + url=f"/executions/{str(execution.id)}/transitions", ) assert response.status_code == 200 From 0194c7305d025f8662f584968a0f91a488fb128e Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 16 Aug 2024 21:21:59 -0400 Subject: [PATCH 068/110] feat(agents-api): ALL TESTS PASS!! :D Signed-off-by: Diwank Tomer --- agents-api/agents_api/activities/demo.py | 13 +- .../agents_api/activities/embed_docs.py | 12 +- .../activities/task_steps/evaluate_step.py | 11 +- .../activities/task_steps/transition_step.py | 23 ++-- .../activities/task_steps/yield_step.py | 20 ++-- agents-api/agents_api/env.py | 10 +- .../agents_api/routers/docs/create_doc.py | 10 +- .../routers/tasks/create_task_execution.py | 111 +++++++++++------- agents-api/tests/test_activities.py | 10 -- agents-api/tests/test_execution_workflow.py | 27 +++++ 10 files changed, 170 insertions(+), 77 deletions(-) create mode 100644 agents-api/tests/test_execution_workflow.py diff --git a/agents-api/agents_api/activities/demo.py b/agents-api/agents_api/activities/demo.py index c883050c1..797ef6c90 100644 --- a/agents-api/agents_api/activities/demo.py +++ b/agents-api/agents_api/activities/demo.py @@ -1,6 +1,17 @@ from temporalio import activity +from ..env import testing + -@activity.defn async def demo_activity(a: int, b: int) -> int: + # Should throw an error if testing is not enabled + raise Exception("This should not be called in production") + + +async def mock_demo_activity(a: int, b: int) -> int: return a + b + + +demo_activity = activity.defn(name="demo_activity")( + demo_activity if not testing else mock_demo_activity +) diff --git a/agents-api/agents_api/activities/embed_docs.py b/agents-api/agents_api/activities/embed_docs.py index 9d606a041..1dd3793b0 100644 --- a/agents-api/agents_api/activities/embed_docs.py +++ b/agents-api/agents_api/activities/embed_docs.py @@ -3,11 +3,11 @@ from ..clients import embed as embedder from ..clients.cozo import get_cozo_client +from ..env import testing from ..models.docs.embed_snippets import embed_snippets as embed_snippets_query from .types import EmbedDocsPayload -@activity.defn @beartype async def embed_docs(payload: EmbedDocsPayload, cozo_client=None) -> None: indices, snippets = list(zip(*enumerate(payload.content))) @@ -30,3 +30,13 @@ async def embed_docs(payload: EmbedDocsPayload, cozo_client=None) -> None: embeddings=embeddings, client=cozo_client or get_cozo_client(), ) + + +async def mock_embed_docs(payload: EmbedDocsPayload, cozo_client=None) -> None: + # Does nothing + return None + + +embed_docs = activity.defn(name="embed_docs")( + embed_docs if not testing else mock_embed_docs +) diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py index 6f6630f4a..14f4f3f12 100644 --- a/agents-api/agents_api/activities/task_steps/evaluate_step.py +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -9,9 +9,9 @@ StepContext, StepOutcome, ) +from ...env import testing -@activity.defn @beartype async def evaluate_step( context: StepContext[EvaluateStep], @@ -20,3 +20,12 @@ async def evaluate_step( output = simple_eval_dict(exprs, values=context.model_dump()) return StepOutcome(output=output) + + +# Note: This is here just for clarity. We could have just imported evaluate_step directly +# They do the same thing, so we dont need to mock the evaluate_step function +mock_evaluate_step = evaluate_step + +evaluate_step = activity.defn(name="evaluate_step")( + evaluate_step if not testing else mock_evaluate_step +) diff --git a/agents-api/agents_api/activities/task_steps/transition_step.py b/agents-api/agents_api/activities/task_steps/transition_step.py index b06c19657..1642075f4 100644 --- a/agents-api/agents_api/activities/task_steps/transition_step.py +++ b/agents-api/agents_api/activities/task_steps/transition_step.py @@ -1,18 +1,14 @@ from beartype import beartype from temporalio import activity -from ...autogen.openapi_model import ( - CreateTransitionRequest, -) -from ...common.protocol.tasks import ( - StepContext, -) +from ...autogen.openapi_model import CreateTransitionRequest +from ...common.protocol.tasks import StepContext +from ...env import testing from ...models.execution.create_execution_transition import ( create_execution_transition as create_execution_transition_query, ) -@activity.defn @beartype async def transition_step( context: StepContext, @@ -34,3 +30,16 @@ async def transition_step( data=transition_info, update_execution_status=True, ) + + +async def mock_transition_step( + context: StepContext, + transition_info: CreateTransitionRequest, +) -> None: + # Does nothing + return None + + +transition_step = activity.defn(name="transition_step")( + transition_step if not testing else mock_transition_step +) diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index f94c8715d..b339b83d1 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -5,17 +5,12 @@ from agents_api.autogen.Executions import TransitionTarget -from ...autogen.openapi_model import ( - YieldStep, -) -from ...common.protocol.tasks import ( - StepContext, - StepOutcome, -) +from ...autogen.openapi_model import YieldStep +from ...common.protocol.tasks import StepContext, StepOutcome +from ...env import testing from .utils import simple_eval_dict -@activity.defn @beartype async def yield_step(context: StepContext[YieldStep]) -> StepOutcome[dict[str, Any]]: all_workflows = context.execution_input.task.workflows @@ -36,3 +31,12 @@ async def yield_step(context: StepContext[YieldStep]) -> StepOutcome[dict[str, A ) return StepOutcome(output=arguments, transition_to=("step", transition_target)) + + +# Note: This is here just for clarity. We could have just imported yield_step directly +# They do the same thing, so we dont need to mock the yield_step function +mock_yield_step = yield_step + +yield_step = activity.defn(name="yield_step")( + yield_step if not testing else mock_yield_step +) diff --git a/agents-api/agents_api/env.py b/agents-api/agents_api/env.py index 38ba2101c..afdc81e1e 100644 --- a/agents-api/agents_api/env.py +++ b/agents-api/agents_api/env.py @@ -85,10 +85,18 @@ temporal_worker_url=temporal_worker_url, temporal_namespace=temporal_namespace, embedding_model_id=embedding_model_id, + testing=testing, ) -if debug: +if debug or testing: # Print the loaded environment variables for debugging purposes. print("Environment variables:") pprint(environment) print() + + # Yell if testing is enabled + print("@" * 80) + print( + f"@@@ Running in {'testing' if testing else 'debug'} mode. This should not be enabled in production. @@@" + ) + print("@" * 80) diff --git a/agents-api/agents_api/routers/docs/create_doc.py b/agents-api/agents_api/routers/docs/create_doc.py index c7e5c6bd9..dd41620ae 100644 --- a/agents-api/agents_api/routers/docs/create_doc.py +++ b/agents-api/agents_api/routers/docs/create_doc.py @@ -29,11 +29,6 @@ async def run_embed_docs_task( client = client or (await temporal.get_client()) - # TODO: Remove this conditional once we have a way to run workflows in - # a test environment. - if testing: - return None - embed_payload = EmbedDocsPayload( developer_id=developer_id, doc_id=doc_id, @@ -49,7 +44,10 @@ async def run_embed_docs_task( id=str(job_id), ) - background_tasks.add_task(handle.result) + # TODO: Remove this conditional once we have a way to run workflows in + # a test environment. + if not testing: + background_tasks.add_task(handle.result) return handle diff --git a/agents-api/agents_api/routers/tasks/create_task_execution.py b/agents-api/agents_api/routers/tasks/create_task_execution.py index 5c40d4927..988af074f 100644 --- a/agents-api/agents_api/routers/tasks/create_task_execution.py +++ b/agents-api/agents_api/routers/tasks/create_task_execution.py @@ -1,37 +1,89 @@ import logging from typing import Annotated -from uuid import uuid4 +from uuid import UUID, uuid4 -from fastapi import Depends, HTTPException, status +from beartype import beartype +from fastapi import BackgroundTasks, Depends, HTTPException, status from jsonschema import validate from jsonschema.exceptions import ValidationError from pycozo.client import QueryException from pydantic import UUID4 from starlette.status import HTTP_201_CREATED +from temporalio.client import WorkflowHandle -from agents_api.autogen.openapi_model import ( +from ...autogen.Executions import Execution +from ...autogen.openapi_model import ( CreateExecutionRequest, ResourceCreatedResponse, UpdateExecutionRequest, ) -from agents_api.clients.temporal import run_task_execution_workflow -from agents_api.dependencies.developer_id import get_developer_id -from agents_api.models.execution.create_execution import ( +from ...clients.temporal import run_task_execution_workflow +from ...dependencies.developer_id import get_developer_id +from ...models.execution.create_execution import ( create_execution as create_execution_query, ) -from agents_api.models.execution.create_temporal_lookup import create_temporal_lookup -from agents_api.models.execution.prepare_execution_input import prepare_execution_input -from agents_api.models.execution.update_execution import ( +from ...models.execution.create_temporal_lookup import create_temporal_lookup +from ...models.execution.prepare_execution_input import prepare_execution_input +from ...models.execution.update_execution import ( update_execution as update_execution_query, ) -from agents_api.models.task.get_task import get_task as get_task_query - +from ...models.task.get_task import get_task as get_task_query from .router import router logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) +@beartype +async def start_execution( + *, + developer_id: UUID, + task_id: UUID, + data: CreateExecutionRequest, + client=None, +) -> tuple[Execution, WorkflowHandle]: + execution_id = uuid4() + + execution = create_execution_query( + developer_id=developer_id, + task_id=task_id, + execution_id=execution_id, + data=data, + client=client, + ) + + execution_input = prepare_execution_input( + developer_id=developer_id, + task_id=task_id, + execution_id=execution_id, + client=client, + ) + + try: + handle = await run_task_execution_workflow( + execution_input=execution_input, + job_id=uuid4(), + ) + + except Exception as e: + logger.exception(e) + + update_execution_query( + developer_id=developer_id, + task_id=task_id, + execution_id=execution_id, + data=UpdateExecutionRequest(status="failed"), + client=client, + ) + + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail="Execution creation failed", + ) from e + + return execution, handle + + @router.post( "/tasks/{task_id}/executions", status_code=HTTP_201_CREATED, @@ -41,6 +93,7 @@ async def create_task_execution( task_id: UUID4, data: CreateExecutionRequest, x_developer_id: Annotated[UUID4, Depends(get_developer_id)], + background_tasks: BackgroundTasks, ) -> ResourceCreatedResponse: try: task = get_task_query(task_id=task_id, developer_id=x_developer_id) @@ -60,44 +113,18 @@ async def create_task_execution( raise - execution_id = uuid4() - execution = create_execution_query( + execution, handle = await start_execution( developer_id=x_developer_id, task_id=task_id, - execution_id=execution_id, data=data, ) - execution_input = prepare_execution_input( + background_tasks.add_task( + create_temporal_lookup, + # developer_id=x_developer_id, task_id=task_id, - execution_id=execution_id, - ) - - try: - handle = await run_task_execution_workflow( - execution_input=execution_input, - job_id=uuid4(), - ) - except Exception as e: - logger.exception(e) - - update_execution_query( - developer_id=x_developer_id, - task_id=task_id, - execution_id=execution_id, - data=UpdateExecutionRequest(status="failed"), - ) - - raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail="Task creation failed", - ) - - create_temporal_lookup( - developer_id=x_developer_id, - task_id=task_id, - execution_id=execution_id, + execution_id=execution.id, workflow_handle=handle, ) diff --git a/agents-api/tests/test_activities.py b/agents-api/tests/test_activities.py index d658019d0..98dfc97b5 100644 --- a/agents-api/tests/test_activities.py +++ b/agents-api/tests/test_activities.py @@ -10,26 +10,18 @@ from .fixtures import ( cozo_client, - patch_embed_acompletion, test_developer_id, test_doc, ) from .utils import patch_testing_temporal -# from agents_api.activities.truncation import get_extra_entries -# from agents_api.autogen.openapi_model import Role -# from agents_api.common.protocol.entries import Entry - @test("activity: call direct embed_docs") async def _( cozo_client=cozo_client, developer_id=test_developer_id, doc=test_doc, - mocks=patch_embed_acompletion, ): - (embed, _) = mocks - title = "title" content = ["content 1"] include_title = True @@ -46,8 +38,6 @@ async def _( cozo_client, ) - embed.assert_called_once() - @test("activity: call demo workflow via temporal client") async def _(): diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py new file mode 100644 index 000000000..254841075 --- /dev/null +++ b/agents-api/tests/test_execution_workflow.py @@ -0,0 +1,27 @@ +# Tests for task queries + +from ward import test + +from agents_api.autogen.openapi_model import CreateExecutionRequest +from agents_api.routers.tasks.create_task_execution import start_execution + +from .fixtures import cozo_client, test_developer_id, test_task +from .utils import patch_testing_temporal + + +@test("workflow: create task execution") +async def _(client=cozo_client, developer_id=test_developer_id, task=test_task): + data = CreateExecutionRequest(input={"test": "input"}) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() From ae112cb5a21b912cd0289a189f8d320c5634a465 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 12:55:42 -0400 Subject: [PATCH 069/110] feat: Add test for evaluate step Signed-off-by: Diwank Tomer --- .../activities/task_steps/evaluate_step.py | 13 +++++------ .../activities/task_steps/if_else_step.py | 6 ++--- .../activities/task_steps/prompt_step.py | 8 +++---- .../activities/task_steps/tool_call_step.py | 6 ++--- .../activities/task_steps/yield_step.py | 4 +--- .../agents_api/autogen/openapi_model.py | 23 ++++++++----------- .../agents_api/common/protocol/tasks.py | 11 ++++----- .../agents_api/workflows/task_execution.py | 7 +++--- agents-api/tests/fixtures.py | 2 +- agents-api/tests/test_execution_workflow.py | 3 +++ 10 files changed, 39 insertions(+), 44 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py index 14f4f3f12..76b72bdbd 100644 --- a/agents-api/agents_api/activities/task_steps/evaluate_step.py +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -1,6 +1,3 @@ -from typing import Any - -from beartype import beartype from temporalio import activity from ...activities.task_steps.utils import simple_eval_dict @@ -12,14 +9,16 @@ from ...env import testing -@beartype async def evaluate_step( context: StepContext[EvaluateStep], -) -> StepOutcome[dict[str, Any]]: - exprs = context.definition.arguments +) -> StepOutcome: + assert isinstance(context.current_step, EvaluateStep) + + exprs = context.current_step.evaluate output = simple_eval_dict(exprs, values=context.model_dump()) - return StepOutcome(output=output) + result = StepOutcome(output=output) + return result # Note: This is here just for clarity. We could have just imported evaluate_step directly diff --git a/agents-api/agents_api/activities/task_steps/if_else_step.py b/agents-api/agents_api/activities/task_steps/if_else_step.py index 363af2ffe..bb05a3094 100644 --- a/agents-api/agents_api/activities/task_steps/if_else_step.py +++ b/agents-api/agents_api/activities/task_steps/if_else_step.py @@ -16,9 +16,9 @@ async def if_else_step(context: StepContext[IfElseWorkflowStep]) -> dict: # context_data: dict = context.model_dump() # next_workflow = ( - # context.definition.then - # if simple_eval(context.definition.if_, names=context_data) - # else context.definition.else_ + # context.current_step.then + # if simple_eval(context.current_step.if_, names=context_data) + # else context.current_step.else_ # ) # return {"goto_workflow": next_workflow} diff --git a/agents-api/agents_api/activities/task_steps/prompt_step.py b/agents-api/agents_api/activities/task_steps/prompt_step.py index 90ab975d3..37f34eab0 100644 --- a/agents-api/agents_api/activities/task_steps/prompt_step.py +++ b/agents-api/agents_api/activities/task_steps/prompt_step.py @@ -25,9 +25,9 @@ async def prompt_step(context: StepContext[PromptStep]) -> StepOutcome: # Render template messages prompt = ( - [InputChatMLMessage(content=context.definition.prompt)] - if isinstance(context.definition.prompt, str) - else context.definition.prompt + [InputChatMLMessage(content=context.current_step.prompt)] + if isinstance(context.current_step.prompt, str) + else context.current_step.prompt ) template_messages: list[InputChatMLMessage] = prompt @@ -47,7 +47,7 @@ async def prompt_step(context: StepContext[PromptStep]) -> StepOutcome: for m in messages ] - settings: dict = context.definition.settings.model_dump() + settings: dict = context.current_step.settings.model_dump() # Get settings and run llm response = await litellm.acompletion( messages=messages, diff --git a/agents-api/agents_api/activities/task_steps/tool_call_step.py b/agents-api/agents_api/activities/task_steps/tool_call_step.py index 05bbe946b..85a119deb 100644 --- a/agents-api/agents_api/activities/task_steps/tool_call_step.py +++ b/agents-api/agents_api/activities/task_steps/tool_call_step.py @@ -10,10 +10,10 @@ @beartype async def tool_call_step(context: StepContext) -> dict: raise NotImplementedError() - # assert isinstance(context.definition, ToolCallStep) + # assert isinstance(context.current_step, ToolCallStep) - # context.definition.tool_id - # context.definition.arguments + # context.current_step.tool_id + # context.current_step.arguments # # get tool by id # # call tool diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index b339b83d1..03462c18f 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -1,5 +1,3 @@ -from typing import Any - from beartype import beartype from temporalio import activity @@ -12,7 +10,7 @@ @beartype -async def yield_step(context: StepContext[YieldStep]) -> StepOutcome[dict[str, Any]]: +async def yield_step(context: StepContext[YieldStep]) -> StepOutcome: all_workflows = context.execution_input.task.workflows workflow = context.current_step.workflow diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index fdaadd30a..dc8d46b70 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -1,11 +1,10 @@ # ruff: noqa: F401, F403, F405 -from typing import Annotated, Generic, Literal, Self, Type, TypeVar +from typing import Annotated, Any, Generic, Literal, Self, Type, TypeVar from uuid import UUID from litellm.utils import _select_tokenizer as select_tokenizer from litellm.utils import token_counter from pydantic import AwareDatetime, Field -from pydantic_partial import create_partial_model from ..common.utils.datetime import utcnow from .Agents import * @@ -97,17 +96,15 @@ class ListResponse(BaseModel, Generic[DataT]): # Create models # ------------- -CreateTransitionRequest = create_partial_model( - Transition, - # - # The following fields are optional - "id", - "execution_id", - "created_at", - "updated_at", - "metadata", -) -CreateTransitionRequest.model_rebuild() + +class CreateTransitionRequest(Transition): + # The following fields are optional in this + + id: UUID | None = None + execution_id: UUID | None = None + created_at: AwareDatetime | None = None + updated_at: AwareDatetime | None = None + metadata: dict[str, Any] | None = None class CreateEntryRequest(BaseEntry): diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index 61466dfdb..65e21ba86 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -93,13 +93,13 @@ def current_workflow(self) -> Workflow: @computed_field @property def current_step(self) -> WorkflowStepType: - step = self.current_workflow[self.cursor.step] + step = self.current_workflow.steps[self.cursor.step] return step @computed_field @property def is_last_step(self) -> bool: - return (self.cursor.step + 1) == len(self.current_workflow) + return (self.cursor.step + 1) == len(self.current_workflow.steps) def model_dump(self, *args, **kwargs) -> dict[str, Any]: dump = super().model_dump(*args, **kwargs) @@ -108,11 +108,8 @@ def model_dump(self, *args, **kwargs) -> dict[str, Any]: return dump -OutcomeType = TypeVar("OutcomeType", bound=BaseModel) - - -class StepOutcome(BaseModel, Generic[OutcomeType]): - output: OutcomeType | None +class StepOutcome(BaseModel): + output: Any transition_to: tuple[TransitionType, TransitionTarget] | None = None diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 9fb74c4c0..f3626d5f4 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -29,11 +29,11 @@ ) from ..common.protocol.tasks import ( ExecutionInput, - # OutcomeType, StepContext, StepOutcome, # Workflow, ) + from ..env import testing STEP_TO_ACTIVITY = { @@ -59,7 +59,7 @@ async def run( context = StepContext( execution_input=execution_input, inputs=previous_inputs, - current=start, + cursor=start, ) step_type = type(context.current_step) @@ -69,7 +69,8 @@ async def run( outcome = await workflow.execute_activity( activity, context, - schedule_to_close_timeout=timedelta(seconds=600), + # TODO: This should be a configurable timeout everywhere based on the task + schedule_to_close_timeout=timedelta(seconds=3 if testing else 600), ) # 2. Then, based on the outcome and step type, decide what to do next diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 323369921..188fe352b 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -232,7 +232,7 @@ def test_task( "name": "test task", "description": "test task about", "input_schema": {"type": "object", "additionalProperties": True}, - "main": [], + "main": [{"evaluate": {"hello": '"world"'}}], } ), client=client, diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 254841075..4092c1d24 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -25,3 +25,6 @@ async def _(client=cozo_client, developer_id=test_developer_id, task=test_task): assert execution.task_id == task.id assert execution.input == data.input mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == "world" From 2fbd8773b49d56ac9ddf4800744e162e33117565 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 13:24:07 -0400 Subject: [PATCH 070/110] feat(agents-api): Add more tests for the task execution Signed-off-by: Diwank Tomer --- .../execution/prepare_execution_input.py | 2 +- .../agents_api/workflows/task_execution.py | 47 +----- agents-api/tests/test_execution_workflow.py | 158 +++++++++++++++++- 3 files changed, 158 insertions(+), 49 deletions(-) diff --git a/agents-api/agents_api/models/execution/prepare_execution_input.py b/agents-api/agents_api/models/execution/prepare_execution_input.py index 39485b90f..8f146da24 100644 --- a/agents-api/agents_api/models/execution/prepare_execution_input.py +++ b/agents-api/agents_api/models/execution/prepare_execution_input.py @@ -167,7 +167,7 @@ def prepare_execution_input( # TODO: Enable these later user = null, session = null, - arguments = {{}}, + arguments = execution->"input" """ queries = [ diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index f3626d5f4..5e5eb8ce6 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -162,48 +162,5 @@ async def run( # Otherwise, recurse to the next step workflow.continue_as_new( - execution_input, next_target, previous_inputs + [final_output] - ) - - ################## - - # should_wait, is_error = False, False - # # Run the step - # match step: - # case PromptStep(): - # outputs = await workflow.execute_activity( - # prompt_step, - # context, - # schedule_to_close_timeout=timedelta(seconds=600), - # ) - # - # # TODO: ChatCompletion does not have tool_calls - # # if outputs.tool_calls is not None: - # # should_wait = True - - # case ToolCallStep(): - # outputs = await workflow.execute_activity( - # tool_call_step, - # context, - # schedule_to_close_timeout=timedelta(seconds=600), - # ) - - # case IfElseWorkflowStep(): - # outputs = await workflow.execute_activity( - # if_else_step, - # context, - # schedule_to_close_timeout=timedelta(seconds=600), - # ) - # workflow_step = YieldStep(**outputs["goto_workflow"]) - # - # outputs = await workflow.execute_child_workflow( - # TaskExecutionWorkflow.run, - # args=[ - # execution_input, - # (workflow_step.workflow, 0), - # previous_inputs, - # ], - # ) - - # case WaitForInputStep(): - # should_wait = True + args=[execution_input, next_target, previous_inputs + [final_output]] + ) \ No newline at end of file diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 4092c1d24..8506b7dc3 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -2,17 +2,36 @@ from ward import test -from agents_api.autogen.openapi_model import CreateExecutionRequest +from agents_api.autogen.openapi_model import CreateExecutionRequest, CreateTaskRequest +from agents_api.models.task.create_task import create_task from agents_api.routers.tasks.create_task_execution import start_execution -from .fixtures import cozo_client, test_developer_id, test_task +from .fixtures import cozo_client, test_agent, test_developer_id from .utils import patch_testing_temporal @test("workflow: create task execution") -async def _(client=cozo_client, developer_id=test_developer_id, task=test_task): +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): data = CreateExecutionRequest(input={"test": "input"}) + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [{"evaluate": {"hello": '"world"'}}], + } + ), + client=client, + ) + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): execution, handle = await start_execution( developer_id=developer_id, @@ -28,3 +47,136 @@ async def _(client=cozo_client, developer_id=test_developer_id, task=test_task): result = await handle.result() assert result["hello"] == "world" + + +@test("workflow: create task execution") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + {"evaluate": {"hello": '"nope"'}}, + {"evaluate": {"hello": '"world"'}}, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == "world" + + +@test("workflow: create task execution") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + # Testing that we can access the input + {"evaluate": {"hello": '_["test"]'}}, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == data.input["test"] + + +@test("workflow: create task execution") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "other_workflow": [ + # Testing that we can access the input + {"evaluate": {"hello": '_["test"]'}}, + ], + "main": [ + # Testing that we can access the input + { + "workflow": "other_workflow", + "arguments": {"test": '_["test"]'}, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == data.input["test"] From 010660b359cb24686b25371e8a822e060485ecfb Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 17:34:05 -0400 Subject: [PATCH 071/110] refactor(agents-api): Add more types Signed-off-by: Diwank Tomer --- agents-api/agents_api/activities/demo.py | 4 ++- .../agents_api/activities/embed_docs.py | 4 +-- agents-api/agents_api/activities/logger.py | 7 +++--- .../agents_api/activities/summarization.py | 4 +-- .../activities/task_steps/yield_step.py | 6 +++-- agents-api/agents_api/clients/cozo.py | 6 +++-- agents-api/agents_api/clients/litellm.py | 5 +++- .../agents_api/common/exceptions/__init__.py | 2 +- agents-api/agents_api/common/utils/cozo.py | 4 +-- agents-api/agents_api/common/utils/json.py | 6 ++--- .../agents_api/common/utils/template.py | 6 +++-- agents-api/agents_api/dependencies/auth.py | 9 +++++-- agents-api/agents_api/env.py | 11 ++++---- agents-api/agents_api/exceptions.py | 6 ++--- agents-api/agents_api/model_registry.py | 17 ++++++++----- .../agents_api/models/agent/create_agent.py | 4 +++ .../models/agent/create_or_update_agent.py | 4 +++ .../agents_api/models/agent/delete_agent.py | 4 +++ .../agents_api/models/agent/get_agent.py | 4 +++ .../agents_api/models/agent/list_agents.py | 5 +++- .../agents_api/models/agent/patch_agent.py | 4 +++ .../agents_api/models/agent/update_agent.py | 4 +++ .../agents_api/models/chat/gather_messages.py | 3 +++ .../models/chat/prepare_chat_context.py | 4 +++ .../models/developer/get_developer.py | 4 +++ .../agents_api/models/docs/create_doc.py | 5 +++- .../agents_api/models/docs/delete_doc.py | 4 +++ .../agents_api/models/docs/embed_snippets.py | 4 +++ agents-api/agents_api/models/docs/get_doc.py | 4 +++ .../agents_api/models/docs/list_docs.py | 5 +++- .../models/docs/search_docs_by_embedding.py | 5 +++- .../models/docs/search_docs_by_text.py | 5 +++- .../agents_api/models/entry/create_entries.py | 4 +++ .../agents_api/models/entry/delete_entries.py | 4 +++ .../agents_api/models/entry/get_history.py | 4 +++ .../agents_api/models/entry/list_entries.py | 5 +++- .../models/execution/create_execution.py | 5 +++- .../execution/create_temporal_lookup.py | 3 +++ .../models/execution/get_execution.py | 4 +++ .../execution/get_execution_transition.py | 4 +++ .../execution/get_paused_execution_token.py | 4 +++ .../execution/get_temporal_workflow_data.py | 4 +++ .../execution/list_execution_transitions.py | 5 +++- .../models/execution/list_executions.py | 5 +++- .../execution/prepare_execution_input.py | 4 +++ .../models/execution/update_execution.py | 4 +++ .../session/create_or_update_session.py | 4 +++ .../models/session/create_session.py | 4 +++ .../models/session/delete_session.py | 4 +++ .../agents_api/models/session/get_session.py | 4 +++ .../models/session/list_sessions.py | 5 +++- .../models/session/patch_session.py | 6 ++++- .../models/session/prepare_session_data.py | 4 +++ .../models/session/update_session.py | 6 ++++- .../models/task/create_or_update_task.py | 4 +++ .../agents_api/models/task/create_task.py | 4 +++ .../agents_api/models/task/delete_task.py | 4 +++ agents-api/agents_api/models/task/get_task.py | 4 +++ .../agents_api/models/task/list_tasks.py | 5 +++- .../agents_api/models/task/patch_task.py | 4 +++ .../agents_api/models/task/update_task.py | 4 +++ .../agents_api/models/tools/create_tools.py | 4 +++ .../agents_api/models/tools/delete_tool.py | 4 +++ .../agents_api/models/tools/get_tool.py | 4 +++ .../agents_api/models/tools/list_tools.py | 5 +++- .../agents_api/models/tools/patch_tool.py | 4 +++ .../agents_api/models/tools/update_tool.py | 4 +++ .../models/user/create_or_update_user.py | 4 +++ .../agents_api/models/user/create_user.py | 4 +++ .../agents_api/models/user/delete_user.py | 4 +++ agents-api/agents_api/models/user/get_user.py | 4 +++ .../agents_api/models/user/list_users.py | 5 +++- .../agents_api/models/user/patch_user.py | 4 +++ .../agents_api/models/user/update_user.py | 4 +++ agents-api/agents_api/models/utils.py | 4 +-- agents-api/agents_api/rec_sum/data.py | 13 +++++----- agents-api/agents_api/rec_sum/entities.py | 6 ++--- agents-api/agents_api/rec_sum/summarize.py | 9 ++++--- agents-api/agents_api/rec_sum/trim.py | 7 +++--- agents-api/agents_api/rec_sum/utils.py | 25 +++++++++++-------- .../agents_api/routers/agents/router.py | 4 ++- agents-api/agents_api/routers/docs/router.py | 4 ++- .../agents_api/routers/docs/search_docs.py | 8 ++++-- agents-api/agents_api/routers/jobs/routers.py | 4 +-- .../agents_api/routers/sessions/exceptions.py | 2 +- .../agents_api/routers/sessions/router.py | 4 ++- .../routers/tasks/create_task_execution.py | 2 +- agents-api/agents_api/routers/tasks/router.py | 4 ++- agents-api/agents_api/routers/users/router.py | 4 ++- agents-api/agents_api/web.py | 11 ++++---- agents-api/agents_api/worker/codec.py | 4 +-- agents-api/agents_api/worker/worker.py | 3 ++- .../agents_api/workflows/task_execution.py | 2 +- 93 files changed, 369 insertions(+), 99 deletions(-) diff --git a/agents-api/agents_api/activities/demo.py b/agents-api/agents_api/activities/demo.py index 797ef6c90..f6d63f206 100644 --- a/agents-api/agents_api/activities/demo.py +++ b/agents-api/agents_api/activities/demo.py @@ -1,3 +1,5 @@ +from typing import Callable + from temporalio import activity from ..env import testing @@ -12,6 +14,6 @@ async def mock_demo_activity(a: int, b: int) -> int: return a + b -demo_activity = activity.defn(name="demo_activity")( +demo_activity: Callable[[int, int], int] = activity.defn(name="demo_activity")( demo_activity if not testing else mock_demo_activity ) diff --git a/agents-api/agents_api/activities/embed_docs.py b/agents-api/agents_api/activities/embed_docs.py index 1dd3793b0..3222ff9e7 100644 --- a/agents-api/agents_api/activities/embed_docs.py +++ b/agents-api/agents_api/activities/embed_docs.py @@ -1,8 +1,8 @@ from beartype import beartype from temporalio import activity +from ..clients import cozo from ..clients import embed as embedder -from ..clients.cozo import get_cozo_client from ..env import testing from ..models.docs.embed_snippets import embed_snippets as embed_snippets_query from .types import EmbedDocsPayload @@ -28,7 +28,7 @@ async def embed_docs(payload: EmbedDocsPayload, cozo_client=None) -> None: doc_id=payload.doc_id, snippet_indices=indices, embeddings=embeddings, - client=cozo_client or get_cozo_client(), + client=cozo_client or cozo.get_cozo_client(), ) diff --git a/agents-api/agents_api/activities/logger.py b/agents-api/agents_api/activities/logger.py index f3c31fece..ed18019dc 100644 --- a/agents-api/agents_api/activities/logger.py +++ b/agents-api/agents_api/activities/logger.py @@ -1,7 +1,8 @@ import logging +from typing import TextIO -logger = logging.getLogger(__name__) -h = logging.StreamHandler() -fmt = logging.Formatter("[%(asctime)s/%(levelname)s] - %(message)s") +logger: logging.Logger = logging.getLogger(__name__) +h: logging.StreamHandler[TextIO] = logging.StreamHandler() +fmt: logging.Formatter = logging.Formatter("[%(asctime)s/%(levelname)s] - %(message)s") h.setFormatter(fmt) logger.addHandler(h) diff --git a/agents-api/agents_api/activities/summarization.py b/agents-api/agents_api/activities/summarization.py index 90a89fde5..a02237bf7 100644 --- a/agents-api/agents_api/activities/summarization.py +++ b/agents-api/agents_api/activities/summarization.py @@ -12,11 +12,11 @@ # TODO: remove stubs -def entries_summarization_query(*args, **kwargs): +def entries_summarization_query(*args, **kwargs) -> pd.DataFrame: return pd.DataFrame() -def get_toplevel_entries_query(*args, **kwargs): +def get_toplevel_entries_query(*args, **kwargs) -> pd.DataFrame: return pd.DataFrame() diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index 03462c18f..dfdb6fa1d 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -1,3 +1,5 @@ +from typing import Callable + from beartype import beartype from temporalio import activity @@ -33,8 +35,8 @@ async def yield_step(context: StepContext[YieldStep]) -> StepOutcome: # Note: This is here just for clarity. We could have just imported yield_step directly # They do the same thing, so we dont need to mock the yield_step function -mock_yield_step = yield_step +mock_yield_step: Callable[[StepContext], StepOutcome] = yield_step -yield_step = activity.defn(name="yield_step")( +yield_step: Callable[[StepContext], StepOutcome] = activity.defn(name="yield_step")( yield_step if not testing else mock_yield_step ) diff --git a/agents-api/agents_api/clients/cozo.py b/agents-api/agents_api/clients/cozo.py index d582e5db4..e2991c9d8 100644 --- a/agents-api/agents_api/clients/cozo.py +++ b/agents-api/agents_api/clients/cozo.py @@ -1,14 +1,16 @@ +from typing import Any, Dict + from pycozo.client import Client from ..env import cozo_auth, cozo_host from ..web import app -options = {"host": cozo_host} +options: Dict[str, str] = {"host": cozo_host} if cozo_auth: options.update({"auth": cozo_auth}) -def get_cozo_client(): +def get_cozo_client() -> Any: client = getattr(app.state, "cozo_client", Client("http", options=options)) if not hasattr(app.state, "cozo_client"): app.state.cozo_client = client diff --git a/agents-api/agents_api/clients/litellm.py b/agents-api/agents_api/clients/litellm.py index 5c3a2e28e..4c78e2876 100644 --- a/agents-api/agents_api/clients/litellm.py +++ b/agents-api/agents_api/clients/litellm.py @@ -1,11 +1,14 @@ from functools import wraps +from typing import List, TypeVar from litellm import acompletion as _acompletion from litellm.utils import CustomStreamWrapper, ModelResponse from ..env import litellm_master_key, litellm_url -__all__ = ["acompletion"] +_RWrapped = TypeVar("_RWrapped") + +__all__: List[str] = ["acompletion"] @wraps(_acompletion) diff --git a/agents-api/agents_api/common/exceptions/__init__.py b/agents-api/agents_api/common/exceptions/__init__.py index fa0016b4e..a38a546a2 100644 --- a/agents-api/agents_api/common/exceptions/__init__.py +++ b/agents-api/agents_api/common/exceptions/__init__.py @@ -11,6 +11,6 @@ class BaseCommonException(Exception): - def __init__(self, msg: str, http_code: int): + def __init__(self, msg: str, http_code: int) -> None: super().__init__(msg) self.http_code = http_code diff --git a/agents-api/agents_api/common/utils/cozo.py b/agents-api/agents_api/common/utils/cozo.py index db8f336f0..a8195a1ba 100644 --- a/agents-api/agents_api/common/utils/cozo.py +++ b/agents-api/agents_api/common/utils/cozo.py @@ -8,7 +8,7 @@ from pycozo import Client # Define a mock client for testing purposes, simulating Cozo API client behavior. -_fake_client = SimpleNamespace() +_fake_client: SimpleNamespace = SimpleNamespace() # Lambda function to process and mutate data dictionaries using the Cozo client's internal method. This is a workaround to access protected member functions for testing. _fake_client._process_mutate_data_dict = lambda data: ( Client._process_mutate_data_dict(_fake_client, data) @@ -20,5 +20,5 @@ ) -def uuid_int_list_to_uuid4(data): +def uuid_int_list_to_uuid4(data) -> UUID: return UUID(bytes=b"".join([i.to_bytes(1, "big") for i in data])) diff --git a/agents-api/agents_api/common/utils/json.py b/agents-api/agents_api/common/utils/json.py index 9beff6049..3157af9c8 100644 --- a/agents-api/agents_api/common/utils/json.py +++ b/agents-api/agents_api/common/utils/json.py @@ -10,7 +10,7 @@ class CustomJSONEncoder(json.JSONEncoder): """A custom JSON encoder subclass that handles None values and UUIDs for JSON serialization. It allows specifying a default value for None objects during initialization.""" - def __init__(self, *args, **kwargs): + def __init__(self, *args, **kwargs) -> None: """Initializes the custom JSON encoder. Parameters: *args: Variable length argument list. @@ -19,7 +19,7 @@ def __init__(self, *args, **kwargs): self._default_empty_value = kwargs.pop("default_empty_value") super().__init__(*args, **kwargs) - def encode(self, o): + def encode(self, o) -> str: """Encodes the given object into a JSON formatted string. Parameters: o: The object to encode. @@ -27,7 +27,7 @@ def encode(self, o): # Use the overridden default method for serialization before encoding return super().encode(self.default(o)) - def default(self, obj): + def default(self, obj) -> Any: """Provides a default serialization for objects that the standard JSON encoder cannot serialize. Parameters: obj: The object to serialize. diff --git a/agents-api/agents_api/common/utils/template.py b/agents-api/agents_api/common/utils/template.py index d806ddb6d..1fcc143b3 100644 --- a/agents-api/agents_api/common/utils/template.py +++ b/agents-api/agents_api/common/utils/template.py @@ -1,14 +1,16 @@ +from typing import List + import arrow from jinja2.sandbox import ImmutableSandboxedEnvironment from jinja2schema import infer, to_json_schema from jsonschema import validate -__all__ = [ +__all__: List[str] = [ "render_template", ] # jinja environment -jinja_env = ImmutableSandboxedEnvironment( +jinja_env: ImmutableSandboxedEnvironment = ImmutableSandboxedEnvironment( autoescape=False, trim_blocks=True, lstrip_blocks=True, diff --git a/agents-api/agents_api/dependencies/auth.py b/agents-api/agents_api/dependencies/auth.py index 0054cb1cc..e5e22995b 100644 --- a/agents-api/agents_api/dependencies/auth.py +++ b/agents-api/agents_api/dependencies/auth.py @@ -1,13 +1,18 @@ +from typing import Any + from fastapi import HTTPException, Security from fastapi.security.api_key import APIKeyHeader from starlette.status import HTTP_403_FORBIDDEN from ..env import api_key, api_key_header_name -api_key_header = APIKeyHeader(name=api_key_header_name, auto_error=False) +api_key_header: Any = APIKeyHeader(name=api_key_header_name, auto_error=False) -async def get_api_key(user_api_key: str = Security(api_key_header)): +async def get_api_key( + user_api_key: str = Security(api_key_header), +) -> str: + user_api_key = str(user_api_key) user_api_key = (user_api_key or "").replace("Bearer ", "").strip() if user_api_key != api_key: diff --git a/agents-api/agents_api/env.py b/agents-api/agents_api/env.py index afdc81e1e..64d9082ef 100644 --- a/agents-api/agents_api/env.py +++ b/agents-api/agents_api/env.py @@ -5,11 +5,12 @@ import random from pprint import pprint +from typing import Any, Dict from environs import Env # Initialize the Env object for environment variable parsing. -env = Env() +env: Any = Env() # Debug @@ -30,7 +31,7 @@ # Auth # ---- -_random_generated_key = "".join(str(random.randint(0, 9)) for _ in range(32)) +_random_generated_key: str = "".join(str(random.randint(0, 9)) for _ in range(32)) api_key: str = env.str("AGENTS_API_KEY", _random_generated_key) if api_key == _random_generated_key: @@ -65,12 +66,12 @@ temporal_namespace: str = env.str("TEMPORAL_NAMESPACE", default="default") temporal_client_cert: str = env.str("TEMPORAL_CLIENT_CERT", default=None) temporal_private_key: str = env.str("TEMPORAL_PRIVATE_KEY", default=None) -temporal_endpoint = env.str("TEMPORAL_ENDPOINT", default="localhost:7233") -temporal_task_queue = env.str("TEMPORAL_TASK_QUEUE", default="julep-task-queue") +temporal_endpoint: Any = env.str("TEMPORAL_ENDPOINT", default="localhost:7233") +temporal_task_queue: Any = env.str("TEMPORAL_TASK_QUEUE", default="julep-task-queue") # Consolidate environment variables -environment = dict( +environment: Dict[str, Any] = dict( debug=debug, cozo_host=cozo_host, cozo_auth=cozo_auth, diff --git a/agents-api/agents_api/exceptions.py b/agents-api/agents_api/exceptions.py index 2ccc5a67f..fbb8f00f8 100644 --- a/agents-api/agents_api/exceptions.py +++ b/agents-api/agents_api/exceptions.py @@ -3,17 +3,17 @@ class AgentsBaseException(Exception): class ModelNotSupportedError(AgentsBaseException): - def __init__(self, model_name): + def __init__(self, model_name) -> None: super().__init__(f"model {model_name} is not supported") class PromptTooBigError(AgentsBaseException): - def __init__(self, token_count, max_tokens): + def __init__(self, token_count, max_tokens) -> None: super().__init__( f"prompt is too big, {token_count} tokens provided, exceeds maximum of {max_tokens}" ) class UnknownTokenizerError(AgentsBaseException): - def __init__(self): + def __init__(self) -> None: super().__init__("unknown tokenizer") diff --git a/agents-api/agents_api/model_registry.py b/agents-api/agents_api/model_registry.py index 99ae66ea3..0120cc205 100644 --- a/agents-api/agents_api/model_registry.py +++ b/agents-api/agents_api/model_registry.py @@ -66,7 +66,7 @@ } -DISCONTINUED_MODELS = { +DISCONTINUED_MODELS: Dict[str, int] = { "code-davinci-002": 8001, "code-davinci-001": 8001, "code-cushman-002": 2048, @@ -84,9 +84,14 @@ "claude-3-haiku-20240307": 180000, } -OPENAI_MODELS = {**GPT4_MODELS, **TURBO_MODELS, **GPT3_5_MODELS, **GPT3_MODELS} +OPENAI_MODELS: Dict[str, int] = { + **GPT4_MODELS, + **TURBO_MODELS, + **GPT3_5_MODELS, + **GPT3_MODELS, +} -LOCAL_MODELS = { +LOCAL_MODELS: Dict[str, int] = { "gpt-4o": 32768, "gpt-4o-awq": 32768, "TinyLlama/TinyLlama_v1.1": 2048, @@ -95,13 +100,13 @@ "OpenPipe/Hermes-2-Theta-Llama-3-8B-32k": 32768, } -LOCAL_MODELS_WITH_TOOL_CALLS = { +LOCAL_MODELS_WITH_TOOL_CALLS: Dict[str, int] = { "OpenPipe/Hermes-2-Theta-Llama-3-8B-32k": 32768, "julep-ai/Hermes-2-Theta-Llama-3-8B": 8192, } -OLLAMA_MODELS = { +OLLAMA_MODELS: Dict[str, int] = { "llama2": 4096, } -CHAT_MODELS = {**GPT4_MODELS, **TURBO_MODELS, **CLAUDE_MODELS} +CHAT_MODELS: Dict[str, int] = {**GPT4_MODELS, **TURBO_MODELS, **CLAUDE_MODELS} diff --git a/agents-api/agents_api/models/agent/create_agent.py b/agents-api/agents_api/models/agent/create_agent.py index a4b408c78..73be4ec77 100644 --- a/agents-api/agents_api/models/agent/create_agent.py +++ b/agents-api/agents_api/models/agent/create_agent.py @@ -3,6 +3,7 @@ It includes functions to construct and execute datalog queries for inserting new agent records. """ +from typing import Any, TypeVar from uuid import UUID, uuid4 from beartype import beartype @@ -20,6 +21,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/agent/create_or_update_agent.py b/agents-api/agents_api/models/agent/create_or_update_agent.py index fb80f6dd7..64b008d44 100644 --- a/agents-api/agents_api/models/agent/create_or_update_agent.py +++ b/agents-api/agents_api/models/agent/create_or_update_agent.py @@ -3,6 +3,7 @@ It includes functions to construct and execute datalog queries for inserting new agent records. """ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -20,6 +21,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/agent/delete_agent.py b/agents-api/agents_api/models/agent/delete_agent.py index e1efd7333..409c755d3 100644 --- a/agents-api/agents_api/models/agent/delete_agent.py +++ b/agents-api/agents_api/models/agent/delete_agent.py @@ -2,6 +2,7 @@ This module contains the implementation of the delete_agent_query function, which is responsible for deleting an agent and its related default settings from the CozoDB database. """ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -20,6 +21,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/agent/get_agent.py b/agents-api/agents_api/models/agent/get_agent.py index ceef527f3..c977fa614 100644 --- a/agents-api/agents_api/models/agent/get_agent.py +++ b/agents-api/agents_api/models/agent/get_agent.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -15,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/agent/list_agents.py b/agents-api/agents_api/models/agent/list_agents.py index 5d291f654..5266659d7 100644 --- a/agents-api/agents_api/models/agent/list_agents.py +++ b/agents-api/agents_api/models/agent/list_agents.py @@ -1,4 +1,4 @@ -from typing import Any, Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/agent/patch_agent.py b/agents-api/agents_api/models/agent/patch_agent.py index 336e33a91..72fdc7811 100644 --- a/agents-api/agents_api/models/agent/patch_agent.py +++ b/agents-api/agents_api/models/agent/patch_agent.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -17,6 +18,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/agent/update_agent.py b/agents-api/agents_api/models/agent/update_agent.py index 4dd489333..be7e9ea21 100644 --- a/agents-api/agents_api/models/agent/update_agent.py +++ b/agents-api/agents_api/models/agent/update_agent.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/chat/gather_messages.py b/agents-api/agents_api/models/chat/gather_messages.py index 2a3c0eca1..f8e08632d 100644 --- a/agents-api/agents_api/models/chat/gather_messages.py +++ b/agents-api/agents_api/models/chat/gather_messages.py @@ -1,3 +1,4 @@ +from typing import TypeVar from uuid import UUID from beartype import beartype @@ -18,6 +19,8 @@ rewrap_exceptions, ) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/chat/prepare_chat_context.py b/agents-api/agents_api/models/chat/prepare_chat_context.py index 5ca93b8c0..742038535 100644 --- a/agents-api/agents_api/models/chat/prepare_chat_context.py +++ b/agents-api/agents_api/models/chat/prepare_chat_context.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -17,6 +18,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/developer/get_developer.py b/agents-api/agents_api/models/developer/get_developer.py index c78517613..31ade5334 100644 --- a/agents-api/agents_api/models/developer/get_developer.py +++ b/agents-api/agents_api/models/developer/get_developer.py @@ -1,5 +1,6 @@ """Module for retrieving document snippets from the CozoDB based on document IDs.""" +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions({QueryException: partialclass(HTTPException, status_code=401)}) @cozo_query diff --git a/agents-api/agents_api/models/docs/create_doc.py b/agents-api/agents_api/models/docs/create_doc.py index 3a86ad0e5..ee26df484 100644 --- a/agents-api/agents_api/models/docs/create_doc.py +++ b/agents-api/agents_api/models/docs/create_doc.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Any, Literal, TypeVar from uuid import UUID, uuid4 from beartype import beartype @@ -17,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/docs/delete_doc.py b/agents-api/agents_api/models/docs/delete_doc.py index e7a02f3d9..c02705756 100644 --- a/agents-api/agents_api/models/docs/delete_doc.py +++ b/agents-api/agents_api/models/docs/delete_doc.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/docs/embed_snippets.py b/agents-api/agents_api/models/docs/embed_snippets.py index a701ef1be..e810d0379 100644 --- a/agents-api/agents_api/models/docs/embed_snippets.py +++ b/agents-api/agents_api/models/docs/embed_snippets.py @@ -1,5 +1,6 @@ """Module for embedding documents in the cozodb database. Contains functions to update document embeddings.""" +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -18,6 +19,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/docs/get_doc.py b/agents-api/agents_api/models/docs/get_doc.py index 6c612fef7..84cd181ec 100644 --- a/agents-api/agents_api/models/docs/get_doc.py +++ b/agents-api/agents_api/models/docs/get_doc.py @@ -1,5 +1,6 @@ """Module for retrieving document snippets from the CozoDB based on document IDs.""" +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/docs/list_docs.py b/agents-api/agents_api/models/docs/list_docs.py index d785cc331..afdf06c2d 100644 --- a/agents-api/agents_api/models/docs/list_docs.py +++ b/agents-api/agents_api/models/docs/list_docs.py @@ -1,7 +1,7 @@ """This module contains functions for querying document-related data from the 'cozodb' database using datalog queries.""" import json -from typing import Any, Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -19,6 +19,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/docs/search_docs_by_embedding.py b/agents-api/agents_api/models/docs/search_docs_by_embedding.py index 3f7114a23..acebd09cd 100644 --- a/agents-api/agents_api/models/docs/search_docs_by_embedding.py +++ b/agents-api/agents_api/models/docs/search_docs_by_embedding.py @@ -1,6 +1,6 @@ """This module contains functions for searching documents in the CozoDB based on embedding queries.""" -from typing import Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -18,6 +18,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/docs/search_docs_by_text.py b/agents-api/agents_api/models/docs/search_docs_by_text.py index a5e379f24..0662aa84d 100644 --- a/agents-api/agents_api/models/docs/search_docs_by_text.py +++ b/agents-api/agents_api/models/docs/search_docs_by_text.py @@ -1,6 +1,6 @@ """This module contains functions for searching documents in the CozoDB based on embedding queries.""" -from typing import Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -18,6 +18,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/entry/create_entries.py b/agents-api/agents_api/models/entry/create_entries.py index e71a81c7a..e227714d1 100644 --- a/agents-api/agents_api/models/entry/create_entries.py +++ b/agents-api/agents_api/models/entry/create_entries.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID, uuid4 from beartype import beartype @@ -19,6 +20,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/entry/delete_entries.py b/agents-api/agents_api/models/entry/delete_entries.py index 1d7cf0386..48c37cd25 100644 --- a/agents-api/agents_api/models/entry/delete_entries.py +++ b/agents-api/agents_api/models/entry/delete_entries.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -17,6 +18,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/entry/get_history.py b/agents-api/agents_api/models/entry/get_history.py index fed658ea5..68fe05979 100644 --- a/agents-api/agents_api/models/entry/get_history.py +++ b/agents-api/agents_api/models/entry/get_history.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/entry/list_entries.py b/agents-api/agents_api/models/entry/list_entries.py index da2341c4c..d3081a9b0 100644 --- a/agents-api/agents_api/models/entry/list_entries.py +++ b/agents-api/agents_api/models/entry/list_entries.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/create_execution.py b/agents-api/agents_api/models/execution/create_execution.py index c564c343d..b4918065b 100644 --- a/agents-api/agents_api/models/execution/create_execution.py +++ b/agents-api/agents_api/models/execution/create_execution.py @@ -1,4 +1,4 @@ -from typing import Annotated +from typing import Annotated, Any, TypeVar from uuid import UUID, uuid4 from beartype import beartype @@ -18,6 +18,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/create_temporal_lookup.py b/agents-api/agents_api/models/execution/create_temporal_lookup.py index 1867b64b9..21c3005dd 100644 --- a/agents-api/agents_api/models/execution/create_temporal_lookup.py +++ b/agents-api/agents_api/models/execution/create_temporal_lookup.py @@ -1,3 +1,4 @@ +from typing import TypeVar from uuid import UUID, uuid4 from beartype import beartype @@ -15,6 +16,8 @@ verify_developer_owns_resource_query, ) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/get_execution.py b/agents-api/agents_api/models/execution/get_execution.py index a0bbe550c..cf9df2e09 100644 --- a/agents-api/agents_api/models/execution/get_execution.py +++ b/agents-api/agents_api/models/execution/get_execution.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -13,6 +14,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/get_execution_transition.py b/agents-api/agents_api/models/execution/get_execution_transition.py index 39d30278c..d418ef5f4 100644 --- a/agents-api/agents_api/models/execution/get_execution_transition.py +++ b/agents-api/agents_api/models/execution/get_execution_transition.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -14,6 +15,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/get_paused_execution_token.py b/agents-api/agents_api/models/execution/get_paused_execution_token.py index 1b1f6e803..d8b0945c1 100644 --- a/agents-api/agents_api/models/execution/get_paused_execution_token.py +++ b/agents-api/agents_api/models/execution/get_paused_execution_token.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -13,6 +14,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/get_temporal_workflow_data.py b/agents-api/agents_api/models/execution/get_temporal_workflow_data.py index 104918f22..bb0a462ef 100644 --- a/agents-api/agents_api/models/execution/get_temporal_workflow_data.py +++ b/agents-api/agents_api/models/execution/get_temporal_workflow_data.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -12,6 +13,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/list_execution_transitions.py b/agents-api/agents_api/models/execution/list_execution_transitions.py index 9b103acf0..45aca935a 100644 --- a/agents-api/agents_api/models/execution/list_execution_transitions.py +++ b/agents-api/agents_api/models/execution/list_execution_transitions.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -9,6 +9,9 @@ from ...autogen.openapi_model import Transition from ..utils import cozo_query, partialclass, rewrap_exceptions, wrap_in_class +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/list_executions.py b/agents-api/agents_api/models/execution/list_executions.py index e497ec4fe..09194cdbd 100644 --- a/agents-api/agents_api/models/execution/list_executions.py +++ b/agents-api/agents_api/models/execution/list_executions.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/prepare_execution_input.py b/agents-api/agents_api/models/execution/prepare_execution_input.py index 8f146da24..c858bc6a0 100644 --- a/agents-api/agents_api/models/execution/prepare_execution_input.py +++ b/agents-api/agents_api/models/execution/prepare_execution_input.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -20,6 +21,9 @@ ) from .get_execution import get_execution +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/execution/update_execution.py b/agents-api/agents_api/models/execution/update_execution.py index f3c1e528e..90a8cb1cc 100644 --- a/agents-api/agents_api/models/execution/update_execution.py +++ b/agents-api/agents_api/models/execution/update_execution.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -22,6 +23,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/session/create_or_update_session.py b/agents-api/agents_api/models/session/create_or_update_session.py index 7b2e39e74..60c0b7f71 100644 --- a/agents-api/agents_api/models/session/create_or_update_session.py +++ b/agents-api/agents_api/models/session/create_or_update_session.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -19,6 +20,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/session/create_session.py b/agents-api/agents_api/models/session/create_session.py index 7bb0576c9..a83837ffd 100644 --- a/agents-api/agents_api/models/session/create_session.py +++ b/agents-api/agents_api/models/session/create_session.py @@ -3,6 +3,7 @@ It constructs and executes a datalog query to insert session data. """ +from typing import Any, TypeVar from uuid import UUID, uuid4 from beartype import beartype @@ -20,6 +21,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/session/delete_session.py b/agents-api/agents_api/models/session/delete_session.py index 71f153fb1..af9e331c7 100644 --- a/agents-api/agents_api/models/session/delete_session.py +++ b/agents-api/agents_api/models/session/delete_session.py @@ -1,5 +1,6 @@ """This module contains the implementation for deleting sessions from the 'cozodb' database using datalog queries.""" +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -18,6 +19,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/session/get_session.py b/agents-api/agents_api/models/session/get_session.py index 8e7d6adb5..0a365df2f 100644 --- a/agents-api/agents_api/models/session/get_session.py +++ b/agents-api/agents_api/models/session/get_session.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -15,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/session/list_sessions.py b/agents-api/agents_api/models/session/list_sessions.py index dba36ab98..fa1097e5e 100644 --- a/agents-api/agents_api/models/session/list_sessions.py +++ b/agents-api/agents_api/models/session/list_sessions.py @@ -1,6 +1,6 @@ """This module contains functions for querying session data from the 'cozodb' database.""" -from typing import Any, Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -18,6 +18,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/session/patch_session.py b/agents-api/agents_api/models/session/patch_session.py index feafcc679..e6e0e5750 100644 --- a/agents-api/agents_api/models/session/patch_session.py +++ b/agents-api/agents_api/models/session/patch_session.py @@ -1,5 +1,6 @@ """This module contains functions for patching session data in the 'cozodb' database using datalog queries.""" +from typing import Any, List, TypeVar from uuid import UUID from beartype import beartype @@ -18,7 +19,10 @@ wrap_in_class, ) -_fields = [ +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + +_fields: List[str] = [ "situation", "summary", "created_at", diff --git a/agents-api/agents_api/models/session/prepare_session_data.py b/agents-api/agents_api/models/session/prepare_session_data.py index 754b0538c..9a936b183 100644 --- a/agents-api/agents_api/models/session/prepare_session_data.py +++ b/agents-api/agents_api/models/session/prepare_session_data.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -15,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/session/update_session.py b/agents-api/agents_api/models/session/update_session.py index 14b989de1..99688bd98 100644 --- a/agents-api/agents_api/models/session/update_session.py +++ b/agents-api/agents_api/models/session/update_session.py @@ -1,3 +1,4 @@ +from typing import Any, List, TypeVar from uuid import UUID from beartype import beartype @@ -16,7 +17,10 @@ wrap_in_class, ) -_fields = [ +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + +_fields: List[str] = [ "situation", "summary", "metadata", diff --git a/agents-api/agents_api/models/task/create_or_update_task.py b/agents-api/agents_api/models/task/create_or_update_task.py index a3014a64f..af7e258d9 100644 --- a/agents-api/agents_api/models/task/create_or_update_task.py +++ b/agents-api/agents_api/models/task/create_or_update_task.py @@ -3,6 +3,7 @@ It constructs and executes a datalog query to insert Task data. """ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -26,6 +27,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/task/create_task.py b/agents-api/agents_api/models/task/create_task.py index 61f0bca72..a44146c34 100644 --- a/agents-api/agents_api/models/task/create_task.py +++ b/agents-api/agents_api/models/task/create_task.py @@ -3,6 +3,7 @@ It constructs and executes a datalog query to insert Task data. """ +from typing import Any, TypeVar from uuid import UUID, uuid4 from beartype import beartype @@ -24,6 +25,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/task/delete_task.py b/agents-api/agents_api/models/task/delete_task.py index 1eb780057..60d6f2756 100644 --- a/agents-api/agents_api/models/task/delete_task.py +++ b/agents-api/agents_api/models/task/delete_task.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/task/get_task.py b/agents-api/agents_api/models/task/get_task.py index 975da28cd..076936b6c 100644 --- a/agents-api/agents_api/models/task/get_task.py +++ b/agents-api/agents_api/models/task/get_task.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -15,6 +16,9 @@ ) from .create_task import spec_to_task +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/task/list_tasks.py b/agents-api/agents_api/models/task/list_tasks.py index 1c6e16efd..573c1404e 100644 --- a/agents-api/agents_api/models/task/list_tasks.py +++ b/agents-api/agents_api/models/task/list_tasks.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +16,9 @@ ) from .create_task import spec_to_task +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/task/patch_task.py b/agents-api/agents_api/models/task/patch_task.py index dc32f83d2..1837064c7 100644 --- a/agents-api/agents_api/models/task/patch_task.py +++ b/agents-api/agents_api/models/task/patch_task.py @@ -3,6 +3,7 @@ It constructs and executes a datalog query to insert Task data. """ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -22,6 +23,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/task/update_task.py b/agents-api/agents_api/models/task/update_task.py index 151d4fb4d..9cfb04357 100644 --- a/agents-api/agents_api/models/task/update_task.py +++ b/agents-api/agents_api/models/task/update_task.py @@ -3,6 +3,7 @@ It constructs and executes a datalog query to insert Task data. """ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -22,6 +23,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/tools/create_tools.py b/agents-api/agents_api/models/tools/create_tools.py index 0c034d4d2..dd8397797 100644 --- a/agents-api/agents_api/models/tools/create_tools.py +++ b/agents-api/agents_api/models/tools/create_tools.py @@ -1,5 +1,6 @@ """This module contains functions for creating tools in the CozoDB database.""" +from typing import Any, TypeVar from uuid import UUID, uuid4 from beartype import beartype @@ -17,6 +18,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/tools/delete_tool.py b/agents-api/agents_api/models/tools/delete_tool.py index e6d00498a..c79cdfd29 100644 --- a/agents-api/agents_api/models/tools/delete_tool.py +++ b/agents-api/agents_api/models/tools/delete_tool.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/tools/get_tool.py b/agents-api/agents_api/models/tools/get_tool.py index f3e6a52c3..5ea009064 100644 --- a/agents-api/agents_api/models/tools/get_tool.py +++ b/agents-api/agents_api/models/tools/get_tool.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -15,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/tools/list_tools.py b/agents-api/agents_api/models/tools/list_tools.py index e1636fdd4..4b44fc1e0 100644 --- a/agents-api/agents_api/models/tools/list_tools.py +++ b/agents-api/agents_api/models/tools/list_tools.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/tools/patch_tool.py b/agents-api/agents_api/models/tools/patch_tool.py index 0b2777030..5bbfe1c91 100644 --- a/agents-api/agents_api/models/tools/patch_tool.py +++ b/agents-api/agents_api/models/tools/patch_tool.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/tools/update_tool.py b/agents-api/agents_api/models/tools/update_tool.py index 3e91b7562..d1676e984 100644 --- a/agents-api/agents_api/models/tools/update_tool.py +++ b/agents-api/agents_api/models/tools/update_tool.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -19,6 +20,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/user/create_or_update_user.py b/agents-api/agents_api/models/user/create_or_update_user.py index 5784db880..9e9045e74 100644 --- a/agents-api/agents_api/models/user/create_or_update_user.py +++ b/agents-api/agents_api/models/user/create_or_update_user.py @@ -3,6 +3,7 @@ It includes functions to construct and execute datalog queries for inserting new user records. """ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -19,6 +20,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/user/create_user.py b/agents-api/agents_api/models/user/create_user.py index 4115b3326..9dd036c57 100644 --- a/agents-api/agents_api/models/user/create_user.py +++ b/agents-api/agents_api/models/user/create_user.py @@ -3,6 +3,7 @@ It defines a query for inserting user data into the 'users' relation. """ +from typing import Any, TypeVar from uuid import UUID, uuid4 from beartype import beartype @@ -19,6 +20,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/user/delete_user.py b/agents-api/agents_api/models/user/delete_user.py index b9ebc2db7..b5fcb8424 100644 --- a/agents-api/agents_api/models/user/delete_user.py +++ b/agents-api/agents_api/models/user/delete_user.py @@ -2,6 +2,7 @@ This module contains the implementation of the delete_user_query function, which is responsible for deleting an user and its related default settings from the CozoDB database. """ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -20,6 +21,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/user/get_user.py b/agents-api/agents_api/models/user/get_user.py index d4556a365..181bf05f0 100644 --- a/agents-api/agents_api/models/user/get_user.py +++ b/agents-api/agents_api/models/user/get_user.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -15,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/user/list_users.py b/agents-api/agents_api/models/user/list_users.py index 5fdc62ef0..57dc9b8c8 100644 --- a/agents-api/agents_api/models/user/list_users.py +++ b/agents-api/agents_api/models/user/list_users.py @@ -1,4 +1,4 @@ -from typing import Any, Literal +from typing import Any, Literal, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +16,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/user/patch_user.py b/agents-api/agents_api/models/user/patch_user.py index d8dfeb2ad..faf38298c 100644 --- a/agents-api/agents_api/models/user/patch_user.py +++ b/agents-api/agents_api/models/user/patch_user.py @@ -1,5 +1,6 @@ """Module for generating datalog queries to update user information in the 'cozodb' database.""" +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -19,6 +20,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/user/update_user.py b/agents-api/agents_api/models/user/update_user.py index 8411ecc3e..9a13d9369 100644 --- a/agents-api/agents_api/models/user/update_user.py +++ b/agents-api/agents_api/models/user/update_user.py @@ -1,3 +1,4 @@ +from typing import Any, TypeVar from uuid import UUID from beartype import beartype @@ -16,6 +17,9 @@ wrap_in_class, ) +ModelT = TypeVar("ModelT", bound=Any) +T = TypeVar("T") + @rewrap_exceptions( { diff --git a/agents-api/agents_api/models/utils.py b/agents-api/agents_api/models/utils.py index ee2ba3fdd..bc36af8d2 100644 --- a/agents-api/agents_api/models/utils.py +++ b/agents-api/agents_api/models/utils.py @@ -195,10 +195,10 @@ def wrapper(*args: P.args, client=None, **kwargs: P.kwargs) -> pd.DataFrame: ) # Run the query - from ..clients.cozo import get_cozo_client + from ..clients import cozo try: - client = client or get_cozo_client() + client = client or cozo.get_cozo_client() result = client.run(query, variables) except Exception as e: diff --git a/agents-api/agents_api/rec_sum/data.py b/agents-api/agents_api/rec_sum/data.py index e7857a037..23474c995 100644 --- a/agents-api/agents_api/rec_sum/data.py +++ b/agents-api/agents_api/rec_sum/data.py @@ -1,24 +1,25 @@ import json from pathlib import Path +from typing import Any -module_directory = Path(__file__).parent +module_directory: Path = Path(__file__).parent with open(f"{module_directory}/entities_example_chat.json", "r") as _f: - entities_example_chat = json.load(_f) + entities_example_chat: Any = json.load(_f) with open(f"{module_directory}/trim_example_chat.json", "r") as _f: - trim_example_chat = json.load(_f) + trim_example_chat: Any = json.load(_f) with open(f"{module_directory}/trim_example_result.json", "r") as _f: - trim_example_result = json.load(_f) + trim_example_result: Any = json.load(_f) with open(f"{module_directory}/summarize_example_chat.json", "r") as _f: - summarize_example_chat = json.load(_f) + summarize_example_chat: Any = json.load(_f) with open(f"{module_directory}/summarize_example_result.json", "r") as _f: - summarize_example_result = json.load(_f) + summarize_example_result: Any = json.load(_f) diff --git a/agents-api/agents_api/rec_sum/entities.py b/agents-api/agents_api/rec_sum/entities.py index 9992063ff..01b29951b 100644 --- a/agents-api/agents_api/rec_sum/entities.py +++ b/agents-api/agents_api/rec_sum/entities.py @@ -12,21 +12,21 @@ ## Entities ## ############## -entities_example_plan = """\ +entities_example_plan: str = """\ Thinking step by step: - To add context for future entries, let's outline the main entities in the session above. - In this session, as mentioned in the first message metadata, the user's name is Camille and the assistant's name is JaneBot. - They talk about Elon Musk and the banana tattoo on Camille's arm briefly.""" -entities_example_result = """\ +entities_example_result: str = """\ 1. Camille (The user): Humorous, creative, and enjoys playful banter. 2. JaneBot (The assistant): Engages in lighthearted conversation and tries to guess user's thoughts. 3. Elon Musk: Camille and JaneBot discuss the polarizing tech and space industry figure. 4. Banana Tattoo: Camille has a tattoo of a banana on their arm.""" -entities_instructions = """\ +entities_instructions: str = """\ Your goal is to identify the main entities in the session. Entities should include: - Characters in the conversation: Assistant, User1, User2 - People references or spoken about diff --git a/agents-api/agents_api/rec_sum/summarize.py b/agents-api/agents_api/rec_sum/summarize.py index f98f35094..46a6662a3 100644 --- a/agents-api/agents_api/rec_sum/summarize.py +++ b/agents-api/agents_api/rec_sum/summarize.py @@ -1,4 +1,5 @@ import json +from typing import List from tenacity import retry, stop_after_attempt @@ -10,7 +11,7 @@ ## summarize ## ########## -summarize_example_plan = """\ +summarize_example_plan: str = """\ Planning step by step: - We can replace entries 1,2,3,4 with a summary of those messages. - We can replace entries 5,6,7,8 similarly. @@ -23,7 +24,7 @@ - We can safely summarize message 34's essay into just the salient points only.""" -summarize_instructions = """\ +summarize_instructions: str = """\ Your goal is to compactify the history by coalescing redundant information in messages into their summary in order to reduce its size and save costs. Instructions: @@ -34,7 +35,9 @@ - VERY IMPORTANT: Add the indices of messages that are being summarized so that those messages can then be removed from the session otherwise, there'll be no way to identify which messages to remove. See example for more details.""" -def make_summarize_prompt(session, user="a user", assistant="gpt-4-turbo", **_): +def make_summarize_prompt( + session, user="a user", assistant="gpt-4-turbo", **_ +) -> List[str]: return [ f"You are given a session history of a chat between {user or 'a user'} and {assistant or 'gpt-4-turbo'}. The session is formatted in the ChatML JSON format (from OpenAI).\n\n{summarize_instructions}\n\n\n{json.dumps(add_indices(summarize_example_chat), indent=2)}\n\n\n\n{summarize_example_plan}\n\n\n\n{json.dumps(summarize_example_result, indent=2)}\n", f"Begin! Write the summarized messages as a json list just like the example above. First write your plan inside and then your answer between . Don't forget to add the indices of the messages being summarized alongside each summary.\n\n\n{json.dumps(add_indices(session), indent=2)}\n\n", diff --git a/agents-api/agents_api/rec_sum/trim.py b/agents-api/agents_api/rec_sum/trim.py index 7e902859c..ee4025ea0 100644 --- a/agents-api/agents_api/rec_sum/trim.py +++ b/agents-api/agents_api/rec_sum/trim.py @@ -1,4 +1,5 @@ import json +from typing import List from tenacity import retry, stop_after_attempt @@ -10,7 +11,7 @@ ## Trim ## ########## -trim_example_plan = """\ +trim_example_plan: str = """\ Thinking step by step: - To trim the context, let's examine the messages in the session above. - Messages 1, 2, and 3 are succinct and do not need trimming. @@ -18,7 +19,7 @@ - Message 7 is short enough and doesn't need any edits.""" -trim_instructions = """\ +trim_instructions: str = """\ Your goal is to identify messages in the session that are needlessly verbose and then trim them in length without losing any meaning or changing the tone of the message. Instructions: @@ -32,7 +33,7 @@ # It is important to make keep the tone, setting and flow of the conversation consistent while trimming the messages. -def make_trim_prompt(session, user="a user", assistant="gpt-4-turbo", **_): +def make_trim_prompt(session, user="a user", assistant="gpt-4-turbo", **_) -> List[str]: return [ f"You are given a session history of a chat between {user or 'a user'} and {assistant or 'gpt-4-turbo'}. The session is formatted in the ChatML JSON format (from OpenAI).\n\n{trim_instructions}\n\n\n{json.dumps(add_indices(trim_example_chat), indent=2)}\n\n\n\n{trim_example_plan}\n\n\n\n{json.dumps(trim_example_result, indent=2)}\n", f"Begin! Write the trimmed messages as a json list. First write your plan inside and then your answer between .\n\n\n{json.dumps(add_indices(session), indent=2)}\n\n", diff --git a/agents-api/agents_api/rec_sum/utils.py b/agents-api/agents_api/rec_sum/utils.py index 596174e08..c674a4d44 100644 --- a/agents-api/agents_api/rec_sum/utils.py +++ b/agents-api/agents_api/rec_sum/utils.py @@ -3,9 +3,14 @@ ########### +from typing import Any, Dict, List, TypeVar + +_T2 = TypeVar("_T2") + + class chatml: @staticmethod - def make(content, role="system", name=None, **_): + def make(content, role="system", name: _T2 = None, **_) -> Dict[str, _T2]: return { key: value for key, value in dict(role=role, name=name, content=content).items() @@ -13,39 +18,39 @@ def make(content, role="system", name=None, **_): } @staticmethod - def user(content, name=None): + def user(content, name=None) -> Any: return chatml.make(role="user", content=content, name=name) @staticmethod - def assistant(content, name=None): + def assistant(content, name=None) -> Any: return chatml.make(role="assistant", content=content, name=name) @staticmethod - def system(content, name=None): + def system(content, name=None) -> Any: return chatml.make(content, name=name) @staticmethod - def thought(content, name=None): + def thought(content, name=None) -> Any: return chatml.make(content, name="thought") @staticmethod - def information(content): + def information(content) -> Any: return chatml.system(content, name="information") @staticmethod - def summary(content): + def summary(content) -> Any: return chatml.system(content, name="summary") @staticmethod - def entities(content): + def entities(content) -> Any: return chatml.system(content, name="entity") -def add_indices(list_of_dicts, idx_name="index"): +def add_indices(list_of_dicts, idx_name="index") -> List[dict]: return [{idx_name: i, **msg} for i, msg in enumerate(list_of_dicts)] -def get_names_from_session(session): +def get_names_from_session(session) -> Dict[str, Any]: return { role: next( (msg.get("name", None) for msg in session if msg["role"] == role), None diff --git a/agents-api/agents_api/routers/agents/router.py b/agents-api/agents_api/routers/agents/router.py index af9233c56..6582baa6f 100644 --- a/agents-api/agents_api/routers/agents/router.py +++ b/agents-api/agents_api/routers/agents/router.py @@ -1,3 +1,5 @@ +from typing import Any + from fastapi import APIRouter -router = APIRouter() +router: Any = APIRouter() diff --git a/agents-api/agents_api/routers/docs/router.py b/agents-api/agents_api/routers/docs/router.py index af9233c56..6582baa6f 100644 --- a/agents-api/agents_api/routers/docs/router.py +++ b/agents-api/agents_api/routers/docs/router.py @@ -1,3 +1,5 @@ +from typing import Any + from fastapi import APIRouter -router = APIRouter() +router: Any = APIRouter() diff --git a/agents-api/agents_api/routers/docs/search_docs.py b/agents-api/agents_api/routers/docs/search_docs.py index ad19a3178..f2864164e 100644 --- a/agents-api/agents_api/routers/docs/search_docs.py +++ b/agents-api/agents_api/routers/docs/search_docs.py @@ -1,5 +1,5 @@ import time -from typing import Annotated +from typing import Annotated, Any, Dict, List, Optional, Tuple, Union from fastapi import Depends from pydantic import UUID4 @@ -17,7 +17,11 @@ from .router import router -def get_search_fn_and_params(search_params): +def get_search_fn_and_params( + search_params, +) -> Tuple[ + Any, Optional[Dict[str, Union[float, int, str, Dict[str, float], List[float]]]] +]: search_fn, params = None, None match search_params: diff --git a/agents-api/agents_api/routers/jobs/routers.py b/agents-api/agents_api/routers/jobs/routers.py index 1ec60c2b4..5f7d3ef67 100644 --- a/agents-api/agents_api/routers/jobs/routers.py +++ b/agents-api/agents_api/routers/jobs/routers.py @@ -1,4 +1,4 @@ -from typing import Literal +from typing import Any, Literal from fastapi import APIRouter from pydantic import UUID4 @@ -7,7 +7,7 @@ from agents_api.autogen.openapi_model import JobStatus from agents_api.clients.temporal import get_client -router = APIRouter() +router: Any = APIRouter() def map_job_status( diff --git a/agents-api/agents_api/routers/sessions/exceptions.py b/agents-api/agents_api/routers/sessions/exceptions.py index add4b79cb..b7a5bb971 100644 --- a/agents-api/agents_api/routers/sessions/exceptions.py +++ b/agents-api/agents_api/routers/sessions/exceptions.py @@ -3,7 +3,7 @@ class BaseSessionException(Exception): class InputTooBigError(BaseSessionException): - def __init__(self, actual_tokens, required_tokens): + def __init__(self, actual_tokens, required_tokens) -> None: super().__init__( f"Input is too big, {actual_tokens} tokens provided, but only {required_tokens} tokens are allowed." ) diff --git a/agents-api/agents_api/routers/sessions/router.py b/agents-api/agents_api/routers/sessions/router.py index af9233c56..6582baa6f 100644 --- a/agents-api/agents_api/routers/sessions/router.py +++ b/agents-api/agents_api/routers/sessions/router.py @@ -1,3 +1,5 @@ +from typing import Any + from fastapi import APIRouter -router = APIRouter() +router: Any = APIRouter() diff --git a/agents-api/agents_api/routers/tasks/create_task_execution.py b/agents-api/agents_api/routers/tasks/create_task_execution.py index 988af074f..6202baa93 100644 --- a/agents-api/agents_api/routers/tasks/create_task_execution.py +++ b/agents-api/agents_api/routers/tasks/create_task_execution.py @@ -30,7 +30,7 @@ from ...models.task.get_task import get_task as get_task_query from .router import router -logger = logging.getLogger(__name__) +logger: logging.Logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) diff --git a/agents-api/agents_api/routers/tasks/router.py b/agents-api/agents_api/routers/tasks/router.py index af9233c56..6582baa6f 100644 --- a/agents-api/agents_api/routers/tasks/router.py +++ b/agents-api/agents_api/routers/tasks/router.py @@ -1,3 +1,5 @@ +from typing import Any + from fastapi import APIRouter -router = APIRouter() +router: Any = APIRouter() diff --git a/agents-api/agents_api/routers/users/router.py b/agents-api/agents_api/routers/users/router.py index af9233c56..6582baa6f 100644 --- a/agents-api/agents_api/routers/users/router.py +++ b/agents-api/agents_api/routers/users/router.py @@ -1,3 +1,5 @@ +from typing import Any + from fastapi import APIRouter -router = APIRouter() +router: Any = APIRouter() diff --git a/agents-api/agents_api/web.py b/agents-api/agents_api/web.py index 2b85ece5f..2a24dcebb 100644 --- a/agents-api/agents_api/web.py +++ b/agents-api/agents_api/web.py @@ -3,6 +3,7 @@ """ import logging +from typing import Any, Callable import fire import sentry_sdk @@ -37,10 +38,10 @@ ) -logger = logging.getLogger(__name__) +logger: logging.Logger = logging.getLogger(__name__) -def make_exception_handler(status: int): +def make_exception_handler(status: int) -> Callable[[Any, Any], Any]: """ Creates a custom exception handler for the application. @@ -60,7 +61,7 @@ async def _handler(request: Request, exc): return _handler -def register_exceptions(app: FastAPI): +def register_exceptions(app: FastAPI) -> None: """ Registers custom exception handlers for the FastAPI application. @@ -77,7 +78,7 @@ def register_exceptions(app: FastAPI): ) -app = FastAPI(dependencies=[Depends(get_api_key)]) +app: Any = FastAPI(dependencies=[Depends(get_api_key)]) app.add_middleware( CORSMiddleware, @@ -147,7 +148,7 @@ def main( timeout_keep_alive=30, workers=None, log_level="info", -): +) -> None: uvicorn.run( app, host=host, diff --git a/agents-api/agents_api/worker/codec.py b/agents-api/agents_api/worker/codec.py index abf3ddfe5..cdd7e3448 100644 --- a/agents-api/agents_api/worker/codec.py +++ b/agents-api/agents_api/worker/codec.py @@ -19,7 +19,7 @@ from agents_api.common.utils.json import dumps as json_dumps # Map of model name to class so that we can look up the class when deserializing -model_class_map = { +model_class_map: dict = { subclass.__module__ + "." + subclass.__name__: subclass for subclass in { # All the models we want to support @@ -83,7 +83,7 @@ def __init__(self) -> None: # Use the default data converter, but change the payload converter. -pydantic_data_converter = dataclasses.replace( +pydantic_data_converter: Any = dataclasses.replace( temporalio.converter.default(), payload_converter_class=PydanticPayloadConverter, ) diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index d3821c902..a5e5016d3 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -1,10 +1,11 @@ from datetime import timedelta +from typing import Any from temporalio.client import Client from temporalio.worker import Worker -def create_worker(client: Client): +def create_worker(client: Client) -> Any: """ Initializes the Temporal client and worker with TLS configuration (if provided), then create a worker to listen for tasks on the configured task queue. diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 5e5eb8ce6..7e8498828 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -163,4 +163,4 @@ async def run( # Otherwise, recurse to the next step workflow.continue_as_new( args=[execution_input, next_target, previous_inputs + [final_output]] - ) \ No newline at end of file + ) From 8e0c9c0aa6d6368722155bbaf8bffcfbc462e519 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 17:38:33 -0400 Subject: [PATCH 072/110] feat(agents-api): Add lots of new steps Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Tasks.py | 328 ++++++++++++- agents-api/poetry.lock | 230 ++++----- sdks/python/julep/api/__init__.py | 130 +++++ sdks/python/julep/api/types/__init__.py | 130 +++++ .../python/julep/api/types/tasks_case_then.py | 52 ++ .../julep/api/types/tasks_case_then_then.py | 32 ++ .../tasks_create_task_request_main_item.py | 444 ++++++++++++++++++ .../julep/api/types/tasks_embed_step.py | 49 ++ .../julep/api/types/tasks_foreach_do.py | 54 +++ .../api/types/tasks_foreach_do_do_item.py | 32 ++ .../julep/api/types/tasks_foreach_step.py | 49 ++ sdks/python/julep/api/types/tasks_get_step.py | 48 ++ .../types/tasks_if_else_workflow_step_else.py | 14 + .../types/tasks_if_else_workflow_step_then.py | 14 + sdks/python/julep/api/types/tasks_log_step.py | 49 ++ sdks/python/julep/api/types/tasks_map_over.py | 51 ++ .../julep/api/types/tasks_map_reduce_step.py | 55 +++ .../julep/api/types/tasks_parallel_step.py | 49 ++ .../tasks_parallel_step_parallel_item.py | 32 ++ .../tasks_patch_task_request_main_item.py | 444 ++++++++++++++++++ .../julep/api/types/tasks_return_step.py | 49 ++ .../julep/api/types/tasks_search_step.py | 49 ++ .../api/types/tasks_search_step_search.py | 11 + sdks/python/julep/api/types/tasks_set_key.py | 51 ++ sdks/python/julep/api/types/tasks_set_step.py | 49 ++ .../julep/api/types/tasks_set_step_set.py | 7 + .../python/julep/api/types/tasks_sleep_for.py | 60 +++ .../julep/api/types/tasks_sleep_step.py | 49 ++ .../julep/api/types/tasks_switch_step.py | 49 ++ .../julep/api/types/tasks_task_main_item.py | 444 ++++++++++++++++++ .../tasks_update_task_request_main_item.py | 444 ++++++++++++++++++ sdks/python/poetry.lock | 12 +- sdks/ts/src/api/index.ts | 32 ++ .../src/api/models/Tasks_BaseWorkflowStep.ts | 15 +- sdks/ts/src/api/models/Tasks_CaseThen.ts | 39 ++ .../src/api/models/Tasks_CreateTaskRequest.ts | 22 + sdks/ts/src/api/models/Tasks_EmbedStep.ts | 13 + sdks/ts/src/api/models/Tasks_ForeachDo.ts | 40 ++ sdks/ts/src/api/models/Tasks_ForeachStep.ts | 13 + sdks/ts/src/api/models/Tasks_GetStep.ts | 12 + .../api/models/Tasks_IfElseWorkflowStep.ts | 21 + sdks/ts/src/api/models/Tasks_LogStep.ts | 13 + sdks/ts/src/api/models/Tasks_MapOver.ts | 15 + sdks/ts/src/api/models/Tasks_MapReduceStep.ts | 18 + sdks/ts/src/api/models/Tasks_ParallelStep.ts | 37 ++ .../src/api/models/Tasks_PatchTaskRequest.ts | 22 + sdks/ts/src/api/models/Tasks_ReturnStep.ts | 13 + sdks/ts/src/api/models/Tasks_SearchStep.ts | 18 + sdks/ts/src/api/models/Tasks_SetKey.ts | 15 + sdks/ts/src/api/models/Tasks_SetStep.ts | 13 + sdks/ts/src/api/models/Tasks_SleepFor.ts | 22 + sdks/ts/src/api/models/Tasks_SleepStep.ts | 13 + sdks/ts/src/api/models/Tasks_SwitchStep.ts | 13 + sdks/ts/src/api/models/Tasks_Task.ts | 22 + .../src/api/models/Tasks_UpdateTaskRequest.ts | 22 + sdks/ts/src/api/schemas/$Tasks_CaseThen.ts | 61 +++ .../api/schemas/$Tasks_CreateTaskRequest.ts | 33 ++ sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts | 30 ++ sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts | 63 +++ sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts | 30 ++ sdks/ts/src/api/schemas/$Tasks_GetStep.ts | 25 + .../api/schemas/$Tasks_IfElseWorkflowStep.ts | 42 ++ sdks/ts/src/api/schemas/$Tasks_LogStep.ts | 30 ++ sdks/ts/src/api/schemas/$Tasks_MapOver.ts | 23 + .../src/api/schemas/$Tasks_MapReduceStep.ts | 40 ++ .../ts/src/api/schemas/$Tasks_ParallelStep.ts | 65 +++ .../api/schemas/$Tasks_PatchTaskRequest.ts | 33 ++ sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts | 27 ++ sdks/ts/src/api/schemas/$Tasks_SearchStep.ts | 36 ++ sdks/ts/src/api/schemas/$Tasks_SetKey.ts | 23 + sdks/ts/src/api/schemas/$Tasks_SetStep.ts | 36 ++ sdks/ts/src/api/schemas/$Tasks_SleepFor.ts | 32 ++ sdks/ts/src/api/schemas/$Tasks_SleepStep.ts | 30 ++ sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts | 27 ++ sdks/ts/src/api/schemas/$Tasks_Task.ts | 33 ++ .../api/schemas/$Tasks_UpdateTaskRequest.ts | 33 ++ typespec/tasks/models.tsp | 109 +---- typespec/tasks/step_kind.tsp | 72 +++ typespec/tasks/steps.tsp | 269 +++++++++++ 79 files changed, 4995 insertions(+), 230 deletions(-) create mode 100644 sdks/python/julep/api/types/tasks_case_then.py create mode 100644 sdks/python/julep/api/types/tasks_case_then_then.py create mode 100644 sdks/python/julep/api/types/tasks_embed_step.py create mode 100644 sdks/python/julep/api/types/tasks_foreach_do.py create mode 100644 sdks/python/julep/api/types/tasks_foreach_do_do_item.py create mode 100644 sdks/python/julep/api/types/tasks_foreach_step.py create mode 100644 sdks/python/julep/api/types/tasks_get_step.py create mode 100644 sdks/python/julep/api/types/tasks_log_step.py create mode 100644 sdks/python/julep/api/types/tasks_map_over.py create mode 100644 sdks/python/julep/api/types/tasks_map_reduce_step.py create mode 100644 sdks/python/julep/api/types/tasks_parallel_step.py create mode 100644 sdks/python/julep/api/types/tasks_parallel_step_parallel_item.py create mode 100644 sdks/python/julep/api/types/tasks_return_step.py create mode 100644 sdks/python/julep/api/types/tasks_search_step.py create mode 100644 sdks/python/julep/api/types/tasks_search_step_search.py create mode 100644 sdks/python/julep/api/types/tasks_set_key.py create mode 100644 sdks/python/julep/api/types/tasks_set_step.py create mode 100644 sdks/python/julep/api/types/tasks_set_step_set.py create mode 100644 sdks/python/julep/api/types/tasks_sleep_for.py create mode 100644 sdks/python/julep/api/types/tasks_sleep_step.py create mode 100644 sdks/python/julep/api/types/tasks_switch_step.py create mode 100644 sdks/ts/src/api/models/Tasks_CaseThen.ts create mode 100644 sdks/ts/src/api/models/Tasks_EmbedStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_ForeachDo.ts create mode 100644 sdks/ts/src/api/models/Tasks_ForeachStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_GetStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_LogStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_MapOver.ts create mode 100644 sdks/ts/src/api/models/Tasks_MapReduceStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_ParallelStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_ReturnStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_SearchStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_SetKey.ts create mode 100644 sdks/ts/src/api/models/Tasks_SetStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_SleepFor.ts create mode 100644 sdks/ts/src/api/models/Tasks_SleepStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_SwitchStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_CaseThen.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_GetStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_LogStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOver.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_SearchStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_SetKey.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_SetStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_SleepFor.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_SleepStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts create mode 100644 typespec/tasks/step_kind.tsp create mode 100644 typespec/tasks/steps.tsp diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index add03d675..8738bc62a 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -9,6 +9,12 @@ from pydantic import AwareDatetime, BaseModel, ConfigDict, Field from .Chat import ChatSettings +from .Docs import ( + EmbedQueryRequest, + HybridDocSearchRequest, + TextOnlyDocSearchRequest, + VectorDocSearchRequest, +) from .Entries import InputChatMLMessage from .Tools import CreateToolRequest @@ -18,13 +24,58 @@ class BaseWorkflowStep(BaseModel): populate_by_name=True, ) kind_: Literal[ - "tool_call", "yield", "prompt", "evaluate", "if_else", "wait_for_input", "error" + "tool_call", + "prompt", + "evaluate", + "wait_for_input", + "log", + "embed", + "search", + "set", + "get", + "foreach", + "map_reduce", + "parallel", + "switch", + "if_else", + "sleep", + "return", + "yield", + "error", ] """ The kind of step """ +class CaseThen(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + case: str + """ + The condition to evaluate + """ + then: ( + Any + | ToolCallStep + | YieldStep + | PromptStep + | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep + | WaitForInputStep + ) + """ + The steps to run if the condition is true + """ + + class CreateTaskRequest(BaseModel): """ Payload for creating a task @@ -41,8 +92,19 @@ class CreateTaskRequest(BaseModel): | YieldStep | PromptStep | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep | WaitForInputStep | IfElseWorkflowStep + | SwitchStep + | ForeachStep + | ParallelStep + | MapReduceStep ] """ The entrypoint of the task. @@ -62,6 +124,17 @@ class CreateTaskRequest(BaseModel): metadata: dict[str, Any] | None = None +class EmbedStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["embed"] = "embed" + embed: EmbedQueryRequest + """ + The text to embed + """ + + class ErrorWorkflowStep(BaseWorkflowStep): model_config = ConfigDict( populate_by_name=True, @@ -84,6 +157,56 @@ class EvaluateStep(BaseWorkflowStep): """ +class ForeachDo(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + in_: Annotated[str, Field(alias="in")] + """ + The variable to iterate over + """ + do: list[ + Any + | ToolCallStep + | YieldStep + | PromptStep + | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep + | WaitForInputStep + ] + """ + The steps to run for each iteration + """ + + +class ForeachStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["foreach"] = "foreach" + foreach: ForeachDo + """ + The steps to run for each iteration + """ + + +class GetStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["get"] = "get" + get: str + """ + The key to get + """ + + class IfElseWorkflowStep(BaseWorkflowStep): model_config = ConfigDict( populate_by_name=True, @@ -99,6 +222,13 @@ class IfElseWorkflowStep(BaseWorkflowStep): | YieldStep | PromptStep | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep | WaitForInputStep ) """ @@ -110,6 +240,13 @@ class IfElseWorkflowStep(BaseWorkflowStep): | YieldStep | PromptStep | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep | WaitForInputStep, Field(alias="else"), ] @@ -118,6 +255,71 @@ class IfElseWorkflowStep(BaseWorkflowStep): """ +class LogStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["log"] = "log" + log: str + """ + The value to log + """ + + +class MapOver(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + over: str + """ + The variable to iterate over + """ + workflow: str + """ + The subworkflow to run for each iteration + """ + + +class MapReduceStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["map_reduce"] = "map_reduce" + map: MapOver + """ + The steps to run for each iteration + """ + reduce: str + """ + The expression to reduce the results (`_` is a list of outputs) + """ + + +class ParallelStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["parallel"] = "parallel" + parallel: list[ + Any + | ToolCallStep + | YieldStep + | PromptStep + | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep + | WaitForInputStep + ] + """ + The steps to run in parallel. Max concurrency will depend on the platform + """ + + class PatchTaskRequest(BaseModel): """ Payload for patching a task @@ -134,8 +336,19 @@ class PatchTaskRequest(BaseModel): | YieldStep | PromptStep | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep | WaitForInputStep | IfElseWorkflowStep + | SwitchStep + | ForeachStep + | ParallelStep + | MapReduceStep ] | None ) = None @@ -172,6 +385,97 @@ class PromptStep(BaseWorkflowStep): """ +class ReturnStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["return"] = "return" + return_: Annotated[dict[str, str], Field(alias="return")] + """ + The value to return + """ + + +class SearchStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["search"] = "search" + search: VectorDocSearchRequest | TextOnlyDocSearchRequest | HybridDocSearchRequest + """ + The search query + """ + + +class SetKey(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + key: str + """ + The key to set + """ + value: str + """ + The value to set + """ + + +class SetStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["set"] = "set" + set: SetKey | list[SetKey] + """ + The value to set + """ + + +class SleepFor(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + seconds: Annotated[int, Field(0, ge=0)] + """ + The number of seconds to sleep for + """ + minutes: Annotated[int, Field(0, ge=0)] + """ + The number of minutes to sleep for + """ + hours: Annotated[int, Field(0, ge=0)] + """ + The number of hours to sleep for + """ + days: Annotated[int, Field(0, ge=0)] + """ + The number of days to sleep for + """ + + +class SleepStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["sleep"] = "sleep" + sleep: SleepFor + """ + The duration to sleep for + """ + + +class SwitchStep(BaseWorkflowStep): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Literal["switch"] = "switch" + switch: list[CaseThen] + """ + The cond tree + """ + + class Task(BaseModel): """ Object describing a Task @@ -188,8 +492,19 @@ class Task(BaseModel): | YieldStep | PromptStep | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep | WaitForInputStep | IfElseWorkflowStep + | SwitchStep + | ForeachStep + | ParallelStep + | MapReduceStep ] """ The entrypoint of the task. @@ -260,8 +575,19 @@ class UpdateTaskRequest(BaseModel): | YieldStep | PromptStep | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep | WaitForInputStep | IfElseWorkflowStep + | SwitchStep + | ForeachStep + | ParallelStep + | MapReduceStep ] """ The entrypoint of the task. diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index aaaada66e..48131f6e8 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2,98 +2,98 @@ [[package]] name = "aiohappyeyeballs" -version = "2.3.5" +version = "2.3.7" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "aiohappyeyeballs-2.3.5-py3-none-any.whl", hash = "sha256:4d6dea59215537dbc746e93e779caea8178c866856a721c9c660d7a5a7b8be03"}, - {file = "aiohappyeyeballs-2.3.5.tar.gz", hash = "sha256:6fa48b9f1317254f122a07a131a86b71ca6946ca989ce6326fff54a99a920105"}, + {file = "aiohappyeyeballs-2.3.7-py3-none-any.whl", hash = "sha256:337ce4dc0e99eb697c3c5a77d6cb3c52925824d9a67ac0dea7c55b8a2d60b222"}, + {file = "aiohappyeyeballs-2.3.7.tar.gz", hash = "sha256:e794cd29ba6a14078092984e43688212a19081de3a73b6796c2fdeb3706dd6ce"}, ] [[package]] name = "aiohttp" -version = "3.10.3" +version = "3.10.4" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cc36cbdedf6f259371dbbbcaae5bb0e95b879bc501668ab6306af867577eb5db"}, - {file = "aiohttp-3.10.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:85466b5a695c2a7db13eb2c200af552d13e6a9313d7fa92e4ffe04a2c0ea74c1"}, - {file = "aiohttp-3.10.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:71bb1d97bfe7e6726267cea169fdf5df7658831bb68ec02c9c6b9f3511e108bb"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:baec1eb274f78b2de54471fc4c69ecbea4275965eab4b556ef7a7698dee18bf2"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:13031e7ec1188274bad243255c328cc3019e36a5a907978501256000d57a7201"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2bbc55a964b8eecb341e492ae91c3bd0848324d313e1e71a27e3d96e6ee7e8e8"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8cc0564b286b625e673a2615ede60a1704d0cbbf1b24604e28c31ed37dc62aa"}, - {file = "aiohttp-3.10.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f817a54059a4cfbc385a7f51696359c642088710e731e8df80d0607193ed2b73"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8542c9e5bcb2bd3115acdf5adc41cda394e7360916197805e7e32b93d821ef93"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:671efce3a4a0281060edf9a07a2f7e6230dca3a1cbc61d110eee7753d28405f7"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0974f3b5b0132edcec92c3306f858ad4356a63d26b18021d859c9927616ebf27"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:44bb159b55926b57812dca1b21c34528e800963ffe130d08b049b2d6b994ada7"}, - {file = "aiohttp-3.10.3-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:6ae9ae382d1c9617a91647575255ad55a48bfdde34cc2185dd558ce476bf16e9"}, - {file = "aiohttp-3.10.3-cp310-cp310-win32.whl", hash = "sha256:aed12a54d4e1ee647376fa541e1b7621505001f9f939debf51397b9329fd88b9"}, - {file = "aiohttp-3.10.3-cp310-cp310-win_amd64.whl", hash = "sha256:b51aef59370baf7444de1572f7830f59ddbabd04e5292fa4218d02f085f8d299"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e021c4c778644e8cdc09487d65564265e6b149896a17d7c0f52e9a088cc44e1b"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:24fade6dae446b183e2410a8628b80df9b7a42205c6bfc2eff783cbeedc224a2"}, - {file = "aiohttp-3.10.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:bc8e9f15939dacb0e1f2d15f9c41b786051c10472c7a926f5771e99b49a5957f"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5a9ec959b5381271c8ec9310aae1713b2aec29efa32e232e5ef7dcca0df0279"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a5d0ea8a6467b15d53b00c4e8ea8811e47c3cc1bdbc62b1aceb3076403d551f"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9ed607dbbdd0d4d39b597e5bf6b0d40d844dfb0ac6a123ed79042ef08c1f87e"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3e66d5b506832e56add66af88c288c1d5ba0c38b535a1a59e436b300b57b23e"}, - {file = "aiohttp-3.10.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fda91ad797e4914cca0afa8b6cccd5d2b3569ccc88731be202f6adce39503189"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:61ccb867b2f2f53df6598eb2a93329b5eee0b00646ee79ea67d68844747a418e"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:6d881353264e6156f215b3cb778c9ac3184f5465c2ece5e6fce82e68946868ef"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b031ce229114825f49cec4434fa844ccb5225e266c3e146cb4bdd025a6da52f1"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5337cc742a03f9e3213b097abff8781f79de7190bbfaa987bd2b7ceb5bb0bdec"}, - {file = "aiohttp-3.10.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ab3361159fd3dcd0e48bbe804006d5cfb074b382666e6c064112056eb234f1a9"}, - {file = "aiohttp-3.10.3-cp311-cp311-win32.whl", hash = "sha256:05d66203a530209cbe40f102ebaac0b2214aba2a33c075d0bf825987c36f1f0b"}, - {file = "aiohttp-3.10.3-cp311-cp311-win_amd64.whl", hash = "sha256:70b4a4984a70a2322b70e088d654528129783ac1ebbf7dd76627b3bd22db2f17"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:166de65e2e4e63357cfa8417cf952a519ac42f1654cb2d43ed76899e2319b1ee"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:7084876352ba3833d5d214e02b32d794e3fd9cf21fdba99cff5acabeb90d9806"}, - {file = "aiohttp-3.10.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d98c604c93403288591d7d6d7d6cc8a63459168f8846aeffd5b3a7f3b3e5e09"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d73b073a25a0bb8bf014345374fe2d0f63681ab5da4c22f9d2025ca3e3ea54fc"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8da6b48c20ce78f5721068f383e0e113dde034e868f1b2f5ee7cb1e95f91db57"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3a9dcdccf50284b1b0dc72bc57e5bbd3cc9bf019060dfa0668f63241ccc16aa7"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56fb94bae2be58f68d000d046172d8b8e6b1b571eb02ceee5535e9633dcd559c"}, - {file = "aiohttp-3.10.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf75716377aad2c718cdf66451c5cf02042085d84522aec1f9246d3e4b8641a6"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:6c51ed03e19c885c8e91f574e4bbe7381793f56f93229731597e4a499ffef2a5"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b84857b66fa6510a163bb083c1199d1ee091a40163cfcbbd0642495fed096204"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c124b9206b1befe0491f48185fd30a0dd51b0f4e0e7e43ac1236066215aff272"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3461d9294941937f07bbbaa6227ba799bc71cc3b22c40222568dc1cca5118f68"}, - {file = "aiohttp-3.10.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:08bd0754d257b2db27d6bab208c74601df6f21bfe4cb2ec7b258ba691aac64b3"}, - {file = "aiohttp-3.10.3-cp312-cp312-win32.whl", hash = "sha256:7f9159ae530297f61a00116771e57516f89a3de6ba33f314402e41560872b50a"}, - {file = "aiohttp-3.10.3-cp312-cp312-win_amd64.whl", hash = "sha256:e1128c5d3a466279cb23c4aa32a0f6cb0e7d2961e74e9e421f90e74f75ec1edf"}, - {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:d1100e68e70eb72eadba2b932b185ebf0f28fd2f0dbfe576cfa9d9894ef49752"}, - {file = "aiohttp-3.10.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a541414578ff47c0a9b0b8b77381ea86b0c8531ab37fc587572cb662ccd80b88"}, - {file = "aiohttp-3.10.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d5548444ef60bf4c7b19ace21f032fa42d822e516a6940d36579f7bfa8513f9c"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ba2e838b5e6a8755ac8297275c9460e729dc1522b6454aee1766c6de6d56e5e"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:48665433bb59144aaf502c324694bec25867eb6630fcd831f7a893ca473fcde4"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bac352fceed158620ce2d701ad39d4c1c76d114255a7c530e057e2b9f55bdf9f"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b0f670502100cdc567188c49415bebba947eb3edaa2028e1a50dd81bd13363f"}, - {file = "aiohttp-3.10.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b09f38a67679e32d380fe512189ccb0b25e15afc79b23fbd5b5e48e4fc8fd9"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:cd788602e239ace64f257d1c9d39898ca65525583f0fbf0988bcba19418fe93f"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:214277dcb07ab3875f17ee1c777d446dcce75bea85846849cc9d139ab8f5081f"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:32007fdcaab789689c2ecaaf4b71f8e37bf012a15cd02c0a9db8c4d0e7989fa8"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:123e5819bfe1b87204575515cf448ab3bf1489cdeb3b61012bde716cda5853e7"}, - {file = "aiohttp-3.10.3-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:812121a201f0c02491a5db335a737b4113151926a79ae9ed1a9f41ea225c0e3f"}, - {file = "aiohttp-3.10.3-cp38-cp38-win32.whl", hash = "sha256:b97dc9a17a59f350c0caa453a3cb35671a2ffa3a29a6ef3568b523b9113d84e5"}, - {file = "aiohttp-3.10.3-cp38-cp38-win_amd64.whl", hash = "sha256:3731a73ddc26969d65f90471c635abd4e1546a25299b687e654ea6d2fc052394"}, - {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:38d91b98b4320ffe66efa56cb0f614a05af53b675ce1b8607cdb2ac826a8d58e"}, - {file = "aiohttp-3.10.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9743fa34a10a36ddd448bba8a3adc2a66a1c575c3c2940301bacd6cc896c6bf1"}, - {file = "aiohttp-3.10.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7c126f532caf238031c19d169cfae3c6a59129452c990a6e84d6e7b198a001dc"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:926e68438f05703e500b06fe7148ef3013dd6f276de65c68558fa9974eeb59ad"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:434b3ab75833accd0b931d11874e206e816f6e6626fd69f643d6a8269cd9166a"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d35235a44ec38109b811c3600d15d8383297a8fab8e3dec6147477ec8636712a"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59c489661edbd863edb30a8bd69ecb044bd381d1818022bc698ba1b6f80e5dd1"}, - {file = "aiohttp-3.10.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:50544fe498c81cb98912afabfc4e4d9d85e89f86238348e3712f7ca6a2f01dab"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:09bc79275737d4dc066e0ae2951866bb36d9c6b460cb7564f111cc0427f14844"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:af4dbec58e37f5afff4f91cdf235e8e4b0bd0127a2a4fd1040e2cad3369d2f06"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:b22cae3c9dd55a6b4c48c63081d31c00fc11fa9db1a20c8a50ee38c1a29539d2"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ba562736d3fbfe9241dad46c1a8994478d4a0e50796d80e29d50cabe8fbfcc3f"}, - {file = "aiohttp-3.10.3-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:f25d6c4e82d7489be84f2b1c8212fafc021b3731abdb61a563c90e37cced3a21"}, - {file = "aiohttp-3.10.3-cp39-cp39-win32.whl", hash = "sha256:b69d832e5f5fa15b1b6b2c8eb6a9fd2c0ec1fd7729cb4322ed27771afc9fc2ac"}, - {file = "aiohttp-3.10.3-cp39-cp39-win_amd64.whl", hash = "sha256:673bb6e3249dc8825df1105f6ef74e2eab779b7ff78e96c15cadb78b04a83752"}, - {file = "aiohttp-3.10.3.tar.gz", hash = "sha256:21650e7032cc2d31fc23d353d7123e771354f2a3d5b05a5647fc30fea214e696"}, + {file = "aiohttp-3.10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:81037ddda8cc0a95c6d8c1b9029d0b19a62db8770c0e239e3bea0109d294ab66"}, + {file = "aiohttp-3.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:71944d4f4090afc07ce96b7029d5a574240e2f39570450df4af0d5b93a5ee64a"}, + {file = "aiohttp-3.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c774f08afecc0a617966f45a9c378456e713a999ee60654d9727617def3e4ee4"}, + {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc990e73613c78ab2930b60266135066f37fdfce6b32dd604f42c5c377ee880a"}, + {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6acd1a908740f708358d240f9a3243cec31a456e3ded65c2cb46f6043bc6735"}, + {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6075e27e7e54fbcd1c129c5699b2d251c885c9892e26d59a0fb7705141c2d14b"}, + {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc98d93d11d860ac823beb6131f292d82efb76f226b5e28a3eab1ec578dfd041"}, + {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:201ddf1471567568be381b6d4701e266a768f7eaa2f99ef753f2c9c5e1e3fb5c"}, + {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7d202ec55e61f06b1a1eaf317fba7546855cbf803c13ce7625d462fb8c88e238"}, + {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:96b2e7c110a941c8c1a692703b8ac1013e47f17ee03356c71d55c0a54de2ce38"}, + {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8ba0fbc56c44883bd757ece433f9caadbca67f565934afe9bc53ba3bd99cc368"}, + {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46cc9069da466652bb7b8b3fac1f8ce2e12a9dc0fb11551faa420c4cdbc60abf"}, + {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:93a19cd1e9dc703257fda78b8e889c3a08eabaa09f6ff0d867850b03964f80d1"}, + {file = "aiohttp-3.10.4-cp310-cp310-win32.whl", hash = "sha256:8593040bcc8075fc0e817a602bc5d3d74c7bd717619ffc175a8ba0188edebadf"}, + {file = "aiohttp-3.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:326fb5228aadfc395981d9b336d56a698da335897c4143105c73b583d7500839"}, + {file = "aiohttp-3.10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dfe48f477e02ef5ab247c6ac431a6109c69b5c24cb3ccbcd3e27c4fb39691fe4"}, + {file = "aiohttp-3.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f6fe78b51852e25d4e20be51ef88c2a0bf31432b9f2223bdbd61c01a0f9253a7"}, + {file = "aiohttp-3.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5cc75ff5efbd92301e63a157fddb18a6964a3f40e31c77d57e97dbb9bb3373b4"}, + {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dca39391f45fbb28daa6412f98c625265bf6b512cc41382df61672d1b242f8f4"}, + {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8616dd5ed8b3b4029021b560305041c62e080bb28f238c27c2e150abe3539587"}, + {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d7958ba22854b3f00a7bbb66cde1dc759760ce8a3e6dfe9ea53f06bccaa9aa2"}, + {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a24ac7164a824ef2e8e4e9a9f6debb1f43c44ad7ad04efc6018a6610555666d"}, + {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:660ad010b8fd0b26e8edb8ae5c036db5b16baac4278198ad238b11956d920b3d"}, + {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:93ee83008d3e505db9846a5a1f48a002676d8dcc90ee431a9462541c9b81393c"}, + {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77071795efd6ba87f409001141fb05c94ee962b9fca6c8fa1f735c2718512de4"}, + {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ff371ae72a1816c3eeba5c9cff42cb739aaa293fec7d78f180d1c7ee342285b6"}, + {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c253e81f12da97f85d45441e8c6da0d9c12e07db4a7136b0a955df6fc5e4bf51"}, + {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2ce101c447cf7ba4b6e5ab07bfa2c0da21cbab66922f78a601f0b84fd7710d72"}, + {file = "aiohttp-3.10.4-cp311-cp311-win32.whl", hash = "sha256:705c311ecf2d30fbcf3570d1a037c657be99095694223488140c47dee4ef2460"}, + {file = "aiohttp-3.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:ebddbfea8a8d6b97f717658fa85a96681a28990072710d3de3a4eba5d6804a37"}, + {file = "aiohttp-3.10.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4d63f42d9c604521b208b754abfafe01218af4a8f6332b43196ee8fe88bbd5"}, + {file = "aiohttp-3.10.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fef7b7bd3a6911b4d148332136d34d3c2aee3d54d354373b1da6d96bc08089a5"}, + {file = "aiohttp-3.10.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fff8606149098935188fe1e135f7e7991e6a36d6fe394fd15939fc57d0aff889"}, + {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eb3df1aa83602be9a5e572c834d74c3c8e382208b59a873aabfe4c493c45ed0"}, + {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c4a71d4a5e0cbfd4bfadd13cb84fe2bc76c64d550dc4f22c22008c9354cffb3"}, + {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf61884a604c399458c4a42c8caea000fbcc44255ed89577ff50cb688a0fe8e2"}, + {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2015e4b40bd5dedc8155c2b2d24a2b07963ae02b5772373d0b599a68e38a316b"}, + {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b06e1a66bf0a1a2d0f12aef25843dfd2093df080d6c1acbc43914bb9c8f36ed3"}, + {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:eb898c9ad5a1228a669ebe2e2ba3d76aebe1f7c10b78f09a36000254f049fc2b"}, + {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2d64a5a7539320c3cecb4bca093ea825fcc906f8461cf8b42a7bf3c706ce1932"}, + {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:438c6e1492d060b21285f4b6675b941cf96dd9ef3dfdd59940561029b82e3e1f"}, + {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e99bf118afb2584848dba169a685fe092b338a4fe52ae08c7243d7bc4cc204fe"}, + {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9dc26781fb95225c6170619dece8b5c6ca7cfb1b0be97b7ee719915773d0c2a9"}, + {file = "aiohttp-3.10.4-cp312-cp312-win32.whl", hash = "sha256:45bb655cb8b3a61e19977183a4e0962051ae90f6d46588ed4addb8232128141c"}, + {file = "aiohttp-3.10.4-cp312-cp312-win_amd64.whl", hash = "sha256:347bbdc48411badc24fe3a13565820bc742db3aa2f9127cd5f48c256caf87e29"}, + {file = "aiohttp-3.10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4ad284cee0fdcdc0216346b849fd53d201b510aff3c48aa3622daec9ada4bf80"}, + {file = "aiohttp-3.10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:58df59234be7d7e80548b9482ebfeafdda21948c25cb2873c7f23870c8053dfe"}, + {file = "aiohttp-3.10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5f52225af7f91f27b633f73473e9ef0aa8e2112d57b69eaf3aa4479e3ea3bc0e"}, + {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93f1a0e12c321d923c024b56d7dcd8012e60bf30a4b3fb69a88be15dcb9ab80b"}, + {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9e9e9a51dd12f2f71fdbd7f7230dcb75ed8f77d8ac8e07c73b599b6d7027e5c"}, + {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:38bb515f1affc36d3d97b02bf82099925a5785c4a96066ff4400a83ad09d3d5d"}, + {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e685afb0e3b7b861d89cb3690d89eeda221b43095352efddaaa735c6baf87f3"}, + {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd5673e3391564871ba6753cf674dcf2051ef19dc508998fe0758a6c7b429a0"}, + {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4b34e5086e1ead3baa740e32adf35cc5e42338e44c4b07f7b62b41ca6d6a5bfd"}, + {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c3fd3b8f0164fb2866400cd6eb9e884ab0dc95f882cf8b25e560ace7350c552d"}, + {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:b95e1694d234f27b4bbf5bdef56bb751974ac5dbe045b1e462bde1fe39421cbe"}, + {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:c031de4dfabe7bb6565743745ab43d20588944ddfc7233360169cab4008eee2f"}, + {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:03c5a3143d4a82c43a3d82ac77d9cdef527a72f1c04dcca7b14770879f33d196"}, + {file = "aiohttp-3.10.4-cp38-cp38-win32.whl", hash = "sha256:b71722b527445e02168e2d1cf435772731874671a647fa159ad000feea7933b6"}, + {file = "aiohttp-3.10.4-cp38-cp38-win_amd64.whl", hash = "sha256:0fd1f57aac7d01c9c768675d531976d20d5b79d9da67fac87e55d41b4ade05f9"}, + {file = "aiohttp-3.10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:15b36a644d1f44ea3d94a0bbb71e75d5f394a3135dc388a209466e22b711ce64"}, + {file = "aiohttp-3.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:394ddf9d216cf0bd429b223239a0ab628f01a7a1799c93ce4685eedcdd51b9bc"}, + {file = "aiohttp-3.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd33f4d571b4143fc9318c3d9256423579c7d183635acc458a6db81919ae5204"}, + {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5991b80886655e6c785aadf3114d4f86e6bec2da436e2bb62892b9f048450a4"}, + {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92021bf0a4b9ad16851a6c1ca3c86e5b09aecca4f7a2576430c6bbf3114922b1"}, + {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:938e37fd337343c67471098736deb33066d72cec7d8927b9c1b6b4ea807ade9e"}, + {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d697023b16c62f9aeb3ffdfb8ec4ac3afd477388993b9164b47dadbd60e7062"}, + {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2f9f07fe6d0d51bd2a788cbb339f1570fd691449c53b5dec83ff838f117703e"}, + {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:50ac670f3fc13ce95e4d6d5a299db9288cc84c663aa630142444ef504756fcf7"}, + {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9bcdd19398212785a9cb82a63a4b75a299998343f3f5732dfd37c1a4275463f9"}, + {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:122c26f0976225aba46f381e3cabb5ef89a08af6503fc30493fb732e578cfa55"}, + {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:d0665e2a346b6b66959f831ffffd8aa71dd07dd2300017d478f5b47573e66cfe"}, + {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:625a4a9d4b9f80e7bbaaf2ace06341cf701b2fee54232843addf0bb7304597fb"}, + {file = "aiohttp-3.10.4-cp39-cp39-win32.whl", hash = "sha256:5115490112f39f16ae87c1b34dff3e2c95306cf456b1d2af5974c4ac7d2d1ec7"}, + {file = "aiohttp-3.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:9b58b2ef7f28a2462ba86acbf3b20371bd80a1faa1cfd82f31968af4ac81ef25"}, + {file = "aiohttp-3.10.4.tar.gz", hash = "sha256:23a5f97e7dd22e181967fb6cb6c3b11653b0fdbbc4bb7739d9b6052890ccab96"}, ] [package.dependencies] @@ -759,19 +759,19 @@ develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest- [[package]] name = "dask" -version = "2024.8.0" +version = "2024.8.1" description = "Parallel PyData with Task Scheduling" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "dask-2024.8.0-py3-none-any.whl", hash = "sha256:250ea3df30d4a25958290eec4f252850091c6cfaed82d098179c3b25bba18309"}, - {file = "dask-2024.8.0.tar.gz", hash = "sha256:f1fec39373d2f101bc045529ad4e9b30e34e6eb33b7aa0fa7073aec7b1bf9eee"}, + {file = "dask-2024.8.1-py3-none-any.whl", hash = "sha256:b8b58cba91dc9c057c8676dcc80b8bc321602b4dfd21529d33b03b55d428e2c3"}, + {file = "dask-2024.8.1.tar.gz", hash = "sha256:4254e43ac8c3affad2b22952f126b00a00f52c87caae91c068d8e395a4ad1a72"}, ] [package.dependencies] click = ">=8.1" -cloudpickle = ">=1.5.0" -distributed = {version = "2024.8.0", optional = true, markers = "extra == \"distributed\""} +cloudpickle = ">=3.0.0" +distributed = {version = "2024.8.1", optional = true, markers = "extra == \"distributed\""} fsspec = ">=2021.09.0" importlib-metadata = {version = ">=4.13.0", markers = "python_version < \"3.12\""} packaging = ">=20.0" @@ -784,7 +784,7 @@ array = ["numpy (>=1.21)"] complete = ["dask[array,dataframe,diagnostics,distributed]", "lz4 (>=4.3.2)", "pyarrow (>=7.0)", "pyarrow-hotfix"] dataframe = ["dask-expr (>=1.1,<1.2)", "dask[array]", "pandas (>=2.0)"] diagnostics = ["bokeh (>=2.4.2)", "jinja2 (>=2.10.3)"] -distributed = ["distributed (==2024.8.0)"] +distributed = ["distributed (==2024.8.1)"] test = ["pandas[test]", "pre-commit", "pytest", "pytest-cov", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist"] [[package]] @@ -896,30 +896,30 @@ files = [ [[package]] name = "distributed" -version = "2024.8.0" +version = "2024.8.1" description = "Distributed scheduler for Dask" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "distributed-2024.8.0-py3-none-any.whl", hash = "sha256:11af55d22dd6e04eb868b87f166b8f59ef1b300f659f87c016643b7f98280ec6"}, - {file = "distributed-2024.8.0.tar.gz", hash = "sha256:b99caf0a7f257f59477a70a334e081c1241f7cd9860211cc669742e6450e1310"}, + {file = "distributed-2024.8.1-py3-none-any.whl", hash = "sha256:03f5d3fe7a407cdc16dd2bc25dff4900b72f8dee896b7174eebe8a10b42d8c06"}, + {file = "distributed-2024.8.1.tar.gz", hash = "sha256:82394ceb68b91118717148dbe182cff679f32621812bd7b2bc27eaaa8589f962"}, ] [package.dependencies] click = ">=8.0" -cloudpickle = ">=1.5.0" -dask = "2024.8.0" +cloudpickle = ">=2.0.0" +dask = "2024.8.1" jinja2 = ">=2.10.3" locket = ">=1.0.0" -msgpack = ">=1.0.0" +msgpack = ">=1.0.2" packaging = ">=20.0" -psutil = ">=5.7.2" -pyyaml = ">=5.3.1" +psutil = ">=5.8.0" +pyyaml = ">=5.4.1" sortedcontainers = ">=2.0.5" tblib = ">=1.6.0" -toolz = ">=0.10.0" -tornado = ">=6.0.4" -urllib3 = ">=1.24.3" +toolz = ">=0.11.2" +tornado = ">=6.2.0" +urllib3 = ">=1.26.5" zict = ">=3.0.0" [[package]] @@ -2149,13 +2149,13 @@ tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" [[package]] name = "langchain-core" -version = "0.2.32" +version = "0.2.33" description = "Building applications with LLMs through composability" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_core-0.2.32-py3-none-any.whl", hash = "sha256:1f5584cf0034909e35ea17010a847d4079417e0ddcb5a9eb3fbb2bd55f3268c0"}, - {file = "langchain_core-0.2.32.tar.gz", hash = "sha256:d82cdc350bbbe74261330d87056b7d9f1fb567828e9e03f708d23a48b941819e"}, + {file = "langchain_core-0.2.33-py3-none-any.whl", hash = "sha256:c8de411336c13fa440b7a52895bfd1c064f04d315344855962988483902cc532"}, + {file = "langchain_core-0.2.33.tar.gz", hash = "sha256:dd2659e0a560fc987b210107bf989aa14a6f4b67dd214c13a2c9669036cda975"}, ] [package.dependencies] @@ -2169,17 +2169,17 @@ typing-extensions = ">=4.7" [[package]] name = "langchain-openai" -version = "0.1.21" +version = "0.1.22" description = "An integration package connecting OpenAI and LangChain" optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langchain_openai-0.1.21-py3-none-any.whl", hash = "sha256:44420f0c84859ae236a80c8ac8754a16d5b660c24377c27ba98308145d346352"}, - {file = "langchain_openai-0.1.21.tar.gz", hash = "sha256:2c65feaf12bb284eccf7bce35725fd06f3035fa751babad6aa84af2f99867f88"}, + {file = "langchain_openai-0.1.22-py3-none-any.whl", hash = "sha256:e184ab867a30f803dc210a388537186b1b670a33d910a7e0fa4e0329d3b6c654"}, + {file = "langchain_openai-0.1.22.tar.gz", hash = "sha256:0cf93133f230a893e3b0cc2a792bbf2580950e879b577f6e8d4ff9963a7de44b"}, ] [package.dependencies] -langchain-core = ">=0.2.29,<0.3.0" +langchain-core = ">=0.2.33,<0.3.0" openai = ">=1.40.0,<2.0.0" tiktoken = ">=0.7,<1" @@ -2255,13 +2255,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.13" +version = "1.43.17" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.13-py3-none-any.whl", hash = "sha256:47c27c1c1b394d6098c68eec637008b07a254dadc4b82206b1a9f960621a8776"}, - {file = "litellm-1.43.13.tar.gz", hash = "sha256:b0273cbed3f7a35f197c98d92b1a13038b430e5e78d30db7d94d8237a3b98641"}, + {file = "litellm-1.43.17-py3-none-any.whl", hash = "sha256:f5d68c812f087b49266631e09ae78b48b3ea03cd2e04e7760162a5919c5ccec7"}, + {file = "litellm-1.43.17.tar.gz", hash = "sha256:8ac82b18bf6ae7c29627e8e5d89b183f075b32fb7027b17d2fb7d7d0b7cf8b7f"}, ] [package.dependencies] @@ -2867,13 +2867,13 @@ files = [ [[package]] name = "openai" -version = "1.40.8" +version = "1.41.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.8-py3-none-any.whl", hash = "sha256:3ed4ddad48e0dde059c9b4d3dc240e47781beca2811e52ba449ddc4a471a2fd4"}, - {file = "openai-1.40.8.tar.gz", hash = "sha256:e225f830b946378e214c5b2cfa8df28ba2aeb7e9d44f738cb2a926fd971f5bc0"}, + {file = "openai-1.41.0-py3-none-any.whl", hash = "sha256:3b6cca4571667f3e0800442ef8f2bfa6a6f3301c51776bc7626159a4d81c242c"}, + {file = "openai-1.41.0.tar.gz", hash = "sha256:26b81f39b49dce92ff5d30c373625ddb212c2f1050e1574e456d18423730cdd0"}, ] [package.dependencies] @@ -4798,13 +4798,13 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "typer" -version = "0.12.3" +version = "0.12.4" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" files = [ - {file = "typer-0.12.3-py3-none-any.whl", hash = "sha256:070d7ca53f785acbccba8e7d28b08dcd88f79f1fbda035ade0aecec71ca5c914"}, - {file = "typer-0.12.3.tar.gz", hash = "sha256:49e73131481d804288ef62598d97a1ceef3058905aa536a1134f90891ba35482"}, + {file = "typer-0.12.4-py3-none-any.whl", hash = "sha256:819aa03699f438397e876aa12b0d63766864ecba1b579092cc9fe35d886e34b6"}, + {file = "typer-0.12.4.tar.gz", hash = "sha256:c9c1613ed6a166162705b3347b8d10b661ccc5d95692654d0fb628118f2c34e6"}, ] [package.dependencies] diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index 69e133f76..a884514b0 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -126,49 +126,114 @@ TaskExecutionsRouteListRequestSortBy, TaskExecutionsRouteListResponse, TasksBaseWorkflowStep, + TasksCaseThen, + TasksCaseThenThen, TasksCreateTaskRequest, TasksCreateTaskRequestMainItem, + TasksCreateTaskRequestMainItem_Embed, TasksCreateTaskRequestMainItem_Error, TasksCreateTaskRequestMainItem_Evaluate, + TasksCreateTaskRequestMainItem_Foreach, + TasksCreateTaskRequestMainItem_Get, TasksCreateTaskRequestMainItem_IfElse, + TasksCreateTaskRequestMainItem_Log, + TasksCreateTaskRequestMainItem_MapReduce, + TasksCreateTaskRequestMainItem_Parallel, TasksCreateTaskRequestMainItem_Prompt, + TasksCreateTaskRequestMainItem_Return, + TasksCreateTaskRequestMainItem_Search, + TasksCreateTaskRequestMainItem_Set, + TasksCreateTaskRequestMainItem_Sleep, + TasksCreateTaskRequestMainItem_Switch, TasksCreateTaskRequestMainItem_ToolCall, TasksCreateTaskRequestMainItem_WaitForInput, TasksCreateTaskRequestMainItem_Yield, + TasksEmbedStep, TasksErrorWorkflowStep, TasksEvaluateStep, + TasksForeachDo, + TasksForeachDoDoItem, + TasksForeachStep, + TasksGetStep, TasksIfElseWorkflowStep, TasksIfElseWorkflowStepElse, TasksIfElseWorkflowStepThen, + TasksLogStep, + TasksMapOver, + TasksMapReduceStep, + TasksParallelStep, + TasksParallelStepParallelItem, TasksPatchTaskRequestMainItem, + TasksPatchTaskRequestMainItem_Embed, TasksPatchTaskRequestMainItem_Error, TasksPatchTaskRequestMainItem_Evaluate, + TasksPatchTaskRequestMainItem_Foreach, + TasksPatchTaskRequestMainItem_Get, TasksPatchTaskRequestMainItem_IfElse, + TasksPatchTaskRequestMainItem_Log, + TasksPatchTaskRequestMainItem_MapReduce, + TasksPatchTaskRequestMainItem_Parallel, TasksPatchTaskRequestMainItem_Prompt, + TasksPatchTaskRequestMainItem_Return, + TasksPatchTaskRequestMainItem_Search, + TasksPatchTaskRequestMainItem_Set, + TasksPatchTaskRequestMainItem_Sleep, + TasksPatchTaskRequestMainItem_Switch, TasksPatchTaskRequestMainItem_ToolCall, TasksPatchTaskRequestMainItem_WaitForInput, TasksPatchTaskRequestMainItem_Yield, TasksPromptStep, TasksPromptStepPrompt, + TasksReturnStep, TasksRouteListRequestDirection, TasksRouteListRequestSortBy, TasksRouteListResponse, + TasksSearchStep, + TasksSearchStepSearch, + TasksSetKey, + TasksSetStep, + TasksSetStepSet, + TasksSleepFor, + TasksSleepStep, + TasksSwitchStep, TasksTask, TasksTaskMainItem, + TasksTaskMainItem_Embed, TasksTaskMainItem_Error, TasksTaskMainItem_Evaluate, + TasksTaskMainItem_Foreach, + TasksTaskMainItem_Get, TasksTaskMainItem_IfElse, + TasksTaskMainItem_Log, + TasksTaskMainItem_MapReduce, + TasksTaskMainItem_Parallel, TasksTaskMainItem_Prompt, + TasksTaskMainItem_Return, + TasksTaskMainItem_Search, + TasksTaskMainItem_Set, + TasksTaskMainItem_Sleep, + TasksTaskMainItem_Switch, TasksTaskMainItem_ToolCall, TasksTaskMainItem_WaitForInput, TasksTaskMainItem_Yield, TasksTaskTool, TasksToolCallStep, TasksUpdateTaskRequestMainItem, + TasksUpdateTaskRequestMainItem_Embed, TasksUpdateTaskRequestMainItem_Error, TasksUpdateTaskRequestMainItem_Evaluate, + TasksUpdateTaskRequestMainItem_Foreach, + TasksUpdateTaskRequestMainItem_Get, TasksUpdateTaskRequestMainItem_IfElse, + TasksUpdateTaskRequestMainItem_Log, + TasksUpdateTaskRequestMainItem_MapReduce, + TasksUpdateTaskRequestMainItem_Parallel, TasksUpdateTaskRequestMainItem_Prompt, + TasksUpdateTaskRequestMainItem_Return, + TasksUpdateTaskRequestMainItem_Search, + TasksUpdateTaskRequestMainItem_Set, + TasksUpdateTaskRequestMainItem_Sleep, + TasksUpdateTaskRequestMainItem_Switch, TasksUpdateTaskRequestMainItem_ToolCall, TasksUpdateTaskRequestMainItem_WaitForInput, TasksUpdateTaskRequestMainItem_Yield, @@ -329,49 +394,114 @@ "TaskExecutionsRouteListRequestSortBy", "TaskExecutionsRouteListResponse", "TasksBaseWorkflowStep", + "TasksCaseThen", + "TasksCaseThenThen", "TasksCreateTaskRequest", "TasksCreateTaskRequestMainItem", + "TasksCreateTaskRequestMainItem_Embed", "TasksCreateTaskRequestMainItem_Error", "TasksCreateTaskRequestMainItem_Evaluate", + "TasksCreateTaskRequestMainItem_Foreach", + "TasksCreateTaskRequestMainItem_Get", "TasksCreateTaskRequestMainItem_IfElse", + "TasksCreateTaskRequestMainItem_Log", + "TasksCreateTaskRequestMainItem_MapReduce", + "TasksCreateTaskRequestMainItem_Parallel", "TasksCreateTaskRequestMainItem_Prompt", + "TasksCreateTaskRequestMainItem_Return", + "TasksCreateTaskRequestMainItem_Search", + "TasksCreateTaskRequestMainItem_Set", + "TasksCreateTaskRequestMainItem_Sleep", + "TasksCreateTaskRequestMainItem_Switch", "TasksCreateTaskRequestMainItem_ToolCall", "TasksCreateTaskRequestMainItem_WaitForInput", "TasksCreateTaskRequestMainItem_Yield", + "TasksEmbedStep", "TasksErrorWorkflowStep", "TasksEvaluateStep", + "TasksForeachDo", + "TasksForeachDoDoItem", + "TasksForeachStep", + "TasksGetStep", "TasksIfElseWorkflowStep", "TasksIfElseWorkflowStepElse", "TasksIfElseWorkflowStepThen", + "TasksLogStep", + "TasksMapOver", + "TasksMapReduceStep", + "TasksParallelStep", + "TasksParallelStepParallelItem", "TasksPatchTaskRequestMainItem", + "TasksPatchTaskRequestMainItem_Embed", "TasksPatchTaskRequestMainItem_Error", "TasksPatchTaskRequestMainItem_Evaluate", + "TasksPatchTaskRequestMainItem_Foreach", + "TasksPatchTaskRequestMainItem_Get", "TasksPatchTaskRequestMainItem_IfElse", + "TasksPatchTaskRequestMainItem_Log", + "TasksPatchTaskRequestMainItem_MapReduce", + "TasksPatchTaskRequestMainItem_Parallel", "TasksPatchTaskRequestMainItem_Prompt", + "TasksPatchTaskRequestMainItem_Return", + "TasksPatchTaskRequestMainItem_Search", + "TasksPatchTaskRequestMainItem_Set", + "TasksPatchTaskRequestMainItem_Sleep", + "TasksPatchTaskRequestMainItem_Switch", "TasksPatchTaskRequestMainItem_ToolCall", "TasksPatchTaskRequestMainItem_WaitForInput", "TasksPatchTaskRequestMainItem_Yield", "TasksPromptStep", "TasksPromptStepPrompt", + "TasksReturnStep", "TasksRouteListRequestDirection", "TasksRouteListRequestSortBy", "TasksRouteListResponse", + "TasksSearchStep", + "TasksSearchStepSearch", + "TasksSetKey", + "TasksSetStep", + "TasksSetStepSet", + "TasksSleepFor", + "TasksSleepStep", + "TasksSwitchStep", "TasksTask", "TasksTaskMainItem", + "TasksTaskMainItem_Embed", "TasksTaskMainItem_Error", "TasksTaskMainItem_Evaluate", + "TasksTaskMainItem_Foreach", + "TasksTaskMainItem_Get", "TasksTaskMainItem_IfElse", + "TasksTaskMainItem_Log", + "TasksTaskMainItem_MapReduce", + "TasksTaskMainItem_Parallel", "TasksTaskMainItem_Prompt", + "TasksTaskMainItem_Return", + "TasksTaskMainItem_Search", + "TasksTaskMainItem_Set", + "TasksTaskMainItem_Sleep", + "TasksTaskMainItem_Switch", "TasksTaskMainItem_ToolCall", "TasksTaskMainItem_WaitForInput", "TasksTaskMainItem_Yield", "TasksTaskTool", "TasksToolCallStep", "TasksUpdateTaskRequestMainItem", + "TasksUpdateTaskRequestMainItem_Embed", "TasksUpdateTaskRequestMainItem_Error", "TasksUpdateTaskRequestMainItem_Evaluate", + "TasksUpdateTaskRequestMainItem_Foreach", + "TasksUpdateTaskRequestMainItem_Get", "TasksUpdateTaskRequestMainItem_IfElse", + "TasksUpdateTaskRequestMainItem_Log", + "TasksUpdateTaskRequestMainItem_MapReduce", + "TasksUpdateTaskRequestMainItem_Parallel", "TasksUpdateTaskRequestMainItem_Prompt", + "TasksUpdateTaskRequestMainItem_Return", + "TasksUpdateTaskRequestMainItem_Search", + "TasksUpdateTaskRequestMainItem_Set", + "TasksUpdateTaskRequestMainItem_Sleep", + "TasksUpdateTaskRequestMainItem_Switch", "TasksUpdateTaskRequestMainItem_ToolCall", "TasksUpdateTaskRequestMainItem_WaitForInput", "TasksUpdateTaskRequestMainItem_Yield", diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index 575f35db9..a534c0a6e 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -161,44 +161,98 @@ ) from .task_executions_route_list_response import TaskExecutionsRouteListResponse from .tasks_base_workflow_step import TasksBaseWorkflowStep +from .tasks_case_then import TasksCaseThen +from .tasks_case_then_then import TasksCaseThenThen from .tasks_create_task_request import TasksCreateTaskRequest from .tasks_create_task_request_main_item import ( TasksCreateTaskRequestMainItem, + TasksCreateTaskRequestMainItem_Embed, TasksCreateTaskRequestMainItem_Error, TasksCreateTaskRequestMainItem_Evaluate, + TasksCreateTaskRequestMainItem_Foreach, + TasksCreateTaskRequestMainItem_Get, TasksCreateTaskRequestMainItem_IfElse, + TasksCreateTaskRequestMainItem_Log, + TasksCreateTaskRequestMainItem_MapReduce, + TasksCreateTaskRequestMainItem_Parallel, TasksCreateTaskRequestMainItem_Prompt, + TasksCreateTaskRequestMainItem_Return, + TasksCreateTaskRequestMainItem_Search, + TasksCreateTaskRequestMainItem_Set, + TasksCreateTaskRequestMainItem_Sleep, + TasksCreateTaskRequestMainItem_Switch, TasksCreateTaskRequestMainItem_ToolCall, TasksCreateTaskRequestMainItem_WaitForInput, TasksCreateTaskRequestMainItem_Yield, ) +from .tasks_embed_step import TasksEmbedStep from .tasks_error_workflow_step import TasksErrorWorkflowStep from .tasks_evaluate_step import TasksEvaluateStep +from .tasks_foreach_do import TasksForeachDo +from .tasks_foreach_do_do_item import TasksForeachDoDoItem +from .tasks_foreach_step import TasksForeachStep +from .tasks_get_step import TasksGetStep from .tasks_if_else_workflow_step import TasksIfElseWorkflowStep from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen +from .tasks_log_step import TasksLogStep +from .tasks_map_over import TasksMapOver +from .tasks_map_reduce_step import TasksMapReduceStep +from .tasks_parallel_step import TasksParallelStep +from .tasks_parallel_step_parallel_item import TasksParallelStepParallelItem from .tasks_patch_task_request_main_item import ( TasksPatchTaskRequestMainItem, + TasksPatchTaskRequestMainItem_Embed, TasksPatchTaskRequestMainItem_Error, TasksPatchTaskRequestMainItem_Evaluate, + TasksPatchTaskRequestMainItem_Foreach, + TasksPatchTaskRequestMainItem_Get, TasksPatchTaskRequestMainItem_IfElse, + TasksPatchTaskRequestMainItem_Log, + TasksPatchTaskRequestMainItem_MapReduce, + TasksPatchTaskRequestMainItem_Parallel, TasksPatchTaskRequestMainItem_Prompt, + TasksPatchTaskRequestMainItem_Return, + TasksPatchTaskRequestMainItem_Search, + TasksPatchTaskRequestMainItem_Set, + TasksPatchTaskRequestMainItem_Sleep, + TasksPatchTaskRequestMainItem_Switch, TasksPatchTaskRequestMainItem_ToolCall, TasksPatchTaskRequestMainItem_WaitForInput, TasksPatchTaskRequestMainItem_Yield, ) from .tasks_prompt_step import TasksPromptStep from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_return_step import TasksReturnStep from .tasks_route_list_request_direction import TasksRouteListRequestDirection from .tasks_route_list_request_sort_by import TasksRouteListRequestSortBy from .tasks_route_list_response import TasksRouteListResponse +from .tasks_search_step import TasksSearchStep +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_key import TasksSetKey +from .tasks_set_step import TasksSetStep +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor +from .tasks_sleep_step import TasksSleepStep +from .tasks_switch_step import TasksSwitchStep from .tasks_task import TasksTask from .tasks_task_main_item import ( TasksTaskMainItem, + TasksTaskMainItem_Embed, TasksTaskMainItem_Error, TasksTaskMainItem_Evaluate, + TasksTaskMainItem_Foreach, + TasksTaskMainItem_Get, TasksTaskMainItem_IfElse, + TasksTaskMainItem_Log, + TasksTaskMainItem_MapReduce, + TasksTaskMainItem_Parallel, TasksTaskMainItem_Prompt, + TasksTaskMainItem_Return, + TasksTaskMainItem_Search, + TasksTaskMainItem_Set, + TasksTaskMainItem_Sleep, + TasksTaskMainItem_Switch, TasksTaskMainItem_ToolCall, TasksTaskMainItem_WaitForInput, TasksTaskMainItem_Yield, @@ -207,10 +261,21 @@ from .tasks_tool_call_step import TasksToolCallStep from .tasks_update_task_request_main_item import ( TasksUpdateTaskRequestMainItem, + TasksUpdateTaskRequestMainItem_Embed, TasksUpdateTaskRequestMainItem_Error, TasksUpdateTaskRequestMainItem_Evaluate, + TasksUpdateTaskRequestMainItem_Foreach, + TasksUpdateTaskRequestMainItem_Get, TasksUpdateTaskRequestMainItem_IfElse, + TasksUpdateTaskRequestMainItem_Log, + TasksUpdateTaskRequestMainItem_MapReduce, + TasksUpdateTaskRequestMainItem_Parallel, TasksUpdateTaskRequestMainItem_Prompt, + TasksUpdateTaskRequestMainItem_Return, + TasksUpdateTaskRequestMainItem_Search, + TasksUpdateTaskRequestMainItem_Set, + TasksUpdateTaskRequestMainItem_Sleep, + TasksUpdateTaskRequestMainItem_Switch, TasksUpdateTaskRequestMainItem_ToolCall, TasksUpdateTaskRequestMainItem_WaitForInput, TasksUpdateTaskRequestMainItem_Yield, @@ -368,49 +433,114 @@ "TaskExecutionsRouteListRequestSortBy", "TaskExecutionsRouteListResponse", "TasksBaseWorkflowStep", + "TasksCaseThen", + "TasksCaseThenThen", "TasksCreateTaskRequest", "TasksCreateTaskRequestMainItem", + "TasksCreateTaskRequestMainItem_Embed", "TasksCreateTaskRequestMainItem_Error", "TasksCreateTaskRequestMainItem_Evaluate", + "TasksCreateTaskRequestMainItem_Foreach", + "TasksCreateTaskRequestMainItem_Get", "TasksCreateTaskRequestMainItem_IfElse", + "TasksCreateTaskRequestMainItem_Log", + "TasksCreateTaskRequestMainItem_MapReduce", + "TasksCreateTaskRequestMainItem_Parallel", "TasksCreateTaskRequestMainItem_Prompt", + "TasksCreateTaskRequestMainItem_Return", + "TasksCreateTaskRequestMainItem_Search", + "TasksCreateTaskRequestMainItem_Set", + "TasksCreateTaskRequestMainItem_Sleep", + "TasksCreateTaskRequestMainItem_Switch", "TasksCreateTaskRequestMainItem_ToolCall", "TasksCreateTaskRequestMainItem_WaitForInput", "TasksCreateTaskRequestMainItem_Yield", + "TasksEmbedStep", "TasksErrorWorkflowStep", "TasksEvaluateStep", + "TasksForeachDo", + "TasksForeachDoDoItem", + "TasksForeachStep", + "TasksGetStep", "TasksIfElseWorkflowStep", "TasksIfElseWorkflowStepElse", "TasksIfElseWorkflowStepThen", + "TasksLogStep", + "TasksMapOver", + "TasksMapReduceStep", + "TasksParallelStep", + "TasksParallelStepParallelItem", "TasksPatchTaskRequestMainItem", + "TasksPatchTaskRequestMainItem_Embed", "TasksPatchTaskRequestMainItem_Error", "TasksPatchTaskRequestMainItem_Evaluate", + "TasksPatchTaskRequestMainItem_Foreach", + "TasksPatchTaskRequestMainItem_Get", "TasksPatchTaskRequestMainItem_IfElse", + "TasksPatchTaskRequestMainItem_Log", + "TasksPatchTaskRequestMainItem_MapReduce", + "TasksPatchTaskRequestMainItem_Parallel", "TasksPatchTaskRequestMainItem_Prompt", + "TasksPatchTaskRequestMainItem_Return", + "TasksPatchTaskRequestMainItem_Search", + "TasksPatchTaskRequestMainItem_Set", + "TasksPatchTaskRequestMainItem_Sleep", + "TasksPatchTaskRequestMainItem_Switch", "TasksPatchTaskRequestMainItem_ToolCall", "TasksPatchTaskRequestMainItem_WaitForInput", "TasksPatchTaskRequestMainItem_Yield", "TasksPromptStep", "TasksPromptStepPrompt", + "TasksReturnStep", "TasksRouteListRequestDirection", "TasksRouteListRequestSortBy", "TasksRouteListResponse", + "TasksSearchStep", + "TasksSearchStepSearch", + "TasksSetKey", + "TasksSetStep", + "TasksSetStepSet", + "TasksSleepFor", + "TasksSleepStep", + "TasksSwitchStep", "TasksTask", "TasksTaskMainItem", + "TasksTaskMainItem_Embed", "TasksTaskMainItem_Error", "TasksTaskMainItem_Evaluate", + "TasksTaskMainItem_Foreach", + "TasksTaskMainItem_Get", "TasksTaskMainItem_IfElse", + "TasksTaskMainItem_Log", + "TasksTaskMainItem_MapReduce", + "TasksTaskMainItem_Parallel", "TasksTaskMainItem_Prompt", + "TasksTaskMainItem_Return", + "TasksTaskMainItem_Search", + "TasksTaskMainItem_Set", + "TasksTaskMainItem_Sleep", + "TasksTaskMainItem_Switch", "TasksTaskMainItem_ToolCall", "TasksTaskMainItem_WaitForInput", "TasksTaskMainItem_Yield", "TasksTaskTool", "TasksToolCallStep", "TasksUpdateTaskRequestMainItem", + "TasksUpdateTaskRequestMainItem_Embed", "TasksUpdateTaskRequestMainItem_Error", "TasksUpdateTaskRequestMainItem_Evaluate", + "TasksUpdateTaskRequestMainItem_Foreach", + "TasksUpdateTaskRequestMainItem_Get", "TasksUpdateTaskRequestMainItem_IfElse", + "TasksUpdateTaskRequestMainItem_Log", + "TasksUpdateTaskRequestMainItem_MapReduce", + "TasksUpdateTaskRequestMainItem_Parallel", "TasksUpdateTaskRequestMainItem_Prompt", + "TasksUpdateTaskRequestMainItem_Return", + "TasksUpdateTaskRequestMainItem_Search", + "TasksUpdateTaskRequestMainItem_Set", + "TasksUpdateTaskRequestMainItem_Sleep", + "TasksUpdateTaskRequestMainItem_Switch", "TasksUpdateTaskRequestMainItem_ToolCall", "TasksUpdateTaskRequestMainItem_WaitForInput", "TasksUpdateTaskRequestMainItem_Yield", diff --git a/sdks/python/julep/api/types/tasks_case_then.py b/sdks/python/julep/api/types/tasks_case_then.py new file mode 100644 index 000000000..1ab5ac484 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_case_then.py @@ -0,0 +1,52 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_py_expression import CommonPyExpression +from .tasks_case_then_then import TasksCaseThenThen + + +class TasksCaseThen(pydantic_v1.BaseModel): + case: CommonPyExpression = pydantic_v1.Field() + """ + The condition to evaluate + """ + + then: TasksCaseThenThen = pydantic_v1.Field() + """ + The steps to run if the condition is true + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_case_then_then.py b/sdks/python/julep/api/types/tasks_case_then_then.py new file mode 100644 index 000000000..3680b8720 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_case_then_then.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .tasks_embed_step import TasksEmbedStep +from .tasks_error_workflow_step import TasksErrorWorkflowStep +from .tasks_get_step import TasksGetStep +from .tasks_log_step import TasksLogStep +from .tasks_prompt_step import TasksPromptStep +from .tasks_return_step import TasksReturnStep +from .tasks_search_step import TasksSearchStep +from .tasks_set_step import TasksSetStep +from .tasks_sleep_step import TasksSleepStep +from .tasks_tool_call_step import TasksToolCallStep +from .tasks_wait_for_input_step import TasksWaitForInputStep +from .tasks_yield_step import TasksYieldStep + +TasksCaseThenThen = typing.Union[ + typing.Any, + TasksToolCallStep, + TasksYieldStep, + TasksPromptStep, + TasksErrorWorkflowStep, + TasksSleepStep, + TasksReturnStep, + TasksGetStep, + TasksSetStep, + TasksLogStep, + TasksEmbedStep, + TasksSearchStep, + TasksWaitForInputStep, +] diff --git a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py index ae9916c31..0cfa46d96 100644 --- a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py @@ -10,9 +10,17 @@ from .chat_chat_settings import ChatChatSettings from .common_py_expression import CommonPyExpression from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_case_then import TasksCaseThen +from .tasks_foreach_do import TasksForeachDo from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen +from .tasks_map_over import TasksMapOver +from .tasks_parallel_step_parallel_item import TasksParallelStepParallelItem from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo @@ -213,6 +221,272 @@ class Config: json_encoders = {dt.datetime: serialize_datetime} +class TasksCreateTaskRequestMainItem_Sleep(pydantic_v1.BaseModel): + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCreateTaskRequestMainItem_Return(pydantic_v1.BaseModel): + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCreateTaskRequestMainItem_Get(pydantic_v1.BaseModel): + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCreateTaskRequestMainItem_Set(pydantic_v1.BaseModel): + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCreateTaskRequestMainItem_Log(pydantic_v1.BaseModel): + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCreateTaskRequestMainItem_Embed(pydantic_v1.BaseModel): + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCreateTaskRequestMainItem_Search(pydantic_v1.BaseModel): + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + class TasksCreateTaskRequestMainItem_WaitForInput(pydantic_v1.BaseModel): info: TasksWaitForInputStepInfo kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( @@ -295,12 +569,182 @@ class Config: json_encoders = {dt.datetime: serialize_datetime} +class TasksCreateTaskRequestMainItem_Switch(pydantic_v1.BaseModel): + switch: typing.List[TasksCaseThen] + kind: typing.Literal["switch"] = pydantic_v1.Field(alias="kind_", default="switch") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCreateTaskRequestMainItem_Foreach(pydantic_v1.BaseModel): + foreach: TasksForeachDo + kind: typing.Literal["foreach"] = pydantic_v1.Field( + alias="kind_", default="foreach" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCreateTaskRequestMainItem_Parallel(pydantic_v1.BaseModel): + parallel: typing.List[TasksParallelStepParallelItem] + kind: typing.Literal["parallel"] = pydantic_v1.Field( + alias="kind_", default="parallel" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCreateTaskRequestMainItem_MapReduce(pydantic_v1.BaseModel): + map_: TasksMapOver = pydantic_v1.Field(alias="map") + reduce: CommonPyExpression + kind: typing.Literal["map_reduce"] = pydantic_v1.Field( + alias="kind_", default="map_reduce" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + TasksCreateTaskRequestMainItem = typing.Union[ TasksCreateTaskRequestMainItem_Evaluate, TasksCreateTaskRequestMainItem_ToolCall, TasksCreateTaskRequestMainItem_Yield, TasksCreateTaskRequestMainItem_Prompt, TasksCreateTaskRequestMainItem_Error, + TasksCreateTaskRequestMainItem_Sleep, + TasksCreateTaskRequestMainItem_Return, + TasksCreateTaskRequestMainItem_Get, + TasksCreateTaskRequestMainItem_Set, + TasksCreateTaskRequestMainItem_Log, + TasksCreateTaskRequestMainItem_Embed, + TasksCreateTaskRequestMainItem_Search, TasksCreateTaskRequestMainItem_WaitForInput, TasksCreateTaskRequestMainItem_IfElse, + TasksCreateTaskRequestMainItem_Switch, + TasksCreateTaskRequestMainItem_Foreach, + TasksCreateTaskRequestMainItem_Parallel, + TasksCreateTaskRequestMainItem_MapReduce, ] diff --git a/sdks/python/julep/api/types/tasks_embed_step.py b/sdks/python/julep/api/types/tasks_embed_step.py new file mode 100644 index 000000000..13e9bcfd8 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_embed_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_base_workflow_step import TasksBaseWorkflowStep + + +class TasksEmbedStep(TasksBaseWorkflowStep): + embed: DocsEmbedQueryRequest = pydantic_v1.Field() + """ + The text to embed + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_foreach_do.py b/sdks/python/julep/api/types/tasks_foreach_do.py new file mode 100644 index 000000000..8ee662103 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_foreach_do.py @@ -0,0 +1,54 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_py_expression import CommonPyExpression +from .tasks_foreach_do_do_item import TasksForeachDoDoItem + + +class TasksForeachDo(pydantic_v1.BaseModel): + in_: CommonPyExpression = pydantic_v1.Field(alias="in") + """ + The variable to iterate over + """ + + do: typing.List[TasksForeachDoDoItem] = pydantic_v1.Field() + """ + The steps to run for each iteration + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_foreach_do_do_item.py b/sdks/python/julep/api/types/tasks_foreach_do_do_item.py new file mode 100644 index 000000000..f8c2a90bf --- /dev/null +++ b/sdks/python/julep/api/types/tasks_foreach_do_do_item.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .tasks_embed_step import TasksEmbedStep +from .tasks_error_workflow_step import TasksErrorWorkflowStep +from .tasks_get_step import TasksGetStep +from .tasks_log_step import TasksLogStep +from .tasks_prompt_step import TasksPromptStep +from .tasks_return_step import TasksReturnStep +from .tasks_search_step import TasksSearchStep +from .tasks_set_step import TasksSetStep +from .tasks_sleep_step import TasksSleepStep +from .tasks_tool_call_step import TasksToolCallStep +from .tasks_wait_for_input_step import TasksWaitForInputStep +from .tasks_yield_step import TasksYieldStep + +TasksForeachDoDoItem = typing.Union[ + typing.Any, + TasksToolCallStep, + TasksYieldStep, + TasksPromptStep, + TasksErrorWorkflowStep, + TasksSleepStep, + TasksReturnStep, + TasksGetStep, + TasksSetStep, + TasksLogStep, + TasksEmbedStep, + TasksSearchStep, + TasksWaitForInputStep, +] diff --git a/sdks/python/julep/api/types/tasks_foreach_step.py b/sdks/python/julep/api/types/tasks_foreach_step.py new file mode 100644 index 000000000..03c86dce2 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_foreach_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .tasks_base_workflow_step import TasksBaseWorkflowStep +from .tasks_foreach_do import TasksForeachDo + + +class TasksForeachStep(TasksBaseWorkflowStep): + foreach: TasksForeachDo = pydantic_v1.Field() + """ + The steps to run for each iteration + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_get_step.py b/sdks/python/julep/api/types/tasks_get_step.py new file mode 100644 index 000000000..c6fa9748e --- /dev/null +++ b/sdks/python/julep/api/types/tasks_get_step.py @@ -0,0 +1,48 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .tasks_base_workflow_step import TasksBaseWorkflowStep + + +class TasksGetStep(TasksBaseWorkflowStep): + get: str = pydantic_v1.Field() + """ + The key to get + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_if_else_workflow_step_else.py b/sdks/python/julep/api/types/tasks_if_else_workflow_step_else.py index 37d64c117..754449134 100644 --- a/sdks/python/julep/api/types/tasks_if_else_workflow_step_else.py +++ b/sdks/python/julep/api/types/tasks_if_else_workflow_step_else.py @@ -2,8 +2,15 @@ import typing +from .tasks_embed_step import TasksEmbedStep from .tasks_error_workflow_step import TasksErrorWorkflowStep +from .tasks_get_step import TasksGetStep +from .tasks_log_step import TasksLogStep from .tasks_prompt_step import TasksPromptStep +from .tasks_return_step import TasksReturnStep +from .tasks_search_step import TasksSearchStep +from .tasks_set_step import TasksSetStep +from .tasks_sleep_step import TasksSleepStep from .tasks_tool_call_step import TasksToolCallStep from .tasks_wait_for_input_step import TasksWaitForInputStep from .tasks_yield_step import TasksYieldStep @@ -14,5 +21,12 @@ TasksYieldStep, TasksPromptStep, TasksErrorWorkflowStep, + TasksSleepStep, + TasksReturnStep, + TasksGetStep, + TasksSetStep, + TasksLogStep, + TasksEmbedStep, + TasksSearchStep, TasksWaitForInputStep, ] diff --git a/sdks/python/julep/api/types/tasks_if_else_workflow_step_then.py b/sdks/python/julep/api/types/tasks_if_else_workflow_step_then.py index a706d4c86..9e914c54d 100644 --- a/sdks/python/julep/api/types/tasks_if_else_workflow_step_then.py +++ b/sdks/python/julep/api/types/tasks_if_else_workflow_step_then.py @@ -2,8 +2,15 @@ import typing +from .tasks_embed_step import TasksEmbedStep from .tasks_error_workflow_step import TasksErrorWorkflowStep +from .tasks_get_step import TasksGetStep +from .tasks_log_step import TasksLogStep from .tasks_prompt_step import TasksPromptStep +from .tasks_return_step import TasksReturnStep +from .tasks_search_step import TasksSearchStep +from .tasks_set_step import TasksSetStep +from .tasks_sleep_step import TasksSleepStep from .tasks_tool_call_step import TasksToolCallStep from .tasks_wait_for_input_step import TasksWaitForInputStep from .tasks_yield_step import TasksYieldStep @@ -14,5 +21,12 @@ TasksYieldStep, TasksPromptStep, TasksErrorWorkflowStep, + TasksSleepStep, + TasksReturnStep, + TasksGetStep, + TasksSetStep, + TasksLogStep, + TasksEmbedStep, + TasksSearchStep, TasksWaitForInputStep, ] diff --git a/sdks/python/julep/api/types/tasks_log_step.py b/sdks/python/julep/api/types/tasks_log_step.py new file mode 100644 index 000000000..649a8471d --- /dev/null +++ b/sdks/python/julep/api/types/tasks_log_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_py_expression import CommonPyExpression +from .tasks_base_workflow_step import TasksBaseWorkflowStep + + +class TasksLogStep(TasksBaseWorkflowStep): + log: CommonPyExpression = pydantic_v1.Field() + """ + The value to log + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_map_over.py b/sdks/python/julep/api/types/tasks_map_over.py new file mode 100644 index 000000000..d3377ff00 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_map_over.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_py_expression import CommonPyExpression + + +class TasksMapOver(pydantic_v1.BaseModel): + over: CommonPyExpression = pydantic_v1.Field() + """ + The variable to iterate over + """ + + workflow: str = pydantic_v1.Field() + """ + The subworkflow to run for each iteration + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_map_reduce_step.py b/sdks/python/julep/api/types/tasks_map_reduce_step.py new file mode 100644 index 000000000..9d43304d5 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_map_reduce_step.py @@ -0,0 +1,55 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_py_expression import CommonPyExpression +from .tasks_base_workflow_step import TasksBaseWorkflowStep +from .tasks_map_over import TasksMapOver + + +class TasksMapReduceStep(TasksBaseWorkflowStep): + map_: TasksMapOver = pydantic_v1.Field(alias="map") + """ + The steps to run for each iteration + """ + + reduce: CommonPyExpression = pydantic_v1.Field() + """ + The expression to reduce the results (`_` is a list of outputs) + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_parallel_step.py b/sdks/python/julep/api/types/tasks_parallel_step.py new file mode 100644 index 000000000..7f3ba3bad --- /dev/null +++ b/sdks/python/julep/api/types/tasks_parallel_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .tasks_base_workflow_step import TasksBaseWorkflowStep +from .tasks_parallel_step_parallel_item import TasksParallelStepParallelItem + + +class TasksParallelStep(TasksBaseWorkflowStep): + parallel: typing.List[TasksParallelStepParallelItem] = pydantic_v1.Field() + """ + The steps to run in parallel. Max concurrency will depend on the platform + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_parallel_step_parallel_item.py b/sdks/python/julep/api/types/tasks_parallel_step_parallel_item.py new file mode 100644 index 000000000..0b881fed7 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_parallel_step_parallel_item.py @@ -0,0 +1,32 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .tasks_embed_step import TasksEmbedStep +from .tasks_error_workflow_step import TasksErrorWorkflowStep +from .tasks_get_step import TasksGetStep +from .tasks_log_step import TasksLogStep +from .tasks_prompt_step import TasksPromptStep +from .tasks_return_step import TasksReturnStep +from .tasks_search_step import TasksSearchStep +from .tasks_set_step import TasksSetStep +from .tasks_sleep_step import TasksSleepStep +from .tasks_tool_call_step import TasksToolCallStep +from .tasks_wait_for_input_step import TasksWaitForInputStep +from .tasks_yield_step import TasksYieldStep + +TasksParallelStepParallelItem = typing.Union[ + typing.Any, + TasksToolCallStep, + TasksYieldStep, + TasksPromptStep, + TasksErrorWorkflowStep, + TasksSleepStep, + TasksReturnStep, + TasksGetStep, + TasksSetStep, + TasksLogStep, + TasksEmbedStep, + TasksSearchStep, + TasksWaitForInputStep, +] diff --git a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py index 6532321d0..83f4ae007 100644 --- a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py @@ -10,9 +10,17 @@ from .chat_chat_settings import ChatChatSettings from .common_py_expression import CommonPyExpression from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_case_then import TasksCaseThen +from .tasks_foreach_do import TasksForeachDo from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen +from .tasks_map_over import TasksMapOver +from .tasks_parallel_step_parallel_item import TasksParallelStepParallelItem from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo @@ -213,6 +221,272 @@ class Config: json_encoders = {dt.datetime: serialize_datetime} +class TasksPatchTaskRequestMainItem_Sleep(pydantic_v1.BaseModel): + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksPatchTaskRequestMainItem_Return(pydantic_v1.BaseModel): + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksPatchTaskRequestMainItem_Get(pydantic_v1.BaseModel): + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksPatchTaskRequestMainItem_Set(pydantic_v1.BaseModel): + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksPatchTaskRequestMainItem_Log(pydantic_v1.BaseModel): + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksPatchTaskRequestMainItem_Embed(pydantic_v1.BaseModel): + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksPatchTaskRequestMainItem_Search(pydantic_v1.BaseModel): + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + class TasksPatchTaskRequestMainItem_WaitForInput(pydantic_v1.BaseModel): info: TasksWaitForInputStepInfo kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( @@ -295,12 +569,182 @@ class Config: json_encoders = {dt.datetime: serialize_datetime} +class TasksPatchTaskRequestMainItem_Switch(pydantic_v1.BaseModel): + switch: typing.List[TasksCaseThen] + kind: typing.Literal["switch"] = pydantic_v1.Field(alias="kind_", default="switch") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksPatchTaskRequestMainItem_Foreach(pydantic_v1.BaseModel): + foreach: TasksForeachDo + kind: typing.Literal["foreach"] = pydantic_v1.Field( + alias="kind_", default="foreach" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksPatchTaskRequestMainItem_Parallel(pydantic_v1.BaseModel): + parallel: typing.List[TasksParallelStepParallelItem] + kind: typing.Literal["parallel"] = pydantic_v1.Field( + alias="kind_", default="parallel" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksPatchTaskRequestMainItem_MapReduce(pydantic_v1.BaseModel): + map_: TasksMapOver = pydantic_v1.Field(alias="map") + reduce: CommonPyExpression + kind: typing.Literal["map_reduce"] = pydantic_v1.Field( + alias="kind_", default="map_reduce" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + TasksPatchTaskRequestMainItem = typing.Union[ TasksPatchTaskRequestMainItem_Evaluate, TasksPatchTaskRequestMainItem_ToolCall, TasksPatchTaskRequestMainItem_Yield, TasksPatchTaskRequestMainItem_Prompt, TasksPatchTaskRequestMainItem_Error, + TasksPatchTaskRequestMainItem_Sleep, + TasksPatchTaskRequestMainItem_Return, + TasksPatchTaskRequestMainItem_Get, + TasksPatchTaskRequestMainItem_Set, + TasksPatchTaskRequestMainItem_Log, + TasksPatchTaskRequestMainItem_Embed, + TasksPatchTaskRequestMainItem_Search, TasksPatchTaskRequestMainItem_WaitForInput, TasksPatchTaskRequestMainItem_IfElse, + TasksPatchTaskRequestMainItem_Switch, + TasksPatchTaskRequestMainItem_Foreach, + TasksPatchTaskRequestMainItem_Parallel, + TasksPatchTaskRequestMainItem_MapReduce, ] diff --git a/sdks/python/julep/api/types/tasks_return_step.py b/sdks/python/julep/api/types/tasks_return_step.py new file mode 100644 index 000000000..33aed561e --- /dev/null +++ b/sdks/python/julep/api/types/tasks_return_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_py_expression import CommonPyExpression +from .tasks_base_workflow_step import TasksBaseWorkflowStep + + +class TasksReturnStep(TasksBaseWorkflowStep): + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + """ + The value to return + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_search_step.py b/sdks/python/julep/api/types/tasks_search_step.py new file mode 100644 index 000000000..82d0a1658 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_search_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .tasks_base_workflow_step import TasksBaseWorkflowStep +from .tasks_search_step_search import TasksSearchStepSearch + + +class TasksSearchStep(TasksBaseWorkflowStep): + search: TasksSearchStepSearch = pydantic_v1.Field() + """ + The search query + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_search_step_search.py b/sdks/python/julep/api/types/tasks_search_step_search.py new file mode 100644 index 000000000..678e79d5f --- /dev/null +++ b/sdks/python/julep/api/types/tasks_search_step_search.py @@ -0,0 +1,11 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .docs_hybrid_doc_search_request import DocsHybridDocSearchRequest +from .docs_text_only_doc_search_request import DocsTextOnlyDocSearchRequest +from .docs_vector_doc_search_request import DocsVectorDocSearchRequest + +TasksSearchStepSearch = typing.Union[ + DocsVectorDocSearchRequest, DocsTextOnlyDocSearchRequest, DocsHybridDocSearchRequest +] diff --git a/sdks/python/julep/api/types/tasks_set_key.py b/sdks/python/julep/api/types/tasks_set_key.py new file mode 100644 index 000000000..c925599a2 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_set_key.py @@ -0,0 +1,51 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_py_expression import CommonPyExpression + + +class TasksSetKey(pydantic_v1.BaseModel): + key: str = pydantic_v1.Field() + """ + The key to set + """ + + value: CommonPyExpression = pydantic_v1.Field() + """ + The value to set + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_set_step.py b/sdks/python/julep/api/types/tasks_set_step.py new file mode 100644 index 000000000..c718d4e7a --- /dev/null +++ b/sdks/python/julep/api/types/tasks_set_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .tasks_base_workflow_step import TasksBaseWorkflowStep +from .tasks_set_step_set import TasksSetStepSet + + +class TasksSetStep(TasksBaseWorkflowStep): + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + """ + The value to set + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_set_step_set.py b/sdks/python/julep/api/types/tasks_set_step_set.py new file mode 100644 index 000000000..0b5c955c1 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_set_step_set.py @@ -0,0 +1,7 @@ +# This file was auto-generated by Fern from our API Definition. + +import typing + +from .tasks_set_key import TasksSetKey + +TasksSetStepSet = typing.Union[TasksSetKey, typing.List[TasksSetKey]] diff --git a/sdks/python/julep/api/types/tasks_sleep_for.py b/sdks/python/julep/api/types/tasks_sleep_for.py new file mode 100644 index 000000000..44a3acd32 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_sleep_for.py @@ -0,0 +1,60 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 + + +class TasksSleepFor(pydantic_v1.BaseModel): + seconds: int = pydantic_v1.Field() + """ + The number of seconds to sleep for + """ + + minutes: int = pydantic_v1.Field() + """ + The number of minutes to sleep for + """ + + hours: int = pydantic_v1.Field() + """ + The number of hours to sleep for + """ + + days: int = pydantic_v1.Field() + """ + The number of days to sleep for + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_sleep_step.py b/sdks/python/julep/api/types/tasks_sleep_step.py new file mode 100644 index 000000000..5261bad5f --- /dev/null +++ b/sdks/python/julep/api/types/tasks_sleep_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .tasks_base_workflow_step import TasksBaseWorkflowStep +from .tasks_sleep_for import TasksSleepFor + + +class TasksSleepStep(TasksBaseWorkflowStep): + sleep: TasksSleepFor = pydantic_v1.Field() + """ + The duration to sleep for + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_switch_step.py b/sdks/python/julep/api/types/tasks_switch_step.py new file mode 100644 index 000000000..0a83a8185 --- /dev/null +++ b/sdks/python/julep/api/types/tasks_switch_step.py @@ -0,0 +1,49 @@ +# This file was auto-generated by Fern from our API Definition. + +import datetime as dt +import typing + +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .tasks_base_workflow_step import TasksBaseWorkflowStep +from .tasks_case_then import TasksCaseThen + + +class TasksSwitchStep(TasksBaseWorkflowStep): + switch: typing.List[TasksCaseThen] = pydantic_v1.Field() + """ + The cond tree + """ + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_task_main_item.py b/sdks/python/julep/api/types/tasks_task_main_item.py index 41cda3d7d..60e7aed65 100644 --- a/sdks/python/julep/api/types/tasks_task_main_item.py +++ b/sdks/python/julep/api/types/tasks_task_main_item.py @@ -10,9 +10,17 @@ from .chat_chat_settings import ChatChatSettings from .common_py_expression import CommonPyExpression from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_case_then import TasksCaseThen +from .tasks_foreach_do import TasksForeachDo from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen +from .tasks_map_over import TasksMapOver +from .tasks_parallel_step_parallel_item import TasksParallelStepParallelItem from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo @@ -213,6 +221,272 @@ class Config: json_encoders = {dt.datetime: serialize_datetime} +class TasksTaskMainItem_Sleep(pydantic_v1.BaseModel): + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksTaskMainItem_Return(pydantic_v1.BaseModel): + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksTaskMainItem_Get(pydantic_v1.BaseModel): + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksTaskMainItem_Set(pydantic_v1.BaseModel): + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksTaskMainItem_Log(pydantic_v1.BaseModel): + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksTaskMainItem_Embed(pydantic_v1.BaseModel): + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksTaskMainItem_Search(pydantic_v1.BaseModel): + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + class TasksTaskMainItem_WaitForInput(pydantic_v1.BaseModel): info: TasksWaitForInputStepInfo kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( @@ -295,12 +569,182 @@ class Config: json_encoders = {dt.datetime: serialize_datetime} +class TasksTaskMainItem_Switch(pydantic_v1.BaseModel): + switch: typing.List[TasksCaseThen] + kind: typing.Literal["switch"] = pydantic_v1.Field(alias="kind_", default="switch") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksTaskMainItem_Foreach(pydantic_v1.BaseModel): + foreach: TasksForeachDo + kind: typing.Literal["foreach"] = pydantic_v1.Field( + alias="kind_", default="foreach" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksTaskMainItem_Parallel(pydantic_v1.BaseModel): + parallel: typing.List[TasksParallelStepParallelItem] + kind: typing.Literal["parallel"] = pydantic_v1.Field( + alias="kind_", default="parallel" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksTaskMainItem_MapReduce(pydantic_v1.BaseModel): + map_: TasksMapOver = pydantic_v1.Field(alias="map") + reduce: CommonPyExpression + kind: typing.Literal["map_reduce"] = pydantic_v1.Field( + alias="kind_", default="map_reduce" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + TasksTaskMainItem = typing.Union[ TasksTaskMainItem_Evaluate, TasksTaskMainItem_ToolCall, TasksTaskMainItem_Yield, TasksTaskMainItem_Prompt, TasksTaskMainItem_Error, + TasksTaskMainItem_Sleep, + TasksTaskMainItem_Return, + TasksTaskMainItem_Get, + TasksTaskMainItem_Set, + TasksTaskMainItem_Log, + TasksTaskMainItem_Embed, + TasksTaskMainItem_Search, TasksTaskMainItem_WaitForInput, TasksTaskMainItem_IfElse, + TasksTaskMainItem_Switch, + TasksTaskMainItem_Foreach, + TasksTaskMainItem_Parallel, + TasksTaskMainItem_MapReduce, ] diff --git a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py index 730545283..442c37707 100644 --- a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py @@ -10,9 +10,17 @@ from .chat_chat_settings import ChatChatSettings from .common_py_expression import CommonPyExpression from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_case_then import TasksCaseThen +from .tasks_foreach_do import TasksForeachDo from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen +from .tasks_map_over import TasksMapOver +from .tasks_parallel_step_parallel_item import TasksParallelStepParallelItem from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo @@ -213,6 +221,272 @@ class Config: json_encoders = {dt.datetime: serialize_datetime} +class TasksUpdateTaskRequestMainItem_Sleep(pydantic_v1.BaseModel): + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksUpdateTaskRequestMainItem_Return(pydantic_v1.BaseModel): + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksUpdateTaskRequestMainItem_Get(pydantic_v1.BaseModel): + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksUpdateTaskRequestMainItem_Set(pydantic_v1.BaseModel): + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksUpdateTaskRequestMainItem_Log(pydantic_v1.BaseModel): + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksUpdateTaskRequestMainItem_Embed(pydantic_v1.BaseModel): + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksUpdateTaskRequestMainItem_Search(pydantic_v1.BaseModel): + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + class TasksUpdateTaskRequestMainItem_WaitForInput(pydantic_v1.BaseModel): info: TasksWaitForInputStepInfo kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( @@ -295,12 +569,182 @@ class Config: json_encoders = {dt.datetime: serialize_datetime} +class TasksUpdateTaskRequestMainItem_Switch(pydantic_v1.BaseModel): + switch: typing.List[TasksCaseThen] + kind: typing.Literal["switch"] = pydantic_v1.Field(alias="kind_", default="switch") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksUpdateTaskRequestMainItem_Foreach(pydantic_v1.BaseModel): + foreach: TasksForeachDo + kind: typing.Literal["foreach"] = pydantic_v1.Field( + alias="kind_", default="foreach" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksUpdateTaskRequestMainItem_Parallel(pydantic_v1.BaseModel): + parallel: typing.List[TasksParallelStepParallelItem] + kind: typing.Literal["parallel"] = pydantic_v1.Field( + alias="kind_", default="parallel" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksUpdateTaskRequestMainItem_MapReduce(pydantic_v1.BaseModel): + map_: TasksMapOver = pydantic_v1.Field(alias="map") + reduce: CommonPyExpression + kind: typing.Literal["map_reduce"] = pydantic_v1.Field( + alias="kind_", default="map_reduce" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + TasksUpdateTaskRequestMainItem = typing.Union[ TasksUpdateTaskRequestMainItem_Evaluate, TasksUpdateTaskRequestMainItem_ToolCall, TasksUpdateTaskRequestMainItem_Yield, TasksUpdateTaskRequestMainItem_Prompt, TasksUpdateTaskRequestMainItem_Error, + TasksUpdateTaskRequestMainItem_Sleep, + TasksUpdateTaskRequestMainItem_Return, + TasksUpdateTaskRequestMainItem_Get, + TasksUpdateTaskRequestMainItem_Set, + TasksUpdateTaskRequestMainItem_Log, + TasksUpdateTaskRequestMainItem_Embed, + TasksUpdateTaskRequestMainItem_Search, TasksUpdateTaskRequestMainItem_WaitForInput, TasksUpdateTaskRequestMainItem_IfElse, + TasksUpdateTaskRequestMainItem_Switch, + TasksUpdateTaskRequestMainItem_Foreach, + TasksUpdateTaskRequestMainItem_Parallel, + TasksUpdateTaskRequestMainItem_MapReduce, ] diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 9b612ca30..f444f51a8 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -848,13 +848,13 @@ test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "p [[package]] name = "importlib-resources" -version = "6.4.2" +version = "6.4.3" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.4.2-py3-none-any.whl", hash = "sha256:8bba8c54a8a3afaa1419910845fa26ebd706dc716dd208d9b158b4b6966f5c5c"}, - {file = "importlib_resources-6.4.2.tar.gz", hash = "sha256:6cbfbefc449cc6e2095dd184691b7a12a04f40bc75dd4c55d31c34f174cdf57a"}, + {file = "importlib_resources-6.4.3-py3-none-any.whl", hash = "sha256:2d6dfe3b9e055f72495c2085890837fc8c758984e209115c8792bddcb762cd93"}, + {file = "importlib_resources-6.4.3.tar.gz", hash = "sha256:4a202b9b9d38563b46da59221d77bb73862ab5d79d461307bcb826d725448b98"}, ] [package.dependencies] @@ -1751,13 +1751,13 @@ files = [ [[package]] name = "openai" -version = "1.40.8" +version = "1.41.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.40.8-py3-none-any.whl", hash = "sha256:3ed4ddad48e0dde059c9b4d3dc240e47781beca2811e52ba449ddc4a471a2fd4"}, - {file = "openai-1.40.8.tar.gz", hash = "sha256:e225f830b946378e214c5b2cfa8df28ba2aeb7e9d44f738cb2a926fd971f5bc0"}, + {file = "openai-1.41.0-py3-none-any.whl", hash = "sha256:3b6cca4571667f3e0800442ef8f2bfa6a6f3301c51776bc7626159a4d81c242c"}, + {file = "openai-1.41.0.tar.gz", hash = "sha256:26b81f39b49dce92ff5d30c373625ddb212c2f1050e1574e456d18423730cdd0"}, ] [package.dependencies] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index d92686f43..64ca78e83 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -96,13 +96,29 @@ export type { Sessions_SingleAgentNoUserSession } from "./models/Sessions_Single export type { Sessions_SingleAgentSingleUserSession } from "./models/Sessions_SingleAgentSingleUserSession"; export type { Sessions_UpdateSessionRequest } from "./models/Sessions_UpdateSessionRequest"; export type { Tasks_BaseWorkflowStep } from "./models/Tasks_BaseWorkflowStep"; +export type { Tasks_CaseThen } from "./models/Tasks_CaseThen"; export type { Tasks_CreateOrUpdateTaskRequest_id } from "./models/Tasks_CreateOrUpdateTaskRequest_id"; export type { Tasks_CreateTaskRequest } from "./models/Tasks_CreateTaskRequest"; +export type { Tasks_EmbedStep } from "./models/Tasks_EmbedStep"; export type { Tasks_ErrorWorkflowStep } from "./models/Tasks_ErrorWorkflowStep"; export type { Tasks_EvaluateStep } from "./models/Tasks_EvaluateStep"; +export type { Tasks_ForeachDo } from "./models/Tasks_ForeachDo"; +export type { Tasks_ForeachStep } from "./models/Tasks_ForeachStep"; +export type { Tasks_GetStep } from "./models/Tasks_GetStep"; export type { Tasks_IfElseWorkflowStep } from "./models/Tasks_IfElseWorkflowStep"; +export type { Tasks_LogStep } from "./models/Tasks_LogStep"; +export type { Tasks_MapOver } from "./models/Tasks_MapOver"; +export type { Tasks_MapReduceStep } from "./models/Tasks_MapReduceStep"; +export type { Tasks_ParallelStep } from "./models/Tasks_ParallelStep"; export type { Tasks_PatchTaskRequest } from "./models/Tasks_PatchTaskRequest"; export type { Tasks_PromptStep } from "./models/Tasks_PromptStep"; +export type { Tasks_ReturnStep } from "./models/Tasks_ReturnStep"; +export type { Tasks_SearchStep } from "./models/Tasks_SearchStep"; +export type { Tasks_SetKey } from "./models/Tasks_SetKey"; +export type { Tasks_SetStep } from "./models/Tasks_SetStep"; +export type { Tasks_SleepFor } from "./models/Tasks_SleepFor"; +export type { Tasks_SleepStep } from "./models/Tasks_SleepStep"; +export type { Tasks_SwitchStep } from "./models/Tasks_SwitchStep"; export type { Tasks_Task } from "./models/Tasks_Task"; export type { Tasks_TaskTool } from "./models/Tasks_TaskTool"; export type { Tasks_ToolCallStep } from "./models/Tasks_ToolCallStep"; @@ -215,13 +231,29 @@ export { $Sessions_SingleAgentNoUserSession } from "./schemas/$Sessions_SingleAg export { $Sessions_SingleAgentSingleUserSession } from "./schemas/$Sessions_SingleAgentSingleUserSession"; export { $Sessions_UpdateSessionRequest } from "./schemas/$Sessions_UpdateSessionRequest"; export { $Tasks_BaseWorkflowStep } from "./schemas/$Tasks_BaseWorkflowStep"; +export { $Tasks_CaseThen } from "./schemas/$Tasks_CaseThen"; export { $Tasks_CreateOrUpdateTaskRequest_id } from "./schemas/$Tasks_CreateOrUpdateTaskRequest_id"; export { $Tasks_CreateTaskRequest } from "./schemas/$Tasks_CreateTaskRequest"; +export { $Tasks_EmbedStep } from "./schemas/$Tasks_EmbedStep"; export { $Tasks_ErrorWorkflowStep } from "./schemas/$Tasks_ErrorWorkflowStep"; export { $Tasks_EvaluateStep } from "./schemas/$Tasks_EvaluateStep"; +export { $Tasks_ForeachDo } from "./schemas/$Tasks_ForeachDo"; +export { $Tasks_ForeachStep } from "./schemas/$Tasks_ForeachStep"; +export { $Tasks_GetStep } from "./schemas/$Tasks_GetStep"; export { $Tasks_IfElseWorkflowStep } from "./schemas/$Tasks_IfElseWorkflowStep"; +export { $Tasks_LogStep } from "./schemas/$Tasks_LogStep"; +export { $Tasks_MapOver } from "./schemas/$Tasks_MapOver"; +export { $Tasks_MapReduceStep } from "./schemas/$Tasks_MapReduceStep"; +export { $Tasks_ParallelStep } from "./schemas/$Tasks_ParallelStep"; export { $Tasks_PatchTaskRequest } from "./schemas/$Tasks_PatchTaskRequest"; export { $Tasks_PromptStep } from "./schemas/$Tasks_PromptStep"; +export { $Tasks_ReturnStep } from "./schemas/$Tasks_ReturnStep"; +export { $Tasks_SearchStep } from "./schemas/$Tasks_SearchStep"; +export { $Tasks_SetKey } from "./schemas/$Tasks_SetKey"; +export { $Tasks_SetStep } from "./schemas/$Tasks_SetStep"; +export { $Tasks_SleepFor } from "./schemas/$Tasks_SleepFor"; +export { $Tasks_SleepStep } from "./schemas/$Tasks_SleepStep"; +export { $Tasks_SwitchStep } from "./schemas/$Tasks_SwitchStep"; export { $Tasks_Task } from "./schemas/$Tasks_Task"; export { $Tasks_TaskTool } from "./schemas/$Tasks_TaskTool"; export { $Tasks_ToolCallStep } from "./schemas/$Tasks_ToolCallStep"; diff --git a/sdks/ts/src/api/models/Tasks_BaseWorkflowStep.ts b/sdks/ts/src/api/models/Tasks_BaseWorkflowStep.ts index c399bcaa8..10be91186 100644 --- a/sdks/ts/src/api/models/Tasks_BaseWorkflowStep.ts +++ b/sdks/ts/src/api/models/Tasks_BaseWorkflowStep.ts @@ -8,10 +8,21 @@ export type Tasks_BaseWorkflowStep = { */ kind_: | "tool_call" - | "yield" | "prompt" | "evaluate" - | "if_else" | "wait_for_input" + | "log" + | "embed" + | "search" + | "set" + | "get" + | "foreach" + | "map_reduce" + | "parallel" + | "switch" + | "if_else" + | "sleep" + | "return" + | "yield" | "error"; }; diff --git a/sdks/ts/src/api/models/Tasks_CaseThen.ts b/sdks/ts/src/api/models/Tasks_CaseThen.ts new file mode 100644 index 000000000..43bc0d588 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_CaseThen.ts @@ -0,0 +1,39 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; +import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; +import type { Tasks_GetStep } from "./Tasks_GetStep"; +import type { Tasks_LogStep } from "./Tasks_LogStep"; +import type { Tasks_PromptStep } from "./Tasks_PromptStep"; +import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; +import type { Tasks_SearchStep } from "./Tasks_SearchStep"; +import type { Tasks_SetStep } from "./Tasks_SetStep"; +import type { Tasks_SleepStep } from "./Tasks_SleepStep"; +import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; +import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; +import type { Tasks_YieldStep } from "./Tasks_YieldStep"; +export type Tasks_CaseThen = { + /** + * The condition to evaluate + */ + case: Common_PyExpression; + /** + * The steps to run if the condition is true + */ + then: + | Tasks_ToolCallStep + | Tasks_YieldStep + | Tasks_PromptStep + | Tasks_ErrorWorkflowStep + | Tasks_SleepStep + | Tasks_ReturnStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep + | Tasks_WaitForInputStep; +}; diff --git a/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts b/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts index 369cae664..274f27b8a 100644 --- a/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts +++ b/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts @@ -2,10 +2,21 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; +import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; +import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; +import type { Tasks_LogStep } from "./Tasks_LogStep"; +import type { Tasks_MapReduceStep } from "./Tasks_MapReduceStep"; +import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; +import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; +import type { Tasks_SearchStep } from "./Tasks_SearchStep"; +import type { Tasks_SetStep } from "./Tasks_SetStep"; +import type { Tasks_SleepStep } from "./Tasks_SleepStep"; +import type { Tasks_SwitchStep } from "./Tasks_SwitchStep"; import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; import type { Tasks_YieldStep } from "./Tasks_YieldStep"; @@ -20,7 +31,18 @@ export type Tasks_CreateTaskRequest = Record< | Tasks_YieldStep | Tasks_PromptStep | Tasks_ErrorWorkflowStep + | Tasks_SleepStep + | Tasks_ReturnStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep | Tasks_WaitForInputStep | Tasks_IfElseWorkflowStep + | Tasks_SwitchStep + | Tasks_ForeachStep + | Tasks_ParallelStep + | Tasks_MapReduceStep > >; diff --git a/sdks/ts/src/api/models/Tasks_EmbedStep.ts b/sdks/ts/src/api/models/Tasks_EmbedStep.ts new file mode 100644 index 000000000..2036ceead --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_EmbedStep.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Docs_EmbedQueryRequest } from "./Docs_EmbedQueryRequest"; +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +export type Tasks_EmbedStep = Tasks_BaseWorkflowStep & { + kind_: "embed"; + /** + * The text to embed + */ + embed: Docs_EmbedQueryRequest; +}; diff --git a/sdks/ts/src/api/models/Tasks_ForeachDo.ts b/sdks/ts/src/api/models/Tasks_ForeachDo.ts new file mode 100644 index 000000000..f035536f5 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_ForeachDo.ts @@ -0,0 +1,40 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; +import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; +import type { Tasks_GetStep } from "./Tasks_GetStep"; +import type { Tasks_LogStep } from "./Tasks_LogStep"; +import type { Tasks_PromptStep } from "./Tasks_PromptStep"; +import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; +import type { Tasks_SearchStep } from "./Tasks_SearchStep"; +import type { Tasks_SetStep } from "./Tasks_SetStep"; +import type { Tasks_SleepStep } from "./Tasks_SleepStep"; +import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; +import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; +import type { Tasks_YieldStep } from "./Tasks_YieldStep"; +export type Tasks_ForeachDo = { + /** + * The variable to iterate over + */ + in: Common_PyExpression; + /** + * The steps to run for each iteration + */ + do: Array< + | Tasks_ToolCallStep + | Tasks_YieldStep + | Tasks_PromptStep + | Tasks_ErrorWorkflowStep + | Tasks_SleepStep + | Tasks_ReturnStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep + | Tasks_WaitForInputStep + >; +}; diff --git a/sdks/ts/src/api/models/Tasks_ForeachStep.ts b/sdks/ts/src/api/models/Tasks_ForeachStep.ts new file mode 100644 index 000000000..31989f782 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_ForeachStep.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +import type { Tasks_ForeachDo } from "./Tasks_ForeachDo"; +export type Tasks_ForeachStep = Tasks_BaseWorkflowStep & { + kind_: "foreach"; + /** + * The steps to run for each iteration + */ + foreach: Tasks_ForeachDo; +}; diff --git a/sdks/ts/src/api/models/Tasks_GetStep.ts b/sdks/ts/src/api/models/Tasks_GetStep.ts new file mode 100644 index 000000000..a8d20ecbd --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_GetStep.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +export type Tasks_GetStep = Tasks_BaseWorkflowStep & { + kind_: "get"; + /** + * The key to get + */ + get: string; +}; diff --git a/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts b/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts index d07ce4d07..dc2212143 100644 --- a/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts +++ b/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts @@ -4,8 +4,15 @@ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; +import type { Tasks_GetStep } from "./Tasks_GetStep"; +import type { Tasks_LogStep } from "./Tasks_LogStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; +import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; +import type { Tasks_SearchStep } from "./Tasks_SearchStep"; +import type { Tasks_SetStep } from "./Tasks_SetStep"; +import type { Tasks_SleepStep } from "./Tasks_SleepStep"; import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; import type { Tasks_YieldStep } from "./Tasks_YieldStep"; @@ -23,6 +30,13 @@ export type Tasks_IfElseWorkflowStep = Tasks_BaseWorkflowStep & { | Tasks_YieldStep | Tasks_PromptStep | Tasks_ErrorWorkflowStep + | Tasks_SleepStep + | Tasks_ReturnStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep | Tasks_WaitForInputStep; /** * The steps to run if the condition is false @@ -32,5 +46,12 @@ export type Tasks_IfElseWorkflowStep = Tasks_BaseWorkflowStep & { | Tasks_YieldStep | Tasks_PromptStep | Tasks_ErrorWorkflowStep + | Tasks_SleepStep + | Tasks_ReturnStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep | Tasks_WaitForInputStep; }; diff --git a/sdks/ts/src/api/models/Tasks_LogStep.ts b/sdks/ts/src/api/models/Tasks_LogStep.ts new file mode 100644 index 000000000..483628b36 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_LogStep.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +export type Tasks_LogStep = Tasks_BaseWorkflowStep & { + kind_: "log"; + /** + * The value to log + */ + log: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapOver.ts b/sdks/ts/src/api/models/Tasks_MapOver.ts new file mode 100644 index 000000000..d293474c3 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_MapOver.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +export type Tasks_MapOver = { + /** + * The variable to iterate over + */ + over: Common_PyExpression; + /** + * The subworkflow to run for each iteration + */ + workflow: string; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapReduceStep.ts b/sdks/ts/src/api/models/Tasks_MapReduceStep.ts new file mode 100644 index 000000000..be542f460 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_MapReduceStep.ts @@ -0,0 +1,18 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +import type { Tasks_MapOver } from "./Tasks_MapOver"; +export type Tasks_MapReduceStep = Tasks_BaseWorkflowStep & { + kind_: "map_reduce"; + /** + * The steps to run for each iteration + */ + map: Tasks_MapOver; + /** + * The expression to reduce the results (`_` is a list of outputs) + */ + reduce: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_ParallelStep.ts b/sdks/ts/src/api/models/Tasks_ParallelStep.ts new file mode 100644 index 000000000..a094a3eca --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_ParallelStep.ts @@ -0,0 +1,37 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; +import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; +import type { Tasks_GetStep } from "./Tasks_GetStep"; +import type { Tasks_LogStep } from "./Tasks_LogStep"; +import type { Tasks_PromptStep } from "./Tasks_PromptStep"; +import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; +import type { Tasks_SearchStep } from "./Tasks_SearchStep"; +import type { Tasks_SetStep } from "./Tasks_SetStep"; +import type { Tasks_SleepStep } from "./Tasks_SleepStep"; +import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; +import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; +import type { Tasks_YieldStep } from "./Tasks_YieldStep"; +export type Tasks_ParallelStep = Tasks_BaseWorkflowStep & { + kind_: "parallel"; + /** + * The steps to run in parallel. Max concurrency will depend on the platform + */ + parallel: Array< + | Tasks_ToolCallStep + | Tasks_YieldStep + | Tasks_PromptStep + | Tasks_ErrorWorkflowStep + | Tasks_SleepStep + | Tasks_ReturnStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep + | Tasks_WaitForInputStep + >; +}; diff --git a/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts b/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts index c5493e70c..0b5b917e1 100644 --- a/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts +++ b/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts @@ -2,10 +2,21 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; +import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; +import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; +import type { Tasks_LogStep } from "./Tasks_LogStep"; +import type { Tasks_MapReduceStep } from "./Tasks_MapReduceStep"; +import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; +import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; +import type { Tasks_SearchStep } from "./Tasks_SearchStep"; +import type { Tasks_SetStep } from "./Tasks_SetStep"; +import type { Tasks_SleepStep } from "./Tasks_SleepStep"; +import type { Tasks_SwitchStep } from "./Tasks_SwitchStep"; import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; import type { Tasks_YieldStep } from "./Tasks_YieldStep"; @@ -20,7 +31,18 @@ export type Tasks_PatchTaskRequest = Record< | Tasks_YieldStep | Tasks_PromptStep | Tasks_ErrorWorkflowStep + | Tasks_SleepStep + | Tasks_ReturnStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep | Tasks_WaitForInputStep | Tasks_IfElseWorkflowStep + | Tasks_SwitchStep + | Tasks_ForeachStep + | Tasks_ParallelStep + | Tasks_MapReduceStep > >; diff --git a/sdks/ts/src/api/models/Tasks_ReturnStep.ts b/sdks/ts/src/api/models/Tasks_ReturnStep.ts new file mode 100644 index 000000000..97488f129 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_ReturnStep.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +export type Tasks_ReturnStep = Tasks_BaseWorkflowStep & { + kind_: "return"; + /** + * The value to return + */ + return: Record; +}; diff --git a/sdks/ts/src/api/models/Tasks_SearchStep.ts b/sdks/ts/src/api/models/Tasks_SearchStep.ts new file mode 100644 index 000000000..eabe9b707 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_SearchStep.ts @@ -0,0 +1,18 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Docs_HybridDocSearchRequest } from "./Docs_HybridDocSearchRequest"; +import type { Docs_TextOnlyDocSearchRequest } from "./Docs_TextOnlyDocSearchRequest"; +import type { Docs_VectorDocSearchRequest } from "./Docs_VectorDocSearchRequest"; +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +export type Tasks_SearchStep = Tasks_BaseWorkflowStep & { + kind_: "search"; + /** + * The search query + */ + search: + | Docs_VectorDocSearchRequest + | Docs_TextOnlyDocSearchRequest + | Docs_HybridDocSearchRequest; +}; diff --git a/sdks/ts/src/api/models/Tasks_SetKey.ts b/sdks/ts/src/api/models/Tasks_SetKey.ts new file mode 100644 index 000000000..a7cef005d --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_SetKey.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +export type Tasks_SetKey = { + /** + * The key to set + */ + key: string; + /** + * The value to set + */ + value: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_SetStep.ts b/sdks/ts/src/api/models/Tasks_SetStep.ts new file mode 100644 index 000000000..a4425ecc3 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_SetStep.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +import type { Tasks_SetKey } from "./Tasks_SetKey"; +export type Tasks_SetStep = Tasks_BaseWorkflowStep & { + kind_: "set"; + /** + * The value to set + */ + set: Tasks_SetKey | Array; +}; diff --git a/sdks/ts/src/api/models/Tasks_SleepFor.ts b/sdks/ts/src/api/models/Tasks_SleepFor.ts new file mode 100644 index 000000000..85b1ef55f --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_SleepFor.ts @@ -0,0 +1,22 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type Tasks_SleepFor = { + /** + * The number of seconds to sleep for + */ + seconds: number; + /** + * The number of minutes to sleep for + */ + minutes: number; + /** + * The number of hours to sleep for + */ + hours: number; + /** + * The number of days to sleep for + */ + days: number; +}; diff --git a/sdks/ts/src/api/models/Tasks_SleepStep.ts b/sdks/ts/src/api/models/Tasks_SleepStep.ts new file mode 100644 index 000000000..c2a14b9b5 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_SleepStep.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +import type { Tasks_SleepFor } from "./Tasks_SleepFor"; +export type Tasks_SleepStep = Tasks_BaseWorkflowStep & { + kind_: "sleep"; + /** + * The duration to sleep for + */ + sleep: Tasks_SleepFor; +}; diff --git a/sdks/ts/src/api/models/Tasks_SwitchStep.ts b/sdks/ts/src/api/models/Tasks_SwitchStep.ts new file mode 100644 index 000000000..27d68c6be --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_SwitchStep.ts @@ -0,0 +1,13 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; +import type { Tasks_CaseThen } from "./Tasks_CaseThen"; +export type Tasks_SwitchStep = Tasks_BaseWorkflowStep & { + kind_: "switch"; + /** + * The cond tree + */ + switch: Array; +}; diff --git a/sdks/ts/src/api/models/Tasks_Task.ts b/sdks/ts/src/api/models/Tasks_Task.ts index 9b273fc4a..fe307a4e8 100644 --- a/sdks/ts/src/api/models/Tasks_Task.ts +++ b/sdks/ts/src/api/models/Tasks_Task.ts @@ -2,10 +2,21 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; +import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; +import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; +import type { Tasks_LogStep } from "./Tasks_LogStep"; +import type { Tasks_MapReduceStep } from "./Tasks_MapReduceStep"; +import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; +import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; +import type { Tasks_SearchStep } from "./Tasks_SearchStep"; +import type { Tasks_SetStep } from "./Tasks_SetStep"; +import type { Tasks_SleepStep } from "./Tasks_SleepStep"; +import type { Tasks_SwitchStep } from "./Tasks_SwitchStep"; import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; import type { Tasks_YieldStep } from "./Tasks_YieldStep"; @@ -20,7 +31,18 @@ export type Tasks_Task = Record< | Tasks_YieldStep | Tasks_PromptStep | Tasks_ErrorWorkflowStep + | Tasks_SleepStep + | Tasks_ReturnStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep | Tasks_WaitForInputStep | Tasks_IfElseWorkflowStep + | Tasks_SwitchStep + | Tasks_ForeachStep + | Tasks_ParallelStep + | Tasks_MapReduceStep > >; diff --git a/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts b/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts index e823f29a4..a6346d173 100644 --- a/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts +++ b/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts @@ -2,10 +2,21 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; +import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; +import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; +import type { Tasks_LogStep } from "./Tasks_LogStep"; +import type { Tasks_MapReduceStep } from "./Tasks_MapReduceStep"; +import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; +import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; +import type { Tasks_SearchStep } from "./Tasks_SearchStep"; +import type { Tasks_SetStep } from "./Tasks_SetStep"; +import type { Tasks_SleepStep } from "./Tasks_SleepStep"; +import type { Tasks_SwitchStep } from "./Tasks_SwitchStep"; import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; import type { Tasks_YieldStep } from "./Tasks_YieldStep"; @@ -20,7 +31,18 @@ export type Tasks_UpdateTaskRequest = Record< | Tasks_YieldStep | Tasks_PromptStep | Tasks_ErrorWorkflowStep + | Tasks_SleepStep + | Tasks_ReturnStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep | Tasks_WaitForInputStep | Tasks_IfElseWorkflowStep + | Tasks_SwitchStep + | Tasks_ForeachStep + | Tasks_ParallelStep + | Tasks_MapReduceStep > >; diff --git a/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts b/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts new file mode 100644 index 000000000..0e8eceebc --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts @@ -0,0 +1,61 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_CaseThen = { + properties: { + case: { + type: "all-of", + description: `The condition to evaluate`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + then: { + type: "any-of", + description: `The steps to run if the condition is true`, + contains: [ + { + type: "Tasks_ToolCallStep", + }, + { + type: "Tasks_YieldStep", + }, + { + type: "Tasks_PromptStep", + }, + { + type: "Tasks_ErrorWorkflowStep", + }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, + { + type: "Tasks_WaitForInputStep", + }, + ], + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts b/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts index 7c15a448d..1116b0b5f 100644 --- a/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts +++ b/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts @@ -24,12 +24,45 @@ export const $Tasks_CreateTaskRequest = { { type: "Tasks_ErrorWorkflowStep", }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, { type: "Tasks_WaitForInputStep", }, { type: "Tasks_IfElseWorkflowStep", }, + { + type: "Tasks_SwitchStep", + }, + { + type: "Tasks_ForeachStep", + }, + { + type: "Tasks_ParallelStep", + }, + { + type: "Tasks_MapReduceStep", + }, ], }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts b/sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts new file mode 100644 index 000000000..11cae473c --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts @@ -0,0 +1,30 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_EmbedStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + embed: { + type: "all-of", + description: `The text to embed`, + contains: [ + { + type: "Docs_EmbedQueryRequest", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts b/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts new file mode 100644 index 000000000..ef7102320 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts @@ -0,0 +1,63 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_ForeachDo = { + properties: { + in: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + do: { + type: "array", + contains: { + type: "any-of", + contains: [ + { + type: "Tasks_ToolCallStep", + }, + { + type: "Tasks_YieldStep", + }, + { + type: "Tasks_PromptStep", + }, + { + type: "Tasks_ErrorWorkflowStep", + }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, + { + type: "Tasks_WaitForInputStep", + }, + ], + }, + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts b/sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts new file mode 100644 index 000000000..3deb761b5 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts @@ -0,0 +1,30 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_ForeachStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + foreach: { + type: "all-of", + description: `The steps to run for each iteration`, + contains: [ + { + type: "Tasks_ForeachDo", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_GetStep.ts b/sdks/ts/src/api/schemas/$Tasks_GetStep.ts new file mode 100644 index 000000000..19da398f8 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_GetStep.ts @@ -0,0 +1,25 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_GetStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + get: { + type: "string", + description: `The key to get`, + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts b/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts index ae1c3bbaf..a1def86dc 100644 --- a/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts @@ -40,6 +40,27 @@ export const $Tasks_IfElseWorkflowStep = { { type: "Tasks_ErrorWorkflowStep", }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, { type: "Tasks_WaitForInputStep", }, @@ -62,6 +83,27 @@ export const $Tasks_IfElseWorkflowStep = { { type: "Tasks_ErrorWorkflowStep", }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, { type: "Tasks_WaitForInputStep", }, diff --git a/sdks/ts/src/api/schemas/$Tasks_LogStep.ts b/sdks/ts/src/api/schemas/$Tasks_LogStep.ts new file mode 100644 index 000000000..3565c937c --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_LogStep.ts @@ -0,0 +1,30 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_LogStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + log: { + type: "all-of", + description: `The value to log`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOver.ts b/sdks/ts/src/api/schemas/$Tasks_MapOver.ts new file mode 100644 index 000000000..b12b438c1 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapOver.ts @@ -0,0 +1,23 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapOver = { + properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + workflow: { + type: "string", + description: `The subworkflow to run for each iteration`, + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts b/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts new file mode 100644 index 000000000..a88a4b6d7 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts @@ -0,0 +1,40 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapReduceStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + map: { + type: "all-of", + description: `The steps to run for each iteration`, + contains: [ + { + type: "Tasks_MapOver", + }, + ], + isRequired: true, + }, + reduce: { + type: "all-of", + description: `The expression to reduce the results (\`_\` is a list of outputs)`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts b/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts new file mode 100644 index 000000000..cf1f97820 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts @@ -0,0 +1,65 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_ParallelStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + parallel: { + type: "array", + contains: { + type: "any-of", + contains: [ + { + type: "Tasks_ToolCallStep", + }, + { + type: "Tasks_YieldStep", + }, + { + type: "Tasks_PromptStep", + }, + { + type: "Tasks_ErrorWorkflowStep", + }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, + { + type: "Tasks_WaitForInputStep", + }, + ], + }, + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts b/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts index 50c6201fe..561808f01 100644 --- a/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts +++ b/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts @@ -24,12 +24,45 @@ export const $Tasks_PatchTaskRequest = { { type: "Tasks_ErrorWorkflowStep", }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, { type: "Tasks_WaitForInputStep", }, { type: "Tasks_IfElseWorkflowStep", }, + { + type: "Tasks_SwitchStep", + }, + { + type: "Tasks_ForeachStep", + }, + { + type: "Tasks_ParallelStep", + }, + { + type: "Tasks_MapReduceStep", + }, ], }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts b/sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts new file mode 100644 index 000000000..04f7d4ab1 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts @@ -0,0 +1,27 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_ReturnStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + return: { + type: "dictionary", + contains: { + type: "Common_PyExpression", + }, + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SearchStep.ts b/sdks/ts/src/api/schemas/$Tasks_SearchStep.ts new file mode 100644 index 000000000..7c2ae3cb6 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_SearchStep.ts @@ -0,0 +1,36 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_SearchStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + search: { + type: "any-of", + description: `The search query`, + contains: [ + { + type: "Docs_VectorDocSearchRequest", + }, + { + type: "Docs_TextOnlyDocSearchRequest", + }, + { + type: "Docs_HybridDocSearchRequest", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SetKey.ts b/sdks/ts/src/api/schemas/$Tasks_SetKey.ts new file mode 100644 index 000000000..c93f367d1 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_SetKey.ts @@ -0,0 +1,23 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_SetKey = { + properties: { + key: { + type: "string", + description: `The key to set`, + isRequired: true, + }, + value: { + type: "all-of", + description: `The value to set`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SetStep.ts b/sdks/ts/src/api/schemas/$Tasks_SetStep.ts new file mode 100644 index 000000000..7c768fc10 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_SetStep.ts @@ -0,0 +1,36 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_SetStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + set: { + type: "any-of", + description: `The value to set`, + contains: [ + { + type: "Tasks_SetKey", + }, + { + type: "array", + contains: { + type: "Tasks_SetKey", + }, + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SleepFor.ts b/sdks/ts/src/api/schemas/$Tasks_SleepFor.ts new file mode 100644 index 000000000..a03d5591c --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_SleepFor.ts @@ -0,0 +1,32 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_SleepFor = { + properties: { + seconds: { + type: "number", + description: `The number of seconds to sleep for`, + isRequired: true, + format: "uint16", + }, + minutes: { + type: "number", + description: `The number of minutes to sleep for`, + isRequired: true, + format: "uint16", + }, + hours: { + type: "number", + description: `The number of hours to sleep for`, + isRequired: true, + format: "uint16", + }, + days: { + type: "number", + description: `The number of days to sleep for`, + isRequired: true, + format: "uint16", + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts b/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts new file mode 100644 index 000000000..79d23cbcf --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts @@ -0,0 +1,30 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_SleepStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + sleep: { + type: "all-of", + description: `The duration to sleep for`, + contains: [ + { + type: "Tasks_SleepFor", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts b/sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts new file mode 100644 index 000000000..c8958af82 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts @@ -0,0 +1,27 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_SwitchStep = { + type: "all-of", + contains: [ + { + type: "Tasks_BaseWorkflowStep", + }, + { + properties: { + kind_: { + type: "Enum", + isRequired: true, + }, + switch: { + type: "array", + contains: { + type: "Tasks_CaseThen", + }, + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_Task.ts b/sdks/ts/src/api/schemas/$Tasks_Task.ts index d0431107d..4343739a9 100644 --- a/sdks/ts/src/api/schemas/$Tasks_Task.ts +++ b/sdks/ts/src/api/schemas/$Tasks_Task.ts @@ -24,12 +24,45 @@ export const $Tasks_Task = { { type: "Tasks_ErrorWorkflowStep", }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, { type: "Tasks_WaitForInputStep", }, { type: "Tasks_IfElseWorkflowStep", }, + { + type: "Tasks_SwitchStep", + }, + { + type: "Tasks_ForeachStep", + }, + { + type: "Tasks_ParallelStep", + }, + { + type: "Tasks_MapReduceStep", + }, ], }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts b/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts index 5127d8f62..782e0bb56 100644 --- a/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts +++ b/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts @@ -24,12 +24,45 @@ export const $Tasks_UpdateTaskRequest = { { type: "Tasks_ErrorWorkflowStep", }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, { type: "Tasks_WaitForInputStep", }, { type: "Tasks_IfElseWorkflowStep", }, + { + type: "Tasks_SwitchStep", + }, + { + type: "Tasks_ForeachStep", + }, + { + type: "Tasks_ParallelStep", + }, + { + type: "Tasks_MapReduceStep", + }, ], }, }, diff --git a/typespec/tasks/models.tsp b/typespec/tasks/models.tsp index 7cde91759..749d47a11 100644 --- a/typespec/tasks/models.tsp +++ b/typespec/tasks/models.tsp @@ -1,17 +1,13 @@ import "@typespec/http"; -import "../agents"; import "../common"; -import "../chat"; -import "../entries"; import "../tools"; +import "./steps.tsp"; + using TypeSpec.Http; -using Agents; -using Chat; using Common; -using Entries; using Tools; namespace Tasks; @@ -20,105 +16,6 @@ namespace Tasks; // TASK MODELS // -alias WorkflowStepKind = - | /** A step that runs a tool */ - "tool_call" - | /** A step that runs a subworkflow */ - "yield" - | /** A step that runs a prompt */ - "prompt" - | /** A step that evaluates an expression */ - "evaluate" - | /** A step that runs a conditional */ - "if_else" - | /** A step that signals that it needs more input before resuming */ - "wait_for_input" - | /** Throw an error */ - "error"; - -model BaseWorkflowStep { - /** The kind of step */ - kind_: WorkflowStepKind; -} - -model ToolCallStep extends BaseWorkflowStep { - kind_: "tool_call" = "tool_call"; - - /** The tool to run */ - tool: toolRef; - - /** The input parameters for the tool */ - arguments: Record; -} - -/** An object where values are strings in the Common Expression Language that get evaluated before being passed downstream */ -alias ExpressionObject = Record; - -model YieldStep extends BaseWorkflowStep { - kind_: "yield" = "yield"; - - /** The subworkflow to run */ - workflow: string; - - /** The input parameters for the subworkflow */ - arguments: ExpressionObject; -} - -model PromptStep extends BaseWorkflowStep { - kind_: "prompt" = "prompt"; - - /** The prompt to run */ - prompt: string | InputChatMLMessage[]; - - /** Settings for the prompt */ - settings: ChatSettings; -} - -model EvaluateStep extends BaseWorkflowStep { - kind_: "evaluate" = "evaluate"; - - /** The expression to evaluate */ - evaluate: ExpressionObject; -} - -model ErrorWorkflowStep extends BaseWorkflowStep { - kind_: "error" = "error"; - - /** The error message */ - error: string; -} - -model WaitForInputStep extends BaseWorkflowStep { - kind_: "wait_for_input" = "wait_for_input"; - - /** Any additional info or data */ - info: string | Record; -} - -alias NonConditionalWorkflowStep = - | EvaluateStep - | ToolCallStep - | YieldStep - | PromptStep - | ErrorWorkflowStep - | WaitForInputStep; - -model IfElseWorkflowStep extends BaseWorkflowStep { - kind_: "if_else" = "if_else"; - - /** The condition to evaluate */ - `if`: PyExpression; - - /** The steps to run if the condition is true */ - then: NonConditionalWorkflowStep; - - /** The steps to run if the condition is false */ - `else`: NonConditionalWorkflowStep; -} - -alias WorkflowStep = NonConditionalWorkflowStep | IfElseWorkflowStep; -alias CreateWorkflowStep = WorkflowStep; - model Workflow { @key name: validPythonIdentifier; @@ -126,7 +23,7 @@ model Workflow { steps: WorkflowStep[]; } -model TaskTool extends CreateToolRequest{ +model TaskTool extends CreateToolRequest { /** Read-only: Whether the tool was inherited or not. Only applies within tasks. */ @visibility("read") inherited?: boolean = false; diff --git a/typespec/tasks/step_kind.tsp b/typespec/tasks/step_kind.tsp new file mode 100644 index 000000000..569c5737a --- /dev/null +++ b/typespec/tasks/step_kind.tsp @@ -0,0 +1,72 @@ +namespace Tasks; + +// +// STEP KINDS +// + +alias WorkflowStepKind = + //////////////////// + /// Common steps /// + //////////////////// + + | /** A step that runs a tool */ + "tool_call" + | /** A step that runs a prompt */ + "prompt" + | /** A step that evaluates an expression */ + "evaluate" + | /** A step that signals that it needs more input before resuming */ + "wait_for_input" + | /** log step */ + "log" + + //////////////////////// + /// Doc search steps /// + //////////////////////// + + | /** A step that can embed text */ + "embed" + | /** A step that can search for documents (in the agents doc store only) */ + "search" + + /////////////////////// + /// Key-value steps /// + /////////////////////// + + | /** set step */ + "set" + | /** get step */ + "get" + + /////////////////////// + /// Iteration steps /// + /////////////////////// + + | /** foreach step */ + "foreach" + | /** map_reduce step */ + "map_reduce" + | /** parallel step */ + "parallel" + + ///////////////////////// + /// Conditional steps /// + ///////////////////////// + + | /** switch step */ + "switch" + | /** A step that runs a conditional */ + "if_else" + + ////////////////////////// + /// Other control flow /// + ////////////////////////// + + | /** sleep step */ + "sleep" + | /** return step */ + "return" + | /** A step that runs a subworkflow */ + "yield" + | /** Throw an error */ + "error"; \ No newline at end of file diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp new file mode 100644 index 000000000..b0db2a1e1 --- /dev/null +++ b/typespec/tasks/steps.tsp @@ -0,0 +1,269 @@ +import "@typespec/http"; + +import "../chat"; +import "../common"; +import "../docs"; +import "../entries"; + +import "./step_kind.tsp"; + +using TypeSpec.Http; + +using Chat; +using Common; +using Docs; +using Entries; + +namespace Tasks; + +// +// STEP DEFINITIONS +// + +/** An object where values are strings in the Common Expression Language that get evaluated before being passed downstream */ +alias ExpressionObject = Record; + +model BaseWorkflowStep { + /** The kind of step */ + kind_: WorkflowStepKind; +} + +alias NonConditionalWorkflowStep = + | EvaluateStep + | ToolCallStep + | YieldStep + | PromptStep + | ErrorWorkflowStep + | SleepStep + | ReturnStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep + | WaitForInputStep; + +alias ConditionalStep = IfElseWorkflowStep | SwitchStep; +alias IterationStep = ForeachStep | ParallelStep | MapReduceStep; +alias WorkflowStep = NonConditionalWorkflowStep | ConditionalStep | IterationStep; + +alias CreateWorkflowStep = WorkflowStep; + +//////////////////// +/// Common steps /// +//////////////////// + +model ToolCallStep extends BaseWorkflowStep { + kind_: "tool_call" = "tool_call"; + + /** The tool to run */ + tool: toolRef; + + /** The input parameters for the tool */ + arguments: Record; +} + +model PromptStep extends BaseWorkflowStep { + kind_: "prompt" = "prompt"; + + /** The prompt to run */ + prompt: string | InputChatMLMessage[]; + + /** Settings for the prompt */ + settings: ChatSettings; +} + +model EvaluateStep extends BaseWorkflowStep { + kind_: "evaluate" = "evaluate"; + + /** The expression to evaluate */ + evaluate: ExpressionObject; +} + +model WaitForInputStep extends BaseWorkflowStep { + kind_: "wait_for_input" = "wait_for_input"; + + /** Any additional info or data */ + info: string | Record; +} + +model LogStep extends BaseWorkflowStep { + kind_: "log" = "log"; + + /** The value to log */ + log: PyExpression; +} + +//////////////////////// +/// Doc search steps /// +//////////////////////// + +model EmbedStep extends BaseWorkflowStep { + kind_: "embed" = "embed"; + + /** The text to embed */ + embed: EmbedQueryRequest; +} + +model SearchStep extends BaseWorkflowStep { + kind_: "search" = "search"; + + /** The search query */ + search: DocSearchRequest; +} + +/////////////////////// +/// Key-value steps /// +/////////////////////// + +model GetStep extends BaseWorkflowStep { + kind_: "get" = "get"; + + /** The key to get */ + get: string; +} + +model SetKey { + /** The key to set */ + key: string; + + /** The value to set */ + value: PyExpression; +} + +model SetStep extends BaseWorkflowStep { + kind_: "set" = "set"; + + /** The value to set */ + set: SetKey | SetKey[]; +} + +/////////////////////// +/// Iteration steps /// +/////////////////////// + + +model ParallelStep extends BaseWorkflowStep { + kind_: "parallel" = "parallel"; + + /** The steps to run in parallel. Max concurrency will depend on the platform */ + parallel: NonConditionalWorkflowStep[]; +} + +model ForeachDo { + /** The variable to iterate over */ + in: PyExpression; + + /** The steps to run for each iteration */ + do: NonConditionalWorkflowStep[]; +} + +model ForeachStep extends BaseWorkflowStep { + kind_: "foreach" = "foreach"; + + /** The steps to run for each iteration */ + foreach: ForeachDo; +} + +model MapOver { + /** The variable to iterate over */ + over: PyExpression; + + /** The subworkflow to run for each iteration */ + workflow: string; +} + +model MapReduceStep extends BaseWorkflowStep { + kind_: "map_reduce" = "map_reduce"; + + /** The steps to run for each iteration */ + map: MapOver; + + /** The expression to reduce the results (`_` is a list of outputs) */ + reduce: PyExpression; +} + +///////////////////////// +/// Conditional steps /// +///////////////////////// + +model IfElseWorkflowStep extends BaseWorkflowStep { + kind_: "if_else" = "if_else"; + + /** The condition to evaluate */ + `if`: PyExpression; + + /** The steps to run if the condition is true */ + then: NonConditionalWorkflowStep; + + /** The steps to run if the condition is false */ + `else`: NonConditionalWorkflowStep; +} + +model CaseThen { + /** The condition to evaluate */ + case: PyExpression; + + /** The steps to run if the condition is true */ + then: NonConditionalWorkflowStep; +} + +model SwitchStep extends BaseWorkflowStep { + kind_: "switch" = "switch"; + + /** The cond tree */ + switch: CaseThen[]; +} + +////////////////////////// +/// Other control flow /// +////////////////////////// + +model YieldStep extends BaseWorkflowStep { + kind_: "yield" = "yield"; + + /** The subworkflow to run */ + workflow: string; + + /** The input parameters for the subworkflow */ + arguments: ExpressionObject; +} + +model ErrorWorkflowStep extends BaseWorkflowStep { + kind_: "error" = "error"; + + /** The error message */ + error: string; +} + +model SleepFor { + /** The number of seconds to sleep for */ + @minValue(0) + seconds: uint16 = 0; + + /** The number of minutes to sleep for */ + @minValue(0) + minutes: uint16 = 0; + + /** The number of hours to sleep for */ + @minValue(0) + hours: uint16 = 0; + + /** The number of days to sleep for */ + @minValue(0) + days: uint16 = 0; +} + +model SleepStep extends BaseWorkflowStep { + kind_: "sleep" = "sleep"; + + /** The duration to sleep for */ + sleep: SleepFor; +} + +model ReturnStep extends BaseWorkflowStep { + kind_: "return" = "return"; + + /** The value to return */ + `return`: ExpressionObject; +} From ae0363419d727391593d16a4ca27a09483599478 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 18:24:56 -0400 Subject: [PATCH 073/110] feat(agents-api): Implement sleep step Signed-off-by: Diwank Tomer --- .../activities/task_steps/__init__.py | 1 + .../activities/task_steps/yield_step.py | 3 +- .../agents_api/autogen/openapi_model.py | 12 ++ agents-api/agents_api/clients/temporal.py | 2 + .../agents_api/workflows/task_execution.py | 108 +++++++++++------- agents-api/tests/test_execution_workflow.py | 50 ++++++++ agents-api/tests/utils.py | 8 +- 7 files changed, 137 insertions(+), 47 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 828fe5e31..a7dcd5a2b 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -4,6 +4,7 @@ from .if_else_step import if_else_step from .prompt_step import prompt_step from .raise_complete_async import raise_complete_async +from .return_step import return_step from .tool_call_step import tool_call_step from .transition_step import transition_step from .yield_step import yield_step diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index dfdb6fa1d..92d4c46b8 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -5,14 +5,13 @@ from agents_api.autogen.Executions import TransitionTarget -from ...autogen.openapi_model import YieldStep from ...common.protocol.tasks import StepContext, StepOutcome from ...env import testing from .utils import simple_eval_dict @beartype -async def yield_step(context: StepContext[YieldStep]) -> StepOutcome: +async def yield_step(context: StepContext) -> StepOutcome: all_workflows = context.execution_input.task.workflows workflow = context.current_step.workflow diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index dc8d46b70..0b0b0e2b5 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -149,6 +149,18 @@ def from_model_input( | ToolCallStep | ErrorWorkflowStep | IfElseWorkflowStep + | ReturnStep + | SleepStep + | WaitForInputStep + | LogStep + | EmbedStep + | SearchStep + | SetStep + | GetStep + | ForeachStep + | MapReduceStep + | ParallelStep + | SwitchStep ) diff --git a/agents-api/agents_api/clients/temporal.py b/agents-api/agents_api/clients/temporal.py index 61f53c71c..f271509b0 100644 --- a/agents-api/agents_api/clients/temporal.py +++ b/agents-api/agents_api/clients/temporal.py @@ -1,3 +1,4 @@ +from datetime import timedelta from uuid import UUID from temporalio.client import Client, TLSConfig @@ -52,4 +53,5 @@ async def run_task_execution_workflow( args=[execution_input, start, previous_inputs], task_queue=temporal_task_queue, id=str(job_id), + run_timeout=timedelta(days=31), ) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 7e8498828..415cb471d 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -1,15 +1,18 @@ #!/usr/bin/env python3 +import asyncio from datetime import timedelta from temporalio import workflow +from temporalio.exceptions import ApplicationError with workflow.unsafe.imports_passed_through(): from ..activities.task_steps import ( evaluate_step, if_else_step, prompt_step, + return_step, tool_call_step, transition_step, yield_step, @@ -20,6 +23,9 @@ EvaluateStep, IfElseWorkflowStep, PromptStep, + ReturnStep, + SleepFor, + SleepStep, ToolCallStep, TransitionTarget, TransitionType, @@ -42,6 +48,7 @@ ToolCallStep: tool_call_step, IfElseWorkflowStep: if_else_step, YieldStep: yield_step, + ReturnStep: return_step, } @@ -63,6 +70,7 @@ async def run( ) step_type = type(context.current_step) + outcome = None # 1. First execute the current step's activity if applicable if activity := STEP_TO_ACTIVITY.get(step_type): @@ -73,8 +81,8 @@ async def run( schedule_to_close_timeout=timedelta(seconds=3 if testing else 600), ) - # 2. Then, based on the outcome and step type, decide what to do next - # (By default, exit if last otherwise transition 'step' to the next step) + # 2a. Then, based on the outcome and step type, decide what to do next + # (By default, exit if last otherwise transition 'step' to the next step) final_output = None transition_type: TransitionType next_target: TransitionTarget | None @@ -90,67 +98,81 @@ async def run( workflow=context.cursor.workflow, step=context.cursor.step + 1 ) + # 2b. Prep a transition request + async def transition(): + # NOTE: The variables are closured from the outer scope + transition_request = CreateTransitionRequest( + type=transition_type, + current=context.cursor, + next=next_target, + output=final_output, + metadata=metadata, + ) + + return await workflow.execute_activity( + transition_step, + args=[context, transition_request], + schedule_to_close_timeout=timedelta(seconds=600), + ) + # 3. Orchestrate the step match context.current_step, outcome: - case EvaluateStep(), StepOutcome(output=output): + case ReturnStep(), StepOutcome(output=output): final_output = output - transition_request = CreateTransitionRequest( - type=transition_type, - current=context.cursor, - next=next_target, - output=final_output, - metadata=metadata, + transition_type = "finish" + await transition() + + case SleepStep( + sleep=SleepFor( + seconds=seconds, + minutes=minutes, + hours=hours, + days=days, ) + ), _: + seconds = seconds + minutes * 60 + hours * 60 * 60 + days * 24 * 60 * 60 + assert seconds > 0, "Sleep duration must be greater than 0" - await workflow.execute_activity( - transition_step, - args=[context, transition_request], - schedule_to_close_timeout=timedelta(seconds=600), + final_output = await asyncio.sleep( + seconds, result=context.current_input ) + await transition() + + case EvaluateStep(), StepOutcome(output=output): + final_output = output + await transition() + case ErrorWorkflowStep(error=error), _: final_output = dict(error=error) transition_type = "error" - transition_request = CreateTransitionRequest( - type=transition_type, - current=context.cursor, - next=None, - output=final_output, - metadata=metadata, - ) - - await workflow.execute_activity( - transition_step, - args=[context, transition_request], - schedule_to_close_timeout=timedelta(seconds=600), - ) + await transition() - raise Exception(f"Error raised by ErrorWorkflowStep: {error}") + raise ApplicationError(f"Error raised by ErrorWorkflowStep: {error}") case YieldStep(), StepOutcome( - output=output, transition_to=(transition_type, next) + output=output, transition_to=(yield_transition_type, yield_next_target) ): + # Save the original transition state + original_next_target = next_target + original_transition_type = transition_type + final_output = output - transition_request = CreateTransitionRequest( - type=transition_type, - current=context.cursor, - next=next, - output=final_output, - metadata=metadata, - ) + transition_type = yield_transition_type + next_target = yield_next_target - await workflow.execute_activity( - transition_step, - args=[context, transition_request], - schedule_to_close_timeout=timedelta(seconds=600), - ) + await transition() yield_outcome: StepOutcome = await workflow.execute_child_workflow( TaskExecutionWorkflow.run, - args=[execution_input, next, [output]], + args=[execution_input, yield_next_target, [output]], ) - final_output = yield_outcome.output + # Restore the next target to the original value + next_target = original_next_target + transition_type = original_transition_type + + final_output = yield_outcome case _: raise NotImplementedError() @@ -161,6 +183,8 @@ async def run( return final_output # Otherwise, recurse to the next step + # TODO: Should use a continue_as_new workflow ONLY if the next step is a conditional or loop + # Otherwise, we should just call the next step as a child workflow workflow.continue_as_new( args=[execution_input, next_target, previous_inputs + [final_output]] ) diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 8506b7dc3..0f18f2431 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -180,3 +180,53 @@ async def _( result = await handle.result() assert result["hello"] == data.input["test"] + + +@test("workflow: create task execution") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "other_workflow": [ + # Testing that we can access the input + {"evaluate": {"hello": '_["test"]'}}, + {"sleep": {"days": 5}}, + ], + "main": [ + # Testing that we can access the input + { + "workflow": "other_workflow", + "arguments": {"test": '_["test"]'}, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == data.input["test"] diff --git a/agents-api/tests/utils.py b/agents-api/tests/utils.py index d1be73a49..cebe84a1c 100644 --- a/agents-api/tests/utils.py +++ b/agents-api/tests/utils.py @@ -20,9 +20,11 @@ async def patch_testing_temporal(): logger.setLevel(logging.ERROR) # Start a local Temporal environment - async with await WorkflowEnvironment.start_local() as env: - # Set the correct codec - env.client._config["data_converter"] = pydantic_data_converter + async with await WorkflowEnvironment.start_time_skipping( + data_converter=pydantic_data_converter + ) as env: + # # Set the correct codec + # env.client._config["data_converter"] = pydantic_data_converter # Create a worker with our workflows and start it worker = create_worker(client=env.client) From b600eafaebb89b01e5889e91362baa1612f6daac Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 18:28:29 -0400 Subject: [PATCH 074/110] feat(agents-api): Add return step Signed-off-by: Diwank Tomer --- .../activities/task_steps/return_step.py | 30 +++++++++++ agents-api/agents_api/worker/worker.py | 8 +-- agents-api/tests/test_execution_workflow.py | 51 +++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 agents-api/agents_api/activities/task_steps/return_step.py diff --git a/agents-api/agents_api/activities/task_steps/return_step.py b/agents-api/agents_api/activities/task_steps/return_step.py new file mode 100644 index 000000000..3dafee3e9 --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/return_step.py @@ -0,0 +1,30 @@ +from temporalio import activity + +from ...activities.task_steps.utils import simple_eval_dict +from ...autogen.openapi_model import ReturnStep +from ...common.protocol.tasks import ( + StepContext, + StepOutcome, +) +from ...env import testing + + +async def return_step( + context: StepContext, +) -> StepOutcome: + assert isinstance(context.current_step, ReturnStep) + + exprs: dict[str, str] = context.current_step.return_ + output = simple_eval_dict(exprs, values=context.model_dump()) + + result = StepOutcome(output=output) + return result + + +# Note: This is here just for clarity. We could have just imported return_step directly +# They do the same thing, so we dont need to mock the return_step function +mock_return_step = return_step + +return_step = activity.defn(name="return_step")( + return_step if not testing else mock_return_step +) diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index a5e5016d3..8303830e9 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -20,6 +20,7 @@ def create_worker(client: Client) -> Any: evaluate_step, if_else_step, prompt_step, + return_step, tool_call_step, transition_step, yield_step, @@ -37,12 +38,13 @@ def create_worker(client: Client) -> Any: from ..workflows.truncation import TruncationWorkflow task_activities = [ - prompt_step, evaluate_step, - yield_step, - tool_call_step, if_else_step, + prompt_step, + return_step, + tool_call_step, transition_step, + yield_step, ] # Initialize the worker with the specified task queue, workflows, and activities diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 0f18f2431..51bc85f9e 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -230,3 +230,54 @@ async def _( result = await handle.result() assert result["hello"] == data.input["test"] + + +@test("workflow: create task execution") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "other_workflow": [ + # Testing that we can access the input + {"evaluate": {"hello": '_["test"]'}}, + {"return": {"value": '_["hello"]'}}, + {"return": {"value": '"banana"'}}, + ], + "main": [ + # Testing that we can access the input + { + "workflow": "other_workflow", + "arguments": {"test": '_["test"]'}, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["value"] == data.input["test"] From bbc71173f0a784afdba23e9e94be0c56a478f171 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 19:29:37 -0400 Subject: [PATCH 075/110] feat(agents-api): Add log step Signed-off-by: Diwank Tomer --- .../activities/task_steps/__init__.py | 1 + .../activities/task_steps/log_step.py | 37 +++++++++++++ agents-api/agents_api/worker/worker.py | 2 + .../agents_api/workflows/task_execution.py | 53 +++++++++++-------- agents-api/tests/test_execution_workflow.py | 50 +++++++++++++++++ 5 files changed, 120 insertions(+), 23 deletions(-) create mode 100644 agents-api/agents_api/activities/task_steps/log_step.py diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index a7dcd5a2b..2c694113f 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -2,6 +2,7 @@ from .evaluate_step import evaluate_step from .if_else_step import if_else_step +from .log_step import log_step from .prompt_step import prompt_step from .raise_complete_async import raise_complete_async from .return_step import return_step diff --git a/agents-api/agents_api/activities/task_steps/log_step.py b/agents-api/agents_api/activities/task_steps/log_step.py new file mode 100644 index 000000000..bfc695993 --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/log_step.py @@ -0,0 +1,37 @@ +import logging + +from simpleeval import simple_eval +from temporalio import activity + +from ...autogen.openapi_model import LogStep +from ...common.protocol.tasks import ( + StepContext, + StepOutcome, +) +from ...env import testing + + +async def log_step( + context: StepContext, +) -> StepOutcome: + # This activity is only for logging, so we just evaluate the expression + # Hence, it's a local activity and SHOULD NOT fail + try: + assert isinstance(context.current_step, LogStep) + + expr: str = context.current_step.log + output = simple_eval(expr, names=context.model_dump()) + + result = StepOutcome(output=output) + return result + + except Exception as e: + logging.error(f"Error in log_step: {e}") + return StepOutcome(output=None) + + +# Note: This is here just for clarity. We could have just imported log_step directly +# They do the same thing, so we dont need to mock the log_step function +mock_log_step = log_step + +log_step = activity.defn(name="log_step")(log_step if not testing else mock_log_step) diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index 8303830e9..bbbc66b9f 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -19,6 +19,7 @@ def create_worker(client: Client) -> Any: from ..activities.task_steps import ( evaluate_step, if_else_step, + log_step, prompt_step, return_step, tool_call_step, @@ -40,6 +41,7 @@ def create_worker(client: Client) -> Any: task_activities = [ evaluate_step, if_else_step, + log_step, prompt_step, return_step, tool_call_step, diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 415cb471d..6bf080c1b 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -11,6 +11,7 @@ from ..activities.task_steps import ( evaluate_step, if_else_step, + log_step, prompt_step, return_step, tool_call_step, @@ -22,6 +23,7 @@ ErrorWorkflowStep, EvaluateStep, IfElseWorkflowStep, + LogStep, PromptStep, ReturnStep, SleepFor, @@ -43,12 +45,16 @@ STEP_TO_ACTIVITY = { - PromptStep: prompt_step, EvaluateStep: evaluate_step, - ToolCallStep: tool_call_step, IfElseWorkflowStep: if_else_step, - YieldStep: yield_step, ReturnStep: return_step, + PromptStep: prompt_step, + ToolCallStep: tool_call_step, + YieldStep: yield_step, +} + +STEP_TO_LOCAL_ACTIVITY = { + LogStep: log_step, } @@ -74,7 +80,14 @@ async def run( # 1. First execute the current step's activity if applicable if activity := STEP_TO_ACTIVITY.get(step_type): - outcome = await workflow.execute_activity( + execute_activity = workflow.execute_activity + elif activity := STEP_TO_LOCAL_ACTIVITY.get(step_type): + execute_activity = workflow.execute_local_activity + else: + execute_activity = None + + if execute_activity: + outcome = await execute_activity( activity, context, # TODO: This should be a configurable timeout everywhere based on the task @@ -99,14 +112,14 @@ async def run( ) # 2b. Prep a transition request - async def transition(): + async def transition(**kwargs): # NOTE: The variables are closured from the outer scope transition_request = CreateTransitionRequest( - type=transition_type, - current=context.cursor, - next=next_target, - output=final_output, - metadata=metadata, + type=kwargs.get("type", transition_type), + current=kwargs.get("current", context.cursor), + next=kwargs.get("next", next_target), + output=kwargs.get("output", final_output), + metadata=kwargs.get("metadata", metadata), ) return await workflow.execute_activity( @@ -117,6 +130,10 @@ async def transition(): # 3. Orchestrate the step match context.current_step, outcome: + case LogStep(), StepOutcome(output=output): + await transition(output=dict(logged=output)) + final_output = context.current_input + case ReturnStep(), StepOutcome(output=output): final_output = output transition_type = "finish" @@ -153,25 +170,15 @@ async def transition(): case YieldStep(), StepOutcome( output=output, transition_to=(yield_transition_type, yield_next_target) ): - # Save the original transition state - original_next_target = next_target - original_transition_type = transition_type - - final_output = output - transition_type = yield_transition_type - next_target = yield_next_target - - await transition() + await transition( + output=output, type=yield_transition_type, next=yield_next_target + ) yield_outcome: StepOutcome = await workflow.execute_child_workflow( TaskExecutionWorkflow.run, args=[execution_input, yield_next_target, [output]], ) - # Restore the next target to the original value - next_target = original_next_target - transition_type = original_transition_type - final_output = yield_outcome case _: diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 51bc85f9e..3b6740c8a 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -281,3 +281,53 @@ async def _( result = await handle.result() assert result["value"] == data.input["test"] + + +@test("workflow: create task execution") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "other_workflow": [ + # Testing that we can access the input + {"evaluate": {"hello": '_["test"]'}}, + {"log": '_["hello"]'}, + ], + "main": [ + # Testing that we can access the input + { + "workflow": "other_workflow", + "arguments": {"test": '_["test"]'}, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == data.input["test"] From d9540770bf8f5bbda5dd8cd13306e155befd3b16 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 20:22:03 -0400 Subject: [PATCH 076/110] feat(agents-api): Make evaluate_step, log_step, etc local activities Signed-off-by: Diwank Tomer --- .../activities/task_steps/evaluate_step.py | 18 +++-- .../activities/task_steps/log_step.py | 4 +- .../activities/task_steps/return_step.py | 22 +++--- .../agents_api/workflows/task_execution.py | 21 ++++-- agents-api/tests/test_execution_workflow.py | 67 ++++++++++++++++--- 5 files changed, 103 insertions(+), 29 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py index 76b72bdbd..7a5155049 100644 --- a/agents-api/agents_api/activities/task_steps/evaluate_step.py +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -1,3 +1,4 @@ +import logging from temporalio import activity from ...activities.task_steps.utils import simple_eval_dict @@ -12,13 +13,20 @@ async def evaluate_step( context: StepContext[EvaluateStep], ) -> StepOutcome: - assert isinstance(context.current_step, EvaluateStep) + # NOTE: This activity is only for returning immediately, so we just evaluate the expression + # Hence, it's a local activity and SHOULD NOT fail + try: + assert isinstance(context.current_step, EvaluateStep) - exprs = context.current_step.evaluate - output = simple_eval_dict(exprs, values=context.model_dump()) + exprs = context.current_step.evaluate + output = simple_eval_dict(exprs, values=context.model_dump()) - result = StepOutcome(output=output) - return result + result = StepOutcome(output=output) + return result + + except Exception as e: + logging.error(f"Error in log_step: {e}") + return StepOutcome(output=None) # Note: This is here just for clarity. We could have just imported evaluate_step directly diff --git a/agents-api/agents_api/activities/task_steps/log_step.py b/agents-api/agents_api/activities/task_steps/log_step.py index bfc695993..41faf46c4 100644 --- a/agents-api/agents_api/activities/task_steps/log_step.py +++ b/agents-api/agents_api/activities/task_steps/log_step.py @@ -14,8 +14,8 @@ async def log_step( context: StepContext, ) -> StepOutcome: - # This activity is only for logging, so we just evaluate the expression - # Hence, it's a local activity and SHOULD NOT fail + # NOTE: This activity is only for logging, so we just evaluate the expression + # Hence, it's a local activity and SHOULD NOT fail try: assert isinstance(context.current_step, LogStep) diff --git a/agents-api/agents_api/activities/task_steps/return_step.py b/agents-api/agents_api/activities/task_steps/return_step.py index 3dafee3e9..e5b1c3c14 100644 --- a/agents-api/agents_api/activities/task_steps/return_step.py +++ b/agents-api/agents_api/activities/task_steps/return_step.py @@ -1,3 +1,4 @@ +import logging from temporalio import activity from ...activities.task_steps.utils import simple_eval_dict @@ -9,16 +10,21 @@ from ...env import testing -async def return_step( - context: StepContext, -) -> StepOutcome: - assert isinstance(context.current_step, ReturnStep) +async def return_step(context: StepContext) -> StepOutcome: + # NOTE: This activity is only for returning immediately, so we just evaluate the expression + # Hence, it's a local activity and SHOULD NOT fail + try: + assert isinstance(context.current_step, ReturnStep) - exprs: dict[str, str] = context.current_step.return_ - output = simple_eval_dict(exprs, values=context.model_dump()) + exprs: dict[str, str] = context.current_step.return_ + output = simple_eval_dict(exprs, values=context.model_dump()) - result = StepOutcome(output=output) - return result + result = StepOutcome(output=output) + return result + + except Exception as e: + logging.error(f"Error in log_step: {e}") + return StepOutcome(output=None) # Note: This is here just for clarity. We could have just imported return_step directly diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 6bf080c1b..6dcad6f57 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -45,16 +45,18 @@ STEP_TO_ACTIVITY = { - EvaluateStep: evaluate_step, - IfElseWorkflowStep: if_else_step, - ReturnStep: return_step, PromptStep: prompt_step, ToolCallStep: tool_call_step, YieldStep: yield_step, } STEP_TO_LOCAL_ACTIVITY = { + # NOTE: local activities are directly called in the workflow executor + # They MUST NOT FAIL, otherwise they will crash the workflow + EvaluateStep: evaluate_step, + IfElseWorkflowStep: if_else_step, LogStep: log_step, + ReturnStep: return_step, } @@ -107,9 +109,7 @@ async def run( else: transition_type = "step" - next_target = TransitionTarget( - workflow=context.cursor.workflow, step=context.cursor.step + 1 - ) + next_target = TransitionTarget(workflow=start.workflow, step=start.step + 1) # 2b. Prep a transition request async def transition(**kwargs): @@ -131,10 +131,16 @@ async def transition(**kwargs): # 3. Orchestrate the step match context.current_step, outcome: case LogStep(), StepOutcome(output=output): + if output is None: + raise ApplicationError("log step threw an error") + await transition(output=dict(logged=output)) final_output = context.current_input case ReturnStep(), StepOutcome(output=output): + if output is None: + raise ApplicationError("return step threw an error") + final_output = output transition_type = "finish" await transition() @@ -157,6 +163,9 @@ async def transition(**kwargs): await transition() case EvaluateStep(), StepOutcome(output=output): + if output is None: + raise ApplicationError("evaluate step threw an error") + final_output = output await transition() diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 3b6740c8a..c4fce10e1 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -1,6 +1,6 @@ # Tests for task queries -from ward import test +from ward import test, raises from agents_api.autogen.openapi_model import CreateExecutionRequest, CreateTaskRequest from agents_api.models.task.create_task import create_task @@ -10,7 +10,7 @@ from .utils import patch_testing_temporal -@test("workflow: create task execution") +@test("workflow: evaluate step single") async def _( client=cozo_client, developer_id=test_developer_id, @@ -49,7 +49,7 @@ async def _( assert result["hello"] == "world" -@test("workflow: create task execution") +@test("workflow: evaluate step multiple") async def _( client=cozo_client, developer_id=test_developer_id, @@ -91,7 +91,7 @@ async def _( assert result["hello"] == "world" -@test("workflow: create task execution") +@test("workflow: variable access in expressions") async def _( client=cozo_client, developer_id=test_developer_id, @@ -133,7 +133,7 @@ async def _( assert result["hello"] == data.input["test"] -@test("workflow: create task execution") +@test("workflow: yield step") async def _( client=cozo_client, developer_id=test_developer_id, @@ -182,7 +182,7 @@ async def _( assert result["hello"] == data.input["test"] -@test("workflow: create task execution") +@test("workflow: sleep step") async def _( client=cozo_client, developer_id=test_developer_id, @@ -232,7 +232,7 @@ async def _( assert result["hello"] == data.input["test"] -@test("workflow: create task execution") +@test("workflow: return step") async def _( client=cozo_client, developer_id=test_developer_id, @@ -283,7 +283,7 @@ async def _( assert result["value"] == data.input["test"] -@test("workflow: create task execution") +@test("workflow: log step") async def _( client=cozo_client, developer_id=test_developer_id, @@ -331,3 +331,54 @@ async def _( result = await handle.result() assert result["hello"] == data.input["test"] + + +@test("workflow: log step expression fail") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "other_workflow": [ + # Testing that we can access the input + {"evaluate": {"hello": '_["test"]'}}, + {"log": '_["hell"]'}, # <--- The "hell" key does not exist + ], + "main": [ + # Testing that we can access the input + { + "workflow": "other_workflow", + "arguments": {"test": '_["test"]'}, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + with raises(BaseException): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == data.input["test"] From 6cd98aef546a916faeee093770392a61aaa43c17 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 21:34:34 -0400 Subject: [PATCH 077/110] feat(agents-api): Add wait_for_input step Signed-off-by: Diwank Tomer --- .../activities/task_steps/__init__.py | 1 + .../activities/task_steps/evaluate_step.py | 10 +-- .../activities/task_steps/return_step.py | 1 + .../task_steps/wait_for_input_step.py | 34 ++++++++++ .../activities/task_steps/yield_step.py | 40 +++++++---- agents-api/agents_api/autogen/Tasks.py | 2 +- agents-api/agents_api/worker/worker.py | 2 + .../agents_api/workflows/task_execution.py | 37 +++++++--- agents-api/tests/test_execution_workflow.py | 68 ++++++++++++++++++- sdks/python/julep/api/__init__.py | 2 - sdks/python/julep/api/types/__init__.py | 2 - .../tasks_create_task_request_main_item.py | 3 +- .../tasks_patch_task_request_main_item.py | 3 +- .../julep/api/types/tasks_task_main_item.py | 3 +- .../tasks_update_task_request_main_item.py | 3 +- .../api/types/tasks_wait_for_input_step.py | 4 +- .../types/tasks_wait_for_input_step_info.py | 5 -- .../src/api/models/Tasks_WaitForInputStep.ts | 3 +- .../api/schemas/$Tasks_WaitForInputStep.ts | 19 ++---- typespec/tasks/steps.tsp | 2 +- 20 files changed, 174 insertions(+), 70 deletions(-) create mode 100644 agents-api/agents_api/activities/task_steps/wait_for_input_step.py delete mode 100644 sdks/python/julep/api/types/tasks_wait_for_input_step_info.py diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 2c694113f..73cf802de 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -8,4 +8,5 @@ from .return_step import return_step from .tool_call_step import tool_call_step from .transition_step import transition_step +from .wait_for_input_step import wait_for_input_step from .yield_step import yield_step diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py index 7a5155049..9ee6beb69 100644 --- a/agents-api/agents_api/activities/task_steps/evaluate_step.py +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -1,18 +1,14 @@ import logging + from temporalio import activity from ...activities.task_steps.utils import simple_eval_dict from ...autogen.openapi_model import EvaluateStep -from ...common.protocol.tasks import ( - StepContext, - StepOutcome, -) +from ...common.protocol.tasks import StepContext, StepOutcome from ...env import testing -async def evaluate_step( - context: StepContext[EvaluateStep], -) -> StepOutcome: +async def evaluate_step(context: StepContext) -> StepOutcome: # NOTE: This activity is only for returning immediately, so we just evaluate the expression # Hence, it's a local activity and SHOULD NOT fail try: diff --git a/agents-api/agents_api/activities/task_steps/return_step.py b/agents-api/agents_api/activities/task_steps/return_step.py index e5b1c3c14..ac4f3ddea 100644 --- a/agents-api/agents_api/activities/task_steps/return_step.py +++ b/agents-api/agents_api/activities/task_steps/return_step.py @@ -1,4 +1,5 @@ import logging + from temporalio import activity from ...activities.task_steps.utils import simple_eval_dict diff --git a/agents-api/agents_api/activities/task_steps/wait_for_input_step.py b/agents-api/agents_api/activities/task_steps/wait_for_input_step.py new file mode 100644 index 000000000..c3759572a --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/wait_for_input_step.py @@ -0,0 +1,34 @@ +import logging + +from temporalio import activity + +from ...activities.task_steps.utils import simple_eval_dict +from ...autogen.openapi_model import WaitForInputStep +from ...common.protocol.tasks import StepContext, StepOutcome +from ...env import testing + + +async def wait_for_input_step(context: StepContext) -> StepOutcome: + # NOTE: This activity is only for returning immediately, so we just evaluate the expression + # Hence, it's a local activity and SHOULD NOT fail + try: + assert isinstance(context.current_step, WaitForInputStep) + + exprs = context.current_step.wait_for_input + output = simple_eval_dict(exprs, values=context.model_dump()) + + result = StepOutcome(output=output) + return result + + except Exception as e: + logging.error(f"Error in log_step: {e}") + return StepOutcome(output=None) + + +# Note: This is here just for clarity. We could have just imported wait_for_input_step directly +# They do the same thing, so we dont need to mock the wait_for_input_step function +mock_wait_for_input_step = wait_for_input_step + +wait_for_input_step = activity.defn(name="wait_for_input_step")( + wait_for_input_step if not testing else mock_wait_for_input_step +) diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index 92d4c46b8..5dace3361 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -1,9 +1,10 @@ +import logging from typing import Callable from beartype import beartype from temporalio import activity -from agents_api.autogen.Executions import TransitionTarget +from agents_api.autogen.openapi_model import TransitionTarget, YieldStep from ...common.protocol.tasks import StepContext, StepOutcome from ...env import testing @@ -12,24 +13,33 @@ @beartype async def yield_step(context: StepContext) -> StepOutcome: - all_workflows = context.execution_input.task.workflows - workflow = context.current_step.workflow + # NOTE: This activity is only for returning immediately, so we just evaluate the expression + # Hence, it's a local activity and SHOULD NOT fail + try: + assert isinstance(context.current_step, YieldStep) - assert workflow in [ - wf.name for wf in all_workflows - ], f"Workflow {workflow} not found in task" + all_workflows = context.execution_input.task.workflows + workflow = context.current_step.workflow - # Evaluate the expressions in the arguments - exprs = context.current_step.arguments - arguments = simple_eval_dict(exprs, values=context.model_dump()) + assert workflow in [ + wf.name for wf in all_workflows + ], f"Workflow {workflow} not found in task" - # Transition to the first step of that workflow - transition_target = TransitionTarget( - workflow=workflow, - step=0, - ) + # Evaluate the expressions in the arguments + exprs = context.current_step.arguments + arguments = simple_eval_dict(exprs, values=context.model_dump()) - return StepOutcome(output=arguments, transition_to=("step", transition_target)) + # Transition to the first step of that workflow + transition_target = TransitionTarget( + workflow=workflow, + step=0, + ) + + return StepOutcome(output=arguments, transition_to=("step", transition_target)) + + except Exception as e: + logging.error(f"Error in log_step: {e}") + return StepOutcome(output=None) # Note: This is here just for clarity. We could have just imported yield_step directly diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index 8738bc62a..01a2cd602 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -612,7 +612,7 @@ class WaitForInputStep(BaseWorkflowStep): populate_by_name=True, ) kind_: Literal["wait_for_input"] = "wait_for_input" - info: str | dict[str, Any] + wait_for_input: dict[str, str] """ Any additional info or data """ diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index bbbc66b9f..10b3753ad 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -24,6 +24,7 @@ def create_worker(client: Client) -> Any: return_step, tool_call_step, transition_step, + wait_for_input_step, yield_step, ) from ..activities.truncation import truncation @@ -46,6 +47,7 @@ def create_worker(client: Client) -> Any: return_step, tool_call_step, transition_step, + wait_for_input_step, yield_step, ] diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 6dcad6f57..dee401ad3 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -10,28 +10,30 @@ with workflow.unsafe.imports_passed_through(): from ..activities.task_steps import ( evaluate_step, - if_else_step, + # if_else_step, log_step, - prompt_step, + # prompt_step, + raise_complete_async, return_step, - tool_call_step, + # tool_call_step, transition_step, + wait_for_input_step, yield_step, ) from ..autogen.openapi_model import ( CreateTransitionRequest, ErrorWorkflowStep, EvaluateStep, - IfElseWorkflowStep, + # IfElseWorkflowStep, LogStep, - PromptStep, + # PromptStep, ReturnStep, SleepFor, SleepStep, - ToolCallStep, + # ToolCallStep, TransitionTarget, TransitionType, - # WaitForInputStep, + WaitForInputStep, # WorkflowStep, YieldStep, ) @@ -45,16 +47,17 @@ STEP_TO_ACTIVITY = { - PromptStep: prompt_step, - ToolCallStep: tool_call_step, - YieldStep: yield_step, + # PromptStep: prompt_step, + # ToolCallStep: tool_call_step, + WaitForInputStep: wait_for_input_step, } STEP_TO_LOCAL_ACTIVITY = { # NOTE: local activities are directly called in the workflow executor # They MUST NOT FAIL, otherwise they will crash the workflow EvaluateStep: evaluate_step, - IfElseWorkflowStep: if_else_step, + # IfElseWorkflowStep: if_else_step, + YieldStep: yield_step, LogStep: log_step, ReturnStep: return_step, } @@ -179,6 +182,9 @@ async def transition(**kwargs): case YieldStep(), StepOutcome( output=output, transition_to=(yield_transition_type, yield_next_target) ): + if output is None: + raise ApplicationError("yield step threw an error") + await transition( output=output, type=yield_transition_type, next=yield_next_target ) @@ -190,6 +196,15 @@ async def transition(**kwargs): final_output = yield_outcome + case WaitForInputStep(), StepOutcome(output=output): + await transition(output=output, type="wait", next=None) + + transition_type = "resume" + final_output = await execute_activity( + raise_complete_async, + schedule_to_close_timeout=timedelta(days=31), + ) + case _: raise NotImplementedError() diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index c4fce10e1..a1108a5c4 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -1,6 +1,9 @@ # Tests for task queries -from ward import test, raises +import asyncio + +from google.protobuf.json_format import MessageToDict +from ward import raises, test from agents_api.autogen.openapi_model import CreateExecutionRequest, CreateTaskRequest from agents_api.models.task.create_task import create_task @@ -382,3 +385,66 @@ async def _( result = await handle.result() assert result["hello"] == data.input["test"] + + +@test("workflow: wait for input step start") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + {"wait_for_input": {"hi": '"bye"'}}, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + mock_run_task_execution_workflow.assert_called_once() + + # Let it run for a bit + await asyncio.sleep(1) + + # Get the history + history = await handle.fetch_history() + events = [MessageToDict(e) for e in history.events] + assert len(events) > 0 + + activities_scheduled = [ + event.get("activityTaskScheduledEventAttributes", {}) + .get("activityType", {}) + .get("name") + for event in events + if "ACTIVITY_TASK_SCHEDULED" in event["eventType"] + ] + activities_scheduled = [ + activity for activity in activities_scheduled if activity + ] + + assert activities_scheduled == [ + "wait_for_input_step", + "transition_step", + "raise_complete_async", + ] diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index a884514b0..d0e7b91b4 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -238,7 +238,6 @@ TasksUpdateTaskRequestMainItem_WaitForInput, TasksUpdateTaskRequestMainItem_Yield, TasksWaitForInputStep, - TasksWaitForInputStepInfo, TasksYieldStep, ToolsChosenFunctionCall, ToolsChosenToolCall, @@ -506,7 +505,6 @@ "TasksUpdateTaskRequestMainItem_WaitForInput", "TasksUpdateTaskRequestMainItem_Yield", "TasksWaitForInputStep", - "TasksWaitForInputStepInfo", "TasksYieldStep", "ToolsChosenFunctionCall", "ToolsChosenToolCall", diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index a534c0a6e..5629f5dd0 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -281,7 +281,6 @@ TasksUpdateTaskRequestMainItem_Yield, ) from .tasks_wait_for_input_step import TasksWaitForInputStep -from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo from .tasks_yield_step import TasksYieldStep from .tools_chosen_function_call import ToolsChosenFunctionCall from .tools_chosen_tool_call import ToolsChosenToolCall, ToolsChosenToolCall_Function @@ -545,7 +544,6 @@ "TasksUpdateTaskRequestMainItem_WaitForInput", "TasksUpdateTaskRequestMainItem_Yield", "TasksWaitForInputStep", - "TasksWaitForInputStepInfo", "TasksYieldStep", "ToolsChosenFunctionCall", "ToolsChosenToolCall", diff --git a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py index 0cfa46d96..5b85a9236 100644 --- a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py @@ -21,7 +21,6 @@ from .tasks_search_step_search import TasksSearchStepSearch from .tasks_set_step_set import TasksSetStepSet from .tasks_sleep_for import TasksSleepFor -from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo class TasksCreateTaskRequestMainItem_Evaluate(pydantic_v1.BaseModel): @@ -488,7 +487,7 @@ class Config: class TasksCreateTaskRequestMainItem_WaitForInput(pydantic_v1.BaseModel): - info: TasksWaitForInputStepInfo + wait_for_input: typing.Dict[str, CommonPyExpression] kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( alias="kind_", default="wait_for_input" ) diff --git a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py index 83f4ae007..112eeff32 100644 --- a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py @@ -21,7 +21,6 @@ from .tasks_search_step_search import TasksSearchStepSearch from .tasks_set_step_set import TasksSetStepSet from .tasks_sleep_for import TasksSleepFor -from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo class TasksPatchTaskRequestMainItem_Evaluate(pydantic_v1.BaseModel): @@ -488,7 +487,7 @@ class Config: class TasksPatchTaskRequestMainItem_WaitForInput(pydantic_v1.BaseModel): - info: TasksWaitForInputStepInfo + wait_for_input: typing.Dict[str, CommonPyExpression] kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( alias="kind_", default="wait_for_input" ) diff --git a/sdks/python/julep/api/types/tasks_task_main_item.py b/sdks/python/julep/api/types/tasks_task_main_item.py index 60e7aed65..4446fe298 100644 --- a/sdks/python/julep/api/types/tasks_task_main_item.py +++ b/sdks/python/julep/api/types/tasks_task_main_item.py @@ -21,7 +21,6 @@ from .tasks_search_step_search import TasksSearchStepSearch from .tasks_set_step_set import TasksSetStepSet from .tasks_sleep_for import TasksSleepFor -from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo class TasksTaskMainItem_Evaluate(pydantic_v1.BaseModel): @@ -488,7 +487,7 @@ class Config: class TasksTaskMainItem_WaitForInput(pydantic_v1.BaseModel): - info: TasksWaitForInputStepInfo + wait_for_input: typing.Dict[str, CommonPyExpression] kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( alias="kind_", default="wait_for_input" ) diff --git a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py index 442c37707..5d50ee656 100644 --- a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py @@ -21,7 +21,6 @@ from .tasks_search_step_search import TasksSearchStepSearch from .tasks_set_step_set import TasksSetStepSet from .tasks_sleep_for import TasksSleepFor -from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo class TasksUpdateTaskRequestMainItem_Evaluate(pydantic_v1.BaseModel): @@ -488,7 +487,7 @@ class Config: class TasksUpdateTaskRequestMainItem_WaitForInput(pydantic_v1.BaseModel): - info: TasksWaitForInputStepInfo + wait_for_input: typing.Dict[str, CommonPyExpression] kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( alias="kind_", default="wait_for_input" ) diff --git a/sdks/python/julep/api/types/tasks_wait_for_input_step.py b/sdks/python/julep/api/types/tasks_wait_for_input_step.py index 4ef962fb9..d3394fcf9 100644 --- a/sdks/python/julep/api/types/tasks_wait_for_input_step.py +++ b/sdks/python/julep/api/types/tasks_wait_for_input_step.py @@ -5,12 +5,12 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_py_expression import CommonPyExpression from .tasks_base_workflow_step import TasksBaseWorkflowStep -from .tasks_wait_for_input_step_info import TasksWaitForInputStepInfo class TasksWaitForInputStep(TasksBaseWorkflowStep): - info: TasksWaitForInputStepInfo = pydantic_v1.Field() + wait_for_input: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field() """ Any additional info or data """ diff --git a/sdks/python/julep/api/types/tasks_wait_for_input_step_info.py b/sdks/python/julep/api/types/tasks_wait_for_input_step_info.py deleted file mode 100644 index 275a35d28..000000000 --- a/sdks/python/julep/api/types/tasks_wait_for_input_step_info.py +++ /dev/null @@ -1,5 +0,0 @@ -# This file was auto-generated by Fern from our API Definition. - -import typing - -TasksWaitForInputStepInfo = typing.Union[str, typing.Dict[str, typing.Any]] diff --git a/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts b/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts index d47196144..7f8e16817 100644 --- a/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts +++ b/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts @@ -2,11 +2,12 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; export type Tasks_WaitForInputStep = Tasks_BaseWorkflowStep & { kind_: "wait_for_input"; /** * Any additional info or data */ - info: string | Record; + wait_for_input: Record; }; diff --git a/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts b/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts index 7e61ce760..227b73b33 100644 --- a/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts @@ -14,20 +14,11 @@ export const $Tasks_WaitForInputStep = { type: "Enum", isRequired: true, }, - info: { - type: "any-of", - description: `Any additional info or data`, - contains: [ - { - type: "string", - }, - { - type: "dictionary", - contains: { - properties: {}, - }, - }, - ], + wait_for_input: { + type: "dictionary", + contains: { + type: "Common_PyExpression", + }, isRequired: true, }, }, diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp index b0db2a1e1..9f13c923b 100644 --- a/typespec/tasks/steps.tsp +++ b/typespec/tasks/steps.tsp @@ -84,7 +84,7 @@ model WaitForInputStep extends BaseWorkflowStep { kind_: "wait_for_input" = "wait_for_input"; /** Any additional info or data */ - info: string | Record; + wait_for_input: ExpressionObject; } model LogStep extends BaseWorkflowStep { From 2c6dadae0769978ee99abdec242440e5db1a1cc4 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sat, 17 Aug 2024 22:27:43 -0400 Subject: [PATCH 078/110] refactor(agents-api): Minor refactors Signed-off-by: Diwank Tomer --- .../activities/task_steps/evaluate_step.py | 4 +- .../activities/task_steps/if_else_step.py | 41 +++++++++++++------ .../activities/task_steps/log_step.py | 6 +-- .../agents_api/workflows/task_execution.py | 28 +++++-------- 4 files changed, 44 insertions(+), 35 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py index 9ee6beb69..d6eaf6d19 100644 --- a/agents-api/agents_api/activities/task_steps/evaluate_step.py +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -1,5 +1,6 @@ import logging +from beartype import beartype from temporalio import activity from ...activities.task_steps.utils import simple_eval_dict @@ -8,6 +9,7 @@ from ...env import testing +@beartype async def evaluate_step(context: StepContext) -> StepOutcome: # NOTE: This activity is only for returning immediately, so we just evaluate the expression # Hence, it's a local activity and SHOULD NOT fail @@ -21,7 +23,7 @@ async def evaluate_step(context: StepContext) -> StepOutcome: return result except Exception as e: - logging.error(f"Error in log_step: {e}") + logging.error(f"Error in evaluate_step: {e}") return StepOutcome(output=None) diff --git a/agents-api/agents_api/activities/task_steps/if_else_step.py b/agents-api/agents_api/activities/task_steps/if_else_step.py index bb05a3094..9e1d8f008 100644 --- a/agents-api/agents_api/activities/task_steps/if_else_step.py +++ b/agents-api/agents_api/activities/task_steps/if_else_step.py @@ -1,24 +1,39 @@ +import logging + from beartype import beartype +from simpleeval import simple_eval from temporalio import activity -from ...autogen.openapi_model import ( - IfElseWorkflowStep, -) +from ...autogen.openapi_model import IfElseWorkflowStep from ...common.protocol.tasks import ( StepContext, + StepOutcome, ) +from ...env import testing -@activity.defn @beartype -async def if_else_step(context: StepContext[IfElseWorkflowStep]) -> dict: - raise NotImplementedError() - # context_data: dict = context.model_dump() +async def if_else_step(context: StepContext) -> StepOutcome: + # NOTE: This activity is only for logging, so we just evaluate the expression + # Hence, it's a local activity and SHOULD NOT fail + try: + assert isinstance(context.current_step, IfElseWorkflowStep) + + expr: str = context.current_step.if_ + output = simple_eval(expr, names=context.model_dump()) + + result = StepOutcome(output=output) + return result - # next_workflow = ( - # context.current_step.then - # if simple_eval(context.current_step.if_, names=context_data) - # else context.current_step.else_ - # ) + except Exception as e: + logging.error(f"Error in if_else_step: {e}") + return StepOutcome(output=None) - # return {"goto_workflow": next_workflow} + +# Note: This is here just for clarity. We could have just imported if_else_step directly +# They do the same thing, so we dont need to mock the if_else_step function +mock_if_else_step = if_else_step + +if_else_step = activity.defn(name="if_else_step")( + if_else_step if not testing else mock_if_else_step +) diff --git a/agents-api/agents_api/activities/task_steps/log_step.py b/agents-api/agents_api/activities/task_steps/log_step.py index 41faf46c4..f1097e8d3 100644 --- a/agents-api/agents_api/activities/task_steps/log_step.py +++ b/agents-api/agents_api/activities/task_steps/log_step.py @@ -1,5 +1,6 @@ import logging +from beartype import beartype from simpleeval import simple_eval from temporalio import activity @@ -11,9 +12,8 @@ from ...env import testing -async def log_step( - context: StepContext, -) -> StepOutcome: +@beartype +async def log_step(context: StepContext) -> StepOutcome: # NOTE: This activity is only for logging, so we just evaluate the expression # Hence, it's a local activity and SHOULD NOT fail try: diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index dee401ad3..3dbd4391b 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -10,7 +10,7 @@ with workflow.unsafe.imports_passed_through(): from ..activities.task_steps import ( evaluate_step, - # if_else_step, + if_else_step, log_step, # prompt_step, raise_complete_async, @@ -24,7 +24,7 @@ CreateTransitionRequest, ErrorWorkflowStep, EvaluateStep, - # IfElseWorkflowStep, + IfElseWorkflowStep, LogStep, # PromptStep, ReturnStep, @@ -56,7 +56,7 @@ # NOTE: local activities are directly called in the workflow executor # They MUST NOT FAIL, otherwise they will crash the workflow EvaluateStep: evaluate_step, - # IfElseWorkflowStep: if_else_step, + IfElseWorkflowStep: if_else_step, YieldStep: yield_step, LogStep: log_step, ReturnStep: return_step, @@ -95,16 +95,17 @@ async def run( outcome = await execute_activity( activity, context, + # # TODO: This should be a configurable timeout everywhere based on the task schedule_to_close_timeout=timedelta(seconds=3 if testing else 600), ) - # 2a. Then, based on the outcome and step type, decide what to do next + # 2a. Set globals # (By default, exit if last otherwise transition 'step' to the next step) final_output = None transition_type: TransitionType next_target: TransitionTarget | None - metadata: dict = {"step_type": step_type.__name__} + metadata: dict = {"__meta__": {"step_type": step_type.__name__}} if context.is_last_step: transition_type = "finish" @@ -131,19 +132,16 @@ async def transition(**kwargs): schedule_to_close_timeout=timedelta(seconds=600), ) - # 3. Orchestrate the step + # 3. Then, based on the outcome and step type, decide what to do next match context.current_step, outcome: - case LogStep(), StepOutcome(output=output): - if output is None: - raise ApplicationError("log step threw an error") + case step, StepOutcome(output=None): + raise ApplicationError(f"{step.__class__.__name__} step threw an error") + case LogStep(), StepOutcome(output=output): await transition(output=dict(logged=output)) final_output = context.current_input case ReturnStep(), StepOutcome(output=output): - if output is None: - raise ApplicationError("return step threw an error") - final_output = output transition_type = "finish" await transition() @@ -166,9 +164,6 @@ async def transition(**kwargs): await transition() case EvaluateStep(), StepOutcome(output=output): - if output is None: - raise ApplicationError("evaluate step threw an error") - final_output = output await transition() @@ -182,9 +177,6 @@ async def transition(**kwargs): case YieldStep(), StepOutcome( output=output, transition_to=(yield_transition_type, yield_next_target) ): - if output is None: - raise ApplicationError("yield step threw an error") - await transition( output=output, type=yield_transition_type, next=yield_next_target ) From 9fdafaf041c96c698b75695fde8d17c9d341906c Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 18 Aug 2024 21:25:56 -0400 Subject: [PATCH 079/110] feat(agents-api): Add if-else step Signed-off-by: Diwank Tomer --- .../activities/task_steps/evaluate_step.py | 4 +- .../activities/task_steps/if_else_step.py | 5 +- .../activities/task_steps/log_step.py | 4 +- .../activities/task_steps/prompt_step.py | 12 +- .../activities/task_steps/return_step.py | 4 +- .../task_steps/wait_for_input_step.py | 19 +- .../activities/task_steps/yield_step.py | 4 +- agents-api/agents_api/autogen/Executions.py | 11 +- .../agents_api/common/protocol/tasks.py | 25 +- .../agents_api/workflows/task_execution.py | 182 ++-- agents-api/poetry.lock | 906 ++---------------- agents-api/pyproject.toml | 35 +- agents-api/tests/test_execution_workflow.py | 46 + .../api/types/executions_transition_target.py | 4 +- sdks/python/poetry.lock | 61 ++ .../api/models/Executions_TransitionTarget.ts | 4 +- .../schemas/$Executions_TransitionTarget.ts | 2 +- typespec/executions/models.tsp | 2 +- typespec/tasks/steps.tsp | 4 + 19 files changed, 370 insertions(+), 964 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py index d6eaf6d19..d15dde869 100644 --- a/agents-api/agents_api/activities/task_steps/evaluate_step.py +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -22,9 +22,9 @@ async def evaluate_step(context: StepContext) -> StepOutcome: result = StepOutcome(output=output) return result - except Exception as e: + except BaseException as e: logging.error(f"Error in evaluate_step: {e}") - return StepOutcome(output=None) + return StepOutcome(error=str(e)) # Note: This is here just for clarity. We could have just imported evaluate_step directly diff --git a/agents-api/agents_api/activities/task_steps/if_else_step.py b/agents-api/agents_api/activities/task_steps/if_else_step.py index 9e1d8f008..7959039cc 100644 --- a/agents-api/agents_api/activities/task_steps/if_else_step.py +++ b/agents-api/agents_api/activities/task_steps/if_else_step.py @@ -21,13 +21,14 @@ async def if_else_step(context: StepContext) -> StepOutcome: expr: str = context.current_step.if_ output = simple_eval(expr, names=context.model_dump()) + output: bool = bool(output) result = StepOutcome(output=output) return result - except Exception as e: + except BaseException as e: logging.error(f"Error in if_else_step: {e}") - return StepOutcome(output=None) + return StepOutcome(error=str(e)) # Note: This is here just for clarity. We could have just imported if_else_step directly diff --git a/agents-api/agents_api/activities/task_steps/log_step.py b/agents-api/agents_api/activities/task_steps/log_step.py index f1097e8d3..458dabdf3 100644 --- a/agents-api/agents_api/activities/task_steps/log_step.py +++ b/agents-api/agents_api/activities/task_steps/log_step.py @@ -25,9 +25,9 @@ async def log_step(context: StepContext) -> StepOutcome: result = StepOutcome(output=output) return result - except Exception as e: + except BaseException as e: logging.error(f"Error in log_step: {e}") - return StepOutcome(output=None) + return StepOutcome(error=str(e)) # Note: This is here just for clarity. We could have just imported log_step directly diff --git a/agents-api/agents_api/activities/task_steps/prompt_step.py b/agents-api/agents_api/activities/task_steps/prompt_step.py index 37f34eab0..cb0088550 100644 --- a/agents-api/agents_api/activities/task_steps/prompt_step.py +++ b/agents-api/agents_api/activities/task_steps/prompt_step.py @@ -3,23 +3,17 @@ from beartype import beartype from temporalio import activity -from ...autogen.openapi_model import ( - InputChatMLMessage, - PromptStep, -) +from ...autogen.openapi_model import InputChatMLMessage from ...clients import ( litellm, # We dont directly import `acompletion` so we can mock it ) -from ...common.protocol.tasks import ( - StepContext, - StepOutcome, -) +from ...common.protocol.tasks import StepContext, StepOutcome from ...common.utils.template import render_template @activity.defn @beartype -async def prompt_step(context: StepContext[PromptStep]) -> StepOutcome: +async def prompt_step(context: StepContext) -> StepOutcome: # Get context data context_data: dict = context.model_dump() diff --git a/agents-api/agents_api/activities/task_steps/return_step.py b/agents-api/agents_api/activities/task_steps/return_step.py index ac4f3ddea..90bc8adf8 100644 --- a/agents-api/agents_api/activities/task_steps/return_step.py +++ b/agents-api/agents_api/activities/task_steps/return_step.py @@ -23,9 +23,9 @@ async def return_step(context: StepContext) -> StepOutcome: result = StepOutcome(output=output) return result - except Exception as e: + except BaseException as e: logging.error(f"Error in log_step: {e}") - return StepOutcome(output=None) + return StepOutcome(error=str(e)) # Note: This is here just for clarity. We could have just imported return_step directly diff --git a/agents-api/agents_api/activities/task_steps/wait_for_input_step.py b/agents-api/agents_api/activities/task_steps/wait_for_input_step.py index c3759572a..4d4378095 100644 --- a/agents-api/agents_api/activities/task_steps/wait_for_input_step.py +++ b/agents-api/agents_api/activities/task_steps/wait_for_input_step.py @@ -1,5 +1,3 @@ -import logging - from temporalio import activity from ...activities.task_steps.utils import simple_eval_dict @@ -9,20 +7,13 @@ async def wait_for_input_step(context: StepContext) -> StepOutcome: - # NOTE: This activity is only for returning immediately, so we just evaluate the expression - # Hence, it's a local activity and SHOULD NOT fail - try: - assert isinstance(context.current_step, WaitForInputStep) - - exprs = context.current_step.wait_for_input - output = simple_eval_dict(exprs, values=context.model_dump()) + assert isinstance(context.current_step, WaitForInputStep) - result = StepOutcome(output=output) - return result + exprs = context.current_step.wait_for_input + output = simple_eval_dict(exprs, values=context.model_dump()) - except Exception as e: - logging.error(f"Error in log_step: {e}") - return StepOutcome(output=None) + result = StepOutcome(output=output) + return result # Note: This is here just for clarity. We could have just imported wait_for_input_step directly diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index 5dace3361..d514df8f9 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -37,9 +37,9 @@ async def yield_step(context: StepContext) -> StepOutcome: return StepOutcome(output=arguments, transition_to=("step", transition_target)) - except Exception as e: + except BaseException as e: logging.error(f"Error in log_step: {e}") - return StepOutcome(output=None) + return StepOutcome(error=str(e)) # Note: This is here just for clarity. We could have just imported yield_step directly diff --git a/agents-api/agents_api/autogen/Executions.py b/agents-api/agents_api/autogen/Executions.py index fbafb54f1..4ff4c4b66 100644 --- a/agents-api/agents_api/autogen/Executions.py +++ b/agents-api/agents_api/autogen/Executions.py @@ -104,9 +104,16 @@ class TransitionTarget(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - workflow: Annotated[str, Field(pattern="^[^\\W0-9]\\w*$")] + workflow: Annotated[ + str, + Field( + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$" + ), + ] """ - Valid python identifier names + For Unicode character safety + See: https://unicode.org/reports/tr31/ + See: https://www.unicode.org/reports/tr39/#Identifier_Characters """ step: int diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index 65e21ba86..4196d73a7 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -1,11 +1,13 @@ -from typing import Any, Generic, TypeVar +from typing import Annotated, Any, Type from uuid import UUID -from pydantic import BaseModel, computed_field +from pydantic import BaseModel, Field, computed_field +from pydantic_partial import create_partial_model from ...autogen.openapi_model import ( Agent, CreateTaskRequest, + CreateTransitionRequest, Execution, PartialTaskSpecDef, PatchTaskRequest, @@ -53,6 +55,9 @@ } +PendingTransition: Type[BaseModel] = create_partial_model(CreateTransitionRequest) + + class ExecutionInput(BaseModel): developer_id: UUID execution: Execution @@ -66,39 +71,36 @@ class ExecutionInput(BaseModel): session: Session | None = None -WorkflowStepType = TypeVar("WorkflowStepType", bound=WorkflowStep) - - -class StepContext(BaseModel, Generic[WorkflowStepType]): +class StepContext(BaseModel): execution_input: ExecutionInput inputs: list[dict[str, Any]] cursor: TransitionTarget @computed_field @property - def outputs(self) -> list[dict[str, Any]]: + def outputs(self) -> Annotated[list[dict[str, Any]], Field(exclude=True)]: return self.inputs[1:] @computed_field @property - def current_input(self) -> dict[str, Any]: + def current_input(self) -> Annotated[dict[str, Any], Field(exclude=True)]: return self.inputs[-1] @computed_field @property - def current_workflow(self) -> Workflow: + def current_workflow(self) -> Annotated[Workflow, Field(exclude=True)]: workflows: list[Workflow] = self.execution_input.task.workflows return next(wf for wf in workflows if wf.name == self.cursor.workflow) @computed_field @property - def current_step(self) -> WorkflowStepType: + def current_step(self) -> Annotated[WorkflowStep, Field(exclude=True)]: step = self.current_workflow.steps[self.cursor.step] return step @computed_field @property - def is_last_step(self) -> bool: + def is_last_step(self) -> Annotated[bool, Field(exclude=True)]: return (self.cursor.step + 1) == len(self.current_workflow.steps) def model_dump(self, *args, **kwargs) -> dict[str, Any]: @@ -109,6 +111,7 @@ def model_dump(self, *args, **kwargs) -> dict[str, Any]: class StepOutcome(BaseModel): + error: str | None = None output: Any transition_to: tuple[TransitionType, TransitionTarget] | None = None diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 3dbd4391b..546bbca62 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -3,23 +3,13 @@ import asyncio from datetime import timedelta +from typing import Any from temporalio import workflow from temporalio.exceptions import ApplicationError with workflow.unsafe.imports_passed_through(): - from ..activities.task_steps import ( - evaluate_step, - if_else_step, - log_step, - # prompt_step, - raise_complete_async, - return_step, - # tool_call_step, - transition_step, - wait_for_input_step, - yield_step, - ) + from ..activities import task_steps from ..autogen.openapi_model import ( CreateTransitionRequest, ErrorWorkflowStep, @@ -32,16 +22,15 @@ SleepStep, # ToolCallStep, TransitionTarget, - TransitionType, WaitForInputStep, - # WorkflowStep, + Workflow, YieldStep, ) from ..common.protocol.tasks import ( ExecutionInput, + PendingTransition, StepContext, StepOutcome, - # Workflow, ) from ..env import testing @@ -49,17 +38,17 @@ STEP_TO_ACTIVITY = { # PromptStep: prompt_step, # ToolCallStep: tool_call_step, - WaitForInputStep: wait_for_input_step, + WaitForInputStep: task_steps.wait_for_input_step, + LogStep: task_steps.log_step, } STEP_TO_LOCAL_ACTIVITY = { # NOTE: local activities are directly called in the workflow executor # They MUST NOT FAIL, otherwise they will crash the workflow - EvaluateStep: evaluate_step, - IfElseWorkflowStep: if_else_step, - YieldStep: yield_step, - LogStep: log_step, - ReturnStep: return_step, + EvaluateStep: task_steps.evaluate_step, + ReturnStep: task_steps.return_step, + YieldStep: task_steps.yield_step, + IfElseWorkflowStep: task_steps.if_else_step, } @@ -71,7 +60,8 @@ async def run( execution_input: ExecutionInput, start: TransitionTarget = TransitionTarget(workflow="main", step=0), previous_inputs: list[dict] = [], - ) -> None: + ) -> Any: + # 0. Prepare context previous_inputs = previous_inputs or [execution_input.arguments] context = StepContext( @@ -81,9 +71,39 @@ async def run( ) step_type = type(context.current_step) - outcome = None - # 1. First execute the current step's activity if applicable + # --- + + # 1a. Set global state + # (By default, exit if last otherwise transition 'step' to the next step) + state = PendingTransition( + type="finish" if context.is_last_step else "step", + next=None + if context.is_last_step + else TransitionTarget(workflow=start.workflow, step=start.step + 1), + metadata={"__meta__": {"step_type": step_type.__name__}}, + ) + + # 1b. Prep a transition request + async def transition(**kwargs) -> None: + # NOTE: The state variable is closured from the outer scope + transition_request = CreateTransitionRequest( + current=context.cursor, + **{ + **state.model_dump(exclude_unset=True), + **kwargs, # Override with any additional kwargs + }, + ) + + await workflow.execute_activity( + task_steps.transition_step, + args=[context, transition_request], + schedule_to_close_timeout=timedelta(seconds=600), + ) + + # --- + + # 2. Execute the current step's activity if applicable if activity := STEP_TO_ACTIVITY.get(step_type): execute_activity = workflow.execute_activity elif activity := STEP_TO_LOCAL_ACTIVITY.get(step_type): @@ -91,6 +111,7 @@ async def run( else: execute_activity = None + outcome = None if execute_activity: outcome = await execute_activity( activity, @@ -100,51 +121,62 @@ async def run( schedule_to_close_timeout=timedelta(seconds=3 if testing else 600), ) - # 2a. Set globals - # (By default, exit if last otherwise transition 'step' to the next step) - final_output = None - transition_type: TransitionType - next_target: TransitionTarget | None - metadata: dict = {"__meta__": {"step_type": step_type.__name__}} - - if context.is_last_step: - transition_type = "finish" - next_target = None - - else: - transition_type = "step" - next_target = TransitionTarget(workflow=start.workflow, step=start.step + 1) - - # 2b. Prep a transition request - async def transition(**kwargs): - # NOTE: The variables are closured from the outer scope - transition_request = CreateTransitionRequest( - type=kwargs.get("type", transition_type), - current=kwargs.get("current", context.cursor), - next=kwargs.get("next", next_target), - output=kwargs.get("output", final_output), - metadata=kwargs.get("metadata", metadata), - ) - - return await workflow.execute_activity( - transition_step, - args=[context, transition_request], - schedule_to_close_timeout=timedelta(seconds=600), - ) + # --- # 3. Then, based on the outcome and step type, decide what to do next match context.current_step, outcome: - case step, StepOutcome(output=None): - raise ApplicationError(f"{step.__class__.__name__} step threw an error") + # Handle errors (activity returns None) + case step, StepOutcome(error=error) if error is not None: + raise ApplicationError( + f"{step.__class__.__name__} step threw error: {error}" + ) case LogStep(), StepOutcome(output=output): + # Add the logged message to transition history await transition(output=dict(logged=output)) - final_output = context.current_input + + # Set the output to the current input + state.output = context.current_input case ReturnStep(), StepOutcome(output=output): - final_output = output - transition_type = "finish" - await transition() + await transition(output=output, type="finish", next=None) + return output # <--- Byeeee! + + case IfElseWorkflowStep(then=then_branch, else_=else_branch), StepOutcome( + output=condition + ): + # Choose the branch based on the condition + chosen_branch = then_branch if condition else else_branch + + # Create a faux workflow + if_else_wf_name = ( + f"{context.cursor.workflow}[{context.cursor.step}].if_else" + ) + if_else_wf_name += ".then" if condition else ".else" + + if_else_task = execution_input.task.model_copy() + if_else_task.workflows = [ + Workflow(name=if_else_wf_name, steps=[chosen_branch]) + ] + + # Create a new execution input + if_else_execution_input = execution_input.model_copy() + if_else_execution_input.task = if_else_task + + # Set the next target to the chosen branch + if_else_next_target = TransitionTarget(workflow=if_else_wf_name, step=0) + + if_else_args = [ + if_else_execution_input, + if_else_next_target, + previous_inputs, + ] + + # Execute the chosen branch and come back here + state.output = await workflow.execute_child_workflow( + TaskExecutionWorkflow.run, + args=if_else_args, + ) case SleepStep( sleep=SleepFor( @@ -157,19 +189,19 @@ async def transition(**kwargs): seconds = seconds + minutes * 60 + hours * 60 * 60 + days * 24 * 60 * 60 assert seconds > 0, "Sleep duration must be greater than 0" - final_output = await asyncio.sleep( + state.output = await asyncio.sleep( seconds, result=context.current_input ) await transition() case EvaluateStep(), StepOutcome(output=output): - final_output = output + state.output = output await transition() case ErrorWorkflowStep(error=error), _: - final_output = dict(error=error) - transition_type = "error" + state.output = dict(error=error) + state.type = "error" await transition() raise ApplicationError(f"Error raised by ErrorWorkflowStep: {error}") @@ -181,33 +213,33 @@ async def transition(**kwargs): output=output, type=yield_transition_type, next=yield_next_target ) - yield_outcome: StepOutcome = await workflow.execute_child_workflow( + state.output = await workflow.execute_child_workflow( TaskExecutionWorkflow.run, args=[execution_input, yield_next_target, [output]], ) - final_output = yield_outcome - case WaitForInputStep(), StepOutcome(output=output): await transition(output=output, type="wait", next=None) - transition_type = "resume" - final_output = await execute_activity( - raise_complete_async, + state.type = "resume" + state.output = await execute_activity( + task_steps.raise_complete_async, schedule_to_close_timeout=timedelta(days=31), ) case _: - raise NotImplementedError() + raise ApplicationError("Not implemented") + + # --- # 4. Closing # End if the last step - if transition_type in ("finish", "cancelled"): - return final_output + if state.type in ("finish", "cancelled"): + return state.output # Otherwise, recurse to the next step # TODO: Should use a continue_as_new workflow ONLY if the next step is a conditional or loop # Otherwise, we should just call the next step as a child workflow workflow.continue_as_new( - args=[execution_input, next_target, previous_inputs + [final_output]] + args=[execution_input, state.next, previous_inputs + [state.output]] ) diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 48131f6e8..d22f8beec 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -121,24 +121,6 @@ files = [ [package.dependencies] frozenlist = ">=1.1.0" -[[package]] -name = "aiosqlite" -version = "0.20.0" -description = "asyncio bridge to the standard sqlite3 module" -optional = false -python-versions = ">=3.8" -files = [ - {file = "aiosqlite-0.20.0-py3-none-any.whl", hash = "sha256:36a1deaca0cac40ebe32aac9977a6e2bbc7f5189f23f4a54d5908986729e5bd6"}, - {file = "aiosqlite-0.20.0.tar.gz", hash = "sha256:6d35c8c256637f4672f843c31021464090805bf925385ac39473fb16eaaca3d7"}, -] - -[package.dependencies] -typing_extensions = ">=4.0" - -[package.extras] -dev = ["attribution (==1.7.0)", "black (==24.2.0)", "coverage[toml] (==7.4.1)", "flake8 (==7.0.0)", "flake8-bugbear (==24.2.6)", "flit (==3.9.0)", "mypy (==1.8.0)", "ufmt (==2.3.0)", "usort (==1.0.8.post1)"] -docs = ["sphinx (==7.2.6)", "sphinx-mdinclude (==0.5.3)"] - [[package]] name = "annotated-types" version = "0.7.0" @@ -670,17 +652,6 @@ click = "*" [package.extras] test = ["pytest"] -[[package]] -name = "cloudpickle" -version = "3.0.0" -description = "Pickler class to extend the standard pickle.Pickler functionality" -optional = false -python-versions = ">=3.8" -files = [ - {file = "cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7"}, - {file = "cloudpickle-3.0.0.tar.gz", hash = "sha256:996d9a482c6fb4f33c1a35335cf8afd065d2a56e973270364840712d9131a882"}, -] - [[package]] name = "colorama" version = "0.4.6" @@ -757,51 +728,6 @@ files = [ [package.extras] develop = ["coverage", "invoke", "path.py", "pylint", "pytest (>=3.2)", "pytest-html (>=1.19.0)", "tox (>=2.9)"] -[[package]] -name = "dask" -version = "2024.8.1" -description = "Parallel PyData with Task Scheduling" -optional = false -python-versions = ">=3.10" -files = [ - {file = "dask-2024.8.1-py3-none-any.whl", hash = "sha256:b8b58cba91dc9c057c8676dcc80b8bc321602b4dfd21529d33b03b55d428e2c3"}, - {file = "dask-2024.8.1.tar.gz", hash = "sha256:4254e43ac8c3affad2b22952f126b00a00f52c87caae91c068d8e395a4ad1a72"}, -] - -[package.dependencies] -click = ">=8.1" -cloudpickle = ">=3.0.0" -distributed = {version = "2024.8.1", optional = true, markers = "extra == \"distributed\""} -fsspec = ">=2021.09.0" -importlib-metadata = {version = ">=4.13.0", markers = "python_version < \"3.12\""} -packaging = ">=20.0" -partd = ">=1.4.0" -pyyaml = ">=5.3.1" -toolz = ">=0.10.0" - -[package.extras] -array = ["numpy (>=1.21)"] -complete = ["dask[array,dataframe,diagnostics,distributed]", "lz4 (>=4.3.2)", "pyarrow (>=7.0)", "pyarrow-hotfix"] -dataframe = ["dask-expr (>=1.1,<1.2)", "dask[array]", "pandas (>=2.0)"] -diagnostics = ["bokeh (>=2.4.2)", "jinja2 (>=2.10.3)"] -distributed = ["distributed (==2024.8.1)"] -test = ["pandas[test]", "pre-commit", "pytest", "pytest-cov", "pytest-rerunfailures", "pytest-timeout", "pytest-xdist"] - -[[package]] -name = "dataclasses-json" -version = "0.6.7" -description = "Easily serialize dataclasses to and from JSON." -optional = false -python-versions = "<4.0,>=3.7" -files = [ - {file = "dataclasses_json-0.6.7-py3-none-any.whl", hash = "sha256:0dbf33f26c8d5305befd61b39d2b3414e8a407bedc2834dea9b8d642666fb40a"}, - {file = "dataclasses_json-0.6.7.tar.gz", hash = "sha256:b6b3e528266ea45b9535223bc53ca645f5208833c29229e847b3f26a1cc55fc0"}, -] - -[package.dependencies] -marshmallow = ">=3.18.0,<4.0.0" -typing-inspect = ">=0.4.0,<1" - [[package]] name = "datamodel-code-generator" version = "0.25.9" @@ -872,17 +798,6 @@ files = [ {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, ] -[[package]] -name = "deepmerge" -version = "1.1.1" -description = "a toolset to deeply merge python dictionaries." -optional = false -python-versions = "*" -files = [ - {file = "deepmerge-1.1.1-py3-none-any.whl", hash = "sha256:7219dad9763f15be9dcd4bcb53e00f48e4eed6f5ed8f15824223eb934bb35977"}, - {file = "deepmerge-1.1.1.tar.gz", hash = "sha256:53a489dc9449636e480a784359ae2aab3191748c920649551c8e378622f0eca4"}, -] - [[package]] name = "defusedxml" version = "0.7.1" @@ -894,34 +809,6 @@ files = [ {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, ] -[[package]] -name = "distributed" -version = "2024.8.1" -description = "Distributed scheduler for Dask" -optional = false -python-versions = ">=3.10" -files = [ - {file = "distributed-2024.8.1-py3-none-any.whl", hash = "sha256:03f5d3fe7a407cdc16dd2bc25dff4900b72f8dee896b7174eebe8a10b42d8c06"}, - {file = "distributed-2024.8.1.tar.gz", hash = "sha256:82394ceb68b91118717148dbe182cff679f32621812bd7b2bc27eaaa8589f962"}, -] - -[package.dependencies] -click = ">=8.0" -cloudpickle = ">=2.0.0" -dask = "2024.8.1" -jinja2 = ">=2.10.3" -locket = ">=1.0.0" -msgpack = ">=1.0.2" -packaging = ">=20.0" -psutil = ">=5.8.0" -pyyaml = ">=5.4.1" -sortedcontainers = ">=2.0.5" -tblib = ">=1.6.0" -toolz = ">=0.11.2" -tornado = ">=6.2.0" -urllib3 = ">=1.26.5" -zict = ">=3.0.0" - [[package]] name = "distro" version = "1.9.0" @@ -1003,62 +890,25 @@ files = [ [package.extras] tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] -[[package]] -name = "faiss-cpu" -version = "1.8.0" -description = "A library for efficient similarity search and clustering of dense vectors." -optional = false -python-versions = ">=3.8" -files = [ - {file = "faiss-cpu-1.8.0.tar.gz", hash = "sha256:3ee1549491728f37b65267c192a94661a907154a8ae0546ad50a564b8be0d82e"}, - {file = "faiss_cpu-1.8.0-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:134a064c7411acf7d1d863173a9d2605c5a59bd573639ab39a5ded5ca983b1b2"}, - {file = "faiss_cpu-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ba8e6202d561ac57394c9d691ff17f8fa6eb9a077913a993fce0a154ec0176f1"}, - {file = "faiss_cpu-1.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a66e9fa7b70556a39681f06e0652f4124c8ddb0a1924afe4f0e40b6924dc845b"}, - {file = "faiss_cpu-1.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:51aaef5a1255d0ea88ea7e52a2415f98c5dd2dd9cec10348d55136541eeec99f"}, - {file = "faiss_cpu-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:38152761242870ec7019e0397cbd0ed0b0716562029ce41a71bb38448bd6d5bc"}, - {file = "faiss_cpu-1.8.0-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:c9e6ad94b86626be1a0faff3e53c4ca169eba88aa156d7e90c5a2e9ba30558fb"}, - {file = "faiss_cpu-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4601dbd81733bf1bc3bff690aac981289fb386dc8e60d0c4eec8a37ba6856d20"}, - {file = "faiss_cpu-1.8.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa943d3b5e8c5c77cdd629d9c3c6f78d7da616e586fdd1b94aecbf2e5fa9ba06"}, - {file = "faiss_cpu-1.8.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b644b366c3b239b34fa3e08bf65bfc78a24eda1e1ea5b2b6d9be3e8fc73d8179"}, - {file = "faiss_cpu-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:f85ecf3514850f93985be238351f5a70736133cfae784b372640aa17c6343a1b"}, - {file = "faiss_cpu-1.8.0-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:61abc0129a357ac00f17f5167f14dff41480de2cc852f306c3d4cd36b893ccbd"}, - {file = "faiss_cpu-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b788186d6eb94e6333e1aa8bb6c84b66e967458ecdd1cee22e16f04c43ee674c"}, - {file = "faiss_cpu-1.8.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5658d90a202c62e4a69c5b065785e9ddcaf6986cb395c16afed8dbe4c58c31a2"}, - {file = "faiss_cpu-1.8.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5d460a372efce547e53d3c47d2c2a8a90b186ad245969048c10c1d7a1e5cf21b"}, - {file = "faiss_cpu-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:9e6520324f0a6764dd267b3c32c76958bf2b1ec36752950f6fab31a7295980a0"}, - {file = "faiss_cpu-1.8.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:fc44be179d5b7f690484ef0d0caf817fea2698a5275a0c7fb6cbf406e5b2e4d1"}, - {file = "faiss_cpu-1.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:bbd6f0bc2e1424a12dc7e19d2cc95b53124867966b21110d26f909227e7ed1f1"}, - {file = "faiss_cpu-1.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:06e7add0c8a06ce8fb0443c38fcaf49c45fb74527ea633b819e56452608e64f5"}, - {file = "faiss_cpu-1.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b864e23c1817fa6cfe9bbec096fd7140d596002934f71aa89b196ffb1b9cd846"}, - {file = "faiss_cpu-1.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:655433755845adbb6f0961e2f8980703640cb9faa96f1cd1ea190252149e0d0a"}, - {file = "faiss_cpu-1.8.0-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:e81fc376a3bcda213ffb395dda1018c953ce927c587731ad582f4e6c2b225363"}, - {file = "faiss_cpu-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:8c6fa6b7eaf558307b4ab118a236e8d1da79a8685222928e4dd52e277dba144a"}, - {file = "faiss_cpu-1.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:652f6812ef2e8b0f9b18209828c590bc618aca82e7f1c1b1888f52928258e406"}, - {file = "faiss_cpu-1.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:304da4e0d19044374b63a5b6467028572eac4bd3f32bc9e8783d800a03fb1f02"}, - {file = "faiss_cpu-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:cb475d3f25f08c97ac64dfe026f113e2aeb9829b206b3b046256c3b40dd7eb62"}, -] - -[package.dependencies] -numpy = "*" - [[package]] name = "fastapi" -version = "0.110.3" +version = "0.112.1" description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" optional = false python-versions = ">=3.8" files = [ - {file = "fastapi-0.110.3-py3-none-any.whl", hash = "sha256:fd7600612f755e4050beb74001310b5a7e1796d149c2ee363124abdfa0289d32"}, - {file = "fastapi-0.110.3.tar.gz", hash = "sha256:555700b0159379e94fdbfc6bb66a0f1c43f4cf7060f25239af3d84b63a656626"}, + {file = "fastapi-0.112.1-py3-none-any.whl", hash = "sha256:bcbd45817fc2a1cd5da09af66815b84ec0d3d634eb173d1ab468ae3103e183e4"}, + {file = "fastapi-0.112.1.tar.gz", hash = "sha256:b2537146f8c23389a7faa8b03d0bd38d4986e6983874557d95eed2acc46448ef"}, ] [package.dependencies] pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" -starlette = ">=0.37.2,<0.38.0" +starlette = ">=0.37.2,<0.39.0" typing-extensions = ">=4.8.0" [package.extras] -all = ["email_validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +all = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.7)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,!=4.0.2,!=4.1.0,!=4.2.0,!=4.3.0,!=5.0.0,!=5.1.0)", "uvicorn[standard] (>=0.12.0)"] +standard = ["email_validator (>=2.0.0)", "fastapi-cli[standard] (>=0.0.5)", "httpx (>=0.23.0)", "jinja2 (>=2.11.2)", "python-multipart (>=0.0.7)", "uvicorn[standard] (>=0.12.0)"] [[package]] name = "fastjsonschema" @@ -1251,77 +1101,6 @@ files = [ {file = "genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37"}, ] -[[package]] -name = "greenlet" -version = "3.0.3" -description = "Lightweight in-process concurrent programming" -optional = false -python-versions = ">=3.7" -files = [ - {file = "greenlet-3.0.3-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83"}, - {file = "greenlet-3.0.3-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f"}, - {file = "greenlet-3.0.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb"}, - {file = "greenlet-3.0.3-cp310-cp310-win_amd64.whl", hash = "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9"}, - {file = "greenlet-3.0.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379"}, - {file = "greenlet-3.0.3-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3"}, - {file = "greenlet-3.0.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d"}, - {file = "greenlet-3.0.3-cp311-cp311-win_amd64.whl", hash = "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728"}, - {file = "greenlet-3.0.3-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230"}, - {file = "greenlet-3.0.3-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305"}, - {file = "greenlet-3.0.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6"}, - {file = "greenlet-3.0.3-cp312-cp312-win_amd64.whl", hash = "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2"}, - {file = "greenlet-3.0.3-cp37-cp37m-macosx_11_0_universal2.whl", hash = "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c"}, - {file = "greenlet-3.0.3-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7"}, - {file = "greenlet-3.0.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6"}, - {file = "greenlet-3.0.3-cp37-cp37m-win32.whl", hash = "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d"}, - {file = "greenlet-3.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67"}, - {file = "greenlet-3.0.3-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b"}, - {file = "greenlet-3.0.3-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5"}, - {file = "greenlet-3.0.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da"}, - {file = "greenlet-3.0.3-cp38-cp38-win32.whl", hash = "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3"}, - {file = "greenlet-3.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf"}, - {file = "greenlet-3.0.3-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61"}, - {file = "greenlet-3.0.3-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6"}, - {file = "greenlet-3.0.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113"}, - {file = "greenlet-3.0.3-cp39-cp39-win32.whl", hash = "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e"}, - {file = "greenlet-3.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067"}, - {file = "greenlet-3.0.3.tar.gz", hash = "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491"}, -] - -[package.extras] -docs = ["Sphinx", "furo"] -test = ["objgraph", "psutil"] - [[package]] name = "h11" version = "0.14.0" @@ -1356,13 +1135,13 @@ trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httpx" -version = "0.26.0" +version = "0.27.0" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.26.0-py3-none-any.whl", hash = "sha256:8915f5a3627c4d47b73e8202457cb28f1266982d1159bd5779d86a80c0eab1cd"}, - {file = "httpx-0.26.0.tar.gz", hash = "sha256:451b55c30d5185ea6b23c2c793abf9bb237d2a7dfb901ced6ff69ad37ec1dfaf"}, + {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, + {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, ] [package.dependencies] @@ -1731,34 +1510,6 @@ files = [ {file = "json5-0.9.25.tar.gz", hash = "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae"}, ] -[[package]] -name = "jsonpatch" -version = "1.33" -description = "Apply JSON-Patches (RFC 6902)" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" -files = [ - {file = "jsonpatch-1.33-py2.py3-none-any.whl", hash = "sha256:0ae28c0cd062bbd8b8ecc26d7d164fbbea9652a1a3693f3b956c1eae5145dade"}, - {file = "jsonpatch-1.33.tar.gz", hash = "sha256:9fcd4009c41e6d12348b4a0ff2563ba56a2923a7dfee731d004e212e1ee5030c"}, -] - -[package.dependencies] -jsonpointer = ">=1.9" - -[[package]] -name = "jsonpath-ng" -version = "1.6.1" -description = "A final implementation of JSONPath for Python that aims to be standard compliant, including arithmetic and binary comparison operators and providing clear AST for metaprogramming." -optional = false -python-versions = "*" -files = [ - {file = "jsonpath-ng-1.6.1.tar.gz", hash = "sha256:086c37ba4917304850bd837aeab806670224d3f038fe2833ff593a672ef0a5fa"}, - {file = "jsonpath_ng-1.6.1-py3-none-any.whl", hash = "sha256:8f22cd8273d7772eea9aaa84d922e0841aa36fdb8a2c6b7f6c3791a16a9bc0be"}, -] - -[package.dependencies] -ply = "*" - [[package]] name = "jsonpointer" version = "3.0.0" @@ -1813,78 +1564,6 @@ files = [ [package.dependencies] referencing = ">=0.31.0" -[[package]] -name = "julep" -version = "0.2.14" -description = "Julep is a platform for creating agents with long-term memory" -optional = false -python-versions = "<3.14,>=3.8" -files = [ - {file = "julep-0.2.14-py3-none-any.whl", hash = "sha256:bc3edab590b7942309e4c03ef3300689d58aa92fb362dc3642046f341ccebf75"}, - {file = "julep-0.2.14.tar.gz", hash = "sha256:504ab31ec6e015f9dac1c5dc0d86a7f56ab1dba0228f215191ff5b016f159c82"}, -] - -[package.dependencies] -beartype = ">=0.14.0,<1.0.0" -environs = ">=9.0.0,<11.0.0" -httpx = ">=0.20.0,<1.0.0" -openai = ">=1.0.1,<2.0.0" -pydantic = ">=2.0.1,<3.0.0" -typing-extensions = ">=4.0.0,<5.0.0" - -[[package]] -name = "jupyter-ai" -version = "2.20.0" -description = "A generative AI extension for JupyterLab" -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_ai-2.20.0-py3-none-any.whl", hash = "sha256:e65fb9d3d566bd67e9846716fba0a712955f8ea9b2779dc0902c99e4d4766a3c"}, - {file = "jupyter_ai-2.20.0.tar.gz", hash = "sha256:3544c8906a1ea15aa012a862964832277bd899e9ca2e715b571ce9c3ea69fa23"}, -] - -[package.dependencies] -aiosqlite = ">=0.18" -dask = {version = "*", extras = ["distributed"]} -deepmerge = ">=1.0" -faiss-cpu = "<=1.8.0" -importlib-metadata = ">=5.2.0" -jupyter-ai-magics = ">=2.13.0" -jupyter-server = ">=1.6,<3" -jupyterlab = ">=4.0,<5.0" -traitlets = ">=5.0" -typing-extensions = ">=4.5.0" - -[package.extras] -all = ["arxiv", "jupyter-ai-magics[all]", "pypdf"] -dev = ["jupyter-ai-magics[dev]"] -test = ["coverage", "jupyter-server[test] (>=1.6,<3)", "pytest", "pytest-asyncio", "pytest-cov", "pytest-jupyter", "pytest-tornasync", "syrupy (>=4.0.8,<4.1.0)"] - -[[package]] -name = "jupyter-ai-magics" -version = "2.20.0" -description = "Jupyter AI magics Python package. Not published on NPM." -optional = false -python-versions = ">=3.8" -files = [ - {file = "jupyter_ai_magics-2.20.0-py3-none-any.whl", hash = "sha256:a25b759d40da59a8f1432cece66ba7cc73ef92b3416026b22a446061ab2bc606"}, - {file = "jupyter_ai_magics-2.20.0.tar.gz", hash = "sha256:b5cdee73b6f0bea56dce5b6b92be89960471c2d877ccfab4795d889d8b834fbf"}, -] - -[package.dependencies] -click = ">=8.0,<9.0" -importlib-metadata = ">=5.2.0" -ipython = "*" -jsonpath-ng = ">=1.5.3,<2" -langchain = ">=0.1.0,<0.3.0" -langchain-community = ">=0.1.0,<0.3.0" -typing-extensions = ">=4.5.0" - -[package.extras] -all = ["ai21", "boto3", "gpt4all", "huggingface-hub", "ipywidgets", "langchain-anthropic", "langchain-aws", "langchain-cohere", "langchain-google-genai", "langchain-mistralai", "langchain-nvidia-ai-endpoints", "langchain-openai", "pillow", "qianfan", "together"] -dev = ["pre-commit (>=3.3.3,<4)"] -test = ["coverage", "pytest", "pytest-asyncio", "pytest-cov"] - [[package]] name = "jupyter-client" version = "8.6.2" @@ -2101,118 +1780,6 @@ files = [ {file = "jupyterlab_widgets-3.0.11.tar.gz", hash = "sha256:dd5ac679593c969af29c9bed054c24f26842baa51352114736756bc035deee27"}, ] -[[package]] -name = "langchain" -version = "0.2.14" -description = "Building applications with LLMs through composability" -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain-0.2.14-py3-none-any.whl", hash = "sha256:eed76194ee7d9c081037a3df7868d4de90e0410b51fc1ca933a8379e464bf40c"}, - {file = "langchain-0.2.14.tar.gz", hash = "sha256:dc2aa5a58882054fb5d043c39ab8332ebd055f88f17839da68e1c7fd0a4fefe2"}, -] - -[package.dependencies] -aiohttp = ">=3.8.3,<4.0.0" -langchain-core = ">=0.2.32,<0.3.0" -langchain-text-splitters = ">=0.2.0,<0.3.0" -langsmith = ">=0.1.17,<0.2.0" -numpy = {version = ">=1,<2", markers = "python_version < \"3.12\""} -pydantic = ">=1,<3" -PyYAML = ">=5.3" -requests = ">=2,<3" -SQLAlchemy = ">=1.4,<3" -tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" - -[[package]] -name = "langchain-community" -version = "0.2.12" -description = "Community contributed LangChain integrations." -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain_community-0.2.12-py3-none-any.whl", hash = "sha256:50e74473dd2309bdef561760afbbf0c5ea17ed91fc4dfa0d52279dd16d6d34e0"}, - {file = "langchain_community-0.2.12.tar.gz", hash = "sha256:d671cfc6a4f3b65f49a2e59ab420d0164f109d0a56fc4b4996518205c63b8c7e"}, -] - -[package.dependencies] -aiohttp = ">=3.8.3,<4.0.0" -dataclasses-json = ">=0.5.7,<0.7" -langchain = ">=0.2.13,<0.3.0" -langchain-core = ">=0.2.30,<0.3.0" -langsmith = ">=0.1.0,<0.2.0" -numpy = {version = ">=1,<2", markers = "python_version < \"3.12\""} -PyYAML = ">=5.3" -requests = ">=2,<3" -SQLAlchemy = ">=1.4,<3" -tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" - -[[package]] -name = "langchain-core" -version = "0.2.33" -description = "Building applications with LLMs through composability" -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain_core-0.2.33-py3-none-any.whl", hash = "sha256:c8de411336c13fa440b7a52895bfd1c064f04d315344855962988483902cc532"}, - {file = "langchain_core-0.2.33.tar.gz", hash = "sha256:dd2659e0a560fc987b210107bf989aa14a6f4b67dd214c13a2c9669036cda975"}, -] - -[package.dependencies] -jsonpatch = ">=1.33,<2.0" -langsmith = ">=0.1.75,<0.2.0" -packaging = ">=23.2,<25" -pydantic = {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""} -PyYAML = ">=5.3" -tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" -typing-extensions = ">=4.7" - -[[package]] -name = "langchain-openai" -version = "0.1.22" -description = "An integration package connecting OpenAI and LangChain" -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain_openai-0.1.22-py3-none-any.whl", hash = "sha256:e184ab867a30f803dc210a388537186b1b670a33d910a7e0fa4e0329d3b6c654"}, - {file = "langchain_openai-0.1.22.tar.gz", hash = "sha256:0cf93133f230a893e3b0cc2a792bbf2580950e879b577f6e8d4ff9963a7de44b"}, -] - -[package.dependencies] -langchain-core = ">=0.2.33,<0.3.0" -openai = ">=1.40.0,<2.0.0" -tiktoken = ">=0.7,<1" - -[[package]] -name = "langchain-text-splitters" -version = "0.2.2" -description = "LangChain text splitting utilities" -optional = false -python-versions = "<4.0,>=3.8.1" -files = [ - {file = "langchain_text_splitters-0.2.2-py3-none-any.whl", hash = "sha256:1c80d4b11b55e2995f02d2a326c0323ee1eeff24507329bb22924e420c782dff"}, - {file = "langchain_text_splitters-0.2.2.tar.gz", hash = "sha256:a1e45de10919fa6fb080ef0525deab56557e9552083600455cb9fa4238076140"}, -] - -[package.dependencies] -langchain-core = ">=0.2.10,<0.3.0" - -[[package]] -name = "langsmith" -version = "0.1.99" -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.99-py3-none-any.whl", hash = "sha256:ef8d1d74a2674c514aa429b0171a9fbb661207dc3835142cca0e8f1bf97b26b0"}, - {file = "langsmith-0.1.99.tar.gz", hash = "sha256:b5c6a1f158abda61600a4a445081ee848b4a28b758d91f2793dc02aeffafcaf1"}, -] - -[package.dependencies] -orjson = ">=3.9.14,<4.0.0" -pydantic = {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""} -requests = ">=2,<3" - [[package]] name = "libcst" version = "1.4.0" @@ -2255,13 +1822,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.17" +version = "1.43.18" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.17-py3-none-any.whl", hash = "sha256:f5d68c812f087b49266631e09ae78b48b3ea03cd2e04e7760162a5919c5ccec7"}, - {file = "litellm-1.43.17.tar.gz", hash = "sha256:8ac82b18bf6ae7c29627e8e5d89b183f075b32fb7027b17d2fb7d7d0b7cf8b7f"}, + {file = "litellm-1.43.18-py3-none-any.whl", hash = "sha256:68d853b4a0198a16e2260e4406a20f8d2e59bd903e019b7f3ba5a9f35ecc3e62"}, + {file = "litellm-1.43.18.tar.gz", hash = "sha256:e22b20065b62663dd060be9da1e84ca05903931c41c49d35a98649ed09e79d29"}, ] [package.dependencies] @@ -2281,17 +1848,6 @@ tokenizers = "*" extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "pynacl (>=1.5.0,<2.0.0)", "resend (>=0.8.0,<0.9.0)"] proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.111.0,<0.112.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] -[[package]] -name = "locket" -version = "1.0.0" -description = "File-based locks for Python on Linux and Windows" -optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" -files = [ - {file = "locket-1.0.0-py2.py3-none-any.whl", hash = "sha256:b6c819a722f7b6bd955b80781788e4a66a55628b858d347536b7e81325a3a5e3"}, - {file = "locket-1.0.0.tar.gz", hash = "sha256:5c0d4c052a8bbbf750e056a8e65ccd309086f4f0f18a2eac306a8dfa4112a632"}, -] - [[package]] name = "markdown-it-py" version = "3.0.0" @@ -2440,71 +1996,6 @@ files = [ {file = "mistune-3.0.2.tar.gz", hash = "sha256:fc7f93ded930c92394ef2cb6f04a8aabab4117a91449e72dcc8dfa646a508be8"}, ] -[[package]] -name = "msgpack" -version = "1.0.8" -description = "MessagePack serializer" -optional = false -python-versions = ">=3.8" -files = [ - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c"}, - {file = "msgpack-1.0.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982"}, - {file = "msgpack-1.0.8-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d"}, - {file = "msgpack-1.0.8-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653"}, - {file = "msgpack-1.0.8-cp310-cp310-win32.whl", hash = "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693"}, - {file = "msgpack-1.0.8-cp310-cp310-win_amd64.whl", hash = "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad"}, - {file = "msgpack-1.0.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85"}, - {file = "msgpack-1.0.8-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b"}, - {file = "msgpack-1.0.8-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce"}, - {file = "msgpack-1.0.8-cp311-cp311-win32.whl", hash = "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305"}, - {file = "msgpack-1.0.8-cp311-cp311-win_amd64.whl", hash = "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b"}, - {file = "msgpack-1.0.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc"}, - {file = "msgpack-1.0.8-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04"}, - {file = "msgpack-1.0.8-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543"}, - {file = "msgpack-1.0.8-cp312-cp312-win32.whl", hash = "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c"}, - {file = "msgpack-1.0.8-cp312-cp312-win_amd64.whl", hash = "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151"}, - {file = "msgpack-1.0.8-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db"}, - {file = "msgpack-1.0.8-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2"}, - {file = "msgpack-1.0.8-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a"}, - {file = "msgpack-1.0.8-cp38-cp38-win32.whl", hash = "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c"}, - {file = "msgpack-1.0.8-cp38-cp38-win_amd64.whl", hash = "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596"}, - {file = "msgpack-1.0.8-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228"}, - {file = "msgpack-1.0.8-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746"}, - {file = "msgpack-1.0.8-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273"}, - {file = "msgpack-1.0.8-cp39-cp39-win32.whl", hash = "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d"}, - {file = "msgpack-1.0.8-cp39-cp39-win_amd64.whl", hash = "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011"}, - {file = "msgpack-1.0.8.tar.gz", hash = "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3"}, -] - [[package]] name = "msgspec" version = "0.18.6" @@ -2822,47 +2313,63 @@ test = ["pytest", "pytest-console-scripts", "pytest-jupyter", "pytest-tornasync" [[package]] name = "numpy" -version = "1.26.4" +version = "2.1.0" description = "Fundamental package for array computing in Python" optional = false -python-versions = ">=3.9" +python-versions = ">=3.10" files = [ - {file = "numpy-1.26.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9ff0f4f29c51e2803569d7a51c2304de5554655a60c5d776e35b4a41413830d0"}, - {file = "numpy-1.26.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2e4ee3380d6de9c9ec04745830fd9e2eccb3e6cf790d39d7b98ffd19b0dd754a"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d209d8969599b27ad20994c8e41936ee0964e6da07478d6c35016bc386b66ad4"}, - {file = "numpy-1.26.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffa75af20b44f8dba823498024771d5ac50620e6915abac414251bd971b4529f"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:62b8e4b1e28009ef2846b4c7852046736bab361f7aeadeb6a5b89ebec3c7055a"}, - {file = "numpy-1.26.4-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4abb4f9001ad2858e7ac189089c42178fcce737e4169dc61321660f1a96c7d2"}, - {file = "numpy-1.26.4-cp310-cp310-win32.whl", hash = "sha256:bfe25acf8b437eb2a8b2d49d443800a5f18508cd811fea3181723922a8a82b07"}, - {file = "numpy-1.26.4-cp310-cp310-win_amd64.whl", hash = "sha256:b97fe8060236edf3662adfc2c633f56a08ae30560c56310562cb4f95500022d5"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:4c66707fabe114439db9068ee468c26bbdf909cac0fb58686a42a24de1760c71"}, - {file = "numpy-1.26.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:edd8b5fe47dab091176d21bb6de568acdd906d1887a4584a15a9a96a1dca06ef"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ab55401287bfec946ced39700c053796e7cc0e3acbef09993a9ad2adba6ca6e"}, - {file = "numpy-1.26.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:666dbfb6ec68962c033a450943ded891bed2d54e6755e35e5835d63f4f6931d5"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:96ff0b2ad353d8f990b63294c8986f1ec3cb19d749234014f4e7eb0112ceba5a"}, - {file = "numpy-1.26.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:60dedbb91afcbfdc9bc0b1f3f402804070deed7392c23eb7a7f07fa857868e8a"}, - {file = "numpy-1.26.4-cp311-cp311-win32.whl", hash = "sha256:1af303d6b2210eb850fcf03064d364652b7120803a0b872f5211f5234b399f20"}, - {file = "numpy-1.26.4-cp311-cp311-win_amd64.whl", hash = "sha256:cd25bcecc4974d09257ffcd1f098ee778f7834c3ad767fe5db785be9a4aa9cb2"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:b3ce300f3644fb06443ee2222c2201dd3a89ea6040541412b8fa189341847218"}, - {file = "numpy-1.26.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:03a8c78d01d9781b28a6989f6fa1bb2c4f2d51201cf99d3dd875df6fbd96b23b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9fad7dcb1aac3c7f0584a5a8133e3a43eeb2fe127f47e3632d43d677c66c102b"}, - {file = "numpy-1.26.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:675d61ffbfa78604709862923189bad94014bef562cc35cf61d3a07bba02a7ed"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ab47dbe5cc8210f55aa58e4805fe224dac469cde56b9f731a4c098b91917159a"}, - {file = "numpy-1.26.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:1dda2e7b4ec9dd512f84935c5f126c8bd8b9f2fc001e9f54af255e8c5f16b0e0"}, - {file = "numpy-1.26.4-cp312-cp312-win32.whl", hash = "sha256:50193e430acfc1346175fcbdaa28ffec49947a06918b7b92130744e81e640110"}, - {file = "numpy-1.26.4-cp312-cp312-win_amd64.whl", hash = "sha256:08beddf13648eb95f8d867350f6a018a4be2e5ad54c8d8caed89ebca558b2818"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7349ab0fa0c429c82442a27a9673fc802ffdb7c7775fad780226cb234965e53c"}, - {file = "numpy-1.26.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:52b8b60467cd7dd1e9ed082188b4e6bb35aa5cdd01777621a1658910745b90be"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5241e0a80d808d70546c697135da2c613f30e28251ff8307eb72ba696945764"}, - {file = "numpy-1.26.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f870204a840a60da0b12273ef34f7051e98c3b5961b61b0c2c1be6dfd64fbcd3"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:679b0076f67ecc0138fd2ede3a8fd196dddc2ad3254069bcb9faf9a79b1cebcd"}, - {file = "numpy-1.26.4-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:47711010ad8555514b434df65f7d7b076bb8261df1ca9bb78f53d3b2db02e95c"}, - {file = "numpy-1.26.4-cp39-cp39-win32.whl", hash = "sha256:a354325ee03388678242a4d7ebcd08b5c727033fcff3b2f536aea978e15ee9e6"}, - {file = "numpy-1.26.4-cp39-cp39-win_amd64.whl", hash = "sha256:3373d5d70a5fe74a2c1bb6d2cfd9609ecf686d47a2d7b1d37a8f3b6bf6003aea"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:afedb719a9dcfc7eaf2287b839d8198e06dcd4cb5d276a3df279231138e83d30"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a7476c59002f2f6c590b9b7b998306fba6a5aa646b1e22ddfeaf8f78c3a29c"}, - {file = "numpy-1.26.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:7e50d0a0cc3189f9cb0aeb3a6a6af18c16f59f004b866cd2be1c14b36134a4a0"}, - {file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"}, + {file = "numpy-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6326ab99b52fafdcdeccf602d6286191a79fe2fda0ae90573c5814cd2b0bc1b8"}, + {file = "numpy-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0937e54c09f7a9a68da6889362ddd2ff584c02d015ec92672c099b61555f8911"}, + {file = "numpy-2.1.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:30014b234f07b5fec20f4146f69e13cfb1e33ee9a18a1879a0142fbb00d47673"}, + {file = "numpy-2.1.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:899da829b362ade41e1e7eccad2cf274035e1cb36ba73034946fccd4afd8606b"}, + {file = "numpy-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08801848a40aea24ce16c2ecde3b756f9ad756586fb2d13210939eb69b023f5b"}, + {file = "numpy-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:398049e237d1aae53d82a416dade04defed1a47f87d18d5bd615b6e7d7e41d1f"}, + {file = "numpy-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0abb3916a35d9090088a748636b2c06dc9a6542f99cd476979fb156a18192b84"}, + {file = "numpy-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10e2350aea18d04832319aac0f887d5fcec1b36abd485d14f173e3e900b83e33"}, + {file = "numpy-2.1.0-cp310-cp310-win32.whl", hash = "sha256:f6b26e6c3b98adb648243670fddc8cab6ae17473f9dc58c51574af3e64d61211"}, + {file = "numpy-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:f505264735ee074250a9c78247ee8618292091d9d1fcc023290e9ac67e8f1afa"}, + {file = "numpy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:76368c788ccb4f4782cf9c842b316140142b4cbf22ff8db82724e82fe1205dce"}, + {file = "numpy-2.1.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f8e93a01a35be08d31ae33021e5268f157a2d60ebd643cfc15de6ab8e4722eb1"}, + {file = "numpy-2.1.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9523f8b46485db6939bd069b28b642fec86c30909cea90ef550373787f79530e"}, + {file = "numpy-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54139e0eb219f52f60656d163cbe67c31ede51d13236c950145473504fa208cb"}, + {file = "numpy-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ebbf9fbdabed208d4ecd2e1dfd2c0741af2f876e7ae522c2537d404ca895c3"}, + {file = "numpy-2.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:378cb4f24c7d93066ee4103204f73ed046eb88f9ad5bb2275bb9fa0f6a02bd36"}, + {file = "numpy-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8f699a709120b220dfe173f79c73cb2a2cab2c0b88dd59d7b49407d032b8ebd"}, + {file = "numpy-2.1.0-cp311-cp311-win32.whl", hash = "sha256:ffbd6faeb190aaf2b5e9024bac9622d2ee549b7ec89ef3a9373fa35313d44e0e"}, + {file = "numpy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0af3a5987f59d9c529c022c8c2a64805b339b7ef506509fba7d0556649b9714b"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe76d75b345dc045acdbc006adcb197cc680754afd6c259de60d358d60c93736"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f358ea9e47eb3c2d6eba121ab512dfff38a88db719c38d1e67349af210bc7529"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:dd94ce596bda40a9618324547cfaaf6650b1a24f5390350142499aa4e34e53d1"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b47c551c6724960479cefd7353656498b86e7232429e3a41ab83be4da1b109e8"}, + {file = "numpy-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0756a179afa766ad7cb6f036de622e8a8f16ffdd55aa31f296c870b5679d745"}, + {file = "numpy-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24003ba8ff22ea29a8c306e61d316ac74111cebf942afbf692df65509a05f111"}, + {file = "numpy-2.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b34fa5e3b5d6dc7e0a4243fa0f81367027cb6f4a7215a17852979634b5544ee0"}, + {file = "numpy-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4f982715e65036c34897eb598d64aef15150c447be2cfc6643ec7a11af06574"}, + {file = "numpy-2.1.0-cp312-cp312-win32.whl", hash = "sha256:c4cd94dfefbefec3f8b544f61286584292d740e6e9d4677769bc76b8f41deb02"}, + {file = "numpy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0cdef204199278f5c461a0bed6ed2e052998276e6d8ab2963d5b5c39a0500bc"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8ab81ccd753859ab89e67199b9da62c543850f819993761c1e94a75a814ed667"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:442596f01913656d579309edcd179a2a2f9977d9a14ff41d042475280fc7f34e"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:848c6b5cad9898e4b9ef251b6f934fa34630371f2e916261070a4eb9092ffd33"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:54c6a63e9d81efe64bfb7bcb0ec64332a87d0b87575f6009c8ba67ea6374770b"}, + {file = "numpy-2.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:652e92fc409e278abdd61e9505649e3938f6d04ce7ef1953f2ec598a50e7c195"}, + {file = "numpy-2.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ab32eb9170bf8ffcbb14f11613f4a0b108d3ffee0832457c5d4808233ba8977"}, + {file = "numpy-2.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8fb49a0ba4d8f41198ae2d52118b050fd34dace4b8f3fb0ee34e23eb4ae775b1"}, + {file = "numpy-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44e44973262dc3ae79e9063a1284a73e09d01b894b534a769732ccd46c28cc62"}, + {file = "numpy-2.1.0-cp313-cp313-win32.whl", hash = "sha256:ab83adc099ec62e044b1fbb3a05499fa1e99f6d53a1dde102b2d85eff66ed324"}, + {file = "numpy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:de844aaa4815b78f6023832590d77da0e3b6805c644c33ce94a1e449f16d6ab5"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:343e3e152bf5a087511cd325e3b7ecfd5b92d369e80e74c12cd87826e263ec06"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f07fa2f15dabe91259828ce7d71b5ca9e2eb7c8c26baa822c825ce43552f4883"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5474dad8c86ee9ba9bb776f4b99ef2d41b3b8f4e0d199d4f7304728ed34d0300"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1f817c71683fd1bb5cff1529a1d085a57f02ccd2ebc5cd2c566f9a01118e3b7d"}, + {file = "numpy-2.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a3336fbfa0d38d3deacd3fe7f3d07e13597f29c13abf4d15c3b6dc2291cbbdd"}, + {file = "numpy-2.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a894c51fd8c4e834f00ac742abad73fc485df1062f1b875661a3c1e1fb1c2f6"}, + {file = "numpy-2.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:9156ca1f79fc4acc226696e95bfcc2b486f165a6a59ebe22b2c1f82ab190384a"}, + {file = "numpy-2.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:624884b572dff8ca8f60fab591413f077471de64e376b17d291b19f56504b2bb"}, + {file = "numpy-2.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:15ef8b2177eeb7e37dd5ef4016f30b7659c57c2c0b57a779f1d537ff33a72c7b"}, + {file = "numpy-2.1.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:e5f0642cdf4636198a4990de7a71b693d824c56a757862230454629cf62e323d"}, + {file = "numpy-2.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15976718c004466406342789f31b6673776360f3b1e3c575f25302d7e789575"}, + {file = "numpy-2.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6c1de77ded79fef664d5098a66810d4d27ca0224e9051906e634b3f7ead134c2"}, + {file = "numpy-2.1.0.tar.gz", hash = "sha256:7dc90da0081f7e1da49ec4e398ede6a8e9cc4f5ebe5f9e06b443ed889ee9aaa2"}, ] [[package]] @@ -2889,72 +2396,6 @@ typing-extensions = ">=4.11,<5" [package.extras] datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] -[[package]] -name = "orjson" -version = "3.10.7" -description = "Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy" -optional = false -python-versions = ">=3.8" -files = [ - {file = "orjson-3.10.7-cp310-cp310-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:74f4544f5a6405b90da8ea724d15ac9c36da4d72a738c64685003337401f5c12"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:34a566f22c28222b08875b18b0dfbf8a947e69df21a9ed5c51a6bf91cfb944ac"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf6ba8ebc8ef5792e2337fb0419f8009729335bb400ece005606336b7fd7bab7"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ac7cf6222b29fbda9e3a472b41e6a5538b48f2c8f99261eecd60aafbdb60690c"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:de817e2f5fc75a9e7dd350c4b0f54617b280e26d1631811a43e7e968fa71e3e9"}, - {file = "orjson-3.10.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:348bdd16b32556cf8d7257b17cf2bdb7ab7976af4af41ebe79f9796c218f7e91"}, - {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:479fd0844ddc3ca77e0fd99644c7fe2de8e8be1efcd57705b5c92e5186e8a250"}, - {file = "orjson-3.10.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:fdf5197a21dd660cf19dfd2a3ce79574588f8f5e2dbf21bda9ee2d2b46924d84"}, - {file = "orjson-3.10.7-cp310-none-win32.whl", hash = "sha256:d374d36726746c81a49f3ff8daa2898dccab6596864ebe43d50733275c629175"}, - {file = "orjson-3.10.7-cp310-none-win_amd64.whl", hash = "sha256:cb61938aec8b0ffb6eef484d480188a1777e67b05d58e41b435c74b9d84e0b9c"}, - {file = "orjson-3.10.7-cp311-cp311-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:7db8539039698ddfb9a524b4dd19508256107568cdad24f3682d5773e60504a2"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:480f455222cb7a1dea35c57a67578848537d2602b46c464472c995297117fa09"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8a9c9b168b3a19e37fe2778c0003359f07822c90fdff8f98d9d2a91b3144d8e0"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8de062de550f63185e4c1c54151bdddfc5625e37daf0aa1e75d2a1293e3b7d9a"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6b0dd04483499d1de9c8f6203f8975caf17a6000b9c0c54630cef02e44ee624e"}, - {file = "orjson-3.10.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b58d3795dafa334fc8fd46f7c5dc013e6ad06fd5b9a4cc98cb1456e7d3558bd6"}, - {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:33cfb96c24034a878d83d1a9415799a73dc77480e6c40417e5dda0710d559ee6"}, - {file = "orjson-3.10.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e724cebe1fadc2b23c6f7415bad5ee6239e00a69f30ee423f319c6af70e2a5c0"}, - {file = "orjson-3.10.7-cp311-none-win32.whl", hash = "sha256:82763b46053727a7168d29c772ed5c870fdae2f61aa8a25994c7984a19b1021f"}, - {file = "orjson-3.10.7-cp311-none-win_amd64.whl", hash = "sha256:eb8d384a24778abf29afb8e41d68fdd9a156cf6e5390c04cc07bbc24b89e98b5"}, - {file = "orjson-3.10.7-cp312-cp312-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:44a96f2d4c3af51bfac6bc4ef7b182aa33f2f054fd7f34cc0ee9a320d051d41f"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76ac14cd57df0572453543f8f2575e2d01ae9e790c21f57627803f5e79b0d3c3"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bdbb61dcc365dd9be94e8f7df91975edc9364d6a78c8f7adb69c1cdff318ec93"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b48b3db6bb6e0a08fa8c83b47bc169623f801e5cc4f24442ab2b6617da3b5313"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:23820a1563a1d386414fef15c249040042b8e5d07b40ab3fe3efbfbbcbcb8864"}, - {file = "orjson-3.10.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0c6a008e91d10a2564edbb6ee5069a9e66df3fbe11c9a005cb411f441fd2c09"}, - {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d352ee8ac1926d6193f602cbe36b1643bbd1bbcb25e3c1a657a4390f3000c9a5"}, - {file = "orjson-3.10.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:d2d9f990623f15c0ae7ac608103c33dfe1486d2ed974ac3f40b693bad1a22a7b"}, - {file = "orjson-3.10.7-cp312-none-win32.whl", hash = "sha256:7c4c17f8157bd520cdb7195f75ddbd31671997cbe10aee559c2d613592e7d7eb"}, - {file = "orjson-3.10.7-cp312-none-win_amd64.whl", hash = "sha256:1d9c0e733e02ada3ed6098a10a8ee0052dd55774de3d9110d29868d24b17faa1"}, - {file = "orjson-3.10.7-cp313-cp313-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:77d325ed866876c0fa6492598ec01fe30e803272a6e8b10e992288b009cbe149"}, - {file = "orjson-3.10.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ea2c232deedcb605e853ae1db2cc94f7390ac776743b699b50b071b02bea6fe"}, - {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:3dcfbede6737fdbef3ce9c37af3fb6142e8e1ebc10336daa05872bfb1d87839c"}, - {file = "orjson-3.10.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:11748c135f281203f4ee695b7f80bb1358a82a63905f9f0b794769483ea854ad"}, - {file = "orjson-3.10.7-cp313-none-win32.whl", hash = "sha256:a7e19150d215c7a13f39eb787d84db274298d3f83d85463e61d277bbd7f401d2"}, - {file = "orjson-3.10.7-cp313-none-win_amd64.whl", hash = "sha256:eef44224729e9525d5261cc8d28d6b11cafc90e6bd0be2157bde69a52ec83024"}, - {file = "orjson-3.10.7-cp38-cp38-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:6ea2b2258eff652c82652d5e0f02bd5e0463a6a52abb78e49ac288827aaa1469"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:430ee4d85841e1483d487e7b81401785a5dfd69db5de01314538f31f8fbf7ee1"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4b6146e439af4c2472c56f8540d799a67a81226e11992008cb47e1267a9b3225"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:084e537806b458911137f76097e53ce7bf5806dda33ddf6aaa66a028f8d43a23"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4829cf2195838e3f93b70fd3b4292156fc5e097aac3739859ac0dcc722b27ac0"}, - {file = "orjson-3.10.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1193b2416cbad1a769f868b1749535d5da47626ac29445803dae7cc64b3f5c98"}, - {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4e6c3da13e5a57e4b3dca2de059f243ebec705857522f188f0180ae88badd354"}, - {file = "orjson-3.10.7-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:c31008598424dfbe52ce8c5b47e0752dca918a4fdc4a2a32004efd9fab41d866"}, - {file = "orjson-3.10.7-cp38-none-win32.whl", hash = "sha256:7122a99831f9e7fe977dc45784d3b2edc821c172d545e6420c375e5a935f5a1c"}, - {file = "orjson-3.10.7-cp38-none-win_amd64.whl", hash = "sha256:a763bc0e58504cc803739e7df040685816145a6f3c8a589787084b54ebc9f16e"}, - {file = "orjson-3.10.7-cp39-cp39-macosx_10_15_x86_64.macosx_11_0_arm64.macosx_10_15_universal2.whl", hash = "sha256:e76be12658a6fa376fcd331b1ea4e58f5a06fd0220653450f0d415b8fd0fbe20"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed350d6978d28b92939bfeb1a0570c523f6170efc3f0a0ef1f1df287cd4f4960"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:144888c76f8520e39bfa121b31fd637e18d4cc2f115727865fdf9fa325b10412"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09b2d92fd95ad2402188cf51573acde57eb269eddabaa60f69ea0d733e789fe9"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5b24a579123fa884f3a3caadaed7b75eb5715ee2b17ab5c66ac97d29b18fe57f"}, - {file = "orjson-3.10.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591bcfe7512353bd609875ab38050efe3d55e18934e2f18950c108334b4ff"}, - {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:f4db56635b58cd1a200b0a23744ff44206ee6aa428185e2b6c4a65b3197abdcd"}, - {file = "orjson-3.10.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:0fa5886854673222618638c6df7718ea7fe2f3f2384c452c9ccedc70b4a510a5"}, - {file = "orjson-3.10.7-cp39-none-win32.whl", hash = "sha256:8272527d08450ab16eb405f47e0f4ef0e5ff5981c3d82afe0efd25dcbef2bcd2"}, - {file = "orjson-3.10.7-cp39-none-win_amd64.whl", hash = "sha256:974683d4618c0c7dbf4f69c95a979734bf183d0658611760017f6e70a145af58"}, - {file = "orjson-3.10.7.tar.gz", hash = "sha256:75ef0640403f945f3a1f9f6400686560dbfb0fb5b16589ad62cd477043c4eee3"}, -] - [[package]] name = "overrides" version = "7.7.0" @@ -3072,24 +2513,6 @@ files = [ qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] testing = ["docopt", "pytest"] -[[package]] -name = "partd" -version = "1.4.2" -description = "Appendable key-value storage" -optional = false -python-versions = ">=3.9" -files = [ - {file = "partd-1.4.2-py3-none-any.whl", hash = "sha256:978e4ac767ec4ba5b86c6eaa52e5a2a3bc748a2ca839e8cc798f1cc6ce6efb0f"}, - {file = "partd-1.4.2.tar.gz", hash = "sha256:d022c33afbdc8405c226621b015e8067888173d85f7f5ecebb3cafed9a20f02c"}, -] - -[package.dependencies] -locket = "*" -toolz = "*" - -[package.extras] -complete = ["blosc", "numpy (>=1.20.0)", "pandas (>=1.3)", "pyzmq"] - [[package]] name = "pastel" version = "0.2.1" @@ -3157,17 +2580,6 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] -[[package]] -name = "ply" -version = "3.11" -description = "Python Lex & Yacc" -optional = false -python-versions = "*" -files = [ - {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, - {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, -] - [[package]] name = "poethepoet" version = "0.25.1" @@ -4171,22 +3583,23 @@ win32 = ["pywin32"] [[package]] name = "sentry-sdk" -version = "1.45.1" +version = "2.13.0" description = "Python client for Sentry (https://sentry.io)" optional = false -python-versions = "*" +python-versions = ">=3.6" files = [ - {file = "sentry_sdk-1.45.1-py2.py3-none-any.whl", hash = "sha256:608887855ccfe39032bfd03936e3a1c4f4fc99b3a4ac49ced54a4220de61c9c1"}, - {file = "sentry_sdk-1.45.1.tar.gz", hash = "sha256:a16c997c0f4e3df63c0fc5e4207ccb1ab37900433e0f72fef88315d317829a26"}, + {file = "sentry_sdk-2.13.0-py2.py3-none-any.whl", hash = "sha256:6beede8fc2ab4043da7f69d95534e320944690680dd9a963178a49de71d726c6"}, + {file = "sentry_sdk-2.13.0.tar.gz", hash = "sha256:8d4a576f7a98eb2fdb40e13106e41f330e5c79d72a68be1316e7852cf4995260"}, ] [package.dependencies] certifi = "*" fastapi = {version = ">=0.79.0", optional = true, markers = "extra == \"fastapi\""} -urllib3 = {version = ">=1.26.11", markers = "python_version >= \"3.6\""} +urllib3 = ">=1.26.11" [package.extras] aiohttp = ["aiohttp (>=3.5)"] +anthropic = ["anthropic (>=0.16)"] arq = ["arq (>=0.23)"] asyncpg = ["asyncpg (>=0.23)"] beam = ["apache-beam (>=2.12)"] @@ -4199,13 +3612,16 @@ django = ["django (>=1.8)"] falcon = ["falcon (>=1.4)"] fastapi = ["fastapi (>=0.79.0)"] flask = ["blinker (>=1.1)", "flask (>=0.11)", "markupsafe"] -grpcio = ["grpcio (>=1.21.1)"] +grpcio = ["grpcio (>=1.21.1)", "protobuf (>=3.8.0)"] httpx = ["httpx (>=0.16.0)"] huey = ["huey (>=2)"] +huggingface-hub = ["huggingface-hub (>=0.22)"] +langchain = ["langchain (>=0.0.210)"] +litestar = ["litestar (>=2.0.0)"] loguru = ["loguru (>=0.5)"] openai = ["openai (>=1.0.0)", "tiktoken (>=0.3.0)"] opentelemetry = ["opentelemetry-distro (>=0.35b0)"] -opentelemetry-experimental = ["opentelemetry-distro (>=0.40b0,<1.0)", "opentelemetry-instrumentation-aiohttp-client (>=0.40b0,<1.0)", "opentelemetry-instrumentation-django (>=0.40b0,<1.0)", "opentelemetry-instrumentation-fastapi (>=0.40b0,<1.0)", "opentelemetry-instrumentation-flask (>=0.40b0,<1.0)", "opentelemetry-instrumentation-requests (>=0.40b0,<1.0)", "opentelemetry-instrumentation-sqlite3 (>=0.40b0,<1.0)", "opentelemetry-instrumentation-urllib (>=0.40b0,<1.0)"] +opentelemetry-experimental = ["opentelemetry-distro"] pure-eval = ["asttokens", "executing", "pure-eval"] pymongo = ["pymongo (>=3.1)"] pyspark = ["pyspark (>=2.4.4)"] @@ -4215,7 +3631,7 @@ sanic = ["sanic (>=0.8)"] sqlalchemy = ["sqlalchemy (>=1.2)"] starlette = ["starlette (>=0.19.1)"] starlite = ["starlite (>=1.48)"] -tornado = ["tornado (>=5)"] +tornado = ["tornado (>=6)"] [[package]] name = "setuptools" @@ -4277,17 +3693,6 @@ files = [ {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, ] -[[package]] -name = "sortedcontainers" -version = "2.4.0" -description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set" -optional = false -python-versions = "*" -files = [ - {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"}, - {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"}, -] - [[package]] name = "soupsieve" version = "2.6" @@ -4299,93 +3704,6 @@ files = [ {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, ] -[[package]] -name = "sqlalchemy" -version = "2.0.32" -description = "Database Abstraction Library" -optional = false -python-versions = ">=3.7" -files = [ - {file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0c9045ecc2e4db59bfc97b20516dfdf8e41d910ac6fb667ebd3a79ea54084619"}, - {file = "SQLAlchemy-2.0.32-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1467940318e4a860afd546ef61fefb98a14d935cd6817ed07a228c7f7c62f389"}, - {file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5954463675cb15db8d4b521f3566a017c8789222b8316b1e6934c811018ee08b"}, - {file = "SQLAlchemy-2.0.32-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:167e7497035c303ae50651b351c28dc22a40bb98fbdb8468cdc971821b1ae533"}, - {file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:b27dfb676ac02529fb6e343b3a482303f16e6bc3a4d868b73935b8792edb52d0"}, - {file = "SQLAlchemy-2.0.32-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:bf2360a5e0f7bd75fa80431bf8ebcfb920c9f885e7956c7efde89031695cafb8"}, - {file = "SQLAlchemy-2.0.32-cp310-cp310-win32.whl", hash = "sha256:306fe44e754a91cd9d600a6b070c1f2fadbb4a1a257b8781ccf33c7067fd3e4d"}, - {file = "SQLAlchemy-2.0.32-cp310-cp310-win_amd64.whl", hash = "sha256:99db65e6f3ab42e06c318f15c98f59a436f1c78179e6a6f40f529c8cc7100b22"}, - {file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:21b053be28a8a414f2ddd401f1be8361e41032d2ef5884b2f31d31cb723e559f"}, - {file = "SQLAlchemy-2.0.32-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b178e875a7a25b5938b53b006598ee7645172fccafe1c291a706e93f48499ff5"}, - {file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:723a40ee2cc7ea653645bd4cf024326dea2076673fc9d3d33f20f6c81db83e1d"}, - {file = "SQLAlchemy-2.0.32-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:295ff8689544f7ee7e819529633d058bd458c1fd7f7e3eebd0f9268ebc56c2a0"}, - {file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49496b68cd190a147118af585173ee624114dfb2e0297558c460ad7495f9dfe2"}, - {file = "SQLAlchemy-2.0.32-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:acd9b73c5c15f0ec5ce18128b1fe9157ddd0044abc373e6ecd5ba376a7e5d961"}, - {file = "SQLAlchemy-2.0.32-cp311-cp311-win32.whl", hash = "sha256:9365a3da32dabd3e69e06b972b1ffb0c89668994c7e8e75ce21d3e5e69ddef28"}, - {file = "SQLAlchemy-2.0.32-cp311-cp311-win_amd64.whl", hash = "sha256:8bd63d051f4f313b102a2af1cbc8b80f061bf78f3d5bd0843ff70b5859e27924"}, - {file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:6bab3db192a0c35e3c9d1560eb8332463e29e5507dbd822e29a0a3c48c0a8d92"}, - {file = "SQLAlchemy-2.0.32-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:19d98f4f58b13900d8dec4ed09dd09ef292208ee44cc9c2fe01c1f0a2fe440e9"}, - {file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cd33c61513cb1b7371fd40cf221256456d26a56284e7d19d1f0b9f1eb7dd7e8"}, - {file = "SQLAlchemy-2.0.32-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d6ba0497c1d066dd004e0f02a92426ca2df20fac08728d03f67f6960271feec"}, - {file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2b6be53e4fde0065524f1a0a7929b10e9280987b320716c1509478b712a7688c"}, - {file = "SQLAlchemy-2.0.32-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:916a798f62f410c0b80b63683c8061f5ebe237b0f4ad778739304253353bc1cb"}, - {file = "SQLAlchemy-2.0.32-cp312-cp312-win32.whl", hash = "sha256:31983018b74908ebc6c996a16ad3690301a23befb643093fcfe85efd292e384d"}, - {file = "SQLAlchemy-2.0.32-cp312-cp312-win_amd64.whl", hash = "sha256:4363ed245a6231f2e2957cccdda3c776265a75851f4753c60f3004b90e69bfeb"}, - {file = "SQLAlchemy-2.0.32-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b8afd5b26570bf41c35c0121801479958b4446751a3971fb9a480c1afd85558e"}, - {file = "SQLAlchemy-2.0.32-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c750987fc876813f27b60d619b987b057eb4896b81117f73bb8d9918c14f1cad"}, - {file = "SQLAlchemy-2.0.32-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ada0102afff4890f651ed91120c1120065663506b760da4e7823913ebd3258be"}, - {file = "SQLAlchemy-2.0.32-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:78c03d0f8a5ab4f3034c0e8482cfcc415a3ec6193491cfa1c643ed707d476f16"}, - {file = "SQLAlchemy-2.0.32-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:3bd1cae7519283ff525e64645ebd7a3e0283f3c038f461ecc1c7b040a0c932a1"}, - {file = "SQLAlchemy-2.0.32-cp37-cp37m-win32.whl", hash = "sha256:01438ebcdc566d58c93af0171c74ec28efe6a29184b773e378a385e6215389da"}, - {file = "SQLAlchemy-2.0.32-cp37-cp37m-win_amd64.whl", hash = "sha256:4979dc80fbbc9d2ef569e71e0896990bc94df2b9fdbd878290bd129b65ab579c"}, - {file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c742be912f57586ac43af38b3848f7688863a403dfb220193a882ea60e1ec3a"}, - {file = "SQLAlchemy-2.0.32-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:62e23d0ac103bcf1c5555b6c88c114089587bc64d048fef5bbdb58dfd26f96da"}, - {file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:251f0d1108aab8ea7b9aadbd07fb47fb8e3a5838dde34aa95a3349876b5a1f1d"}, - {file = "SQLAlchemy-2.0.32-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ef18a84e5116340e38eca3e7f9eeaaef62738891422e7c2a0b80feab165905f"}, - {file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:3eb6a97a1d39976f360b10ff208c73afb6a4de86dd2a6212ddf65c4a6a2347d5"}, - {file = "SQLAlchemy-2.0.32-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0c1c9b673d21477cec17ab10bc4decb1322843ba35b481585facd88203754fc5"}, - {file = "SQLAlchemy-2.0.32-cp38-cp38-win32.whl", hash = "sha256:c41a2b9ca80ee555decc605bd3c4520cc6fef9abde8fd66b1cf65126a6922d65"}, - {file = "SQLAlchemy-2.0.32-cp38-cp38-win_amd64.whl", hash = "sha256:8a37e4d265033c897892279e8adf505c8b6b4075f2b40d77afb31f7185cd6ecd"}, - {file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:52fec964fba2ef46476312a03ec8c425956b05c20220a1a03703537824b5e8e1"}, - {file = "SQLAlchemy-2.0.32-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:328429aecaba2aee3d71e11f2477c14eec5990fb6d0e884107935f7fb6001632"}, - {file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:85a01b5599e790e76ac3fe3aa2f26e1feba56270023d6afd5550ed63c68552b3"}, - {file = "SQLAlchemy-2.0.32-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aaf04784797dcdf4c0aa952c8d234fa01974c4729db55c45732520ce12dd95b4"}, - {file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:4488120becf9b71b3ac718f4138269a6be99a42fe023ec457896ba4f80749525"}, - {file = "SQLAlchemy-2.0.32-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:14e09e083a5796d513918a66f3d6aedbc131e39e80875afe81d98a03312889e6"}, - {file = "SQLAlchemy-2.0.32-cp39-cp39-win32.whl", hash = "sha256:0d322cc9c9b2154ba7e82f7bf25ecc7c36fbe2d82e2933b3642fc095a52cfc78"}, - {file = "SQLAlchemy-2.0.32-cp39-cp39-win_amd64.whl", hash = "sha256:7dd8583df2f98dea28b5cd53a1beac963f4f9d087888d75f22fcc93a07cf8d84"}, - {file = "SQLAlchemy-2.0.32-py3-none-any.whl", hash = "sha256:e567a8793a692451f706b363ccf3c45e056b67d90ead58c3bc9471af5d212202"}, - {file = "SQLAlchemy-2.0.32.tar.gz", hash = "sha256:c1b88cc8b02b6a5f0efb0345a03672d4c897dc7d92585176f88c67346f565ea8"}, -] - -[package.dependencies] -greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"} -typing-extensions = ">=4.6.0" - -[package.extras] -aiomysql = ["aiomysql (>=0.2.0)", "greenlet (!=0.4.17)"] -aioodbc = ["aioodbc", "greenlet (!=0.4.17)"] -aiosqlite = ["aiosqlite", "greenlet (!=0.4.17)", "typing_extensions (!=3.10.0.1)"] -asyncio = ["greenlet (!=0.4.17)"] -asyncmy = ["asyncmy (>=0.2.3,!=0.2.4,!=0.2.6)", "greenlet (!=0.4.17)"] -mariadb-connector = ["mariadb (>=1.0.1,!=1.1.2,!=1.1.5)"] -mssql = ["pyodbc"] -mssql-pymssql = ["pymssql"] -mssql-pyodbc = ["pyodbc"] -mypy = ["mypy (>=0.910)"] -mysql = ["mysqlclient (>=1.4.0)"] -mysql-connector = ["mysql-connector-python"] -oracle = ["cx_oracle (>=8)"] -oracle-oracledb = ["oracledb (>=1.0.1)"] -postgresql = ["psycopg2 (>=2.7)"] -postgresql-asyncpg = ["asyncpg", "greenlet (!=0.4.17)"] -postgresql-pg8000 = ["pg8000 (>=1.29.1)"] -postgresql-psycopg = ["psycopg (>=3.0.7)"] -postgresql-psycopg2binary = ["psycopg2-binary"] -postgresql-psycopg2cffi = ["psycopg2cffi"] -postgresql-psycopgbinary = ["psycopg[binary] (>=3.0.7)"] -pymysql = ["pymysql"] -sqlcipher = ["sqlcipher3_binary"] - [[package]] name = "stack-data" version = "0.6.3" @@ -4407,13 +3725,13 @@ tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"] [[package]] name = "starlette" -version = "0.37.2" +version = "0.38.2" description = "The little ASGI library that shines." optional = false python-versions = ">=3.8" files = [ - {file = "starlette-0.37.2-py3-none-any.whl", hash = "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee"}, - {file = "starlette-0.37.2.tar.gz", hash = "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823"}, + {file = "starlette-0.38.2-py3-none-any.whl", hash = "sha256:4ec6a59df6bbafdab5f567754481657f7ed90dc9d69b0c9ff017907dd54faeff"}, + {file = "starlette-0.38.2.tar.gz", hash = "sha256:c7c0441065252160993a1a37cf2a73bb64d271b17303e0b0c1eb7191cfb12d75"}, ] [package.dependencies] @@ -4436,17 +3754,6 @@ files = [ [package.extras] widechars = ["wcwidth"] -[[package]] -name = "tblib" -version = "3.0.0" -description = "Traceback serialization library." -optional = false -python-versions = ">=3.8" -files = [ - {file = "tblib-3.0.0-py3-none-any.whl", hash = "sha256:80a6c77e59b55e83911e1e607c649836a69c103963c5f28a46cbeef44acf8129"}, - {file = "tblib-3.0.0.tar.gz", hash = "sha256:93622790a0a29e04f0346458face1e144dc4d32f493714c6c3dff82a4adb77e6"}, -] - [[package]] name = "temporalio" version = "1.6.0" @@ -4473,13 +3780,13 @@ opentelemetry = ["opentelemetry-api (>=1.11.1,<2.0.0)", "opentelemetry-sdk (>=1. [[package]] name = "tenacity" -version = "8.5.0" +version = "9.0.0" description = "Retry code until it succeeds" optional = false python-versions = ">=3.8" files = [ - {file = "tenacity-8.5.0-py3-none-any.whl", hash = "sha256:b594c2a5945830c267ce6b79a166228323ed52718f30302c1359836112346687"}, - {file = "tenacity-8.5.0.tar.gz", hash = "sha256:8bc6c0c8a09b31e6cad13c47afbed1a567518250a9a171418582ed8d9c20ca78"}, + {file = "tenacity-9.0.0-py3-none-any.whl", hash = "sha256:93de0c98785b27fcf659856aa9f54bfbd399e29969b0621bc7f762bd441b4539"}, + {file = "tenacity-9.0.0.tar.gz", hash = "sha256:807f37ca97d62aa361264d497b0e31e92b8027044942bfa756160d908320d73b"}, ] [package.extras] @@ -4730,17 +4037,6 @@ files = [ {file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"}, ] -[[package]] -name = "toolz" -version = "0.12.1" -description = "List processing tools and functional utilities" -optional = false -python-versions = ">=3.7" -files = [ - {file = "toolz-0.12.1-py3-none-any.whl", hash = "sha256:d22731364c07d72eea0a0ad45bafb2c2937ab6fd38a3507bf55eae8744aa7d85"}, - {file = "toolz-0.12.1.tar.gz", hash = "sha256:ecca342664893f177a13dac0e6b41cbd8ac25a358e5f215316d43e2100224f4d"}, -] - [[package]] name = "tornado" version = "6.4.1" @@ -4846,21 +4142,6 @@ files = [ {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, ] -[[package]] -name = "typing-inspect" -version = "0.9.0" -description = "Runtime inspection utilities for typing module." -optional = false -python-versions = "*" -files = [ - {file = "typing_inspect-0.9.0-py3-none-any.whl", hash = "sha256:9ee6fc59062311ef8547596ab6b955e1b8aa46242d854bfc78f4f6b0eff35f9f"}, - {file = "typing_inspect-0.9.0.tar.gz", hash = "sha256:b23fc42ff6f6ef6954e4852c1fb512cdd18dbea03134f91f856a95ccc9461f78"}, -] - -[package.dependencies] -mypy-extensions = ">=0.3.0" -typing-extensions = ">=3.7.4" - [[package]] name = "tzdata" version = "2024.1" @@ -4905,13 +4186,13 @@ zstd = ["zstandard (>=0.18.0)"] [[package]] name = "uvicorn" -version = "0.23.2" +version = "0.30.6" description = "The lightning-fast ASGI server." optional = false python-versions = ">=3.8" files = [ - {file = "uvicorn-0.23.2-py3-none-any.whl", hash = "sha256:1f9be6558f01239d4fdf22ef8126c39cb1ad0addf76c40e760549d2c2f43ab53"}, - {file = "uvicorn-0.23.2.tar.gz", hash = "sha256:4d3cc12d7727ba72b64d12d3cc7743124074c0a69f7b201512fc50c3e3f1569a"}, + {file = "uvicorn-0.30.6-py3-none-any.whl", hash = "sha256:65fd46fe3fda5bdc1b03b94eb634923ff18cd35b2f084813ea79d1f103f711b5"}, + {file = "uvicorn-0.30.6.tar.gz", hash = "sha256:4b15decdda1e72be08209e860a1e10e92439ad5b97cf44cc945fcbee66fc5788"}, ] [package.dependencies] @@ -5120,17 +4401,6 @@ files = [ idna = ">=2.0" multidict = ">=4.0" -[[package]] -name = "zict" -version = "3.0.0" -description = "Mutable mapping tools" -optional = false -python-versions = ">=3.8" -files = [ - {file = "zict-3.0.0-py2.py3-none-any.whl", hash = "sha256:5796e36bd0e0cc8cf0fbc1ace6a68912611c1dbd74750a3f3026b9b9d6a327ae"}, - {file = "zict-3.0.0.tar.gz", hash = "sha256:e321e263b6a97aafc0790c3cfb3c04656b7066e6738c37fffcca95d803c9fba5"}, -] - [[package]] name = "zipp" version = "3.20.0" @@ -5149,4 +4419,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "df48532534cbce05d6360e5c818a984efaa498b5d3f465a8669c0a1b05977bc4" +content-hash = "9ba28e1010a979afba89f2fa3924462e004f7f1bdf4e3a4a839b96241df04856" diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index 88068f962..e27a2c1f9 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -8,43 +8,40 @@ packages = [{include = "agents_api"}] [tool.poetry.dependencies] python = ">=3.11,<3.12" -fastapi = "^0.110.1" +fastapi = "^0.112.1" pycozo = {extras = ["embedded"], version = "^0.7.6"} -uvicorn = "^0.23.2" +uvicorn = "^0.30.6" fire = "^0.5.0" environs = "^10.3.0" -pandas = "^2.1.0" -openai = "^1.12.0" -httpx = "^0.26.0" -sentry-sdk = {extras = ["fastapi"], version = "^1.38.0"} -temporalio = "^1.4.0" -pydantic = "^2.5.3" +pandas = "^2.2.2" +openai = "^1.41.0" +httpx = "^0.27.0" +sentry-sdk = {extras = ["fastapi"], version = "^2.13.0"} +temporalio = "^1.6.0" +pydantic = "^2.8.2" arrow = "^1.3.0" -jinja2 = "^3.1.3" +jinja2 = "^3.1.4" jinja2schema = "^0.1.4" jsonschema = "^4.21.1" -litellm = "^1.43.3" -numpy = "^1.26.4" +litellm = "^1.43.18" +numpy = "^2.1.0" tiktoken = "^0.7.0" -tenacity = "^8.3.0" +tenacity = "^9.0.0" beartype = "^0.18.5" pydantic-partial = "^0.5.5" simpleeval = "^0.9.13" [tool.poetry.group.dev.dependencies] -ipython = "^8.18.1" +ipython = "^8.26.0" ruff = "^0.5.5" -datamodel-code-generator = "^0.25.8" +datamodel-code-generator = "^0.25.9" cozo-migrate = "^0.2.0" poethepoet = "^0.25.1" pytype = ">=2024.4.11" -julep = "^0.2.4" pyjwt = "^2.8.0" ward = "^0.68.0b0" -jupyterlab = "^4.1.8" -ipywidgets = "^8.1.2" -jupyter-ai = "^2.14.1" -langchain-openai = "^0.1.6" +jupyterlab = "^4.2.4" +ipywidgets = "^8.1.3" wat-inspector = "^0.2.1" [build-system] diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index a1108a5c4..999643168 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -448,3 +448,49 @@ async def _( "transition_step", "raise_complete_async", ] + + +@test("workflow: if-else step") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + { + "if": "True", + "then": {"evaluate": {"hello": '"world"'}}, + "else": {"evaluate": {"hello": '"nope"'}}, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == "world" diff --git a/sdks/python/julep/api/types/executions_transition_target.py b/sdks/python/julep/api/types/executions_transition_target.py index 30b348941..5efc68e37 100644 --- a/sdks/python/julep/api/types/executions_transition_target.py +++ b/sdks/python/julep/api/types/executions_transition_target.py @@ -5,11 +5,11 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .common_valid_python_identifier import CommonValidPythonIdentifier +from .common_identifier_safe_unicode import CommonIdentifierSafeUnicode class ExecutionsTransitionTarget(pydantic_v1.BaseModel): - workflow: CommonValidPythonIdentifier + workflow: CommonIdentifierSafeUnicode step: int def json(self, **kwargs: typing.Any) -> str: diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index f444f51a8..cb99f66f7 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -1749,6 +1749,67 @@ files = [ {file = "numpy-2.0.1.tar.gz", hash = "sha256:485b87235796410c3519a699cfe1faab097e509e90ebb05dcd098db2ae87e7b3"}, ] +[[package]] +name = "numpy" +version = "2.1.0" +description = "Fundamental package for array computing in Python" +optional = false +python-versions = ">=3.10" +files = [ + {file = "numpy-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6326ab99b52fafdcdeccf602d6286191a79fe2fda0ae90573c5814cd2b0bc1b8"}, + {file = "numpy-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0937e54c09f7a9a68da6889362ddd2ff584c02d015ec92672c099b61555f8911"}, + {file = "numpy-2.1.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:30014b234f07b5fec20f4146f69e13cfb1e33ee9a18a1879a0142fbb00d47673"}, + {file = "numpy-2.1.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:899da829b362ade41e1e7eccad2cf274035e1cb36ba73034946fccd4afd8606b"}, + {file = "numpy-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:08801848a40aea24ce16c2ecde3b756f9ad756586fb2d13210939eb69b023f5b"}, + {file = "numpy-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:398049e237d1aae53d82a416dade04defed1a47f87d18d5bd615b6e7d7e41d1f"}, + {file = "numpy-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0abb3916a35d9090088a748636b2c06dc9a6542f99cd476979fb156a18192b84"}, + {file = "numpy-2.1.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:10e2350aea18d04832319aac0f887d5fcec1b36abd485d14f173e3e900b83e33"}, + {file = "numpy-2.1.0-cp310-cp310-win32.whl", hash = "sha256:f6b26e6c3b98adb648243670fddc8cab6ae17473f9dc58c51574af3e64d61211"}, + {file = "numpy-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:f505264735ee074250a9c78247ee8618292091d9d1fcc023290e9ac67e8f1afa"}, + {file = "numpy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:76368c788ccb4f4782cf9c842b316140142b4cbf22ff8db82724e82fe1205dce"}, + {file = "numpy-2.1.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:f8e93a01a35be08d31ae33021e5268f157a2d60ebd643cfc15de6ab8e4722eb1"}, + {file = "numpy-2.1.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9523f8b46485db6939bd069b28b642fec86c30909cea90ef550373787f79530e"}, + {file = "numpy-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:54139e0eb219f52f60656d163cbe67c31ede51d13236c950145473504fa208cb"}, + {file = "numpy-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5ebbf9fbdabed208d4ecd2e1dfd2c0741af2f876e7ae522c2537d404ca895c3"}, + {file = "numpy-2.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:378cb4f24c7d93066ee4103204f73ed046eb88f9ad5bb2275bb9fa0f6a02bd36"}, + {file = "numpy-2.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8f699a709120b220dfe173f79c73cb2a2cab2c0b88dd59d7b49407d032b8ebd"}, + {file = "numpy-2.1.0-cp311-cp311-win32.whl", hash = "sha256:ffbd6faeb190aaf2b5e9024bac9622d2ee549b7ec89ef3a9373fa35313d44e0e"}, + {file = "numpy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:0af3a5987f59d9c529c022c8c2a64805b339b7ef506509fba7d0556649b9714b"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fe76d75b345dc045acdbc006adcb197cc680754afd6c259de60d358d60c93736"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f358ea9e47eb3c2d6eba121ab512dfff38a88db719c38d1e67349af210bc7529"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:dd94ce596bda40a9618324547cfaaf6650b1a24f5390350142499aa4e34e53d1"}, + {file = "numpy-2.1.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:b47c551c6724960479cefd7353656498b86e7232429e3a41ab83be4da1b109e8"}, + {file = "numpy-2.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0756a179afa766ad7cb6f036de622e8a8f16ffdd55aa31f296c870b5679d745"}, + {file = "numpy-2.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24003ba8ff22ea29a8c306e61d316ac74111cebf942afbf692df65509a05f111"}, + {file = "numpy-2.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:b34fa5e3b5d6dc7e0a4243fa0f81367027cb6f4a7215a17852979634b5544ee0"}, + {file = "numpy-2.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c4f982715e65036c34897eb598d64aef15150c447be2cfc6643ec7a11af06574"}, + {file = "numpy-2.1.0-cp312-cp312-win32.whl", hash = "sha256:c4cd94dfefbefec3f8b544f61286584292d740e6e9d4677769bc76b8f41deb02"}, + {file = "numpy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:a0cdef204199278f5c461a0bed6ed2e052998276e6d8ab2963d5b5c39a0500bc"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:8ab81ccd753859ab89e67199b9da62c543850f819993761c1e94a75a814ed667"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:442596f01913656d579309edcd179a2a2f9977d9a14ff41d042475280fc7f34e"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:848c6b5cad9898e4b9ef251b6f934fa34630371f2e916261070a4eb9092ffd33"}, + {file = "numpy-2.1.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:54c6a63e9d81efe64bfb7bcb0ec64332a87d0b87575f6009c8ba67ea6374770b"}, + {file = "numpy-2.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:652e92fc409e278abdd61e9505649e3938f6d04ce7ef1953f2ec598a50e7c195"}, + {file = "numpy-2.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0ab32eb9170bf8ffcbb14f11613f4a0b108d3ffee0832457c5d4808233ba8977"}, + {file = "numpy-2.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8fb49a0ba4d8f41198ae2d52118b050fd34dace4b8f3fb0ee34e23eb4ae775b1"}, + {file = "numpy-2.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:44e44973262dc3ae79e9063a1284a73e09d01b894b534a769732ccd46c28cc62"}, + {file = "numpy-2.1.0-cp313-cp313-win32.whl", hash = "sha256:ab83adc099ec62e044b1fbb3a05499fa1e99f6d53a1dde102b2d85eff66ed324"}, + {file = "numpy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:de844aaa4815b78f6023832590d77da0e3b6805c644c33ce94a1e449f16d6ab5"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:343e3e152bf5a087511cd325e3b7ecfd5b92d369e80e74c12cd87826e263ec06"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f07fa2f15dabe91259828ce7d71b5ca9e2eb7c8c26baa822c825ce43552f4883"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5474dad8c86ee9ba9bb776f4b99ef2d41b3b8f4e0d199d4f7304728ed34d0300"}, + {file = "numpy-2.1.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:1f817c71683fd1bb5cff1529a1d085a57f02ccd2ebc5cd2c566f9a01118e3b7d"}, + {file = "numpy-2.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a3336fbfa0d38d3deacd3fe7f3d07e13597f29c13abf4d15c3b6dc2291cbbdd"}, + {file = "numpy-2.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a894c51fd8c4e834f00ac742abad73fc485df1062f1b875661a3c1e1fb1c2f6"}, + {file = "numpy-2.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:9156ca1f79fc4acc226696e95bfcc2b486f165a6a59ebe22b2c1f82ab190384a"}, + {file = "numpy-2.1.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:624884b572dff8ca8f60fab591413f077471de64e376b17d291b19f56504b2bb"}, + {file = "numpy-2.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:15ef8b2177eeb7e37dd5ef4016f30b7659c57c2c0b57a779f1d537ff33a72c7b"}, + {file = "numpy-2.1.0-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:e5f0642cdf4636198a4990de7a71b693d824c56a757862230454629cf62e323d"}, + {file = "numpy-2.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15976718c004466406342789f31b6673776360f3b1e3c575f25302d7e789575"}, + {file = "numpy-2.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:6c1de77ded79fef664d5098a66810d4d27ca0224e9051906e634b3f7ead134c2"}, + {file = "numpy-2.1.0.tar.gz", hash = "sha256:7dc90da0081f7e1da49ec4e398ede6a8e9cc4f5ebe5f9e06b443ed889ee9aaa2"}, +] + [[package]] name = "openai" version = "1.41.0" diff --git a/sdks/ts/src/api/models/Executions_TransitionTarget.ts b/sdks/ts/src/api/models/Executions_TransitionTarget.ts index 804d89dcd..fa511c40d 100644 --- a/sdks/ts/src/api/models/Executions_TransitionTarget.ts +++ b/sdks/ts/src/api/models/Executions_TransitionTarget.ts @@ -2,8 +2,8 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Common_validPythonIdentifier } from "./Common_validPythonIdentifier"; +import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; export type Executions_TransitionTarget = { - workflow: Common_validPythonIdentifier; + workflow: Common_identifierSafeUnicode; step: number; }; diff --git a/sdks/ts/src/api/schemas/$Executions_TransitionTarget.ts b/sdks/ts/src/api/schemas/$Executions_TransitionTarget.ts index d1ead50a5..62d5b4962 100644 --- a/sdks/ts/src/api/schemas/$Executions_TransitionTarget.ts +++ b/sdks/ts/src/api/schemas/$Executions_TransitionTarget.ts @@ -5,7 +5,7 @@ export const $Executions_TransitionTarget = { properties: { workflow: { - type: "Common_validPythonIdentifier", + type: "Common_identifierSafeUnicode", isRequired: true, }, step: { diff --git a/typespec/executions/models.tsp b/typespec/executions/models.tsp index 961c8389a..0f7be074f 100644 --- a/typespec/executions/models.tsp +++ b/typespec/executions/models.tsp @@ -109,7 +109,7 @@ alias TransitionType = ( ); model TransitionTarget { - workflow: validPythonIdentifier; + workflow: identifierSafeUnicode; step: uint16; } diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp index 9f13c923b..152f6482b 100644 --- a/typespec/tasks/steps.tsp +++ b/typespec/tasks/steps.tsp @@ -28,6 +28,10 @@ model BaseWorkflowStep { kind_: WorkflowStepKind; } +BREAKING THIS FILE INTENTIONALLY SO IT DOESTN'T GET COMPILED +BECAUSE IT SEEMS LIKE TYPESPEC HAS A BUG WHERE IT ADDS `{}` TO THE RENDERED `NonConditionalWorkflowStep` +YOU CAN COMMENT THIS AND COMPILE IF YOU KNOW WHAT YOU'RE DOING + alias NonConditionalWorkflowStep = | EvaluateStep | ToolCallStep From aead2ab85d4b384a8fad0b5537f410bb697e7845 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 18 Aug 2024 21:40:16 -0400 Subject: [PATCH 080/110] wip(agents-api): Add stub for switch step Signed-off-by: Diwank Tomer --- .../activities/task_steps/__init__.py | 1 + .../activities/task_steps/switch_step.py | 48 ++++++++++++++++ agents-api/agents_api/worker/worker.py | 2 + .../agents_api/workflows/task_execution.py | 12 +++- agents-api/tests/test_execution_workflow.py | 57 +++++++++++++++++++ 5 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 agents-api/agents_api/activities/task_steps/switch_step.py diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 73cf802de..e1792f051 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -6,6 +6,7 @@ from .prompt_step import prompt_step from .raise_complete_async import raise_complete_async from .return_step import return_step +from .switch_step import switch_step from .tool_call_step import tool_call_step from .transition_step import transition_step from .wait_for_input_step import wait_for_input_step diff --git a/agents-api/agents_api/activities/task_steps/switch_step.py b/agents-api/agents_api/activities/task_steps/switch_step.py new file mode 100644 index 000000000..60e64033e --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/switch_step.py @@ -0,0 +1,48 @@ +import logging + +from beartype import beartype +from simpleeval import simple_eval +from temporalio import activity + +from ...autogen.openapi_model import SwitchStep +from ...common.protocol.tasks import ( + StepContext, + StepOutcome, +) +from ...env import testing + + +@beartype +async def switch_step(context: StepContext) -> StepOutcome: + # NOTE: This activity is only for logging, so we just evaluate the expression + # Hence, it's a local activity and SHOULD NOT fail + try: + assert isinstance(context.current_step, SwitchStep) + + # Assume that none of the cases evaluate to truthy + output: int = -1 + + cases: list[str] = [c.case for c in context.current_step.switch] + + for i, case in enumerate(cases): + result = simple_eval(case, names=context.model_dump()) + + if result: + output = i + break + + result = StepOutcome(output=output) + return result + + except BaseException as e: + logging.error(f"Error in switch_step: {e}") + return StepOutcome(error=str(e)) + + +# Note: This is here just for clarity. We could have just imported switch_step directly +# They do the same thing, so we dont need to mock the switch_step function +mock_switch_step = switch_step + +switch_step = activity.defn(name="switch_step")( + switch_step if not testing else mock_switch_step +) diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index 10b3753ad..604454c4c 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -22,6 +22,7 @@ def create_worker(client: Client) -> Any: log_step, prompt_step, return_step, + switch_step, tool_call_step, transition_step, wait_for_input_step, @@ -45,6 +46,7 @@ def create_worker(client: Client) -> Any: log_step, prompt_step, return_step, + switch_step, tool_call_step, transition_step, wait_for_input_step, diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 546bbca62..d2d357cef 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -20,6 +20,7 @@ ReturnStep, SleepFor, SleepStep, + SwitchStep, # ToolCallStep, TransitionTarget, WaitForInputStep, @@ -40,8 +41,10 @@ # ToolCallStep: tool_call_step, WaitForInputStep: task_steps.wait_for_input_step, LogStep: task_steps.log_step, + SwitchStep: task_steps.switch_step, } +# Use few local activities (currently experimental) STEP_TO_LOCAL_ACTIVITY = { # NOTE: local activities are directly called in the workflow executor # They MUST NOT FAIL, otherwise they will crash the workflow @@ -142,6 +145,13 @@ async def transition(**kwargs) -> None: await transition(output=output, type="finish", next=None) return output # <--- Byeeee! + case SwitchStep(switch=switch), StepOutcome(output=index) if index >= 0: + raise NotImplementedError("SwitchStep is not implemented") + + case SwitchStep(), StepOutcome(output=index) if index < 0: + # If no case matched, then the output will be -1 + raise NotImplementedError("SwitchStep is not implemented") + case IfElseWorkflowStep(then=then_branch, else_=else_branch), StepOutcome( output=condition ): @@ -150,7 +160,7 @@ async def transition(**kwargs) -> None: # Create a faux workflow if_else_wf_name = ( - f"{context.cursor.workflow}[{context.cursor.step}].if_else" + f"`{context.cursor.workflow}`[{context.cursor.step}].if_else" ) if_else_wf_name += ".then" if condition else ".else" diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 999643168..2f6d434a3 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -494,3 +494,60 @@ async def _( result = await handle.result() assert result["hello"] == "world" + + +@test("workflow: switch step") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + { + "switch": [ + { + "case": "False", + "then": {"evaluate": {"hello": '"bubbles"'}}, + }, + { + "case": "True", + "then": {"evaluate": {"hello": '"world"'}}, + }, + { + "case": "True", + "then": {"evaluate": {"hello": '"bye"'}}, + }, + ] + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == "world" From 9c019287684aff91a4b2fc601da722962e3ca8a6 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 19 Aug 2024 14:09:46 -0400 Subject: [PATCH 081/110] fix(agents-api): Fix typespec for foreach step and regenerate Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Tasks.py | 18 +- agents-api/poetry.lock | 232 +++--- sdks/python/julep/api/__init__.py | 38 +- sdks/python/julep/api/client.py | 24 +- sdks/python/julep/api/reference.md | 2 +- sdks/python/julep/api/types/__init__.py | 42 +- .../api/types/tasks_base_workflow_step.py | 670 +++++++++++++++++- .../tasks_create_task_request_main_item.py | 2 +- .../julep/api/types/tasks_embed_step.py | 5 +- .../api/types/tasks_error_workflow_step.py | 5 +- .../julep/api/types/tasks_evaluate_step.py | 5 +- .../julep/api/types/tasks_foreach_do.py | 4 +- ...h_do_do_item.py => tasks_foreach_do_do.py} | 2 +- .../julep/api/types/tasks_foreach_step.py | 5 +- sdks/python/julep/api/types/tasks_get_step.py | 5 +- .../api/types/tasks_if_else_workflow_step.py | 3 +- sdks/python/julep/api/types/tasks_log_step.py | 5 +- .../julep/api/types/tasks_map_reduce_step.py | 7 +- .../julep/api/types/tasks_parallel_step.py | 5 +- .../tasks_patch_task_request_main_item.py | 2 +- .../julep/api/types/tasks_prompt_step.py | 5 +- .../julep/api/types/tasks_return_step.py | 3 +- .../julep/api/types/tasks_search_step.py | 5 +- sdks/python/julep/api/types/tasks_set_step.py | 3 +- .../julep/api/types/tasks_sleep_step.py | 5 +- .../julep/api/types/tasks_switch_step.py | 5 +- .../julep/api/types/tasks_task_main_item.py | 2 +- .../julep/api/types/tasks_tool_call_step.py | 5 +- .../tasks_update_task_request_main_item.py | 2 +- .../api/types/tasks_wait_for_input_step.py | 5 +- .../julep/api/types/tasks_yield_step.py | 5 +- sdks/python/poetry.lock | 220 +++--- sdks/ts/src/api/models/Tasks_ForeachDo.ts | 5 +- sdks/ts/src/api/models/Tasks_MapReduceStep.ts | 4 +- sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts | 82 ++- .../src/api/schemas/$Tasks_MapReduceStep.ts | 3 +- sdks/ts/src/api/services/DefaultService.ts | 8 +- typespec/chat/endpoints.tsp | 2 - typespec/common/interfaces.tsp | 12 +- typespec/tasks/endpoints.tsp | 49 +- typespec/tasks/steps.tsp | 7 +- 41 files changed, 1116 insertions(+), 402 deletions(-) rename sdks/python/julep/api/types/{tasks_foreach_do_do_item.py => tasks_foreach_do_do.py} (96%) diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index 01a2cd602..742dcc3cf 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -57,7 +57,7 @@ class CaseThen(BaseModel): The condition to evaluate """ then: ( - Any + EvaluateStep | ToolCallStep | YieldStep | PromptStep @@ -165,8 +165,8 @@ class ForeachDo(BaseModel): """ The variable to iterate over """ - do: list[ - Any + do: ( + EvaluateStep | ToolCallStep | YieldStep | PromptStep @@ -179,7 +179,7 @@ class ForeachDo(BaseModel): | EmbedStep | SearchStep | WaitForInputStep - ] + ) """ The steps to run for each iteration """ @@ -217,7 +217,7 @@ class IfElseWorkflowStep(BaseWorkflowStep): The condition to evaluate """ then: ( - Any + EvaluateStep | ToolCallStep | YieldStep | PromptStep @@ -235,7 +235,7 @@ class IfElseWorkflowStep(BaseWorkflowStep): The steps to run if the condition is true """ else_: Annotated[ - Any + EvaluateStep | ToolCallStep | YieldStep | PromptStep @@ -289,9 +289,9 @@ class MapReduceStep(BaseWorkflowStep): """ The steps to run for each iteration """ - reduce: str + reduce: str | None = None """ - The expression to reduce the results (`_` is a list of outputs) + The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. """ @@ -301,7 +301,7 @@ class ParallelStep(BaseWorkflowStep): ) kind_: Literal["parallel"] = "parallel" parallel: list[ - Any + EvaluateStep | ToolCallStep | YieldStep | PromptStep diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index d22f8beec..98bc5c498 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -2,13 +2,13 @@ [[package]] name = "aiohappyeyeballs" -version = "2.3.7" +version = "2.4.0" description = "Happy Eyeballs for asyncio" optional = false python-versions = ">=3.8" files = [ - {file = "aiohappyeyeballs-2.3.7-py3-none-any.whl", hash = "sha256:337ce4dc0e99eb697c3c5a77d6cb3c52925824d9a67ac0dea7c55b8a2d60b222"}, - {file = "aiohappyeyeballs-2.3.7.tar.gz", hash = "sha256:e794cd29ba6a14078092984e43688212a19081de3a73b6796c2fdeb3706dd6ce"}, + {file = "aiohappyeyeballs-2.4.0-py3-none-any.whl", hash = "sha256:7ce92076e249169a13c2f49320d1967425eaf1f407522d707d59cac7628d62bd"}, + {file = "aiohappyeyeballs-2.4.0.tar.gz", hash = "sha256:55a1714f084e63d49639800f95716da97a1f173d46a16dfcfda0016abb93b6b2"}, ] [[package]] @@ -1159,13 +1159,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "huggingface-hub" -version = "0.24.5" +version = "0.24.6" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false python-versions = ">=3.8.0" files = [ - {file = "huggingface_hub-0.24.5-py3-none-any.whl", hash = "sha256:d93fb63b1f1a919a22ce91a14518974e81fc4610bf344dfe7572343ce8d3aced"}, - {file = "huggingface_hub-0.24.5.tar.gz", hash = "sha256:7b45d6744dd53ce9cbf9880957de00e9d10a9ae837f1c9b7255fc8fa4e8264f3"}, + {file = "huggingface_hub-0.24.6-py3-none-any.whl", hash = "sha256:a990f3232aa985fe749bc9474060cbad75e8b2f115f6665a9fda5b9c97818970"}, + {file = "huggingface_hub-0.24.6.tar.gz", hash = "sha256:cc2579e761d070713eaa9c323e3debe39d5b464ae3a7261c39a9195b27bb8000"}, ] [package.dependencies] @@ -3140,120 +3140,120 @@ files = [ [[package]] name = "pyzmq" -version = "26.1.0" +version = "26.1.1" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e"}, - {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88"}, - {file = "pyzmq-26.1.0-cp310-cp310-win32.whl", hash = "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b"}, - {file = "pyzmq-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2"}, - {file = "pyzmq-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1"}, - {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71"}, - {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b"}, - {file = "pyzmq-26.1.0-cp311-cp311-win32.whl", hash = "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829"}, - {file = "pyzmq-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29"}, - {file = "pyzmq-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb"}, - {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072"}, - {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83"}, - {file = "pyzmq-26.1.0-cp312-cp312-win32.whl", hash = "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3"}, - {file = "pyzmq-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd"}, - {file = "pyzmq-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4"}, - {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322"}, - {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4"}, - {file = "pyzmq-26.1.0-cp313-cp313-win32.whl", hash = "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277"}, - {file = "pyzmq-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250"}, - {file = "pyzmq-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1"}, - {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d"}, - {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3"}, - {file = "pyzmq-26.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3"}, - {file = "pyzmq-26.1.0-cp37-cp37m-win32.whl", hash = "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1"}, - {file = "pyzmq-26.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a"}, - {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0"}, - {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c"}, - {file = "pyzmq-26.1.0-cp38-cp38-win32.whl", hash = "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741"}, - {file = "pyzmq-26.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86"}, - {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b"}, - {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8"}, - {file = "pyzmq-26.1.0-cp39-cp39-win32.whl", hash = "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2"}, - {file = "pyzmq-26.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402"}, - {file = "pyzmq-26.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c"}, - {file = "pyzmq-26.1.0.tar.gz", hash = "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f"}, + {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b1bb952d1e407463c9333ea7e0c0600001e54e08ce836d4f0aff1fb3f902cf63"}, + {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65e2a18e845c6ea7ab849c70db932eaeadee5edede9e379eb21c0a44cf523b2e"}, + {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:def7ae3006924b8a0c146a89ab4008310913fa903beedb95e25dea749642528e"}, + {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8234571df7816f99dde89c3403cb396d70c6554120b795853a8ea56fcc26cd3"}, + {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18da8e84dbc30688fd2baefd41df7190607511f916be34f9a24b0e007551822e"}, + {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c70dab93d98b2bf3f0ac1265edbf6e7f83acbf71dabcc4611889bb0dea45bed7"}, + {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fcb90592c5d5c562e1b1a1ceccf6f00036d73c51db0271bf4d352b8d6b31d468"}, + {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cf4be7460a0c1bc71e9b0e64ecdd75a86386ca6afaa36641686f5542d0314e9d"}, + {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4cbecda4ddbfc1e309c3be04d333f9be3fc6178b8b6592b309676f929767a15"}, + {file = "pyzmq-26.1.1-cp310-cp310-win32.whl", hash = "sha256:583f73b113b8165713b6ce028d221402b1b69483055b5aa3f991937e34dd1ead"}, + {file = "pyzmq-26.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5e6f39ecb8eb7bfcb976c49262e8cf83ff76e082b77ca23ba90c9b6691a345be"}, + {file = "pyzmq-26.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:8d042d6446cab3a1388b38596f5acabb9926b0b95c3894c519356b577a549458"}, + {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:362cac2423e36966d336d79d3ec3eafeabc153ee3e7a5cf580d7e74a34b3d912"}, + {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0841633446cb1539a832a19bb24c03a20c00887d0cedd1d891b495b07e5c5cb5"}, + {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e1fcdc333afbf9918d0a614a6e10858aede7da49a60f6705a77e343fe86a317"}, + {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc8d655627d775475eafdcf0e49e74bcc1e5e90afd9ab813b4da98f092ed7b93"}, + {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32de51744820857a6f7c3077e620ab3f607d0e4388dfead885d5124ab9bcdc5e"}, + {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a880240597010914ffb1d6edd04d3deb7ce6a2abf79a0012751438d13630a671"}, + {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:26131b1cec02f941ed2d2b4b8cc051662b1c248b044eff5069df1f500bbced56"}, + {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce05841322b58510607f9508a573138d995a46c7928887bc433de9cb760fd2ad"}, + {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32123ff0a6db521aadf2b95201e967a4e0d11fb89f73663a99d2f54881c07214"}, + {file = "pyzmq-26.1.1-cp311-cp311-win32.whl", hash = "sha256:e790602d7ea1d6c7d8713d571226d67de7ffe47b1e22ae2c043ebd537de1bccb"}, + {file = "pyzmq-26.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:717960855f2d6fdc2dba9df49dff31c414187bb11c76af36343a57d1f7083d9a"}, + {file = "pyzmq-26.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:08956c26dbcd4fd8835cb777a16e21958ed2412317630e19f0018d49dbeeb470"}, + {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e80345900ae241c2c51bead7c9fa247bba6d4b2a83423e9791bae8b0a7f12c52"}, + {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ec8fe214fcc45dfb0c32e4a7ad1db20244ba2d2fecbf0cbf9d5242d81ca0a375"}, + {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf4e283f97688d993cb7a8acbc22889effbbb7cbaa19ee9709751f44be928f5d"}, + {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2508bdc8ab246e5ed7c92023d4352aaad63020ca3b098a4e3f1822db202f703d"}, + {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:741bdb4d96efe8192616abdc3671931d51a8bcd38c71da2d53fb3127149265d1"}, + {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:76154943e4c4054b2591792eb3484ef1dd23d59805759f9cebd2f010aa30ee8c"}, + {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9498ac427d20d0e0ef0e4bbd6200841e91640dfdf619f544ceec7f464cfb6070"}, + {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f34453ef3496ca3462f30435bf85f535f9550392987341f9ccc92c102825a79"}, + {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:50f0669324e27cc2091ef6ab76ca7112f364b6249691790b4cffce31e73fda28"}, + {file = "pyzmq-26.1.1-cp312-cp312-win32.whl", hash = "sha256:3ee5cbf2625b94de21c68d0cefd35327c8dfdbd6a98fcc41682b4e8bb00d841f"}, + {file = "pyzmq-26.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:75bd448a28b1001b6928679015bc95dd5f172703ed30135bb9e34fc9cda0a3e7"}, + {file = "pyzmq-26.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:4350233569b4bbef88595c5e77ee38995a6f1f1790fae148b578941bfffd1c24"}, + {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8087a3281c20b1d11042d372ed5a47734af05975d78e4d1d6e7bd1018535f3"}, + {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ebef7d3fe11fe4c688f08bc0211a976c3318c097057f258428200737b9fff4da"}, + {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a5342110510045a47de1e87f5f1dcc1d9d90109522316dc9830cfc6157c800f"}, + {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af690ea4be6ca92a67c2b44a779a023bf0838e92d48497a2268175dc4a505691"}, + {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc994e220c1403ae087d7f0fa45129d583e46668a019e389060da811a5a9320e"}, + {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b8e153f5dffb0310af71fc6fc9cd8174f4c8ea312c415adcb815d786fee78179"}, + {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0065026e624052a51033857e5cd45a94b52946b44533f965f0bdf182460e965d"}, + {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:63351392f948b5d50b9f55161994bc4feedbfb3f3cfe393d2f503dea2c3ec445"}, + {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ffecc43b3c18e36b62fcec995761829b6ac325d8dd74a4f2c5c1653afbb4495a"}, + {file = "pyzmq-26.1.1-cp313-cp313-win32.whl", hash = "sha256:6ff14c2fae6c0c2c1c02590c5c5d75aa1db35b859971b3ca2fcd28f983d9f2b6"}, + {file = "pyzmq-26.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:85f2d2ee5ea9a8f1de86a300e1062fbab044f45b5ce34d20580c0198a8196db0"}, + {file = "pyzmq-26.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:cc09b1de8b985ca5a0ca343dd7fb007267c6b329347a74e200f4654268084239"}, + {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:bc904e86de98f8fc5bd41597da5d61232d2d6d60c4397f26efffabb961b2b245"}, + {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:00f39c367bbd6aa8e4bc36af6510561944c619b58eb36199fa334b594a18f615"}, + {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de6f384864a959866b782e6a3896538d1424d183f2d3c7ef079f71dcecde7284"}, + {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3abb15df0c763339edb27a644c19381b2425ddd1aea3dbd77c1601a3b31867b8"}, + {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40908ec2dd3b29bbadc0916a0d3c87f8dbeebbd8fead8e618539f09e0506dec4"}, + {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c11a95d3f6fc7e714ccd1066f68f9c1abd764a8b3596158be92f46dd49f41e03"}, + {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:4437af9fee7a58302dbd511cc49f0cc2b35c112a33a1111fb123cf0be45205ca"}, + {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:76390d3d66406cb01b9681c382874400e9dfd77f30ecdea4bd1bf5226dd4aff0"}, + {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:4d4c7fe5e50e269f9c63a260638488fec194a73993008618a59b54c47ef6ae72"}, + {file = "pyzmq-26.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25d128524207f53f7aae7c5abdc2b63f8957a060b00521af5ffcd20986b5d8f4"}, + {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d74b925d997e4f92b042bdd7085cd0a309ee0fd7cb4dc376059bbff6b32ff34f"}, + {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:732f957441e5b1c65a7509395e6b6cafee9e12df9aa5f4bf92ed266fe0ba70ee"}, + {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0a45102ad7ed9f9ddf2bd699cc5df37742cf7301111cba06001b927efecb120"}, + {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9f380d5333fc7cd17423f486125dcc073918676e33db70a6a8172b19fc78d23d"}, + {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8eaffcd6bf6a9d00b66a2052a33fa7e6a6575427e9644395f13c3d070f2918dc"}, + {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f1483d4975ae1b387b39bb8e23d1ff32fe5621aa9e4ed3055d05e9c5613fea53"}, + {file = "pyzmq-26.1.1-cp37-cp37m-win32.whl", hash = "sha256:a83653c6bbe5887caea55e49fbd2909c14b73acf43bcc051eb60b2d514bbd46e"}, + {file = "pyzmq-26.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9763a8d3f5f74ef679989b373c37cc22e8d07e56d26439205cb83edb7722357f"}, + {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2b045647caf620ce0ed6c8fd9fb6a73116f99aceed966b152a5ba1b416d25311"}, + {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f66dcb6625c002f209cdc12cae1a1fec926493cd2262efe37dc6b25a30cea863"}, + {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0cf1d980c969fb9e538f52abd2227f09e015096bc5c3ef7aa26e0d64051c1db8"}, + {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:443ebf5e261a95ee9725693f2a5a71401f89b89df0e0ea58844b074067aac2f1"}, + {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29de77ba1b1877fe7defc1b9140e65cbd35f72a63bc501e56c2eae55bde5fff4"}, + {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f6071ec95af145d7b659dae6786871cd85f0acc599286b6f8ba0c74592d83dd"}, + {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f0512fc87629ad968889176bf2165d721cd817401a281504329e2a2ed0ca6a3"}, + {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5ccfcf13e80719f6a2d9c0a021d9e47d4550907a29253554be2c09582f6d7963"}, + {file = "pyzmq-26.1.1-cp38-cp38-win32.whl", hash = "sha256:809673947e95752e407aaaaf03f205ee86ebfff9ca51db6d4003dfd87b8428d1"}, + {file = "pyzmq-26.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:62b5180e23e6f581600459cd983473cd723fdc64350f606d21407c99832aaf5f"}, + {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:fe73d7c89d6f803bed122135ff5783364e8cdb479cf6fe2d764a44b6349e7e0f"}, + {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db1b7e2b50ef21f398036786da4c153db63203a402396d9f21e08ea61f3f8dba"}, + {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c506a51cb01bb997a3f6440db0d121e5e7a32396e9948b1fdb6a7bfa67243f4"}, + {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:92eca4f80e8a748d880e55d3cf57ef487692e439f12d5c5a2e1cce84aaa7f6cb"}, + {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14bdbae02f72f4716b0ffe7500e9da303d719ddde1f3dcfb4c4f6cc1cf73bb02"}, + {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e03be7ed17836c9434cce0668ac1e2cc9143d7169f90f46a0167f6155e176e32"}, + {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc5df31e36e4fddd4c8b5c42daee8d54d7b529e898ac984be97bf5517de166a7"}, + {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f218179c90a12d660906e04b25a340dd63e9743000ba16232ddaf46888f269da"}, + {file = "pyzmq-26.1.1-cp39-cp39-win32.whl", hash = "sha256:7dfabc180a4da422a4b349c63077347392463a75fa07aa3be96712ed6d42c547"}, + {file = "pyzmq-26.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c5248e6e0fcbbbc912982e99cdd51c342601f495b0fa5bd667f3bdbdbf3e170f"}, + {file = "pyzmq-26.1.1-cp39-cp39-win_arm64.whl", hash = "sha256:2ae7aa1408778dc74582a1226052b930f9083b54b64d7e6ef6ec0466cfdcdec2"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:be3fc2b11c0c384949cf1f01f9a48555039408b0f3e877863b1754225635953e"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48dee75c2a9fa4f4a583d4028d564a0453447ee1277a29b07acc3743c092e259"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23f2fe4fb567e8098ebaa7204819658195b10ddd86958a97a6058eed2901eed3"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:472cacd16f627c06d3c8b2d374345ab74446bae913584a6245e2aa935336d929"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8285b25aa20fcc46f1ca4afbc39fd3d5f2fe4c4bbf7f2c7f907a214e87a70024"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2067e63fd9d5c13cfe12624dab0366053e523b37a7a01678ce4321f839398939"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cc109be2ee3638035d276e18eaf66a1e1f44201c0c4bea4ee0c692766bbd3570"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d0da97e65ee73261dba70469cc8f63d8da3a8a825337a2e3d246b9e95141cdd0"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa79c528706561306938b275f89bb2c6985ce08469c27e5de05bc680df5e826f"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3ddbd851a3a2651fdc5065a2804d50cf2f4b13b1bcd66de8e9e855d0217d4fcd"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3df226ab7464684ae6706e20a5cbab717c3735a7e409b3fa598b754d49f1946"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abad7b897e960d577eb4a0f3f789c1780bc3ffe2e7c27cf317e7c90ad26acf12"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c513d829a548c2d5c88983167be2b3aa537f6d1191edcdc6fcd8999e18bdd994"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70af4c9c991714ef1c65957605a8de42ef0d0620dd5f125953c8e682281bdb80"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8d4234f335b0d0842f7d661d8cd50cbad0729be58f1c4deb85cd96b38fe95025"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2c0fdb7b758e0e1605157e480b00b3a599073068a37091a1c75ec65bf7498645"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc657577f057d60dd3642c9f95f28b432889b73143140061f7c1331d02f03df6"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e3b66fe6131b4f33d239f7d4c3bfb2f8532d8644bae3b3da4f3987073edac55"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b57e912feef6951aec8bb03fe0faa5ad5f36962883c72a30a9c965e6d988fd"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:146956aec7d947c5afc5e7da0841423d7a53f84fd160fff25e682361dcfb32cb"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9521b874fd489495865172f344e46e0159095d1f161858e3fc6e28e43ca15160"}, + {file = "pyzmq-26.1.1.tar.gz", hash = "sha256:a7db05d8b7cd1a8c6610e9e9aa55d525baae7a44a43e18bc3260eb3f92de96c6"}, ] [package.dependencies] diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index d0e7b91b4..5f56e7611 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -126,6 +126,23 @@ TaskExecutionsRouteListRequestSortBy, TaskExecutionsRouteListResponse, TasksBaseWorkflowStep, + TasksBaseWorkflowStep_Embed, + TasksBaseWorkflowStep_Error, + TasksBaseWorkflowStep_Foreach, + TasksBaseWorkflowStep_Get, + TasksBaseWorkflowStep_IfElse, + TasksBaseWorkflowStep_Log, + TasksBaseWorkflowStep_MapReduce, + TasksBaseWorkflowStep_Parallel, + TasksBaseWorkflowStep_Prompt, + TasksBaseWorkflowStep_Return, + TasksBaseWorkflowStep_Search, + TasksBaseWorkflowStep_Set, + TasksBaseWorkflowStep_Sleep, + TasksBaseWorkflowStep_Switch, + TasksBaseWorkflowStep_ToolCall, + TasksBaseWorkflowStep_WaitForInput, + TasksBaseWorkflowStep_Yield, TasksCaseThen, TasksCaseThenThen, TasksCreateTaskRequest, @@ -152,7 +169,7 @@ TasksErrorWorkflowStep, TasksEvaluateStep, TasksForeachDo, - TasksForeachDoDoItem, + TasksForeachDoDo, TasksForeachStep, TasksGetStep, TasksIfElseWorkflowStep, @@ -393,6 +410,23 @@ "TaskExecutionsRouteListRequestSortBy", "TaskExecutionsRouteListResponse", "TasksBaseWorkflowStep", + "TasksBaseWorkflowStep_Embed", + "TasksBaseWorkflowStep_Error", + "TasksBaseWorkflowStep_Foreach", + "TasksBaseWorkflowStep_Get", + "TasksBaseWorkflowStep_IfElse", + "TasksBaseWorkflowStep_Log", + "TasksBaseWorkflowStep_MapReduce", + "TasksBaseWorkflowStep_Parallel", + "TasksBaseWorkflowStep_Prompt", + "TasksBaseWorkflowStep_Return", + "TasksBaseWorkflowStep_Search", + "TasksBaseWorkflowStep_Set", + "TasksBaseWorkflowStep_Sleep", + "TasksBaseWorkflowStep_Switch", + "TasksBaseWorkflowStep_ToolCall", + "TasksBaseWorkflowStep_WaitForInput", + "TasksBaseWorkflowStep_Yield", "TasksCaseThen", "TasksCaseThenThen", "TasksCreateTaskRequest", @@ -419,7 +453,7 @@ "TasksErrorWorkflowStep", "TasksEvaluateStep", "TasksForeachDo", - "TasksForeachDoDoItem", + "TasksForeachDoDo", "TasksForeachStep", "TasksGetStep", "TasksIfElseWorkflowStep", diff --git a/sdks/python/julep/api/client.py b/sdks/python/julep/api/client.py index 2030b74ce..c71d2534e 100644 --- a/sdks/python/julep/api/client.py +++ b/sdks/python/julep/api/client.py @@ -1042,7 +1042,7 @@ def tasks_route_create( Returns ------- CommonResourceCreatedResponse - The request has succeeded and a new resource has been created as a result. + The request has succeeded. Examples -------- @@ -1709,14 +1709,14 @@ def tasks_create_or_update_route_create_or_update( input_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> CommonResourceUpdatedResponse: + ) -> CommonResourceCreatedResponse: """ Create or update a task Parameters ---------- parent_id : CommonUuid - ID of parent resource + ID of the agent id : CommonUuid @@ -1743,8 +1743,8 @@ def tasks_create_or_update_route_create_or_update( Returns ------- - CommonResourceUpdatedResponse - The request has succeeded. + CommonResourceCreatedResponse + The request has succeeded and a new resource has been created as a result. Examples -------- @@ -1787,7 +1787,7 @@ def tasks_create_or_update_route_create_or_update( ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore + return pydantic_v1.parse_obj_as(CommonResourceCreatedResponse, _response.json()) # type: ignore _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) @@ -4729,7 +4729,7 @@ async def tasks_route_create( Returns ------- CommonResourceCreatedResponse - The request has succeeded and a new resource has been created as a result. + The request has succeeded. Examples -------- @@ -5468,14 +5468,14 @@ async def tasks_create_or_update_route_create_or_update( input_schema: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, metadata: typing.Optional[typing.Dict[str, typing.Any]] = OMIT, request_options: typing.Optional[RequestOptions] = None, - ) -> CommonResourceUpdatedResponse: + ) -> CommonResourceCreatedResponse: """ Create or update a task Parameters ---------- parent_id : CommonUuid - ID of parent resource + ID of the agent id : CommonUuid @@ -5502,8 +5502,8 @@ async def tasks_create_or_update_route_create_or_update( Returns ------- - CommonResourceUpdatedResponse - The request has succeeded. + CommonResourceCreatedResponse + The request has succeeded and a new resource has been created as a result. Examples -------- @@ -5554,7 +5554,7 @@ async def main() -> None: ) try: if 200 <= _response.status_code < 300: - return pydantic_v1.parse_obj_as(CommonResourceUpdatedResponse, _response.json()) # type: ignore + return pydantic_v1.parse_obj_as(CommonResourceCreatedResponse, _response.json()) # type: ignore _response_json = _response.json() except JSONDecodeError: raise ApiError(status_code=_response.status_code, body=_response.text) diff --git a/sdks/python/julep/api/reference.md b/sdks/python/julep/api/reference.md index 85fbaf94c..9ab2e4f12 100644 --- a/sdks/python/julep/api/reference.md +++ b/sdks/python/julep/api/reference.md @@ -2345,7 +2345,7 @@ client.tasks_create_or_update_route_create_or_update(
    -**parent_id:** `CommonUuid` — ID of parent resource +**parent_id:** `CommonUuid` — ID of the agent
    diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index 5629f5dd0..351c3ff7d 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -160,7 +160,26 @@ TaskExecutionsRouteListRequestSortBy, ) from .task_executions_route_list_response import TaskExecutionsRouteListResponse -from .tasks_base_workflow_step import TasksBaseWorkflowStep +from .tasks_base_workflow_step import ( + TasksBaseWorkflowStep, + TasksBaseWorkflowStep_Embed, + TasksBaseWorkflowStep_Error, + TasksBaseWorkflowStep_Foreach, + TasksBaseWorkflowStep_Get, + TasksBaseWorkflowStep_IfElse, + TasksBaseWorkflowStep_Log, + TasksBaseWorkflowStep_MapReduce, + TasksBaseWorkflowStep_Parallel, + TasksBaseWorkflowStep_Prompt, + TasksBaseWorkflowStep_Return, + TasksBaseWorkflowStep_Search, + TasksBaseWorkflowStep_Set, + TasksBaseWorkflowStep_Sleep, + TasksBaseWorkflowStep_Switch, + TasksBaseWorkflowStep_ToolCall, + TasksBaseWorkflowStep_WaitForInput, + TasksBaseWorkflowStep_Yield, +) from .tasks_case_then import TasksCaseThen from .tasks_case_then_then import TasksCaseThenThen from .tasks_create_task_request import TasksCreateTaskRequest @@ -189,7 +208,7 @@ from .tasks_error_workflow_step import TasksErrorWorkflowStep from .tasks_evaluate_step import TasksEvaluateStep from .tasks_foreach_do import TasksForeachDo -from .tasks_foreach_do_do_item import TasksForeachDoDoItem +from .tasks_foreach_do_do import TasksForeachDoDo from .tasks_foreach_step import TasksForeachStep from .tasks_get_step import TasksGetStep from .tasks_if_else_workflow_step import TasksIfElseWorkflowStep @@ -432,6 +451,23 @@ "TaskExecutionsRouteListRequestSortBy", "TaskExecutionsRouteListResponse", "TasksBaseWorkflowStep", + "TasksBaseWorkflowStep_Embed", + "TasksBaseWorkflowStep_Error", + "TasksBaseWorkflowStep_Foreach", + "TasksBaseWorkflowStep_Get", + "TasksBaseWorkflowStep_IfElse", + "TasksBaseWorkflowStep_Log", + "TasksBaseWorkflowStep_MapReduce", + "TasksBaseWorkflowStep_Parallel", + "TasksBaseWorkflowStep_Prompt", + "TasksBaseWorkflowStep_Return", + "TasksBaseWorkflowStep_Search", + "TasksBaseWorkflowStep_Set", + "TasksBaseWorkflowStep_Sleep", + "TasksBaseWorkflowStep_Switch", + "TasksBaseWorkflowStep_ToolCall", + "TasksBaseWorkflowStep_WaitForInput", + "TasksBaseWorkflowStep_Yield", "TasksCaseThen", "TasksCaseThenThen", "TasksCreateTaskRequest", @@ -458,7 +494,7 @@ "TasksErrorWorkflowStep", "TasksEvaluateStep", "TasksForeachDo", - "TasksForeachDoDoItem", + "TasksForeachDoDo", "TasksForeachStep", "TasksGetStep", "TasksIfElseWorkflowStep", diff --git a/sdks/python/julep/api/types/tasks_base_workflow_step.py b/sdks/python/julep/api/types/tasks_base_workflow_step.py index a5c68ac3d..3a3c9062c 100644 --- a/sdks/python/julep/api/types/tasks_base_workflow_step.py +++ b/sdks/python/julep/api/types/tasks_base_workflow_step.py @@ -1,13 +1,303 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + import datetime as dt import typing from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings +from .common_py_expression import CommonPyExpression +from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_case_then import TasksCaseThen +from .tasks_foreach_do import TasksForeachDo +from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse +from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen +from .tasks_map_over import TasksMapOver +from .tasks_parallel_step_parallel_item import TasksParallelStepParallelItem +from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor + + +class TasksBaseWorkflowStep_ToolCall(pydantic_v1.BaseModel): + tool: CommonToolRef + arguments: typing.Dict[str, typing.Any] + kind: typing.Literal["tool_call"] = pydantic_v1.Field( + alias="kind_", default="tool_call" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Yield(pydantic_v1.BaseModel): + workflow: str + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["yield"] = pydantic_v1.Field(alias="kind_", default="yield") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Prompt(pydantic_v1.BaseModel): + prompt: TasksPromptStepPrompt + settings: ChatChatSettings + kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Error(pydantic_v1.BaseModel): + error: str + kind: typing.Literal["error"] = pydantic_v1.Field(alias="kind_", default="error") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Sleep(pydantic_v1.BaseModel): + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Return(pydantic_v1.BaseModel): + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Get(pydantic_v1.BaseModel): + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} -class TasksBaseWorkflowStep(pydantic_v1.BaseModel): +class TasksBaseWorkflowStep_Set(pydantic_v1.BaseModel): + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + def json(self, **kwargs: typing.Any) -> str: kwargs_with_defaults: typing.Any = { "by_alias": True, @@ -36,5 +326,383 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True + allow_population_by_field_name = True + populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Log(pydantic_v1.BaseModel): + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Embed(pydantic_v1.BaseModel): + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Search(pydantic_v1.BaseModel): + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_WaitForInput(pydantic_v1.BaseModel): + wait_for_input: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( + alias="kind_", default="wait_for_input" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_IfElse(pydantic_v1.BaseModel): + if_: CommonPyExpression = pydantic_v1.Field(alias="if") + then: TasksIfElseWorkflowStepThen + else_: TasksIfElseWorkflowStepElse = pydantic_v1.Field(alias="else") + kind: typing.Literal["if_else"] = pydantic_v1.Field( + alias="kind_", default="if_else" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Switch(pydantic_v1.BaseModel): + switch: typing.List[TasksCaseThen] + kind: typing.Literal["switch"] = pydantic_v1.Field(alias="kind_", default="switch") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Foreach(pydantic_v1.BaseModel): + foreach: TasksForeachDo + kind: typing.Literal["foreach"] = pydantic_v1.Field( + alias="kind_", default="foreach" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_Parallel(pydantic_v1.BaseModel): + parallel: typing.List[TasksParallelStepParallelItem] + kind: typing.Literal["parallel"] = pydantic_v1.Field( + alias="kind_", default="parallel" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksBaseWorkflowStep_MapReduce(pydantic_v1.BaseModel): + map_: TasksMapOver = pydantic_v1.Field(alias="map") + reduce: typing.Optional[CommonPyExpression] = None + kind: typing.Literal["map_reduce"] = pydantic_v1.Field( + alias="kind_", default="map_reduce" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +TasksBaseWorkflowStep = typing.Union[ + TasksBaseWorkflowStep_ToolCall, + TasksBaseWorkflowStep_Yield, + TasksBaseWorkflowStep_Prompt, + TasksBaseWorkflowStep_Error, + TasksBaseWorkflowStep_Sleep, + TasksBaseWorkflowStep_Return, + TasksBaseWorkflowStep_Get, + TasksBaseWorkflowStep_Set, + TasksBaseWorkflowStep_Log, + TasksBaseWorkflowStep_Embed, + TasksBaseWorkflowStep_Search, + TasksBaseWorkflowStep_WaitForInput, + TasksBaseWorkflowStep_IfElse, + TasksBaseWorkflowStep_Switch, + TasksBaseWorkflowStep_Foreach, + TasksBaseWorkflowStep_Parallel, + TasksBaseWorkflowStep_MapReduce, +] diff --git a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py index 5b85a9236..ba5929b30 100644 --- a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py @@ -688,7 +688,7 @@ class Config: class TasksCreateTaskRequestMainItem_MapReduce(pydantic_v1.BaseModel): map_: TasksMapOver = pydantic_v1.Field(alias="map") - reduce: CommonPyExpression + reduce: typing.Optional[CommonPyExpression] = None kind: typing.Literal["map_reduce"] = pydantic_v1.Field( alias="kind_", default="map_reduce" ) diff --git a/sdks/python/julep/api/types/tasks_embed_step.py b/sdks/python/julep/api/types/tasks_embed_step.py index 13e9bcfd8..3e3a2661d 100644 --- a/sdks/python/julep/api/types/tasks_embed_step.py +++ b/sdks/python/julep/api/types/tasks_embed_step.py @@ -6,10 +6,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .docs_embed_query_request import DocsEmbedQueryRequest -from .tasks_base_workflow_step import TasksBaseWorkflowStep -class TasksEmbedStep(TasksBaseWorkflowStep): +class TasksEmbedStep(pydantic_v1.BaseModel): embed: DocsEmbedQueryRequest = pydantic_v1.Field() """ The text to embed @@ -43,7 +42,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_error_workflow_step.py b/sdks/python/julep/api/types/tasks_error_workflow_step.py index 37bc482ef..cc3a4e641 100644 --- a/sdks/python/julep/api/types/tasks_error_workflow_step.py +++ b/sdks/python/julep/api/types/tasks_error_workflow_step.py @@ -5,10 +5,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .tasks_base_workflow_step import TasksBaseWorkflowStep -class TasksErrorWorkflowStep(TasksBaseWorkflowStep): +class TasksErrorWorkflowStep(pydantic_v1.BaseModel): error: str = pydantic_v1.Field() """ The error message @@ -42,7 +41,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_evaluate_step.py b/sdks/python/julep/api/types/tasks_evaluate_step.py index ebb8a9781..b16e9d1f5 100644 --- a/sdks/python/julep/api/types/tasks_evaluate_step.py +++ b/sdks/python/julep/api/types/tasks_evaluate_step.py @@ -6,10 +6,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_py_expression import CommonPyExpression -from .tasks_base_workflow_step import TasksBaseWorkflowStep -class TasksEvaluateStep(TasksBaseWorkflowStep): +class TasksEvaluateStep(pydantic_v1.BaseModel): evaluate: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field() """ The expression to evaluate @@ -43,7 +42,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_foreach_do.py b/sdks/python/julep/api/types/tasks_foreach_do.py index 8ee662103..9261240fc 100644 --- a/sdks/python/julep/api/types/tasks_foreach_do.py +++ b/sdks/python/julep/api/types/tasks_foreach_do.py @@ -6,7 +6,7 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_py_expression import CommonPyExpression -from .tasks_foreach_do_do_item import TasksForeachDoDoItem +from .tasks_foreach_do_do import TasksForeachDoDo class TasksForeachDo(pydantic_v1.BaseModel): @@ -15,7 +15,7 @@ class TasksForeachDo(pydantic_v1.BaseModel): The variable to iterate over """ - do: typing.List[TasksForeachDoDoItem] = pydantic_v1.Field() + do: TasksForeachDoDo = pydantic_v1.Field() """ The steps to run for each iteration """ diff --git a/sdks/python/julep/api/types/tasks_foreach_do_do_item.py b/sdks/python/julep/api/types/tasks_foreach_do_do.py similarity index 96% rename from sdks/python/julep/api/types/tasks_foreach_do_do_item.py rename to sdks/python/julep/api/types/tasks_foreach_do_do.py index f8c2a90bf..9e4baf072 100644 --- a/sdks/python/julep/api/types/tasks_foreach_do_do_item.py +++ b/sdks/python/julep/api/types/tasks_foreach_do_do.py @@ -15,7 +15,7 @@ from .tasks_wait_for_input_step import TasksWaitForInputStep from .tasks_yield_step import TasksYieldStep -TasksForeachDoDoItem = typing.Union[ +TasksForeachDoDo = typing.Union[ typing.Any, TasksToolCallStep, TasksYieldStep, diff --git a/sdks/python/julep/api/types/tasks_foreach_step.py b/sdks/python/julep/api/types/tasks_foreach_step.py index 03c86dce2..fc4be5c18 100644 --- a/sdks/python/julep/api/types/tasks_foreach_step.py +++ b/sdks/python/julep/api/types/tasks_foreach_step.py @@ -5,11 +5,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_foreach_do import TasksForeachDo -class TasksForeachStep(TasksBaseWorkflowStep): +class TasksForeachStep(pydantic_v1.BaseModel): foreach: TasksForeachDo = pydantic_v1.Field() """ The steps to run for each iteration @@ -43,7 +42,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_get_step.py b/sdks/python/julep/api/types/tasks_get_step.py index c6fa9748e..560a0c8f3 100644 --- a/sdks/python/julep/api/types/tasks_get_step.py +++ b/sdks/python/julep/api/types/tasks_get_step.py @@ -5,10 +5,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .tasks_base_workflow_step import TasksBaseWorkflowStep -class TasksGetStep(TasksBaseWorkflowStep): +class TasksGetStep(pydantic_v1.BaseModel): get: str = pydantic_v1.Field() """ The key to get @@ -42,7 +41,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_if_else_workflow_step.py b/sdks/python/julep/api/types/tasks_if_else_workflow_step.py index 2370d66cf..cc3670431 100644 --- a/sdks/python/julep/api/types/tasks_if_else_workflow_step.py +++ b/sdks/python/julep/api/types/tasks_if_else_workflow_step.py @@ -6,12 +6,11 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_py_expression import CommonPyExpression -from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen -class TasksIfElseWorkflowStep(TasksBaseWorkflowStep): +class TasksIfElseWorkflowStep(pydantic_v1.BaseModel): if_: CommonPyExpression = pydantic_v1.Field(alias="if") """ The condition to evaluate diff --git a/sdks/python/julep/api/types/tasks_log_step.py b/sdks/python/julep/api/types/tasks_log_step.py index 649a8471d..990301464 100644 --- a/sdks/python/julep/api/types/tasks_log_step.py +++ b/sdks/python/julep/api/types/tasks_log_step.py @@ -6,10 +6,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_py_expression import CommonPyExpression -from .tasks_base_workflow_step import TasksBaseWorkflowStep -class TasksLogStep(TasksBaseWorkflowStep): +class TasksLogStep(pydantic_v1.BaseModel): log: CommonPyExpression = pydantic_v1.Field() """ The value to log @@ -43,7 +42,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_map_reduce_step.py b/sdks/python/julep/api/types/tasks_map_reduce_step.py index 9d43304d5..18a1bb091 100644 --- a/sdks/python/julep/api/types/tasks_map_reduce_step.py +++ b/sdks/python/julep/api/types/tasks_map_reduce_step.py @@ -6,19 +6,18 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_py_expression import CommonPyExpression -from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_map_over import TasksMapOver -class TasksMapReduceStep(TasksBaseWorkflowStep): +class TasksMapReduceStep(pydantic_v1.BaseModel): map_: TasksMapOver = pydantic_v1.Field(alias="map") """ The steps to run for each iteration """ - reduce: CommonPyExpression = pydantic_v1.Field() + reduce: typing.Optional[CommonPyExpression] = pydantic_v1.Field(default=None) """ - The expression to reduce the results (`_` is a list of outputs) + The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. """ def json(self, **kwargs: typing.Any) -> str: diff --git a/sdks/python/julep/api/types/tasks_parallel_step.py b/sdks/python/julep/api/types/tasks_parallel_step.py index 7f3ba3bad..71979f12f 100644 --- a/sdks/python/julep/api/types/tasks_parallel_step.py +++ b/sdks/python/julep/api/types/tasks_parallel_step.py @@ -5,11 +5,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_parallel_step_parallel_item import TasksParallelStepParallelItem -class TasksParallelStep(TasksBaseWorkflowStep): +class TasksParallelStep(pydantic_v1.BaseModel): parallel: typing.List[TasksParallelStepParallelItem] = pydantic_v1.Field() """ The steps to run in parallel. Max concurrency will depend on the platform @@ -43,7 +42,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py index 112eeff32..8821ddfa8 100644 --- a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py @@ -688,7 +688,7 @@ class Config: class TasksPatchTaskRequestMainItem_MapReduce(pydantic_v1.BaseModel): map_: TasksMapOver = pydantic_v1.Field(alias="map") - reduce: CommonPyExpression + reduce: typing.Optional[CommonPyExpression] = None kind: typing.Literal["map_reduce"] = pydantic_v1.Field( alias="kind_", default="map_reduce" ) diff --git a/sdks/python/julep/api/types/tasks_prompt_step.py b/sdks/python/julep/api/types/tasks_prompt_step.py index 998d97355..f99cb8192 100644 --- a/sdks/python/julep/api/types/tasks_prompt_step.py +++ b/sdks/python/julep/api/types/tasks_prompt_step.py @@ -6,11 +6,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .chat_chat_settings import ChatChatSettings -from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_prompt_step_prompt import TasksPromptStepPrompt -class TasksPromptStep(TasksBaseWorkflowStep): +class TasksPromptStep(pydantic_v1.BaseModel): prompt: TasksPromptStepPrompt = pydantic_v1.Field() """ The prompt to run @@ -49,7 +48,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_return_step.py b/sdks/python/julep/api/types/tasks_return_step.py index 33aed561e..c5e5f835c 100644 --- a/sdks/python/julep/api/types/tasks_return_step.py +++ b/sdks/python/julep/api/types/tasks_return_step.py @@ -6,10 +6,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_py_expression import CommonPyExpression -from .tasks_base_workflow_step import TasksBaseWorkflowStep -class TasksReturnStep(TasksBaseWorkflowStep): +class TasksReturnStep(pydantic_v1.BaseModel): return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") """ The value to return diff --git a/sdks/python/julep/api/types/tasks_search_step.py b/sdks/python/julep/api/types/tasks_search_step.py index 82d0a1658..01d1cce2b 100644 --- a/sdks/python/julep/api/types/tasks_search_step.py +++ b/sdks/python/julep/api/types/tasks_search_step.py @@ -5,11 +5,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_search_step_search import TasksSearchStepSearch -class TasksSearchStep(TasksBaseWorkflowStep): +class TasksSearchStep(pydantic_v1.BaseModel): search: TasksSearchStepSearch = pydantic_v1.Field() """ The search query @@ -43,7 +42,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_set_step.py b/sdks/python/julep/api/types/tasks_set_step.py index c718d4e7a..5195812a8 100644 --- a/sdks/python/julep/api/types/tasks_set_step.py +++ b/sdks/python/julep/api/types/tasks_set_step.py @@ -5,11 +5,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_set_step_set import TasksSetStepSet -class TasksSetStep(TasksBaseWorkflowStep): +class TasksSetStep(pydantic_v1.BaseModel): set_: TasksSetStepSet = pydantic_v1.Field(alias="set") """ The value to set diff --git a/sdks/python/julep/api/types/tasks_sleep_step.py b/sdks/python/julep/api/types/tasks_sleep_step.py index 5261bad5f..73c75079e 100644 --- a/sdks/python/julep/api/types/tasks_sleep_step.py +++ b/sdks/python/julep/api/types/tasks_sleep_step.py @@ -5,11 +5,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_sleep_for import TasksSleepFor -class TasksSleepStep(TasksBaseWorkflowStep): +class TasksSleepStep(pydantic_v1.BaseModel): sleep: TasksSleepFor = pydantic_v1.Field() """ The duration to sleep for @@ -43,7 +42,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_switch_step.py b/sdks/python/julep/api/types/tasks_switch_step.py index 0a83a8185..b7623975f 100644 --- a/sdks/python/julep/api/types/tasks_switch_step.py +++ b/sdks/python/julep/api/types/tasks_switch_step.py @@ -5,11 +5,10 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 -from .tasks_base_workflow_step import TasksBaseWorkflowStep from .tasks_case_then import TasksCaseThen -class TasksSwitchStep(TasksBaseWorkflowStep): +class TasksSwitchStep(pydantic_v1.BaseModel): switch: typing.List[TasksCaseThen] = pydantic_v1.Field() """ The cond tree @@ -43,7 +42,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_task_main_item.py b/sdks/python/julep/api/types/tasks_task_main_item.py index 4446fe298..3206c4a8e 100644 --- a/sdks/python/julep/api/types/tasks_task_main_item.py +++ b/sdks/python/julep/api/types/tasks_task_main_item.py @@ -688,7 +688,7 @@ class Config: class TasksTaskMainItem_MapReduce(pydantic_v1.BaseModel): map_: TasksMapOver = pydantic_v1.Field(alias="map") - reduce: CommonPyExpression + reduce: typing.Optional[CommonPyExpression] = None kind: typing.Literal["map_reduce"] = pydantic_v1.Field( alias="kind_", default="map_reduce" ) diff --git a/sdks/python/julep/api/types/tasks_tool_call_step.py b/sdks/python/julep/api/types/tasks_tool_call_step.py index 08e616a94..e9ee19b9e 100644 --- a/sdks/python/julep/api/types/tasks_tool_call_step.py +++ b/sdks/python/julep/api/types/tasks_tool_call_step.py @@ -6,10 +6,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_tool_ref import CommonToolRef -from .tasks_base_workflow_step import TasksBaseWorkflowStep -class TasksToolCallStep(TasksBaseWorkflowStep): +class TasksToolCallStep(pydantic_v1.BaseModel): tool: CommonToolRef = pydantic_v1.Field() """ The tool to run @@ -48,7 +47,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py index 5d50ee656..2897a5e8e 100644 --- a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py @@ -688,7 +688,7 @@ class Config: class TasksUpdateTaskRequestMainItem_MapReduce(pydantic_v1.BaseModel): map_: TasksMapOver = pydantic_v1.Field(alias="map") - reduce: CommonPyExpression + reduce: typing.Optional[CommonPyExpression] = None kind: typing.Literal["map_reduce"] = pydantic_v1.Field( alias="kind_", default="map_reduce" ) diff --git a/sdks/python/julep/api/types/tasks_wait_for_input_step.py b/sdks/python/julep/api/types/tasks_wait_for_input_step.py index d3394fcf9..3d6aafda2 100644 --- a/sdks/python/julep/api/types/tasks_wait_for_input_step.py +++ b/sdks/python/julep/api/types/tasks_wait_for_input_step.py @@ -6,10 +6,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_py_expression import CommonPyExpression -from .tasks_base_workflow_step import TasksBaseWorkflowStep -class TasksWaitForInputStep(TasksBaseWorkflowStep): +class TasksWaitForInputStep(pydantic_v1.BaseModel): wait_for_input: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field() """ Any additional info or data @@ -43,7 +42,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/julep/api/types/tasks_yield_step.py b/sdks/python/julep/api/types/tasks_yield_step.py index 2c8eb48d3..e2632183f 100644 --- a/sdks/python/julep/api/types/tasks_yield_step.py +++ b/sdks/python/julep/api/types/tasks_yield_step.py @@ -6,10 +6,9 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 from .common_py_expression import CommonPyExpression -from .tasks_base_workflow_step import TasksBaseWorkflowStep -class TasksYieldStep(TasksBaseWorkflowStep): +class TasksYieldStep(pydantic_v1.BaseModel): workflow: str = pydantic_v1.Field() """ The subworkflow to run @@ -48,7 +47,5 @@ def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: class Config: frozen = True smart_union = True - allow_population_by_field_name = True - populate_by_name = True extra = pydantic_v1.Extra.allow json_encoders = {dt.datetime: serialize_datetime} diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index cb99f66f7..f7ccaf4a7 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -2572,120 +2572,120 @@ files = [ [[package]] name = "pyzmq" -version = "26.1.0" +version = "26.1.1" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:263cf1e36862310bf5becfbc488e18d5d698941858860c5a8c079d1511b3b18e"}, - {file = "pyzmq-26.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d5c8b17f6e8f29138678834cf8518049e740385eb2dbf736e8f07fc6587ec682"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:75a95c2358fcfdef3374cb8baf57f1064d73246d55e41683aaffb6cfe6862917"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f99de52b8fbdb2a8f5301ae5fc0f9e6b3ba30d1d5fc0421956967edcc6914242"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7bcbfbab4e1895d58ab7da1b5ce9a327764f0366911ba5b95406c9104bceacb0"}, - {file = "pyzmq-26.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:77ce6a332c7e362cb59b63f5edf730e83590d0ab4e59c2aa5bd79419a42e3449"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ba0a31d00e8616149a5ab440d058ec2da621e05d744914774c4dde6837e1f545"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:8b88641384e84a258b740801cd4dbc45c75f148ee674bec3149999adda4a8598"}, - {file = "pyzmq-26.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2fa76ebcebe555cce90f16246edc3ad83ab65bb7b3d4ce408cf6bc67740c4f88"}, - {file = "pyzmq-26.1.0-cp310-cp310-win32.whl", hash = "sha256:fbf558551cf415586e91160d69ca6416f3fce0b86175b64e4293644a7416b81b"}, - {file = "pyzmq-26.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:a7b8aab50e5a288c9724d260feae25eda69582be84e97c012c80e1a5e7e03fb2"}, - {file = "pyzmq-26.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:08f74904cb066e1178c1ec706dfdb5c6c680cd7a8ed9efebeac923d84c1f13b1"}, - {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:46d6800b45015f96b9d92ece229d92f2aef137d82906577d55fadeb9cf5fcb71"}, - {file = "pyzmq-26.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bc2431167adc50ba42ea3e5e5f5cd70d93e18ab7b2f95e724dd8e1bd2c38120"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b3bb34bebaa1b78e562931a1687ff663d298013f78f972a534f36c523311a84d"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3f6329340cef1c7ba9611bd038f2d523cea79f09f9c8f6b0553caba59ec562"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:471880c4c14e5a056a96cd224f5e71211997d40b4bf5e9fdded55dafab1f98f2"}, - {file = "pyzmq-26.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:ce6f2b66799971cbae5d6547acefa7231458289e0ad481d0be0740535da38d8b"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a1f6ea5b1d6cdbb8cfa0536f0d470f12b4b41ad83625012e575f0e3ecfe97f0"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b45e6445ac95ecb7d728604bae6538f40ccf4449b132b5428c09918523abc96d"}, - {file = "pyzmq-26.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:94c4262626424683feea0f3c34951d39d49d354722db2745c42aa6bb50ecd93b"}, - {file = "pyzmq-26.1.0-cp311-cp311-win32.whl", hash = "sha256:a0f0ab9df66eb34d58205913f4540e2ad17a175b05d81b0b7197bc57d000e829"}, - {file = "pyzmq-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:8efb782f5a6c450589dbab4cb0f66f3a9026286333fe8f3a084399149af52f29"}, - {file = "pyzmq-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:f133d05aaf623519f45e16ab77526e1e70d4e1308e084c2fb4cedb1a0c764bbb"}, - {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:3d3146b1c3dcc8a1539e7cc094700b2be1e605a76f7c8f0979b6d3bde5ad4072"}, - {file = "pyzmq-26.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d9270fbf038bf34ffca4855bcda6e082e2c7f906b9eb8d9a8ce82691166060f7"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:995301f6740a421afc863a713fe62c0aaf564708d4aa057dfdf0f0f56525294b"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7eca8b89e56fb8c6c26dd3e09bd41b24789022acf1cf13358e96f1cafd8cae3"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d4feb2e83dfe9ace6374a847e98ee9d1246ebadcc0cb765482e272c34e5820"}, - {file = "pyzmq-26.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:d4fafc2eb5d83f4647331267808c7e0c5722c25a729a614dc2b90479cafa78bd"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:58c33dc0e185dd97a9ac0288b3188d1be12b756eda67490e6ed6a75cf9491d79"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:68a0a1d83d33d8367ddddb3e6bb4afbb0f92bd1dac2c72cd5e5ddc86bdafd3eb"}, - {file = "pyzmq-26.1.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:2ae7c57e22ad881af78075e0cea10a4c778e67234adc65c404391b417a4dda83"}, - {file = "pyzmq-26.1.0-cp312-cp312-win32.whl", hash = "sha256:347e84fc88cc4cb646597f6d3a7ea0998f887ee8dc31c08587e9c3fd7b5ccef3"}, - {file = "pyzmq-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:9f136a6e964830230912f75b5a116a21fe8e34128dcfd82285aa0ef07cb2c7bd"}, - {file = "pyzmq-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:a4b7a989c8f5a72ab1b2bbfa58105578753ae77b71ba33e7383a31ff75a504c4"}, - {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d416f2088ac8f12daacffbc2e8918ef4d6be8568e9d7155c83b7cebed49d2322"}, - {file = "pyzmq-26.1.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ecb6c88d7946166d783a635efc89f9a1ff11c33d680a20df9657b6902a1d133b"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:471312a7375571857a089342beccc1a63584315188560c7c0da7e0a23afd8a5c"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0e6cea102ffa16b737d11932c426f1dc14b5938cf7bc12e17269559c458ac334"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ec7248673ffc7104b54e4957cee38b2f3075a13442348c8d651777bf41aa45ee"}, - {file = "pyzmq-26.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:0614aed6f87d550b5cecb03d795f4ddbb1544b78d02a4bd5eecf644ec98a39f6"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:e8746ce968be22a8a1801bf4a23e565f9687088580c3ed07af5846580dd97f76"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:7688653574392d2eaeef75ddcd0b2de5b232d8730af29af56c5adf1df9ef8d6f"}, - {file = "pyzmq-26.1.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8d4dac7d97f15c653a5fedcafa82626bd6cee1450ccdaf84ffed7ea14f2b07a4"}, - {file = "pyzmq-26.1.0-cp313-cp313-win32.whl", hash = "sha256:ccb42ca0a4a46232d716779421bbebbcad23c08d37c980f02cc3a6bd115ad277"}, - {file = "pyzmq-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:e1e5d0a25aea8b691a00d6b54b28ac514c8cc0d8646d05f7ca6cb64b97358250"}, - {file = "pyzmq-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:fc82269d24860cfa859b676d18850cbb8e312dcd7eada09e7d5b007e2f3d9eb1"}, - {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:416ac51cabd54f587995c2b05421324700b22e98d3d0aa2cfaec985524d16f1d"}, - {file = "pyzmq-26.1.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:ff832cce719edd11266ca32bc74a626b814fff236824aa1aeaad399b69fe6eae"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:393daac1bcf81b2a23e696b7b638eedc965e9e3d2112961a072b6cd8179ad2eb"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9869fa984c8670c8ab899a719eb7b516860a29bc26300a84d24d8c1b71eae3ec"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b3b8e36fd4c32c0825b4461372949ecd1585d326802b1321f8b6dc1d7e9318c"}, - {file = "pyzmq-26.1.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:3ee647d84b83509b7271457bb428cc347037f437ead4b0b6e43b5eba35fec0aa"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:45cb1a70eb00405ce3893041099655265fabcd9c4e1e50c330026e82257892c1"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:5cca7b4adb86d7470e0fc96037771981d740f0b4cb99776d5cb59cd0e6684a73"}, - {file = "pyzmq-26.1.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:91d1a20bdaf3b25f3173ff44e54b1cfbc05f94c9e8133314eb2962a89e05d6e3"}, - {file = "pyzmq-26.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c0665d85535192098420428c779361b8823d3d7ec4848c6af3abb93bc5c915bf"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:96d7c1d35ee4a495df56c50c83df7af1c9688cce2e9e0edffdbf50889c167595"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b281b5ff5fcc9dcbfe941ac5c7fcd4b6c065adad12d850f95c9d6f23c2652384"}, - {file = "pyzmq-26.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5384c527a9a004445c5074f1e20db83086c8ff1682a626676229aafd9cf9f7d1"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:754c99a9840839375ee251b38ac5964c0f369306eddb56804a073b6efdc0cd88"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:9bdfcb74b469b592972ed881bad57d22e2c0acc89f5e8c146782d0d90fb9f4bf"}, - {file = "pyzmq-26.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:bd13f0231f4788db619347b971ca5f319c5b7ebee151afc7c14632068c6261d3"}, - {file = "pyzmq-26.1.0-cp37-cp37m-win32.whl", hash = "sha256:c5668dac86a869349828db5fc928ee3f58d450dce2c85607067d581f745e4fb1"}, - {file = "pyzmq-26.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad875277844cfaeca7fe299ddf8c8d8bfe271c3dc1caf14d454faa5cdbf2fa7a"}, - {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:65c6e03cc0222eaf6aad57ff4ecc0a070451e23232bb48db4322cc45602cede0"}, - {file = "pyzmq-26.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:038ae4ffb63e3991f386e7fda85a9baab7d6617fe85b74a8f9cab190d73adb2b"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:bdeb2c61611293f64ac1073f4bf6723b67d291905308a7de9bb2ca87464e3273"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:61dfa5ee9d7df297c859ac82b1226d8fefaf9c5113dc25c2c00ecad6feeeb04f"}, - {file = "pyzmq-26.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3292d384537b9918010769b82ab3e79fca8b23d74f56fc69a679106a3e2c2cf"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f9499c70c19ff0fbe1007043acb5ad15c1dec7d8e84ab429bca8c87138e8f85c"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d3dd5523ed258ad58fed7e364c92a9360d1af8a9371e0822bd0146bdf017ef4c"}, - {file = "pyzmq-26.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:baba2fd199b098c5544ef2536b2499d2e2155392973ad32687024bd8572a7d1c"}, - {file = "pyzmq-26.1.0-cp38-cp38-win32.whl", hash = "sha256:ddbb2b386128d8eca92bd9ca74e80f73fe263bcca7aa419f5b4cbc1661e19741"}, - {file = "pyzmq-26.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:79e45a4096ec8388cdeb04a9fa5e9371583bcb826964d55b8b66cbffe7b33c86"}, - {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:add52c78a12196bc0fda2de087ba6c876ea677cbda2e3eba63546b26e8bf177b"}, - {file = "pyzmq-26.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:98c03bd7f3339ff47de7ea9ac94a2b34580a8d4df69b50128bb6669e1191a895"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:dcc37d9d708784726fafc9c5e1232de655a009dbf97946f117aefa38d5985a0f"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5a6ed52f0b9bf8dcc64cc82cce0607a3dfed1dbb7e8c6f282adfccc7be9781de"}, - {file = "pyzmq-26.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:451e16ae8bea3d95649317b463c9f95cd9022641ec884e3d63fc67841ae86dfe"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:906e532c814e1d579138177a00ae835cd6becbf104d45ed9093a3aaf658f6a6a"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:05bacc4f94af468cc82808ae3293390278d5f3375bb20fef21e2034bb9a505b6"}, - {file = "pyzmq-26.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:57bb2acba798dc3740e913ffadd56b1fcef96f111e66f09e2a8db3050f1f12c8"}, - {file = "pyzmq-26.1.0-cp39-cp39-win32.whl", hash = "sha256:f774841bb0e8588505002962c02da420bcfb4c5056e87a139c6e45e745c0e2e2"}, - {file = "pyzmq-26.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:359c533bedc62c56415a1f5fcfd8279bc93453afdb0803307375ecf81c962402"}, - {file = "pyzmq-26.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:7907419d150b19962138ecec81a17d4892ea440c184949dc29b358bc730caf69"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b24079a14c9596846bf7516fe75d1e2188d4a528364494859106a33d8b48be38"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59d0acd2976e1064f1b398a00e2c3e77ed0a157529779e23087d4c2fb8aaa416"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:911c43a4117915203c4cc8755e0f888e16c4676a82f61caee2f21b0c00e5b894"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b10163e586cc609f5f85c9b233195554d77b1e9a0801388907441aaeb22841c5"}, - {file = "pyzmq-26.1.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:28a8b2abb76042f5fd7bd720f7fea48c0fd3e82e9de0a1bf2c0de3812ce44a42"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:bef24d3e4ae2c985034439f449e3f9e06bf579974ce0e53d8a507a1577d5b2ab"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2cd0f4d314f4a2518e8970b6f299ae18cff7c44d4a1fc06fc713f791c3a9e3ea"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fa25a620eed2a419acc2cf10135b995f8f0ce78ad00534d729aa761e4adcef8a"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef3b048822dca6d231d8a8ba21069844ae38f5d83889b9b690bf17d2acc7d099"}, - {file = "pyzmq-26.1.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:9a6847c92d9851b59b9f33f968c68e9e441f9a0f8fc972c5580c5cd7cbc6ee24"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c9b9305004d7e4e6a824f4f19b6d8f32b3578aad6f19fc1122aaf320cbe3dc83"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:63c1d3a65acb2f9c92dce03c4e1758cc552f1ae5c78d79a44e3bb88d2fa71f3a"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d36b8fffe8b248a1b961c86fbdfa0129dfce878731d169ede7fa2631447331be"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:67976d12ebfd61a3bc7d77b71a9589b4d61d0422282596cf58c62c3866916544"}, - {file = "pyzmq-26.1.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:998444debc8816b5d8d15f966e42751032d0f4c55300c48cc337f2b3e4f17d03"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:e5c88b2f13bcf55fee78ea83567b9fe079ba1a4bef8b35c376043440040f7edb"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8d906d43e1592be4b25a587b7d96527cb67277542a5611e8ea9e996182fae410"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80b0c9942430d731c786545da6be96d824a41a51742e3e374fedd9018ea43106"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:314d11564c00b77f6224d12eb3ddebe926c301e86b648a1835c5b28176c83eab"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:093a1a3cae2496233f14b57f4b485da01b4ff764582c854c0f42c6dd2be37f3d"}, - {file = "pyzmq-26.1.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:3c397b1b450f749a7e974d74c06d69bd22dd362142f370ef2bd32a684d6b480c"}, - {file = "pyzmq-26.1.0.tar.gz", hash = "sha256:6c5aeea71f018ebd3b9115c7cb13863dd850e98ca6b9258509de1246461a7e7f"}, + {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b1bb952d1e407463c9333ea7e0c0600001e54e08ce836d4f0aff1fb3f902cf63"}, + {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65e2a18e845c6ea7ab849c70db932eaeadee5edede9e379eb21c0a44cf523b2e"}, + {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:def7ae3006924b8a0c146a89ab4008310913fa903beedb95e25dea749642528e"}, + {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8234571df7816f99dde89c3403cb396d70c6554120b795853a8ea56fcc26cd3"}, + {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18da8e84dbc30688fd2baefd41df7190607511f916be34f9a24b0e007551822e"}, + {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c70dab93d98b2bf3f0ac1265edbf6e7f83acbf71dabcc4611889bb0dea45bed7"}, + {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fcb90592c5d5c562e1b1a1ceccf6f00036d73c51db0271bf4d352b8d6b31d468"}, + {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cf4be7460a0c1bc71e9b0e64ecdd75a86386ca6afaa36641686f5542d0314e9d"}, + {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4cbecda4ddbfc1e309c3be04d333f9be3fc6178b8b6592b309676f929767a15"}, + {file = "pyzmq-26.1.1-cp310-cp310-win32.whl", hash = "sha256:583f73b113b8165713b6ce028d221402b1b69483055b5aa3f991937e34dd1ead"}, + {file = "pyzmq-26.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5e6f39ecb8eb7bfcb976c49262e8cf83ff76e082b77ca23ba90c9b6691a345be"}, + {file = "pyzmq-26.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:8d042d6446cab3a1388b38596f5acabb9926b0b95c3894c519356b577a549458"}, + {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:362cac2423e36966d336d79d3ec3eafeabc153ee3e7a5cf580d7e74a34b3d912"}, + {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0841633446cb1539a832a19bb24c03a20c00887d0cedd1d891b495b07e5c5cb5"}, + {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e1fcdc333afbf9918d0a614a6e10858aede7da49a60f6705a77e343fe86a317"}, + {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc8d655627d775475eafdcf0e49e74bcc1e5e90afd9ab813b4da98f092ed7b93"}, + {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32de51744820857a6f7c3077e620ab3f607d0e4388dfead885d5124ab9bcdc5e"}, + {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a880240597010914ffb1d6edd04d3deb7ce6a2abf79a0012751438d13630a671"}, + {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:26131b1cec02f941ed2d2b4b8cc051662b1c248b044eff5069df1f500bbced56"}, + {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce05841322b58510607f9508a573138d995a46c7928887bc433de9cb760fd2ad"}, + {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32123ff0a6db521aadf2b95201e967a4e0d11fb89f73663a99d2f54881c07214"}, + {file = "pyzmq-26.1.1-cp311-cp311-win32.whl", hash = "sha256:e790602d7ea1d6c7d8713d571226d67de7ffe47b1e22ae2c043ebd537de1bccb"}, + {file = "pyzmq-26.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:717960855f2d6fdc2dba9df49dff31c414187bb11c76af36343a57d1f7083d9a"}, + {file = "pyzmq-26.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:08956c26dbcd4fd8835cb777a16e21958ed2412317630e19f0018d49dbeeb470"}, + {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e80345900ae241c2c51bead7c9fa247bba6d4b2a83423e9791bae8b0a7f12c52"}, + {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ec8fe214fcc45dfb0c32e4a7ad1db20244ba2d2fecbf0cbf9d5242d81ca0a375"}, + {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf4e283f97688d993cb7a8acbc22889effbbb7cbaa19ee9709751f44be928f5d"}, + {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2508bdc8ab246e5ed7c92023d4352aaad63020ca3b098a4e3f1822db202f703d"}, + {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:741bdb4d96efe8192616abdc3671931d51a8bcd38c71da2d53fb3127149265d1"}, + {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:76154943e4c4054b2591792eb3484ef1dd23d59805759f9cebd2f010aa30ee8c"}, + {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9498ac427d20d0e0ef0e4bbd6200841e91640dfdf619f544ceec7f464cfb6070"}, + {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f34453ef3496ca3462f30435bf85f535f9550392987341f9ccc92c102825a79"}, + {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:50f0669324e27cc2091ef6ab76ca7112f364b6249691790b4cffce31e73fda28"}, + {file = "pyzmq-26.1.1-cp312-cp312-win32.whl", hash = "sha256:3ee5cbf2625b94de21c68d0cefd35327c8dfdbd6a98fcc41682b4e8bb00d841f"}, + {file = "pyzmq-26.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:75bd448a28b1001b6928679015bc95dd5f172703ed30135bb9e34fc9cda0a3e7"}, + {file = "pyzmq-26.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:4350233569b4bbef88595c5e77ee38995a6f1f1790fae148b578941bfffd1c24"}, + {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8087a3281c20b1d11042d372ed5a47734af05975d78e4d1d6e7bd1018535f3"}, + {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ebef7d3fe11fe4c688f08bc0211a976c3318c097057f258428200737b9fff4da"}, + {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a5342110510045a47de1e87f5f1dcc1d9d90109522316dc9830cfc6157c800f"}, + {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af690ea4be6ca92a67c2b44a779a023bf0838e92d48497a2268175dc4a505691"}, + {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc994e220c1403ae087d7f0fa45129d583e46668a019e389060da811a5a9320e"}, + {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b8e153f5dffb0310af71fc6fc9cd8174f4c8ea312c415adcb815d786fee78179"}, + {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0065026e624052a51033857e5cd45a94b52946b44533f965f0bdf182460e965d"}, + {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:63351392f948b5d50b9f55161994bc4feedbfb3f3cfe393d2f503dea2c3ec445"}, + {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ffecc43b3c18e36b62fcec995761829b6ac325d8dd74a4f2c5c1653afbb4495a"}, + {file = "pyzmq-26.1.1-cp313-cp313-win32.whl", hash = "sha256:6ff14c2fae6c0c2c1c02590c5c5d75aa1db35b859971b3ca2fcd28f983d9f2b6"}, + {file = "pyzmq-26.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:85f2d2ee5ea9a8f1de86a300e1062fbab044f45b5ce34d20580c0198a8196db0"}, + {file = "pyzmq-26.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:cc09b1de8b985ca5a0ca343dd7fb007267c6b329347a74e200f4654268084239"}, + {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:bc904e86de98f8fc5bd41597da5d61232d2d6d60c4397f26efffabb961b2b245"}, + {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:00f39c367bbd6aa8e4bc36af6510561944c619b58eb36199fa334b594a18f615"}, + {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de6f384864a959866b782e6a3896538d1424d183f2d3c7ef079f71dcecde7284"}, + {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3abb15df0c763339edb27a644c19381b2425ddd1aea3dbd77c1601a3b31867b8"}, + {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40908ec2dd3b29bbadc0916a0d3c87f8dbeebbd8fead8e618539f09e0506dec4"}, + {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c11a95d3f6fc7e714ccd1066f68f9c1abd764a8b3596158be92f46dd49f41e03"}, + {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:4437af9fee7a58302dbd511cc49f0cc2b35c112a33a1111fb123cf0be45205ca"}, + {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:76390d3d66406cb01b9681c382874400e9dfd77f30ecdea4bd1bf5226dd4aff0"}, + {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:4d4c7fe5e50e269f9c63a260638488fec194a73993008618a59b54c47ef6ae72"}, + {file = "pyzmq-26.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25d128524207f53f7aae7c5abdc2b63f8957a060b00521af5ffcd20986b5d8f4"}, + {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d74b925d997e4f92b042bdd7085cd0a309ee0fd7cb4dc376059bbff6b32ff34f"}, + {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:732f957441e5b1c65a7509395e6b6cafee9e12df9aa5f4bf92ed266fe0ba70ee"}, + {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0a45102ad7ed9f9ddf2bd699cc5df37742cf7301111cba06001b927efecb120"}, + {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9f380d5333fc7cd17423f486125dcc073918676e33db70a6a8172b19fc78d23d"}, + {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8eaffcd6bf6a9d00b66a2052a33fa7e6a6575427e9644395f13c3d070f2918dc"}, + {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f1483d4975ae1b387b39bb8e23d1ff32fe5621aa9e4ed3055d05e9c5613fea53"}, + {file = "pyzmq-26.1.1-cp37-cp37m-win32.whl", hash = "sha256:a83653c6bbe5887caea55e49fbd2909c14b73acf43bcc051eb60b2d514bbd46e"}, + {file = "pyzmq-26.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9763a8d3f5f74ef679989b373c37cc22e8d07e56d26439205cb83edb7722357f"}, + {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2b045647caf620ce0ed6c8fd9fb6a73116f99aceed966b152a5ba1b416d25311"}, + {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f66dcb6625c002f209cdc12cae1a1fec926493cd2262efe37dc6b25a30cea863"}, + {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0cf1d980c969fb9e538f52abd2227f09e015096bc5c3ef7aa26e0d64051c1db8"}, + {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:443ebf5e261a95ee9725693f2a5a71401f89b89df0e0ea58844b074067aac2f1"}, + {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29de77ba1b1877fe7defc1b9140e65cbd35f72a63bc501e56c2eae55bde5fff4"}, + {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f6071ec95af145d7b659dae6786871cd85f0acc599286b6f8ba0c74592d83dd"}, + {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f0512fc87629ad968889176bf2165d721cd817401a281504329e2a2ed0ca6a3"}, + {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5ccfcf13e80719f6a2d9c0a021d9e47d4550907a29253554be2c09582f6d7963"}, + {file = "pyzmq-26.1.1-cp38-cp38-win32.whl", hash = "sha256:809673947e95752e407aaaaf03f205ee86ebfff9ca51db6d4003dfd87b8428d1"}, + {file = "pyzmq-26.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:62b5180e23e6f581600459cd983473cd723fdc64350f606d21407c99832aaf5f"}, + {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:fe73d7c89d6f803bed122135ff5783364e8cdb479cf6fe2d764a44b6349e7e0f"}, + {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db1b7e2b50ef21f398036786da4c153db63203a402396d9f21e08ea61f3f8dba"}, + {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c506a51cb01bb997a3f6440db0d121e5e7a32396e9948b1fdb6a7bfa67243f4"}, + {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:92eca4f80e8a748d880e55d3cf57ef487692e439f12d5c5a2e1cce84aaa7f6cb"}, + {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14bdbae02f72f4716b0ffe7500e9da303d719ddde1f3dcfb4c4f6cc1cf73bb02"}, + {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e03be7ed17836c9434cce0668ac1e2cc9143d7169f90f46a0167f6155e176e32"}, + {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc5df31e36e4fddd4c8b5c42daee8d54d7b529e898ac984be97bf5517de166a7"}, + {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f218179c90a12d660906e04b25a340dd63e9743000ba16232ddaf46888f269da"}, + {file = "pyzmq-26.1.1-cp39-cp39-win32.whl", hash = "sha256:7dfabc180a4da422a4b349c63077347392463a75fa07aa3be96712ed6d42c547"}, + {file = "pyzmq-26.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c5248e6e0fcbbbc912982e99cdd51c342601f495b0fa5bd667f3bdbdbf3e170f"}, + {file = "pyzmq-26.1.1-cp39-cp39-win_arm64.whl", hash = "sha256:2ae7aa1408778dc74582a1226052b930f9083b54b64d7e6ef6ec0466cfdcdec2"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:be3fc2b11c0c384949cf1f01f9a48555039408b0f3e877863b1754225635953e"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48dee75c2a9fa4f4a583d4028d564a0453447ee1277a29b07acc3743c092e259"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23f2fe4fb567e8098ebaa7204819658195b10ddd86958a97a6058eed2901eed3"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:472cacd16f627c06d3c8b2d374345ab74446bae913584a6245e2aa935336d929"}, + {file = "pyzmq-26.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8285b25aa20fcc46f1ca4afbc39fd3d5f2fe4c4bbf7f2c7f907a214e87a70024"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2067e63fd9d5c13cfe12624dab0366053e523b37a7a01678ce4321f839398939"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cc109be2ee3638035d276e18eaf66a1e1f44201c0c4bea4ee0c692766bbd3570"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d0da97e65ee73261dba70469cc8f63d8da3a8a825337a2e3d246b9e95141cdd0"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa79c528706561306938b275f89bb2c6985ce08469c27e5de05bc680df5e826f"}, + {file = "pyzmq-26.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3ddbd851a3a2651fdc5065a2804d50cf2f4b13b1bcd66de8e9e855d0217d4fcd"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3df226ab7464684ae6706e20a5cbab717c3735a7e409b3fa598b754d49f1946"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abad7b897e960d577eb4a0f3f789c1780bc3ffe2e7c27cf317e7c90ad26acf12"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c513d829a548c2d5c88983167be2b3aa537f6d1191edcdc6fcd8999e18bdd994"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70af4c9c991714ef1c65957605a8de42ef0d0620dd5f125953c8e682281bdb80"}, + {file = "pyzmq-26.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8d4234f335b0d0842f7d661d8cd50cbad0729be58f1c4deb85cd96b38fe95025"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2c0fdb7b758e0e1605157e480b00b3a599073068a37091a1c75ec65bf7498645"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc657577f057d60dd3642c9f95f28b432889b73143140061f7c1331d02f03df6"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e3b66fe6131b4f33d239f7d4c3bfb2f8532d8644bae3b3da4f3987073edac55"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b57e912feef6951aec8bb03fe0faa5ad5f36962883c72a30a9c965e6d988fd"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:146956aec7d947c5afc5e7da0841423d7a53f84fd160fff25e682361dcfb32cb"}, + {file = "pyzmq-26.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9521b874fd489495865172f344e46e0159095d1f161858e3fc6e28e43ca15160"}, + {file = "pyzmq-26.1.1.tar.gz", hash = "sha256:a7db05d8b7cd1a8c6610e9e9aa55d525baae7a44a43e18bc3260eb3f92de96c6"}, ] [package.dependencies] diff --git a/sdks/ts/src/api/models/Tasks_ForeachDo.ts b/sdks/ts/src/api/models/Tasks_ForeachDo.ts index f035536f5..5858114ed 100644 --- a/sdks/ts/src/api/models/Tasks_ForeachDo.ts +++ b/sdks/ts/src/api/models/Tasks_ForeachDo.ts @@ -23,7 +23,7 @@ export type Tasks_ForeachDo = { /** * The steps to run for each iteration */ - do: Array< + do: | Tasks_ToolCallStep | Tasks_YieldStep | Tasks_PromptStep @@ -35,6 +35,5 @@ export type Tasks_ForeachDo = { | Tasks_LogStep | Tasks_EmbedStep | Tasks_SearchStep - | Tasks_WaitForInputStep - >; + | Tasks_WaitForInputStep; }; diff --git a/sdks/ts/src/api/models/Tasks_MapReduceStep.ts b/sdks/ts/src/api/models/Tasks_MapReduceStep.ts index be542f460..e450d9162 100644 --- a/sdks/ts/src/api/models/Tasks_MapReduceStep.ts +++ b/sdks/ts/src/api/models/Tasks_MapReduceStep.ts @@ -12,7 +12,7 @@ export type Tasks_MapReduceStep = Tasks_BaseWorkflowStep & { */ map: Tasks_MapOver; /** - * The expression to reduce the results (`_` is a list of outputs) + * The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. */ - reduce: Common_PyExpression; + reduce?: Common_PyExpression; }; diff --git a/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts b/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts index ef7102320..3b05392db 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts @@ -15,48 +15,46 @@ export const $Tasks_ForeachDo = { isRequired: true, }, do: { - type: "array", - contains: { - type: "any-of", - contains: [ - { - type: "Tasks_ToolCallStep", - }, - { - type: "Tasks_YieldStep", - }, - { - type: "Tasks_PromptStep", - }, - { - type: "Tasks_ErrorWorkflowStep", - }, - { - type: "Tasks_SleepStep", - }, - { - type: "Tasks_ReturnStep", - }, - { - type: "Tasks_GetStep", - }, - { - type: "Tasks_SetStep", - }, - { - type: "Tasks_LogStep", - }, - { - type: "Tasks_EmbedStep", - }, - { - type: "Tasks_SearchStep", - }, - { - type: "Tasks_WaitForInputStep", - }, - ], - }, + type: "any-of", + description: `The steps to run for each iteration`, + contains: [ + { + type: "Tasks_ToolCallStep", + }, + { + type: "Tasks_YieldStep", + }, + { + type: "Tasks_PromptStep", + }, + { + type: "Tasks_ErrorWorkflowStep", + }, + { + type: "Tasks_SleepStep", + }, + { + type: "Tasks_ReturnStep", + }, + { + type: "Tasks_GetStep", + }, + { + type: "Tasks_SetStep", + }, + { + type: "Tasks_LogStep", + }, + { + type: "Tasks_EmbedStep", + }, + { + type: "Tasks_SearchStep", + }, + { + type: "Tasks_WaitForInputStep", + }, + ], isRequired: true, }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts b/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts index a88a4b6d7..3d52b1727 100644 --- a/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts @@ -26,13 +26,12 @@ export const $Tasks_MapReduceStep = { }, reduce: { type: "all-of", - description: `The expression to reduce the results (\`_\` is a list of outputs)`, + description: `The expression to reduce the results (\`_\` is a list of outputs). If not provided, the results are returned as a list.`, contains: [ { type: "Common_PyExpression", }, ], - isRequired: true, }, }, }, diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index 70cb5c0aa..1b4a4680a 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -421,7 +421,7 @@ export class DefaultService { } /** * Create a new task - * @returns Common_ResourceCreatedResponse The request has succeeded and a new resource has been created as a result. + * @returns Common_ResourceCreatedResponse The request has succeeded. * @throws ApiError */ public tasksRouteCreate({ @@ -704,7 +704,7 @@ export class DefaultService { } /** * Create or update a task - * @returns Common_ResourceUpdatedResponse The request has succeeded. + * @returns Common_ResourceCreatedResponse The request has succeeded and a new resource has been created as a result. * @throws ApiError */ public tasksCreateOrUpdateRouteCreateOrUpdate({ @@ -713,12 +713,12 @@ export class DefaultService { requestBody, }: { /** - * ID of parent resource + * ID of the agent */ parentId: Common_uuid; id: Common_uuid; requestBody: Tasks_CreateTaskRequest; - }): CancelablePromise { + }): CancelablePromise { return this.httpRequest.request({ method: "POST", url: "/agents/{parent_id}/tasks/{id}", diff --git a/typespec/chat/endpoints.tsp b/typespec/chat/endpoints.tsp index 232a44ab0..64eb6700d 100644 --- a/typespec/chat/endpoints.tsp +++ b/typespec/chat/endpoints.tsp @@ -19,8 +19,6 @@ interface Endpoints { @post @doc("Generate a response from the model") generate( - @header contentType: yaml | json; - @path @doc("The session ID") id: uuid; diff --git a/typespec/common/interfaces.tsp b/typespec/common/interfaces.tsp index f6a6f2510..b7a15bf25 100644 --- a/typespec/common/interfaces.tsp +++ b/typespec/common/interfaces.tsp @@ -28,7 +28,7 @@ interface CreateEndpoint< > { @post @doc(DocString) - create(@header contentType: yaml | json, ...CreateType): { + create(...CreateType): { @statusCode _: "201"; @body @@ -43,7 +43,7 @@ interface CreateOrUpdateEndpoint< > { @post @doc(DocString) - createOrUpdate(@header contentType: yaml | json, ...CreateOrUpdateType): { + createOrUpdate(...CreateOrUpdateType): { @statusCode _: "200"; @body @@ -59,8 +59,6 @@ interface UpdateEndpoint< @put @doc(DocString) update( - @header contentType: yaml | json, - @path @doc("ID of the resource") id: uuid, @@ -82,8 +80,6 @@ interface PatchEndpoint< @patch @doc(DocString) patch( - @header contentType: yaml | json, - @path @doc("ID of the resource") id: uuid, @@ -151,8 +147,6 @@ interface ChildCreateEndpoint< @post @doc(DocString) create( - @header contentType: yaml | json, - @path @doc("ID of parent resource") id: uuid, @@ -174,8 +168,6 @@ interface ChildCreateOrUpdateEndpoint< @post @doc(DocString) createOrUpdate( - @header contentType: yaml | json, - @path @doc("ID of parent resource") parent_id: uuid, diff --git a/typespec/tasks/endpoints.tsp b/typespec/tasks/endpoints.tsp index 73f50e457..21cab7ed6 100644 --- a/typespec/tasks/endpoints.tsp +++ b/typespec/tasks/endpoints.tsp @@ -1,7 +1,11 @@ +import "@typespec/http"; + import "../common"; import "../executions"; import "./models.tsp"; +using TypeSpec.Http; + using Common; using Executions; @@ -11,11 +15,25 @@ namespace Tasks; // TASK ENDPOINTS // -interface CreateOrUpdateEndpoints - extends ChildCreateOrUpdateEndpoint< - CreateOrUpdateTaskRequest, - "Create or update a task" - > {} +interface CreateOrUpdateEndpoints { + @post + @doc("Create or update a task") + createOrUpdate( + @header contentType: yaml | json, + + @path + @doc("ID of the agent") + parent_id: uuid, + + ...CreateOrUpdateTaskRequest, + ): { + @statusCode _: "201"; + + @body + @doc("Details of the task updated along with ID") + body: ResourceCreatedResponse; + }; +} interface Endpoints extends ChildUpdateEndpoint< @@ -27,5 +45,22 @@ interface Endpoints "Update an existing task (merges with existing values)" >, ChildDeleteEndpoint<"Delete a task by its id">, - ChildLimitOffsetPagination, - ChildCreateEndpoint {} \ No newline at end of file + ChildLimitOffsetPagination { + @post + @doc("Create a new task") + create( + @header contentType: yaml | json, + + @path + @doc("ID of parent resource") + id: uuid, + + ...CreateTaskRequest, + ): { + @statusCode _: "200"; + + @body + @doc("Details of the task created") + body: ResourceCreatedResponse; + }; +} diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp index 152f6482b..9cf3b9b06 100644 --- a/typespec/tasks/steps.tsp +++ b/typespec/tasks/steps.tsp @@ -23,6 +23,7 @@ namespace Tasks; /** An object where values are strings in the Common Expression Language that get evaluated before being passed downstream */ alias ExpressionObject = Record; +@discriminator("kind_") model BaseWorkflowStep { /** The kind of step */ kind_: WorkflowStepKind; @@ -159,7 +160,7 @@ model ForeachDo { in: PyExpression; /** The steps to run for each iteration */ - do: NonConditionalWorkflowStep[]; + do: NonConditionalWorkflowStep; } model ForeachStep extends BaseWorkflowStep { @@ -183,8 +184,8 @@ model MapReduceStep extends BaseWorkflowStep { /** The steps to run for each iteration */ map: MapOver; - /** The expression to reduce the results (`_` is a list of outputs) */ - reduce: PyExpression; + /** The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. */ + reduce?: PyExpression; } ///////////////////////// From f8111eaf3e1ecf25cab76e00c090a2da28a72457 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 19 Aug 2024 21:09:56 -0400 Subject: [PATCH 082/110] fix(agents-api): Fix the typespec bug and regenerate Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Agents.py | 5 + agents-api/agents_api/autogen/Chat.py | 29 +- agents-api/agents_api/autogen/Common.py | 3 + agents-api/agents_api/autogen/Docs.py | 12 + agents-api/agents_api/autogen/Entries.py | 19 +- agents-api/agents_api/autogen/Executions.py | 8 + agents-api/agents_api/autogen/Jobs.py | 5 +- agents-api/agents_api/autogen/Sessions.py | 23 +- agents-api/agents_api/autogen/Tasks.py | 43 +- agents-api/agents_api/autogen/Tools.py | 12 + agents-api/agents_api/autogen/Users.py | 5 + agents-api/poetry.lock | 243 ++++--- agents-api/pyproject.toml | 6 +- agents-api/tests/utils.py | 3 - scripts/generate_openapi_code.sh | 3 + sdks/python/julep/api/__init__.py | 130 ++++ sdks/python/julep/api/types/__init__.py | 150 ++++- .../api/types/tasks_base_workflow_step.py | 2 +- .../julep/api/types/tasks_case_then_then.py | 607 +++++++++++++++++- .../tasks_create_task_request_main_item.py | 2 +- .../julep/api/types/tasks_foreach_do_do.py | 607 +++++++++++++++++- .../types/tasks_if_else_workflow_step_else.py | 607 +++++++++++++++++- .../types/tasks_if_else_workflow_step_then.py | 607 +++++++++++++++++- .../tasks_parallel_step_parallel_item.py | 555 +++++++++++++++- .../tasks_patch_task_request_main_item.py | 2 +- .../julep/api/types/tasks_task_main_item.py | 2 +- .../julep/api/types/tasks_tool_call_step.py | 3 +- .../tasks_update_task_request_main_item.py | 2 +- sdks/python/poetry.lock | 22 +- sdks/ts/src/api/models/Tasks_CaseThen.ts | 2 + sdks/ts/src/api/models/Tasks_ForeachDo.ts | 2 + .../api/models/Tasks_IfElseWorkflowStep.ts | 3 + sdks/ts/src/api/models/Tasks_ParallelStep.ts | 2 + sdks/ts/src/api/models/Tasks_ToolCallStep.ts | 3 +- sdks/ts/src/api/schemas/$Tasks_CaseThen.ts | 3 + sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts | 3 + .../api/schemas/$Tasks_IfElseWorkflowStep.ts | 6 + .../ts/src/api/schemas/$Tasks_ParallelStep.ts | 3 + .../ts/src/api/schemas/$Tasks_ToolCallStep.ts | 2 +- typespec/package-lock.json | 114 ++-- typespec/package.json | 12 +- typespec/tasks/steps.tsp | 7 +- 42 files changed, 3540 insertions(+), 339 deletions(-) diff --git a/agents-api/agents_api/autogen/Agents.py b/agents-api/agents_api/autogen/Agents.py index b4acfb563..a20dd5392 100644 --- a/agents-api/agents_api/autogen/Agents.py +++ b/agents-api/agents_api/autogen/Agents.py @@ -13,6 +13,7 @@ class Agent(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -59,6 +60,7 @@ class CreateAgentRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -92,6 +94,7 @@ class CreateAgentRequest(BaseModel): class CreateOrUpdateAgentRequest(CreateAgentRequest): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: UUID @@ -130,6 +133,7 @@ class PatchAgentRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -167,6 +171,7 @@ class UpdateAgentRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 4d157f7c5..19c9fb05e 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, StrictBool from .Common import LogitBias from .Docs import DocReference @@ -16,6 +16,7 @@ class BaseChatOutput(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) index: int @@ -31,6 +32,7 @@ class BaseChatOutput(BaseModel): class BaseChatResponse(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) usage: CompetionUsage | None = None @@ -54,6 +56,7 @@ class BaseChatResponse(BaseModel): class BaseTokenLogProb(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) token: str @@ -66,6 +69,7 @@ class BaseTokenLogProb(BaseModel): class ChatInputData(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) messages: Annotated[list[InputChatMLMessage], Field(min_length=1)] @@ -88,6 +92,7 @@ class ChatOutputChunk(BaseChatOutput): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) delta: InputChatMLMessage @@ -98,6 +103,7 @@ class ChatOutputChunk(BaseChatOutput): class ChunkChatResponse(BaseChatResponse): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) choices: list[ChatOutputChunk] @@ -112,6 +118,7 @@ class CompetionUsage(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) completion_tokens: Annotated[ @@ -136,6 +143,7 @@ class CompetionUsage(BaseModel): class CompletionResponseFormat(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["text", "json_object"] = "text" @@ -146,6 +154,7 @@ class CompletionResponseFormat(BaseModel): class LogProbResponse(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) content: Annotated[list[TokenLogProb] | None, Field(...)] @@ -156,6 +165,7 @@ class LogProbResponse(BaseModel): class MessageChatResponse(BaseChatResponse): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) choices: list[SingleChatOutput | MultipleChatOutput] @@ -170,6 +180,7 @@ class MultipleChatOutput(BaseChatOutput): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) messages: list[InputChatMLMessage] @@ -177,6 +188,7 @@ class MultipleChatOutput(BaseChatOutput): class OpenAISettings(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] @@ -203,6 +215,7 @@ class SingleChatOutput(BaseChatOutput): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) message: InputChatMLMessage @@ -210,6 +223,7 @@ class SingleChatOutput(BaseChatOutput): class TokenLogProb(BaseTokenLogProb): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) top_logprobs: list[BaseTokenLogProb] @@ -217,17 +231,18 @@ class TokenLogProb(BaseTokenLogProb): class ChatInput(ChatInputData): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) - remember: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] + remember: Annotated[StrictBool, Field(False, json_schema_extra={"readOnly": True})] """ DISABLED: Whether this interaction should form new memories or not (will be enabled in a future release) """ - recall: bool = True + recall: StrictBool = True """ Whether previous memories and docs should be recalled or not """ - save: bool = True + save: StrictBool = True """ Whether this interaction should be stored in the session history or not """ @@ -241,7 +256,7 @@ class ChatInput(ChatInputData): """ Identifier of the model to be used """ - stream: bool = False + stream: StrictBool = False """ Indicates if the server should stream the response as it's generated """ @@ -305,6 +320,7 @@ class DefaultChatSettings(OpenAISettings): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] @@ -323,6 +339,7 @@ class DefaultChatSettings(OpenAISettings): class ChatSettings(DefaultChatSettings): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) model: Annotated[ @@ -335,7 +352,7 @@ class ChatSettings(DefaultChatSettings): """ Identifier of the model to be used """ - stream: bool = False + stream: StrictBool = False """ Indicates if the server should stream the response as it's generated """ diff --git a/agents-api/agents_api/autogen/Common.py b/agents-api/agents_api/autogen/Common.py index aab88621d..7dedd743e 100644 --- a/agents-api/agents_api/autogen/Common.py +++ b/agents-api/agents_api/autogen/Common.py @@ -38,6 +38,7 @@ class Offset(RootModel[int]): class ResourceCreatedResponse(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: UUID @@ -56,6 +57,7 @@ class ResourceCreatedResponse(BaseModel): class ResourceDeletedResponse(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: UUID @@ -74,6 +76,7 @@ class ResourceDeletedResponse(BaseModel): class ResourceUpdatedResponse(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: UUID diff --git a/agents-api/agents_api/autogen/Docs.py b/agents-api/agents_api/autogen/Docs.py index 36d0c5a95..3039fc3f4 100644 --- a/agents-api/agents_api/autogen/Docs.py +++ b/agents-api/agents_api/autogen/Docs.py @@ -11,6 +11,7 @@ class BaseDocSearchRequest(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) limit: Annotated[int, Field(10, ge=1, le=100)] @@ -26,6 +27,7 @@ class CreateDocRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -46,6 +48,7 @@ class CreateDocRequest(BaseModel): class Doc(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -71,6 +74,7 @@ class Doc(BaseModel): class DocOwner(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: UUID @@ -79,6 +83,7 @@ class DocOwner(BaseModel): class DocReference(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) owner: DocOwner @@ -96,6 +101,7 @@ class DocReference(BaseModel): class DocSearchResponse(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) docs: list[DocReference] @@ -110,6 +116,7 @@ class DocSearchResponse(BaseModel): class EmbedQueryRequest(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) text: str | list[str] @@ -120,6 +127,7 @@ class EmbedQueryRequest(BaseModel): class EmbedQueryResponse(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) vectors: list[list[float]] @@ -130,6 +138,7 @@ class EmbedQueryResponse(BaseModel): class HybridDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) confidence: Annotated[float, Field(0.5, ge=0.0, le=1.0)] @@ -152,6 +161,7 @@ class HybridDocSearchRequest(BaseDocSearchRequest): class Snippet(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) index: int @@ -160,6 +170,7 @@ class Snippet(BaseModel): class TextOnlyDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) text: str @@ -170,6 +181,7 @@ class TextOnlyDocSearchRequest(BaseDocSearchRequest): class VectorDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) confidence: Annotated[float, Field(0.5, ge=0.0, le=1.0)] diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index 00070bd71..dc64872a1 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -6,13 +6,21 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import ( + AnyUrl, + AwareDatetime, + BaseModel, + ConfigDict, + Field, + StrictBool, +) from .Tools import ChosenToolCall, Tool, ToolResponse class BaseEntry(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) role: Literal[ @@ -55,6 +63,7 @@ class BaseEntry(BaseModel): class ChatMLImageContentPart(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) image_url: ImageURL @@ -69,6 +78,7 @@ class ChatMLImageContentPart(BaseModel): class ChatMLTextContentPart(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) text: str @@ -80,6 +90,7 @@ class ChatMLTextContentPart(BaseModel): class Entry(BaseEntry): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] @@ -91,6 +102,7 @@ class Entry(BaseEntry): class History(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) entries: list[Entry] @@ -104,6 +116,7 @@ class History(BaseModel): class ImageURL(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) url: AnyUrl @@ -118,6 +131,7 @@ class ImageURL(BaseModel): class InputChatMLMessage(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) role: Literal[ @@ -140,7 +154,7 @@ class InputChatMLMessage(BaseModel): """ Name """ - continue_: Annotated[bool | None, Field(None, alias="continue")] + continue_: Annotated[StrictBool | None, Field(None, alias="continue")] """ Whether to continue this message or return a new one """ @@ -148,6 +162,7 @@ class InputChatMLMessage(BaseModel): class Relation(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) head: UUID diff --git a/agents-api/agents_api/autogen/Executions.py b/agents-api/agents_api/autogen/Executions.py index 4ff4c4b66..97046d7d9 100644 --- a/agents-api/agents_api/autogen/Executions.py +++ b/agents-api/agents_api/autogen/Executions.py @@ -15,6 +15,7 @@ class CreateExecutionRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) input: dict[str, Any] @@ -26,6 +27,7 @@ class CreateExecutionRequest(BaseModel): class Execution(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) task_id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -65,6 +67,7 @@ class Execution(BaseModel): class TaskTokenResumeExecutionRequest(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) status: Literal["running"] = "running" @@ -76,6 +79,7 @@ class TaskTokenResumeExecutionRequest(BaseModel): class Transition(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Annotated[ @@ -102,6 +106,7 @@ class Transition(BaseModel): class TransitionTarget(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) workflow: Annotated[ @@ -120,6 +125,7 @@ class TransitionTarget(BaseModel): class UpdateExecutionRequest(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) status: Literal[ @@ -135,6 +141,7 @@ class UpdateExecutionRequest(BaseModel): class ResumeExecutionRequest(UpdateExecutionRequest): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) status: Literal["running"] = "running" @@ -146,6 +153,7 @@ class ResumeExecutionRequest(UpdateExecutionRequest): class StopExecutionRequest(UpdateExecutionRequest): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) status: Literal["cancelled"] = "cancelled" diff --git a/agents-api/agents_api/autogen/Jobs.py b/agents-api/agents_api/autogen/Jobs.py index 92acce9ad..a3402d04d 100644 --- a/agents-api/agents_api/autogen/Jobs.py +++ b/agents-api/agents_api/autogen/Jobs.py @@ -6,11 +6,12 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, StrictBool class JobStatus(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -36,7 +37,7 @@ class JobStatus(BaseModel): """ Reason for the current state of the job """ - has_progress: bool = False + has_progress: StrictBool = False """ Whether this Job supports progress updates """ diff --git a/agents-api/agents_api/autogen/Sessions.py b/agents-api/agents_api/autogen/Sessions.py index fdfe25c92..caf47c278 100644 --- a/agents-api/agents_api/autogen/Sessions.py +++ b/agents-api/agents_api/autogen/Sessions.py @@ -6,7 +6,7 @@ from typing import Annotated, Any, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, StrictBool class CreateSessionRequest(BaseModel): @@ -15,6 +15,7 @@ class CreateSessionRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) user: UUID | None = None @@ -31,7 +32,7 @@ class CreateSessionRequest(BaseModel): """ A specific situation that sets the background for this session """ - render_templates: bool = True + render_templates: StrictBool = True """ Render system and assistant message content as jinja templates """ @@ -52,13 +53,14 @@ class PatchSessionRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' """ A specific situation that sets the background for this session """ - render_templates: bool = True + render_templates: StrictBool = True """ Render system and assistant message content as jinja templates """ @@ -75,6 +77,7 @@ class PatchSessionRequest(BaseModel): class Session(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' @@ -85,7 +88,7 @@ class Session(BaseModel): """ Summary (null at the beginning) - generated automatically after every interaction """ - render_templates: bool = True + render_templates: StrictBool = True """ Render system and assistant message content as jinja templates """ @@ -115,6 +118,7 @@ class Session(BaseModel): class SingleAgentMultiUserSession(Session): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) agent: UUID @@ -123,6 +127,7 @@ class SingleAgentMultiUserSession(Session): class SingleAgentNoUserSession(Session): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) agent: UUID @@ -130,6 +135,7 @@ class SingleAgentNoUserSession(Session): class SingleAgentSingleUserSession(Session): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) agent: UUID @@ -142,13 +148,14 @@ class UpdateSessionRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' """ A specific situation that sets the background for this session """ - render_templates: bool = True + render_templates: StrictBool = True """ Render system and assistant message content as jinja templates """ @@ -165,6 +172,7 @@ class UpdateSessionRequest(BaseModel): class CreateOrUpdateSessionRequest(CreateSessionRequest): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: UUID @@ -182,7 +190,7 @@ class CreateOrUpdateSessionRequest(CreateSessionRequest): """ A specific situation that sets the background for this session """ - render_templates: bool = True + render_templates: StrictBool = True """ Render system and assistant message content as jinja templates """ @@ -199,6 +207,7 @@ class CreateOrUpdateSessionRequest(CreateSessionRequest): class MultiAgentMultiUserSession(Session): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) agents: list[UUID] @@ -207,6 +216,7 @@ class MultiAgentMultiUserSession(Session): class MultiAgentNoUserSession(Session): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) agents: list[UUID] @@ -214,6 +224,7 @@ class MultiAgentNoUserSession(Session): class MultiAgentSingleUserSession(Session): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) agents: list[UUID] diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index 742dcc3cf..b3059cc12 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -6,7 +6,7 @@ from typing import Annotated, Any, Literal from uuid import UUID -from pydantic import AwareDatetime, BaseModel, ConfigDict, Field +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, StrictBool from .Chat import ChatSettings from .Docs import ( @@ -21,6 +21,7 @@ class BaseWorkflowStep(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal[ @@ -50,6 +51,7 @@ class BaseWorkflowStep(BaseModel): class CaseThen(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) case: str @@ -82,6 +84,7 @@ class CreateTaskRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) name: str @@ -117,7 +120,7 @@ class CreateTaskRequest(BaseModel): """ Tools defined specifically for this task not included in the Agent itself. """ - inherit_tools: bool = True + inherit_tools: StrictBool = True """ Whether to inherit tools from the parent agent or not. Defaults to true. """ @@ -126,6 +129,7 @@ class CreateTaskRequest(BaseModel): class EmbedStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["embed"] = "embed" @@ -137,6 +141,7 @@ class EmbedStep(BaseWorkflowStep): class ErrorWorkflowStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["error"] = "error" @@ -148,6 +153,7 @@ class ErrorWorkflowStep(BaseWorkflowStep): class EvaluateStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["evaluate"] = "evaluate" @@ -159,6 +165,7 @@ class EvaluateStep(BaseWorkflowStep): class ForeachDo(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) in_: Annotated[str, Field(alias="in")] @@ -187,6 +194,7 @@ class ForeachDo(BaseModel): class ForeachStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["foreach"] = "foreach" @@ -198,6 +206,7 @@ class ForeachStep(BaseWorkflowStep): class GetStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["get"] = "get" @@ -209,6 +218,7 @@ class GetStep(BaseWorkflowStep): class IfElseWorkflowStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["if_else"] = "if_else" @@ -257,6 +267,7 @@ class IfElseWorkflowStep(BaseWorkflowStep): class LogStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["log"] = "log" @@ -268,6 +279,7 @@ class LogStep(BaseWorkflowStep): class MapOver(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) over: str @@ -282,6 +294,7 @@ class MapOver(BaseModel): class MapReduceStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["map_reduce"] = "map_reduce" @@ -297,6 +310,7 @@ class MapReduceStep(BaseWorkflowStep): class ParallelStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["parallel"] = "parallel" @@ -326,6 +340,7 @@ class PatchTaskRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) description: str = "" @@ -363,7 +378,7 @@ class PatchTaskRequest(BaseModel): """ Tools defined specifically for this task not included in the Agent itself. """ - inherit_tools: bool = True + inherit_tools: StrictBool = True """ Whether to inherit tools from the parent agent or not. Defaults to true. """ @@ -372,6 +387,7 @@ class PatchTaskRequest(BaseModel): class PromptStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["prompt"] = "prompt" @@ -387,6 +403,7 @@ class PromptStep(BaseWorkflowStep): class ReturnStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["return"] = "return" @@ -398,6 +415,7 @@ class ReturnStep(BaseWorkflowStep): class SearchStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["search"] = "search" @@ -409,6 +427,7 @@ class SearchStep(BaseWorkflowStep): class SetKey(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) key: str @@ -423,6 +442,7 @@ class SetKey(BaseModel): class SetStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["set"] = "set" @@ -434,6 +454,7 @@ class SetStep(BaseWorkflowStep): class SleepFor(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) seconds: Annotated[int, Field(0, ge=0)] @@ -456,6 +477,7 @@ class SleepFor(BaseModel): class SleepStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["sleep"] = "sleep" @@ -467,6 +489,7 @@ class SleepStep(BaseWorkflowStep): class SwitchStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["switch"] = "switch" @@ -482,6 +505,7 @@ class Task(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) name: str @@ -517,7 +541,7 @@ class Task(BaseModel): """ Tools defined specifically for this task not included in the Agent itself. """ - inherit_tools: bool = True + inherit_tools: StrictBool = True """ Whether to inherit tools from the parent agent or not. Defaults to true. """ @@ -535,9 +559,10 @@ class Task(BaseModel): class TaskTool(CreateToolRequest): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) - inherited: Annotated[bool, Field(False, json_schema_extra={"readOnly": True})] + inherited: Annotated[StrictBool, Field(False, json_schema_extra={"readOnly": True})] """ Read-only: Whether the tool was inherited or not. Only applies within tasks. """ @@ -545,6 +570,7 @@ class TaskTool(CreateToolRequest): class ToolCallStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["tool_call"] = "tool_call" @@ -554,7 +580,7 @@ class ToolCallStep(BaseWorkflowStep): """ The tool to run """ - arguments: dict[str, Any] + arguments: dict[str, str] """ The input parameters for the tool """ @@ -566,6 +592,7 @@ class UpdateTaskRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) description: str = "" @@ -600,7 +627,7 @@ class UpdateTaskRequest(BaseModel): """ Tools defined specifically for this task not included in the Agent itself. """ - inherit_tools: bool = True + inherit_tools: StrictBool = True """ Whether to inherit tools from the parent agent or not. Defaults to true. """ @@ -609,6 +636,7 @@ class UpdateTaskRequest(BaseModel): class WaitForInputStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["wait_for_input"] = "wait_for_input" @@ -620,6 +648,7 @@ class WaitForInputStep(BaseWorkflowStep): class YieldStep(BaseWorkflowStep): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) kind_: Literal["yield"] = "yield" diff --git a/agents-api/agents_api/autogen/Tools.py b/agents-api/agents_api/autogen/Tools.py index 6d7f60a6b..74d207d06 100644 --- a/agents-api/agents_api/autogen/Tools.py +++ b/agents-api/agents_api/autogen/Tools.py @@ -15,6 +15,7 @@ class ChosenToolCall(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] @@ -34,6 +35,7 @@ class CreateToolRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] @@ -52,6 +54,7 @@ class CreateToolRequest(BaseModel): class FunctionCallOption(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) name: str @@ -66,6 +69,7 @@ class FunctionDef(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) name: Any | None = None @@ -90,6 +94,7 @@ class FunctionDef(BaseModel): class NamedToolChoice(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] @@ -108,6 +113,7 @@ class PatchToolRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] | None = None @@ -126,6 +132,7 @@ class PatchToolRequest(BaseModel): class Tool(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] @@ -153,6 +160,7 @@ class Tool(BaseModel): class ToolResponse(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: UUID @@ -168,6 +176,7 @@ class UpdateToolRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] @@ -186,6 +195,7 @@ class UpdateToolRequest(BaseModel): class ChosenFunctionCall(ChosenToolCall): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["function"] = "function" @@ -197,6 +207,7 @@ class ChosenFunctionCall(ChosenToolCall): class FunctionTool(Tool): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["function"] = "function" @@ -209,6 +220,7 @@ class FunctionTool(Tool): class NamedFunctionChoice(NamedToolChoice): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) type: Literal["function"] = "function" diff --git a/agents-api/agents_api/autogen/Users.py b/agents-api/agents_api/autogen/Users.py index 409f5039b..24ba132ac 100644 --- a/agents-api/agents_api/autogen/Users.py +++ b/agents-api/agents_api/autogen/Users.py @@ -15,6 +15,7 @@ class CreateUserRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -40,6 +41,7 @@ class PatchUserRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -65,6 +67,7 @@ class UpdateUserRequest(BaseModel): """ model_config = ConfigDict( + extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -86,6 +89,7 @@ class UpdateUserRequest(BaseModel): class User(BaseModel): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -116,6 +120,7 @@ class User(BaseModel): class CreateOrUpdateUserRequest(CreateUserRequest): model_config = ConfigDict( + extra="allow", populate_by_name=True, ) id: UUID diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 98bc5c498..00d7a4708 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -13,87 +13,102 @@ files = [ [[package]] name = "aiohttp" -version = "3.10.4" +version = "3.10.5" description = "Async http client/server framework (asyncio)" optional = false python-versions = ">=3.8" files = [ - {file = "aiohttp-3.10.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:81037ddda8cc0a95c6d8c1b9029d0b19a62db8770c0e239e3bea0109d294ab66"}, - {file = "aiohttp-3.10.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:71944d4f4090afc07ce96b7029d5a574240e2f39570450df4af0d5b93a5ee64a"}, - {file = "aiohttp-3.10.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c774f08afecc0a617966f45a9c378456e713a999ee60654d9727617def3e4ee4"}, - {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc990e73613c78ab2930b60266135066f37fdfce6b32dd604f42c5c377ee880a"}, - {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6acd1a908740f708358d240f9a3243cec31a456e3ded65c2cb46f6043bc6735"}, - {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6075e27e7e54fbcd1c129c5699b2d251c885c9892e26d59a0fb7705141c2d14b"}, - {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc98d93d11d860ac823beb6131f292d82efb76f226b5e28a3eab1ec578dfd041"}, - {file = "aiohttp-3.10.4-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:201ddf1471567568be381b6d4701e266a768f7eaa2f99ef753f2c9c5e1e3fb5c"}, - {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7d202ec55e61f06b1a1eaf317fba7546855cbf803c13ce7625d462fb8c88e238"}, - {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:96b2e7c110a941c8c1a692703b8ac1013e47f17ee03356c71d55c0a54de2ce38"}, - {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8ba0fbc56c44883bd757ece433f9caadbca67f565934afe9bc53ba3bd99cc368"}, - {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:46cc9069da466652bb7b8b3fac1f8ce2e12a9dc0fb11551faa420c4cdbc60abf"}, - {file = "aiohttp-3.10.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:93a19cd1e9dc703257fda78b8e889c3a08eabaa09f6ff0d867850b03964f80d1"}, - {file = "aiohttp-3.10.4-cp310-cp310-win32.whl", hash = "sha256:8593040bcc8075fc0e817a602bc5d3d74c7bd717619ffc175a8ba0188edebadf"}, - {file = "aiohttp-3.10.4-cp310-cp310-win_amd64.whl", hash = "sha256:326fb5228aadfc395981d9b336d56a698da335897c4143105c73b583d7500839"}, - {file = "aiohttp-3.10.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:dfe48f477e02ef5ab247c6ac431a6109c69b5c24cb3ccbcd3e27c4fb39691fe4"}, - {file = "aiohttp-3.10.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f6fe78b51852e25d4e20be51ef88c2a0bf31432b9f2223bdbd61c01a0f9253a7"}, - {file = "aiohttp-3.10.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5cc75ff5efbd92301e63a157fddb18a6964a3f40e31c77d57e97dbb9bb3373b4"}, - {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dca39391f45fbb28daa6412f98c625265bf6b512cc41382df61672d1b242f8f4"}, - {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8616dd5ed8b3b4029021b560305041c62e080bb28f238c27c2e150abe3539587"}, - {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9d7958ba22854b3f00a7bbb66cde1dc759760ce8a3e6dfe9ea53f06bccaa9aa2"}, - {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a24ac7164a824ef2e8e4e9a9f6debb1f43c44ad7ad04efc6018a6610555666d"}, - {file = "aiohttp-3.10.4-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:660ad010b8fd0b26e8edb8ae5c036db5b16baac4278198ad238b11956d920b3d"}, - {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:93ee83008d3e505db9846a5a1f48a002676d8dcc90ee431a9462541c9b81393c"}, - {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:77071795efd6ba87f409001141fb05c94ee962b9fca6c8fa1f735c2718512de4"}, - {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ff371ae72a1816c3eeba5c9cff42cb739aaa293fec7d78f180d1c7ee342285b6"}, - {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c253e81f12da97f85d45441e8c6da0d9c12e07db4a7136b0a955df6fc5e4bf51"}, - {file = "aiohttp-3.10.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:2ce101c447cf7ba4b6e5ab07bfa2c0da21cbab66922f78a601f0b84fd7710d72"}, - {file = "aiohttp-3.10.4-cp311-cp311-win32.whl", hash = "sha256:705c311ecf2d30fbcf3570d1a037c657be99095694223488140c47dee4ef2460"}, - {file = "aiohttp-3.10.4-cp311-cp311-win_amd64.whl", hash = "sha256:ebddbfea8a8d6b97f717658fa85a96681a28990072710d3de3a4eba5d6804a37"}, - {file = "aiohttp-3.10.4-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:fe4d63f42d9c604521b208b754abfafe01218af4a8f6332b43196ee8fe88bbd5"}, - {file = "aiohttp-3.10.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:fef7b7bd3a6911b4d148332136d34d3c2aee3d54d354373b1da6d96bc08089a5"}, - {file = "aiohttp-3.10.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fff8606149098935188fe1e135f7e7991e6a36d6fe394fd15939fc57d0aff889"}, - {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eb3df1aa83602be9a5e572c834d74c3c8e382208b59a873aabfe4c493c45ed0"}, - {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5c4a71d4a5e0cbfd4bfadd13cb84fe2bc76c64d550dc4f22c22008c9354cffb3"}, - {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bf61884a604c399458c4a42c8caea000fbcc44255ed89577ff50cb688a0fe8e2"}, - {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2015e4b40bd5dedc8155c2b2d24a2b07963ae02b5772373d0b599a68e38a316b"}, - {file = "aiohttp-3.10.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b06e1a66bf0a1a2d0f12aef25843dfd2093df080d6c1acbc43914bb9c8f36ed3"}, - {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:eb898c9ad5a1228a669ebe2e2ba3d76aebe1f7c10b78f09a36000254f049fc2b"}, - {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:2d64a5a7539320c3cecb4bca093ea825fcc906f8461cf8b42a7bf3c706ce1932"}, - {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:438c6e1492d060b21285f4b6675b941cf96dd9ef3dfdd59940561029b82e3e1f"}, - {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:e99bf118afb2584848dba169a685fe092b338a4fe52ae08c7243d7bc4cc204fe"}, - {file = "aiohttp-3.10.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9dc26781fb95225c6170619dece8b5c6ca7cfb1b0be97b7ee719915773d0c2a9"}, - {file = "aiohttp-3.10.4-cp312-cp312-win32.whl", hash = "sha256:45bb655cb8b3a61e19977183a4e0962051ae90f6d46588ed4addb8232128141c"}, - {file = "aiohttp-3.10.4-cp312-cp312-win_amd64.whl", hash = "sha256:347bbdc48411badc24fe3a13565820bc742db3aa2f9127cd5f48c256caf87e29"}, - {file = "aiohttp-3.10.4-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:4ad284cee0fdcdc0216346b849fd53d201b510aff3c48aa3622daec9ada4bf80"}, - {file = "aiohttp-3.10.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:58df59234be7d7e80548b9482ebfeafdda21948c25cb2873c7f23870c8053dfe"}, - {file = "aiohttp-3.10.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:5f52225af7f91f27b633f73473e9ef0aa8e2112d57b69eaf3aa4479e3ea3bc0e"}, - {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93f1a0e12c321d923c024b56d7dcd8012e60bf30a4b3fb69a88be15dcb9ab80b"}, - {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9e9e9a51dd12f2f71fdbd7f7230dcb75ed8f77d8ac8e07c73b599b6d7027e5c"}, - {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:38bb515f1affc36d3d97b02bf82099925a5785c4a96066ff4400a83ad09d3d5d"}, - {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e685afb0e3b7b861d89cb3690d89eeda221b43095352efddaaa735c6baf87f3"}, - {file = "aiohttp-3.10.4-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:abd5673e3391564871ba6753cf674dcf2051ef19dc508998fe0758a6c7b429a0"}, - {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:4b34e5086e1ead3baa740e32adf35cc5e42338e44c4b07f7b62b41ca6d6a5bfd"}, - {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c3fd3b8f0164fb2866400cd6eb9e884ab0dc95f882cf8b25e560ace7350c552d"}, - {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:b95e1694d234f27b4bbf5bdef56bb751974ac5dbe045b1e462bde1fe39421cbe"}, - {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:c031de4dfabe7bb6565743745ab43d20588944ddfc7233360169cab4008eee2f"}, - {file = "aiohttp-3.10.4-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:03c5a3143d4a82c43a3d82ac77d9cdef527a72f1c04dcca7b14770879f33d196"}, - {file = "aiohttp-3.10.4-cp38-cp38-win32.whl", hash = "sha256:b71722b527445e02168e2d1cf435772731874671a647fa159ad000feea7933b6"}, - {file = "aiohttp-3.10.4-cp38-cp38-win_amd64.whl", hash = "sha256:0fd1f57aac7d01c9c768675d531976d20d5b79d9da67fac87e55d41b4ade05f9"}, - {file = "aiohttp-3.10.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:15b36a644d1f44ea3d94a0bbb71e75d5f394a3135dc388a209466e22b711ce64"}, - {file = "aiohttp-3.10.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:394ddf9d216cf0bd429b223239a0ab628f01a7a1799c93ce4685eedcdd51b9bc"}, - {file = "aiohttp-3.10.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:dd33f4d571b4143fc9318c3d9256423579c7d183635acc458a6db81919ae5204"}, - {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5991b80886655e6c785aadf3114d4f86e6bec2da436e2bb62892b9f048450a4"}, - {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:92021bf0a4b9ad16851a6c1ca3c86e5b09aecca4f7a2576430c6bbf3114922b1"}, - {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:938e37fd337343c67471098736deb33066d72cec7d8927b9c1b6b4ea807ade9e"}, - {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1d697023b16c62f9aeb3ffdfb8ec4ac3afd477388993b9164b47dadbd60e7062"}, - {file = "aiohttp-3.10.4-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c2f9f07fe6d0d51bd2a788cbb339f1570fd691449c53b5dec83ff838f117703e"}, - {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:50ac670f3fc13ce95e4d6d5a299db9288cc84c663aa630142444ef504756fcf7"}, - {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:9bcdd19398212785a9cb82a63a4b75a299998343f3f5732dfd37c1a4275463f9"}, - {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:122c26f0976225aba46f381e3cabb5ef89a08af6503fc30493fb732e578cfa55"}, - {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:d0665e2a346b6b66959f831ffffd8aa71dd07dd2300017d478f5b47573e66cfe"}, - {file = "aiohttp-3.10.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:625a4a9d4b9f80e7bbaaf2ace06341cf701b2fee54232843addf0bb7304597fb"}, - {file = "aiohttp-3.10.4-cp39-cp39-win32.whl", hash = "sha256:5115490112f39f16ae87c1b34dff3e2c95306cf456b1d2af5974c4ac7d2d1ec7"}, - {file = "aiohttp-3.10.4-cp39-cp39-win_amd64.whl", hash = "sha256:9b58b2ef7f28a2462ba86acbf3b20371bd80a1faa1cfd82f31968af4ac81ef25"}, - {file = "aiohttp-3.10.4.tar.gz", hash = "sha256:23a5f97e7dd22e181967fb6cb6c3b11653b0fdbbc4bb7739d9b6052890ccab96"}, + {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:18a01eba2574fb9edd5f6e5fb25f66e6ce061da5dab5db75e13fe1558142e0a3"}, + {file = "aiohttp-3.10.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:94fac7c6e77ccb1ca91e9eb4cb0ac0270b9fb9b289738654120ba8cebb1189c6"}, + {file = "aiohttp-3.10.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2f1f1c75c395991ce9c94d3e4aa96e5c59c8356a15b1c9231e783865e2772699"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f7acae3cf1a2a2361ec4c8e787eaaa86a94171d2417aae53c0cca6ca3118ff6"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:94c4381ffba9cc508b37d2e536b418d5ea9cfdc2848b9a7fea6aebad4ec6aac1"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c31ad0c0c507894e3eaa843415841995bf8de4d6b2d24c6e33099f4bc9fc0d4f"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0912b8a8fadeb32ff67a3ed44249448c20148397c1ed905d5dac185b4ca547bb"}, + {file = "aiohttp-3.10.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0d93400c18596b7dc4794d48a63fb361b01a0d8eb39f28800dc900c8fbdaca91"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3c5e0d764a5c9aa5a62d99728c56d455310bcc288a79cab10157b3af426f"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:d742c36ed44f2798c8d3f4bc511f479b9ceef2b93f348671184139e7d708042c"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:814375093edae5f1cb31e3407997cf3eacefb9010f96df10d64829362ae2df69"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8224f98be68a84b19f48e0bdc14224b5a71339aff3a27df69989fa47d01296f3"}, + {file = "aiohttp-3.10.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9a487ef090aea982d748b1b0d74fe7c3950b109df967630a20584f9a99c0683"}, + {file = "aiohttp-3.10.5-cp310-cp310-win32.whl", hash = "sha256:d9ef084e3dc690ad50137cc05831c52b6ca428096e6deb3c43e95827f531d5ef"}, + {file = "aiohttp-3.10.5-cp310-cp310-win_amd64.whl", hash = "sha256:66bf9234e08fe561dccd62083bf67400bdbf1c67ba9efdc3dac03650e97c6088"}, + {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8c6a4e5e40156d72a40241a25cc226051c0a8d816610097a8e8f517aeacd59a2"}, + {file = "aiohttp-3.10.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2c634a3207a5445be65536d38c13791904fda0748b9eabf908d3fe86a52941cf"}, + {file = "aiohttp-3.10.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4aff049b5e629ef9b3e9e617fa6e2dfeda1bf87e01bcfecaf3949af9e210105e"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1942244f00baaacaa8155eca94dbd9e8cc7017deb69b75ef67c78e89fdad3c77"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e04a1f2a65ad2f93aa20f9ff9f1b672bf912413e5547f60749fa2ef8a644e061"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7f2bfc0032a00405d4af2ba27f3c429e851d04fad1e5ceee4080a1c570476697"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:424ae21498790e12eb759040bbb504e5e280cab64693d14775c54269fd1d2bb7"}, + {file = "aiohttp-3.10.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:975218eee0e6d24eb336d0328c768ebc5d617609affaca5dbbd6dd1984f16ed0"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:4120d7fefa1e2d8fb6f650b11489710091788de554e2b6f8347c7a20ceb003f5"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b90078989ef3fc45cf9221d3859acd1108af7560c52397ff4ace8ad7052a132e"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:ba5a8b74c2a8af7d862399cdedce1533642fa727def0b8c3e3e02fcb52dca1b1"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:02594361128f780eecc2a29939d9dfc870e17b45178a867bf61a11b2a4367277"}, + {file = "aiohttp-3.10.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8fb4fc029e135859f533025bc82047334e24b0d489e75513144f25408ecaf058"}, + {file = "aiohttp-3.10.5-cp311-cp311-win32.whl", hash = "sha256:e1ca1ef5ba129718a8fc827b0867f6aa4e893c56eb00003b7367f8a733a9b072"}, + {file = "aiohttp-3.10.5-cp311-cp311-win_amd64.whl", hash = "sha256:349ef8a73a7c5665cca65c88ab24abe75447e28aa3bc4c93ea5093474dfdf0ff"}, + {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:305be5ff2081fa1d283a76113b8df7a14c10d75602a38d9f012935df20731487"}, + {file = "aiohttp-3.10.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3a1c32a19ee6bbde02f1cb189e13a71b321256cc1d431196a9f824050b160d5a"}, + {file = "aiohttp-3.10.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:61645818edd40cc6f455b851277a21bf420ce347baa0b86eaa41d51ef58ba23d"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c225286f2b13bab5987425558baa5cbdb2bc925b2998038fa028245ef421e75"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8ba01ebc6175e1e6b7275c907a3a36be48a2d487549b656aa90c8a910d9f3178"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8eaf44ccbc4e35762683078b72bf293f476561d8b68ec8a64f98cf32811c323e"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1c43eb1ab7cbf411b8e387dc169acb31f0ca0d8c09ba63f9eac67829585b44f"}, + {file = "aiohttp-3.10.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:de7a5299827253023c55ea549444e058c0eb496931fa05d693b95140a947cb73"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4790f0e15f00058f7599dab2b206d3049d7ac464dc2e5eae0e93fa18aee9e7bf"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:44b324a6b8376a23e6ba25d368726ee3bc281e6ab306db80b5819999c737d820"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:0d277cfb304118079e7044aad0b76685d30ecb86f83a0711fc5fb257ffe832ca"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:54d9ddea424cd19d3ff6128601a4a4d23d54a421f9b4c0fff740505813739a91"}, + {file = "aiohttp-3.10.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4f1c9866ccf48a6df2b06823e6ae80573529f2af3a0992ec4fe75b1a510df8a6"}, + {file = "aiohttp-3.10.5-cp312-cp312-win32.whl", hash = "sha256:dc4826823121783dccc0871e3f405417ac116055bf184ac04c36f98b75aacd12"}, + {file = "aiohttp-3.10.5-cp312-cp312-win_amd64.whl", hash = "sha256:22c0a23a3b3138a6bf76fc553789cb1a703836da86b0f306b6f0dc1617398abc"}, + {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:7f6b639c36734eaa80a6c152a238242bedcee9b953f23bb887e9102976343092"}, + {file = "aiohttp-3.10.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f29930bc2921cef955ba39a3ff87d2c4398a0394ae217f41cb02d5c26c8b1b77"}, + {file = "aiohttp-3.10.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f489a2c9e6455d87eabf907ac0b7d230a9786be43fbe884ad184ddf9e9c1e385"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:123dd5b16b75b2962d0fff566effb7a065e33cd4538c1692fb31c3bda2bfb972"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b98e698dc34966e5976e10bbca6d26d6724e6bdea853c7c10162a3235aba6e16"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c3b9162bab7e42f21243effc822652dc5bb5e8ff42a4eb62fe7782bcbcdfacf6"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1923a5c44061bffd5eebeef58cecf68096e35003907d8201a4d0d6f6e387ccaa"}, + {file = "aiohttp-3.10.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d55f011da0a843c3d3df2c2cf4e537b8070a419f891c930245f05d329c4b0689"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:afe16a84498441d05e9189a15900640a2d2b5e76cf4efe8cbb088ab4f112ee57"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f8112fb501b1e0567a1251a2fd0747baae60a4ab325a871e975b7bb67e59221f"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:1e72589da4c90337837fdfe2026ae1952c0f4a6e793adbbfbdd40efed7c63599"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:4d46c7b4173415d8e583045fbc4daa48b40e31b19ce595b8d92cf639396c15d5"}, + {file = "aiohttp-3.10.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:33e6bc4bab477c772a541f76cd91e11ccb6d2efa2b8d7d7883591dfb523e5987"}, + {file = "aiohttp-3.10.5-cp313-cp313-win32.whl", hash = "sha256:c58c6837a2c2a7cf3133983e64173aec11f9c2cd8e87ec2fdc16ce727bcf1a04"}, + {file = "aiohttp-3.10.5-cp313-cp313-win_amd64.whl", hash = "sha256:38172a70005252b6893088c0f5e8a47d173df7cc2b2bd88650957eb84fcf5022"}, + {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:f6f18898ace4bcd2d41a122916475344a87f1dfdec626ecde9ee802a711bc569"}, + {file = "aiohttp-3.10.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5ede29d91a40ba22ac1b922ef510aab871652f6c88ef60b9dcdf773c6d32ad7a"}, + {file = "aiohttp-3.10.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:673f988370f5954df96cc31fd99c7312a3af0a97f09e407399f61583f30da9bc"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58718e181c56a3c02d25b09d4115eb02aafe1a732ce5714ab70326d9776457c3"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b38b1570242fbab8d86a84128fb5b5234a2f70c2e32f3070143a6d94bc854cf"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:074d1bff0163e107e97bd48cad9f928fa5a3eb4b9d33366137ffce08a63e37fe"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd31f176429cecbc1ba499d4aba31aaccfea488f418d60376b911269d3b883c5"}, + {file = "aiohttp-3.10.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7384d0b87d4635ec38db9263e6a3f1eb609e2e06087f0aa7f63b76833737b471"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:8989f46f3d7ef79585e98fa991e6ded55d2f48ae56d2c9fa5e491a6e4effb589"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:c83f7a107abb89a227d6c454c613e7606c12a42b9a4ca9c5d7dad25d47c776ae"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:cde98f323d6bf161041e7627a5fd763f9fd829bcfcd089804a5fdce7bb6e1b7d"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:676f94c5480d8eefd97c0c7e3953315e4d8c2b71f3b49539beb2aa676c58272f"}, + {file = "aiohttp-3.10.5-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2d21ac12dc943c68135ff858c3a989f2194a709e6e10b4c8977d7fcd67dfd511"}, + {file = "aiohttp-3.10.5-cp38-cp38-win32.whl", hash = "sha256:17e997105bd1a260850272bfb50e2a328e029c941c2708170d9d978d5a30ad9a"}, + {file = "aiohttp-3.10.5-cp38-cp38-win_amd64.whl", hash = "sha256:1c19de68896747a2aa6257ae4cf6ef59d73917a36a35ee9d0a6f48cff0f94db8"}, + {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:7e2fe37ac654032db1f3499fe56e77190282534810e2a8e833141a021faaab0e"}, + {file = "aiohttp-3.10.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f5bf3ead3cb66ab990ee2561373b009db5bc0e857549b6c9ba84b20bc462e172"}, + {file = "aiohttp-3.10.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b2c16a919d936ca87a3c5f0e43af12a89a3ce7ccbce59a2d6784caba945b68b"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad146dae5977c4dd435eb31373b3fe9b0b1bf26858c6fc452bf6af394067e10b"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8c5c6fa16412b35999320f5c9690c0f554392dc222c04e559217e0f9ae244b92"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:95c4dc6f61d610bc0ee1edc6f29d993f10febfe5b76bb470b486d90bbece6b22"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:da452c2c322e9ce0cfef392e469a26d63d42860f829026a63374fde6b5c5876f"}, + {file = "aiohttp-3.10.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:898715cf566ec2869d5cb4d5fb4be408964704c46c96b4be267442d265390f32"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:391cc3a9c1527e424c6865e087897e766a917f15dddb360174a70467572ac6ce"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:380f926b51b92d02a34119d072f178d80bbda334d1a7e10fa22d467a66e494db"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce91db90dbf37bb6fa0997f26574107e1b9d5ff939315247b7e615baa8ec313b"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:9093a81e18c45227eebe4c16124ebf3e0d893830c6aca7cc310bfca8fe59d857"}, + {file = "aiohttp-3.10.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:ee40b40aa753d844162dcc80d0fe256b87cba48ca0054f64e68000453caead11"}, + {file = "aiohttp-3.10.5-cp39-cp39-win32.whl", hash = "sha256:03f2645adbe17f274444953bdea69f8327e9d278d961d85657cb0d06864814c1"}, + {file = "aiohttp-3.10.5-cp39-cp39-win_amd64.whl", hash = "sha256:d17920f18e6ee090bdd3d0bfffd769d9f2cb4c8ffde3eb203777a3895c128862"}, + {file = "aiohttp-3.10.5.tar.gz", hash = "sha256:f071854b47d39591ce9a17981c46790acb30518e2f83dfca8db2dfa091178691"}, ] [package.dependencies] @@ -1229,13 +1244,13 @@ networkx = ">=2" [[package]] name = "importlib-metadata" -version = "8.2.0" +version = "8.3.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, - {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, + {file = "importlib_metadata-8.3.0-py3-none-any.whl", hash = "sha256:42817a4a0be5845d22c6e212db66a94ad261e2318d80b3e0d363894a79df2b67"}, + {file = "importlib_metadata-8.3.0.tar.gz", hash = "sha256:9c8fa6e8ea0f9516ad5c8db9246a731c948193c7754d3babb0114a05b27dd364"}, ] [package.dependencies] @@ -1848,6 +1863,56 @@ tokenizers = "*" extra-proxy = ["azure-identity (>=1.15.0,<2.0.0)", "azure-keyvault-secrets (>=4.8.0,<5.0.0)", "google-cloud-kms (>=2.21.3,<3.0.0)", "prisma (==0.11.0)", "pynacl (>=1.5.0,<2.0.0)", "resend (>=0.8.0,<0.9.0)"] proxy = ["PyJWT (>=2.8.0,<3.0.0)", "apscheduler (>=3.10.4,<4.0.0)", "backoff", "cryptography (>=42.0.5,<43.0.0)", "fastapi (>=0.111.0,<0.112.0)", "fastapi-sso (>=0.10.0,<0.11.0)", "gunicorn (>=22.0.0,<23.0.0)", "orjson (>=3.9.7,<4.0.0)", "python-multipart (>=0.0.9,<0.0.10)", "pyyaml (>=6.0.1,<7.0.0)", "rq", "uvicorn (>=0.22.0,<0.23.0)"] +[[package]] +name = "lz4" +version = "4.3.3" +description = "LZ4 Bindings for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "lz4-4.3.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b891880c187e96339474af2a3b2bfb11a8e4732ff5034be919aa9029484cd201"}, + {file = "lz4-4.3.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:222a7e35137d7539c9c33bb53fcbb26510c5748779364014235afc62b0ec797f"}, + {file = "lz4-4.3.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f76176492ff082657ada0d0f10c794b6da5800249ef1692b35cf49b1e93e8ef7"}, + {file = "lz4-4.3.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1d18718f9d78182c6b60f568c9a9cec8a7204d7cb6fad4e511a2ef279e4cb05"}, + {file = "lz4-4.3.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cdc60e21ec70266947a48839b437d46025076eb4b12c76bd47f8e5eb8a75dcc"}, + {file = "lz4-4.3.3-cp310-cp310-win32.whl", hash = "sha256:c81703b12475da73a5d66618856d04b1307e43428a7e59d98cfe5a5d608a74c6"}, + {file = "lz4-4.3.3-cp310-cp310-win_amd64.whl", hash = "sha256:43cf03059c0f941b772c8aeb42a0813d68d7081c009542301637e5782f8a33e2"}, + {file = "lz4-4.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:30e8c20b8857adef7be045c65f47ab1e2c4fabba86a9fa9a997d7674a31ea6b6"}, + {file = "lz4-4.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2f7b1839f795315e480fb87d9bc60b186a98e3e5d17203c6e757611ef7dcef61"}, + {file = "lz4-4.3.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edfd858985c23523f4e5a7526ca6ee65ff930207a7ec8a8f57a01eae506aaee7"}, + {file = "lz4-4.3.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e9c410b11a31dbdc94c05ac3c480cb4b222460faf9231f12538d0074e56c563"}, + {file = "lz4-4.3.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d2507ee9c99dbddd191c86f0e0c8b724c76d26b0602db9ea23232304382e1f21"}, + {file = "lz4-4.3.3-cp311-cp311-win32.whl", hash = "sha256:f180904f33bdd1e92967923a43c22899e303906d19b2cf8bb547db6653ea6e7d"}, + {file = "lz4-4.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:b14d948e6dce389f9a7afc666d60dd1e35fa2138a8ec5306d30cd2e30d36b40c"}, + {file = "lz4-4.3.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e36cd7b9d4d920d3bfc2369840da506fa68258f7bb176b8743189793c055e43d"}, + {file = "lz4-4.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:31ea4be9d0059c00b2572d700bf2c1bc82f241f2c3282034a759c9a4d6ca4dc2"}, + {file = "lz4-4.3.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:33c9a6fd20767ccaf70649982f8f3eeb0884035c150c0b818ea660152cf3c809"}, + {file = "lz4-4.3.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bca8fccc15e3add173da91be8f34121578dc777711ffd98d399be35487c934bf"}, + {file = "lz4-4.3.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e7d84b479ddf39fe3ea05387f10b779155fc0990125f4fb35d636114e1c63a2e"}, + {file = "lz4-4.3.3-cp312-cp312-win32.whl", hash = "sha256:337cb94488a1b060ef1685187d6ad4ba8bc61d26d631d7ba909ee984ea736be1"}, + {file = "lz4-4.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:5d35533bf2cee56f38ced91f766cd0038b6abf46f438a80d50c52750088be93f"}, + {file = "lz4-4.3.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:363ab65bf31338eb364062a15f302fc0fab0a49426051429866d71c793c23394"}, + {file = "lz4-4.3.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a136e44a16fc98b1abc404fbabf7f1fada2bdab6a7e970974fb81cf55b636d0"}, + {file = "lz4-4.3.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abc197e4aca8b63f5ae200af03eb95fb4b5055a8f990079b5bdf042f568469dd"}, + {file = "lz4-4.3.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56f4fe9c6327adb97406f27a66420b22ce02d71a5c365c48d6b656b4aaeb7775"}, + {file = "lz4-4.3.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f0e822cd7644995d9ba248cb4b67859701748a93e2ab7fc9bc18c599a52e4604"}, + {file = "lz4-4.3.3-cp38-cp38-win32.whl", hash = "sha256:24b3206de56b7a537eda3a8123c644a2b7bf111f0af53bc14bed90ce5562d1aa"}, + {file = "lz4-4.3.3-cp38-cp38-win_amd64.whl", hash = "sha256:b47839b53956e2737229d70714f1d75f33e8ac26e52c267f0197b3189ca6de24"}, + {file = "lz4-4.3.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6756212507405f270b66b3ff7f564618de0606395c0fe10a7ae2ffcbbe0b1fba"}, + {file = "lz4-4.3.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee9ff50557a942d187ec85462bb0960207e7ec5b19b3b48949263993771c6205"}, + {file = "lz4-4.3.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2b901c7784caac9a1ded4555258207d9e9697e746cc8532129f150ffe1f6ba0d"}, + {file = "lz4-4.3.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6d9ec061b9eca86e4dcc003d93334b95d53909afd5a32c6e4f222157b50c071"}, + {file = "lz4-4.3.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4c7bf687303ca47d69f9f0133274958fd672efaa33fb5bcde467862d6c621f0"}, + {file = "lz4-4.3.3-cp39-cp39-win32.whl", hash = "sha256:054b4631a355606e99a42396f5db4d22046a3397ffc3269a348ec41eaebd69d2"}, + {file = "lz4-4.3.3-cp39-cp39-win_amd64.whl", hash = "sha256:eac9af361e0d98335a02ff12fb56caeb7ea1196cf1a49dbf6f17828a131da807"}, + {file = "lz4-4.3.3.tar.gz", hash = "sha256:01fe674ef2889dbb9899d8a67361e0c4a2c833af5aeb37dd505727cf5d2a131e"}, +] + +[package.extras] +docs = ["sphinx (>=1.6.0)", "sphinx-bootstrap-theme"] +flake8 = ["flake8"] +tests = ["psutil", "pytest (!=3.3.0)", "pytest-cov"] + [[package]] name = "markdown-it-py" version = "3.0.0" @@ -2374,13 +2439,13 @@ files = [ [[package]] name = "openai" -version = "1.41.0" +version = "1.41.1" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.41.0-py3-none-any.whl", hash = "sha256:3b6cca4571667f3e0800442ef8f2bfa6a6f3301c51776bc7626159a4d81c242c"}, - {file = "openai-1.41.0.tar.gz", hash = "sha256:26b81f39b49dce92ff5d30c373625ddb212c2f1050e1574e456d18423730cdd0"}, + {file = "openai-1.41.1-py3-none-any.whl", hash = "sha256:56fb04105263f79559aff3ceea2e1dd16f8c5385e8238cb66cf0e6888fa8bfcf"}, + {file = "openai-1.41.1.tar.gz", hash = "sha256:e38e376efd91e0d4db071e2a6517b6b4cac1c2a6fd63efdc5ec6be10c5967c1b"}, ] [package.dependencies] @@ -3635,19 +3700,19 @@ tornado = ["tornado (>=6)"] [[package]] name = "setuptools" -version = "72.2.0" +version = "73.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, - {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, + {file = "setuptools-73.0.0-py3-none-any.whl", hash = "sha256:f2bfcce7ae1784d90b04c57c2802e8649e1976530bb25dc72c2b078d3ecf4864"}, + {file = "setuptools-73.0.0.tar.gz", hash = "sha256:3c08705fadfc8c7c445cf4d98078f0fafb9225775b2b4e8447e40348f82597c0"}, ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] [[package]] name = "shellingham" @@ -4419,4 +4484,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "9ba28e1010a979afba89f2fa3924462e004f7f1bdf4e3a4a839b96241df04856" +content-hash = "d0445b363a8642838c50a25b5f9e440e1defca471cb3bf547e800a44dc9fe083" diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index e27a2c1f9..59111206f 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -30,6 +30,7 @@ tenacity = "^9.0.0" beartype = "^0.18.5" pydantic-partial = "^0.5.5" simpleeval = "^0.9.13" +lz4 = "^4.3.3" [tool.poetry.group.dev.dependencies] ipython = "^8.26.0" @@ -66,9 +67,12 @@ datamodel-codegen \ --input-file-type openapi \ --output agents_api/autogen/ \ --output-model-type pydantic_v2.BaseModel \ + --strict-types bool \ --strict-nullable \ --allow-population-by-field-name \ + --allow-extra-fields \ --field-include-all-keys \ + --snake-case-field \ --enum-field-as-literal all \ --field-constraints \ --reuse-model \ @@ -86,7 +90,7 @@ datamodel-codegen \ --use-standard-collections \ --use-non-positive-negative-number-constrained-types \ --collapse-root-models \ - --target-python-version 3.10 \ + --target-python-version 3.11 \ --openapi-scopes schemas \ --keep-model-order \ --disable-timestamp""" diff --git a/agents-api/tests/utils.py b/agents-api/tests/utils.py index cebe84a1c..b15332300 100644 --- a/agents-api/tests/utils.py +++ b/agents-api/tests/utils.py @@ -23,9 +23,6 @@ async def patch_testing_temporal(): async with await WorkflowEnvironment.start_time_skipping( data_converter=pydantic_data_converter ) as env: - # # Set the correct codec - # env.client._config["data_converter"] = pydantic_data_converter - # Create a worker with our workflows and start it worker = create_worker(client=env.client) asyncio.create_task(worker.run()) diff --git a/scripts/generate_openapi_code.sh b/scripts/generate_openapi_code.sh index 8ea3acf8f..1c1d22476 100755 --- a/scripts/generate_openapi_code.sh +++ b/scripts/generate_openapi_code.sh @@ -3,6 +3,9 @@ # Turn on echo command set -x +# Exit on error +set -e + cd typespec/ && \ tsp compile . cd - diff --git a/sdks/python/julep/api/__init__.py b/sdks/python/julep/api/__init__.py index 5f56e7611..c570a4827 100644 --- a/sdks/python/julep/api/__init__.py +++ b/sdks/python/julep/api/__init__.py @@ -145,6 +145,19 @@ TasksBaseWorkflowStep_Yield, TasksCaseThen, TasksCaseThenThen, + TasksCaseThenThen_Embed, + TasksCaseThenThen_Error, + TasksCaseThenThen_Evaluate, + TasksCaseThenThen_Get, + TasksCaseThenThen_Log, + TasksCaseThenThen_Prompt, + TasksCaseThenThen_Return, + TasksCaseThenThen_Search, + TasksCaseThenThen_Set, + TasksCaseThenThen_Sleep, + TasksCaseThenThen_ToolCall, + TasksCaseThenThen_WaitForInput, + TasksCaseThenThen_Yield, TasksCreateTaskRequest, TasksCreateTaskRequestMainItem, TasksCreateTaskRequestMainItem_Embed, @@ -170,16 +183,68 @@ TasksEvaluateStep, TasksForeachDo, TasksForeachDoDo, + TasksForeachDoDo_Embed, + TasksForeachDoDo_Error, + TasksForeachDoDo_Evaluate, + TasksForeachDoDo_Get, + TasksForeachDoDo_Log, + TasksForeachDoDo_Prompt, + TasksForeachDoDo_Return, + TasksForeachDoDo_Search, + TasksForeachDoDo_Set, + TasksForeachDoDo_Sleep, + TasksForeachDoDo_ToolCall, + TasksForeachDoDo_WaitForInput, + TasksForeachDoDo_Yield, TasksForeachStep, TasksGetStep, TasksIfElseWorkflowStep, TasksIfElseWorkflowStepElse, + TasksIfElseWorkflowStepElse_Embed, + TasksIfElseWorkflowStepElse_Error, + TasksIfElseWorkflowStepElse_Evaluate, + TasksIfElseWorkflowStepElse_Get, + TasksIfElseWorkflowStepElse_Log, + TasksIfElseWorkflowStepElse_Prompt, + TasksIfElseWorkflowStepElse_Return, + TasksIfElseWorkflowStepElse_Search, + TasksIfElseWorkflowStepElse_Set, + TasksIfElseWorkflowStepElse_Sleep, + TasksIfElseWorkflowStepElse_ToolCall, + TasksIfElseWorkflowStepElse_WaitForInput, + TasksIfElseWorkflowStepElse_Yield, TasksIfElseWorkflowStepThen, + TasksIfElseWorkflowStepThen_Embed, + TasksIfElseWorkflowStepThen_Error, + TasksIfElseWorkflowStepThen_Evaluate, + TasksIfElseWorkflowStepThen_Get, + TasksIfElseWorkflowStepThen_Log, + TasksIfElseWorkflowStepThen_Prompt, + TasksIfElseWorkflowStepThen_Return, + TasksIfElseWorkflowStepThen_Search, + TasksIfElseWorkflowStepThen_Set, + TasksIfElseWorkflowStepThen_Sleep, + TasksIfElseWorkflowStepThen_ToolCall, + TasksIfElseWorkflowStepThen_WaitForInput, + TasksIfElseWorkflowStepThen_Yield, TasksLogStep, TasksMapOver, TasksMapReduceStep, TasksParallelStep, TasksParallelStepParallelItem, + TasksParallelStepParallelItem_Embed, + TasksParallelStepParallelItem_Error, + TasksParallelStepParallelItem_Evaluate, + TasksParallelStepParallelItem_Get, + TasksParallelStepParallelItem_Log, + TasksParallelStepParallelItem_Prompt, + TasksParallelStepParallelItem_Return, + TasksParallelStepParallelItem_Search, + TasksParallelStepParallelItem_Set, + TasksParallelStepParallelItem_Sleep, + TasksParallelStepParallelItem_ToolCall, + TasksParallelStepParallelItem_WaitForInput, + TasksParallelStepParallelItem_Yield, TasksPatchTaskRequestMainItem, TasksPatchTaskRequestMainItem_Embed, TasksPatchTaskRequestMainItem_Error, @@ -429,6 +494,19 @@ "TasksBaseWorkflowStep_Yield", "TasksCaseThen", "TasksCaseThenThen", + "TasksCaseThenThen_Embed", + "TasksCaseThenThen_Error", + "TasksCaseThenThen_Evaluate", + "TasksCaseThenThen_Get", + "TasksCaseThenThen_Log", + "TasksCaseThenThen_Prompt", + "TasksCaseThenThen_Return", + "TasksCaseThenThen_Search", + "TasksCaseThenThen_Set", + "TasksCaseThenThen_Sleep", + "TasksCaseThenThen_ToolCall", + "TasksCaseThenThen_WaitForInput", + "TasksCaseThenThen_Yield", "TasksCreateTaskRequest", "TasksCreateTaskRequestMainItem", "TasksCreateTaskRequestMainItem_Embed", @@ -454,16 +532,68 @@ "TasksEvaluateStep", "TasksForeachDo", "TasksForeachDoDo", + "TasksForeachDoDo_Embed", + "TasksForeachDoDo_Error", + "TasksForeachDoDo_Evaluate", + "TasksForeachDoDo_Get", + "TasksForeachDoDo_Log", + "TasksForeachDoDo_Prompt", + "TasksForeachDoDo_Return", + "TasksForeachDoDo_Search", + "TasksForeachDoDo_Set", + "TasksForeachDoDo_Sleep", + "TasksForeachDoDo_ToolCall", + "TasksForeachDoDo_WaitForInput", + "TasksForeachDoDo_Yield", "TasksForeachStep", "TasksGetStep", "TasksIfElseWorkflowStep", "TasksIfElseWorkflowStepElse", + "TasksIfElseWorkflowStepElse_Embed", + "TasksIfElseWorkflowStepElse_Error", + "TasksIfElseWorkflowStepElse_Evaluate", + "TasksIfElseWorkflowStepElse_Get", + "TasksIfElseWorkflowStepElse_Log", + "TasksIfElseWorkflowStepElse_Prompt", + "TasksIfElseWorkflowStepElse_Return", + "TasksIfElseWorkflowStepElse_Search", + "TasksIfElseWorkflowStepElse_Set", + "TasksIfElseWorkflowStepElse_Sleep", + "TasksIfElseWorkflowStepElse_ToolCall", + "TasksIfElseWorkflowStepElse_WaitForInput", + "TasksIfElseWorkflowStepElse_Yield", "TasksIfElseWorkflowStepThen", + "TasksIfElseWorkflowStepThen_Embed", + "TasksIfElseWorkflowStepThen_Error", + "TasksIfElseWorkflowStepThen_Evaluate", + "TasksIfElseWorkflowStepThen_Get", + "TasksIfElseWorkflowStepThen_Log", + "TasksIfElseWorkflowStepThen_Prompt", + "TasksIfElseWorkflowStepThen_Return", + "TasksIfElseWorkflowStepThen_Search", + "TasksIfElseWorkflowStepThen_Set", + "TasksIfElseWorkflowStepThen_Sleep", + "TasksIfElseWorkflowStepThen_ToolCall", + "TasksIfElseWorkflowStepThen_WaitForInput", + "TasksIfElseWorkflowStepThen_Yield", "TasksLogStep", "TasksMapOver", "TasksMapReduceStep", "TasksParallelStep", "TasksParallelStepParallelItem", + "TasksParallelStepParallelItem_Embed", + "TasksParallelStepParallelItem_Error", + "TasksParallelStepParallelItem_Evaluate", + "TasksParallelStepParallelItem_Get", + "TasksParallelStepParallelItem_Log", + "TasksParallelStepParallelItem_Prompt", + "TasksParallelStepParallelItem_Return", + "TasksParallelStepParallelItem_Search", + "TasksParallelStepParallelItem_Set", + "TasksParallelStepParallelItem_Sleep", + "TasksParallelStepParallelItem_ToolCall", + "TasksParallelStepParallelItem_WaitForInput", + "TasksParallelStepParallelItem_Yield", "TasksPatchTaskRequestMainItem", "TasksPatchTaskRequestMainItem_Embed", "TasksPatchTaskRequestMainItem_Error", diff --git a/sdks/python/julep/api/types/__init__.py b/sdks/python/julep/api/types/__init__.py index 351c3ff7d..b1aead7fe 100644 --- a/sdks/python/julep/api/types/__init__.py +++ b/sdks/python/julep/api/types/__init__.py @@ -181,7 +181,22 @@ TasksBaseWorkflowStep_Yield, ) from .tasks_case_then import TasksCaseThen -from .tasks_case_then_then import TasksCaseThenThen +from .tasks_case_then_then import ( + TasksCaseThenThen, + TasksCaseThenThen_Embed, + TasksCaseThenThen_Error, + TasksCaseThenThen_Evaluate, + TasksCaseThenThen_Get, + TasksCaseThenThen_Log, + TasksCaseThenThen_Prompt, + TasksCaseThenThen_Return, + TasksCaseThenThen_Search, + TasksCaseThenThen_Set, + TasksCaseThenThen_Sleep, + TasksCaseThenThen_ToolCall, + TasksCaseThenThen_WaitForInput, + TasksCaseThenThen_Yield, +) from .tasks_create_task_request import TasksCreateTaskRequest from .tasks_create_task_request_main_item import ( TasksCreateTaskRequestMainItem, @@ -208,17 +223,77 @@ from .tasks_error_workflow_step import TasksErrorWorkflowStep from .tasks_evaluate_step import TasksEvaluateStep from .tasks_foreach_do import TasksForeachDo -from .tasks_foreach_do_do import TasksForeachDoDo +from .tasks_foreach_do_do import ( + TasksForeachDoDo, + TasksForeachDoDo_Embed, + TasksForeachDoDo_Error, + TasksForeachDoDo_Evaluate, + TasksForeachDoDo_Get, + TasksForeachDoDo_Log, + TasksForeachDoDo_Prompt, + TasksForeachDoDo_Return, + TasksForeachDoDo_Search, + TasksForeachDoDo_Set, + TasksForeachDoDo_Sleep, + TasksForeachDoDo_ToolCall, + TasksForeachDoDo_WaitForInput, + TasksForeachDoDo_Yield, +) from .tasks_foreach_step import TasksForeachStep from .tasks_get_step import TasksGetStep from .tasks_if_else_workflow_step import TasksIfElseWorkflowStep -from .tasks_if_else_workflow_step_else import TasksIfElseWorkflowStepElse -from .tasks_if_else_workflow_step_then import TasksIfElseWorkflowStepThen +from .tasks_if_else_workflow_step_else import ( + TasksIfElseWorkflowStepElse, + TasksIfElseWorkflowStepElse_Embed, + TasksIfElseWorkflowStepElse_Error, + TasksIfElseWorkflowStepElse_Evaluate, + TasksIfElseWorkflowStepElse_Get, + TasksIfElseWorkflowStepElse_Log, + TasksIfElseWorkflowStepElse_Prompt, + TasksIfElseWorkflowStepElse_Return, + TasksIfElseWorkflowStepElse_Search, + TasksIfElseWorkflowStepElse_Set, + TasksIfElseWorkflowStepElse_Sleep, + TasksIfElseWorkflowStepElse_ToolCall, + TasksIfElseWorkflowStepElse_WaitForInput, + TasksIfElseWorkflowStepElse_Yield, +) +from .tasks_if_else_workflow_step_then import ( + TasksIfElseWorkflowStepThen, + TasksIfElseWorkflowStepThen_Embed, + TasksIfElseWorkflowStepThen_Error, + TasksIfElseWorkflowStepThen_Evaluate, + TasksIfElseWorkflowStepThen_Get, + TasksIfElseWorkflowStepThen_Log, + TasksIfElseWorkflowStepThen_Prompt, + TasksIfElseWorkflowStepThen_Return, + TasksIfElseWorkflowStepThen_Search, + TasksIfElseWorkflowStepThen_Set, + TasksIfElseWorkflowStepThen_Sleep, + TasksIfElseWorkflowStepThen_ToolCall, + TasksIfElseWorkflowStepThen_WaitForInput, + TasksIfElseWorkflowStepThen_Yield, +) from .tasks_log_step import TasksLogStep from .tasks_map_over import TasksMapOver from .tasks_map_reduce_step import TasksMapReduceStep from .tasks_parallel_step import TasksParallelStep -from .tasks_parallel_step_parallel_item import TasksParallelStepParallelItem +from .tasks_parallel_step_parallel_item import ( + TasksParallelStepParallelItem, + TasksParallelStepParallelItem_Embed, + TasksParallelStepParallelItem_Error, + TasksParallelStepParallelItem_Evaluate, + TasksParallelStepParallelItem_Get, + TasksParallelStepParallelItem_Log, + TasksParallelStepParallelItem_Prompt, + TasksParallelStepParallelItem_Return, + TasksParallelStepParallelItem_Search, + TasksParallelStepParallelItem_Set, + TasksParallelStepParallelItem_Sleep, + TasksParallelStepParallelItem_ToolCall, + TasksParallelStepParallelItem_WaitForInput, + TasksParallelStepParallelItem_Yield, +) from .tasks_patch_task_request_main_item import ( TasksPatchTaskRequestMainItem, TasksPatchTaskRequestMainItem_Embed, @@ -470,6 +545,19 @@ "TasksBaseWorkflowStep_Yield", "TasksCaseThen", "TasksCaseThenThen", + "TasksCaseThenThen_Embed", + "TasksCaseThenThen_Error", + "TasksCaseThenThen_Evaluate", + "TasksCaseThenThen_Get", + "TasksCaseThenThen_Log", + "TasksCaseThenThen_Prompt", + "TasksCaseThenThen_Return", + "TasksCaseThenThen_Search", + "TasksCaseThenThen_Set", + "TasksCaseThenThen_Sleep", + "TasksCaseThenThen_ToolCall", + "TasksCaseThenThen_WaitForInput", + "TasksCaseThenThen_Yield", "TasksCreateTaskRequest", "TasksCreateTaskRequestMainItem", "TasksCreateTaskRequestMainItem_Embed", @@ -495,16 +583,68 @@ "TasksEvaluateStep", "TasksForeachDo", "TasksForeachDoDo", + "TasksForeachDoDo_Embed", + "TasksForeachDoDo_Error", + "TasksForeachDoDo_Evaluate", + "TasksForeachDoDo_Get", + "TasksForeachDoDo_Log", + "TasksForeachDoDo_Prompt", + "TasksForeachDoDo_Return", + "TasksForeachDoDo_Search", + "TasksForeachDoDo_Set", + "TasksForeachDoDo_Sleep", + "TasksForeachDoDo_ToolCall", + "TasksForeachDoDo_WaitForInput", + "TasksForeachDoDo_Yield", "TasksForeachStep", "TasksGetStep", "TasksIfElseWorkflowStep", "TasksIfElseWorkflowStepElse", + "TasksIfElseWorkflowStepElse_Embed", + "TasksIfElseWorkflowStepElse_Error", + "TasksIfElseWorkflowStepElse_Evaluate", + "TasksIfElseWorkflowStepElse_Get", + "TasksIfElseWorkflowStepElse_Log", + "TasksIfElseWorkflowStepElse_Prompt", + "TasksIfElseWorkflowStepElse_Return", + "TasksIfElseWorkflowStepElse_Search", + "TasksIfElseWorkflowStepElse_Set", + "TasksIfElseWorkflowStepElse_Sleep", + "TasksIfElseWorkflowStepElse_ToolCall", + "TasksIfElseWorkflowStepElse_WaitForInput", + "TasksIfElseWorkflowStepElse_Yield", "TasksIfElseWorkflowStepThen", + "TasksIfElseWorkflowStepThen_Embed", + "TasksIfElseWorkflowStepThen_Error", + "TasksIfElseWorkflowStepThen_Evaluate", + "TasksIfElseWorkflowStepThen_Get", + "TasksIfElseWorkflowStepThen_Log", + "TasksIfElseWorkflowStepThen_Prompt", + "TasksIfElseWorkflowStepThen_Return", + "TasksIfElseWorkflowStepThen_Search", + "TasksIfElseWorkflowStepThen_Set", + "TasksIfElseWorkflowStepThen_Sleep", + "TasksIfElseWorkflowStepThen_ToolCall", + "TasksIfElseWorkflowStepThen_WaitForInput", + "TasksIfElseWorkflowStepThen_Yield", "TasksLogStep", "TasksMapOver", "TasksMapReduceStep", "TasksParallelStep", "TasksParallelStepParallelItem", + "TasksParallelStepParallelItem_Embed", + "TasksParallelStepParallelItem_Error", + "TasksParallelStepParallelItem_Evaluate", + "TasksParallelStepParallelItem_Get", + "TasksParallelStepParallelItem_Log", + "TasksParallelStepParallelItem_Prompt", + "TasksParallelStepParallelItem_Return", + "TasksParallelStepParallelItem_Search", + "TasksParallelStepParallelItem_Set", + "TasksParallelStepParallelItem_Sleep", + "TasksParallelStepParallelItem_ToolCall", + "TasksParallelStepParallelItem_WaitForInput", + "TasksParallelStepParallelItem_Yield", "TasksPatchTaskRequestMainItem", "TasksPatchTaskRequestMainItem_Embed", "TasksPatchTaskRequestMainItem_Error", diff --git a/sdks/python/julep/api/types/tasks_base_workflow_step.py b/sdks/python/julep/api/types/tasks_base_workflow_step.py index 3a3c9062c..f6798a97a 100644 --- a/sdks/python/julep/api/types/tasks_base_workflow_step.py +++ b/sdks/python/julep/api/types/tasks_base_workflow_step.py @@ -25,7 +25,7 @@ class TasksBaseWorkflowStep_ToolCall(pydantic_v1.BaseModel): tool: CommonToolRef - arguments: typing.Dict[str, typing.Any] + arguments: typing.Dict[str, CommonPyExpression] kind: typing.Literal["tool_call"] = pydantic_v1.Field( alias="kind_", default="tool_call" ) diff --git a/sdks/python/julep/api/types/tasks_case_then_then.py b/sdks/python/julep/api/types/tasks_case_then_then.py index 3680b8720..58be8eaab 100644 --- a/sdks/python/julep/api/types/tasks_case_then_then.py +++ b/sdks/python/julep/api/types/tasks_case_then_then.py @@ -1,32 +1,589 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + +import datetime as dt import typing -from .tasks_embed_step import TasksEmbedStep -from .tasks_error_workflow_step import TasksErrorWorkflowStep -from .tasks_get_step import TasksGetStep -from .tasks_log_step import TasksLogStep -from .tasks_prompt_step import TasksPromptStep -from .tasks_return_step import TasksReturnStep -from .tasks_search_step import TasksSearchStep -from .tasks_set_step import TasksSetStep -from .tasks_sleep_step import TasksSleepStep -from .tasks_tool_call_step import TasksToolCallStep -from .tasks_wait_for_input_step import TasksWaitForInputStep -from .tasks_yield_step import TasksYieldStep +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings +from .common_py_expression import CommonPyExpression +from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor + + +class TasksCaseThenThen_Evaluate(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + evaluate: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["evaluate"] = pydantic_v1.Field( + alias="kind_", default="evaluate" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_ToolCall(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + tool: CommonToolRef + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["tool_call"] = pydantic_v1.Field( + alias="kind_", default="tool_call" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Yield(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + workflow: str + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["yield"] = pydantic_v1.Field(alias="kind_", default="yield") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Prompt(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + prompt: TasksPromptStepPrompt + settings: ChatChatSettings + kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Error(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + error: str + kind: typing.Literal["error"] = pydantic_v1.Field(alias="kind_", default="error") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Sleep(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Return(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Get(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Set(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Log(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Embed(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_Search(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksCaseThenThen_WaitForInput(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + wait_for_input: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( + alias="kind_", default="wait_for_input" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + TasksCaseThenThen = typing.Union[ - typing.Any, - TasksToolCallStep, - TasksYieldStep, - TasksPromptStep, - TasksErrorWorkflowStep, - TasksSleepStep, - TasksReturnStep, - TasksGetStep, - TasksSetStep, - TasksLogStep, - TasksEmbedStep, - TasksSearchStep, - TasksWaitForInputStep, + TasksCaseThenThen_Evaluate, + TasksCaseThenThen_ToolCall, + TasksCaseThenThen_Yield, + TasksCaseThenThen_Prompt, + TasksCaseThenThen_Error, + TasksCaseThenThen_Sleep, + TasksCaseThenThen_Return, + TasksCaseThenThen_Get, + TasksCaseThenThen_Set, + TasksCaseThenThen_Log, + TasksCaseThenThen_Embed, + TasksCaseThenThen_Search, + TasksCaseThenThen_WaitForInput, ] diff --git a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py index ba5929b30..64a8f1a98 100644 --- a/sdks/python/julep/api/types/tasks_create_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_create_task_request_main_item.py @@ -65,7 +65,7 @@ class Config: class TasksCreateTaskRequestMainItem_ToolCall(pydantic_v1.BaseModel): tool: CommonToolRef - arguments: typing.Dict[str, typing.Any] + arguments: typing.Dict[str, CommonPyExpression] kind: typing.Literal["tool_call"] = pydantic_v1.Field( alias="kind_", default="tool_call" ) diff --git a/sdks/python/julep/api/types/tasks_foreach_do_do.py b/sdks/python/julep/api/types/tasks_foreach_do_do.py index 9e4baf072..90979bb54 100644 --- a/sdks/python/julep/api/types/tasks_foreach_do_do.py +++ b/sdks/python/julep/api/types/tasks_foreach_do_do.py @@ -1,32 +1,589 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + +import datetime as dt import typing -from .tasks_embed_step import TasksEmbedStep -from .tasks_error_workflow_step import TasksErrorWorkflowStep -from .tasks_get_step import TasksGetStep -from .tasks_log_step import TasksLogStep -from .tasks_prompt_step import TasksPromptStep -from .tasks_return_step import TasksReturnStep -from .tasks_search_step import TasksSearchStep -from .tasks_set_step import TasksSetStep -from .tasks_sleep_step import TasksSleepStep -from .tasks_tool_call_step import TasksToolCallStep -from .tasks_wait_for_input_step import TasksWaitForInputStep -from .tasks_yield_step import TasksYieldStep +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings +from .common_py_expression import CommonPyExpression +from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor + + +class TasksForeachDoDo_Evaluate(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + evaluate: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["evaluate"] = pydantic_v1.Field( + alias="kind_", default="evaluate" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_ToolCall(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + tool: CommonToolRef + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["tool_call"] = pydantic_v1.Field( + alias="kind_", default="tool_call" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Yield(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + workflow: str + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["yield"] = pydantic_v1.Field(alias="kind_", default="yield") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Prompt(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + prompt: TasksPromptStepPrompt + settings: ChatChatSettings + kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Error(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + error: str + kind: typing.Literal["error"] = pydantic_v1.Field(alias="kind_", default="error") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Sleep(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Return(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Get(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Set(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Log(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Embed(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_Search(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksForeachDoDo_WaitForInput(pydantic_v1.BaseModel): + """ + The steps to run for each iteration + """ + + wait_for_input: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( + alias="kind_", default="wait_for_input" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + TasksForeachDoDo = typing.Union[ - typing.Any, - TasksToolCallStep, - TasksYieldStep, - TasksPromptStep, - TasksErrorWorkflowStep, - TasksSleepStep, - TasksReturnStep, - TasksGetStep, - TasksSetStep, - TasksLogStep, - TasksEmbedStep, - TasksSearchStep, - TasksWaitForInputStep, + TasksForeachDoDo_Evaluate, + TasksForeachDoDo_ToolCall, + TasksForeachDoDo_Yield, + TasksForeachDoDo_Prompt, + TasksForeachDoDo_Error, + TasksForeachDoDo_Sleep, + TasksForeachDoDo_Return, + TasksForeachDoDo_Get, + TasksForeachDoDo_Set, + TasksForeachDoDo_Log, + TasksForeachDoDo_Embed, + TasksForeachDoDo_Search, + TasksForeachDoDo_WaitForInput, ] diff --git a/sdks/python/julep/api/types/tasks_if_else_workflow_step_else.py b/sdks/python/julep/api/types/tasks_if_else_workflow_step_else.py index 754449134..4f3cf5111 100644 --- a/sdks/python/julep/api/types/tasks_if_else_workflow_step_else.py +++ b/sdks/python/julep/api/types/tasks_if_else_workflow_step_else.py @@ -1,32 +1,589 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + +import datetime as dt import typing -from .tasks_embed_step import TasksEmbedStep -from .tasks_error_workflow_step import TasksErrorWorkflowStep -from .tasks_get_step import TasksGetStep -from .tasks_log_step import TasksLogStep -from .tasks_prompt_step import TasksPromptStep -from .tasks_return_step import TasksReturnStep -from .tasks_search_step import TasksSearchStep -from .tasks_set_step import TasksSetStep -from .tasks_sleep_step import TasksSleepStep -from .tasks_tool_call_step import TasksToolCallStep -from .tasks_wait_for_input_step import TasksWaitForInputStep -from .tasks_yield_step import TasksYieldStep +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings +from .common_py_expression import CommonPyExpression +from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor + + +class TasksIfElseWorkflowStepElse_Evaluate(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + evaluate: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["evaluate"] = pydantic_v1.Field( + alias="kind_", default="evaluate" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_ToolCall(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + tool: CommonToolRef + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["tool_call"] = pydantic_v1.Field( + alias="kind_", default="tool_call" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Yield(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + workflow: str + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["yield"] = pydantic_v1.Field(alias="kind_", default="yield") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Prompt(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + prompt: TasksPromptStepPrompt + settings: ChatChatSettings + kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Error(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + error: str + kind: typing.Literal["error"] = pydantic_v1.Field(alias="kind_", default="error") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Sleep(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Return(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Get(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Set(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Log(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Embed(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_Search(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepElse_WaitForInput(pydantic_v1.BaseModel): + """ + The steps to run if the condition is false + """ + + wait_for_input: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( + alias="kind_", default="wait_for_input" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + TasksIfElseWorkflowStepElse = typing.Union[ - typing.Any, - TasksToolCallStep, - TasksYieldStep, - TasksPromptStep, - TasksErrorWorkflowStep, - TasksSleepStep, - TasksReturnStep, - TasksGetStep, - TasksSetStep, - TasksLogStep, - TasksEmbedStep, - TasksSearchStep, - TasksWaitForInputStep, + TasksIfElseWorkflowStepElse_Evaluate, + TasksIfElseWorkflowStepElse_ToolCall, + TasksIfElseWorkflowStepElse_Yield, + TasksIfElseWorkflowStepElse_Prompt, + TasksIfElseWorkflowStepElse_Error, + TasksIfElseWorkflowStepElse_Sleep, + TasksIfElseWorkflowStepElse_Return, + TasksIfElseWorkflowStepElse_Get, + TasksIfElseWorkflowStepElse_Set, + TasksIfElseWorkflowStepElse_Log, + TasksIfElseWorkflowStepElse_Embed, + TasksIfElseWorkflowStepElse_Search, + TasksIfElseWorkflowStepElse_WaitForInput, ] diff --git a/sdks/python/julep/api/types/tasks_if_else_workflow_step_then.py b/sdks/python/julep/api/types/tasks_if_else_workflow_step_then.py index 9e914c54d..4d760543a 100644 --- a/sdks/python/julep/api/types/tasks_if_else_workflow_step_then.py +++ b/sdks/python/julep/api/types/tasks_if_else_workflow_step_then.py @@ -1,32 +1,589 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + +import datetime as dt import typing -from .tasks_embed_step import TasksEmbedStep -from .tasks_error_workflow_step import TasksErrorWorkflowStep -from .tasks_get_step import TasksGetStep -from .tasks_log_step import TasksLogStep -from .tasks_prompt_step import TasksPromptStep -from .tasks_return_step import TasksReturnStep -from .tasks_search_step import TasksSearchStep -from .tasks_set_step import TasksSetStep -from .tasks_sleep_step import TasksSleepStep -from .tasks_tool_call_step import TasksToolCallStep -from .tasks_wait_for_input_step import TasksWaitForInputStep -from .tasks_yield_step import TasksYieldStep +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings +from .common_py_expression import CommonPyExpression +from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor + + +class TasksIfElseWorkflowStepThen_Evaluate(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + evaluate: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["evaluate"] = pydantic_v1.Field( + alias="kind_", default="evaluate" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_ToolCall(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + tool: CommonToolRef + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["tool_call"] = pydantic_v1.Field( + alias="kind_", default="tool_call" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Yield(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + workflow: str + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["yield"] = pydantic_v1.Field(alias="kind_", default="yield") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Prompt(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + prompt: TasksPromptStepPrompt + settings: ChatChatSettings + kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Error(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + error: str + kind: typing.Literal["error"] = pydantic_v1.Field(alias="kind_", default="error") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Sleep(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Return(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Get(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Set(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Log(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Embed(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_Search(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksIfElseWorkflowStepThen_WaitForInput(pydantic_v1.BaseModel): + """ + The steps to run if the condition is true + """ + + wait_for_input: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( + alias="kind_", default="wait_for_input" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + TasksIfElseWorkflowStepThen = typing.Union[ - typing.Any, - TasksToolCallStep, - TasksYieldStep, - TasksPromptStep, - TasksErrorWorkflowStep, - TasksSleepStep, - TasksReturnStep, - TasksGetStep, - TasksSetStep, - TasksLogStep, - TasksEmbedStep, - TasksSearchStep, - TasksWaitForInputStep, + TasksIfElseWorkflowStepThen_Evaluate, + TasksIfElseWorkflowStepThen_ToolCall, + TasksIfElseWorkflowStepThen_Yield, + TasksIfElseWorkflowStepThen_Prompt, + TasksIfElseWorkflowStepThen_Error, + TasksIfElseWorkflowStepThen_Sleep, + TasksIfElseWorkflowStepThen_Return, + TasksIfElseWorkflowStepThen_Get, + TasksIfElseWorkflowStepThen_Set, + TasksIfElseWorkflowStepThen_Log, + TasksIfElseWorkflowStepThen_Embed, + TasksIfElseWorkflowStepThen_Search, + TasksIfElseWorkflowStepThen_WaitForInput, ] diff --git a/sdks/python/julep/api/types/tasks_parallel_step_parallel_item.py b/sdks/python/julep/api/types/tasks_parallel_step_parallel_item.py index 0b881fed7..72c55b610 100644 --- a/sdks/python/julep/api/types/tasks_parallel_step_parallel_item.py +++ b/sdks/python/julep/api/types/tasks_parallel_step_parallel_item.py @@ -1,32 +1,537 @@ # This file was auto-generated by Fern from our API Definition. +from __future__ import annotations + +import datetime as dt import typing -from .tasks_embed_step import TasksEmbedStep -from .tasks_error_workflow_step import TasksErrorWorkflowStep -from .tasks_get_step import TasksGetStep -from .tasks_log_step import TasksLogStep -from .tasks_prompt_step import TasksPromptStep -from .tasks_return_step import TasksReturnStep -from .tasks_search_step import TasksSearchStep -from .tasks_set_step import TasksSetStep -from .tasks_sleep_step import TasksSleepStep -from .tasks_tool_call_step import TasksToolCallStep -from .tasks_wait_for_input_step import TasksWaitForInputStep -from .tasks_yield_step import TasksYieldStep +from ..core.datetime_utils import serialize_datetime +from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .chat_chat_settings import ChatChatSettings +from .common_py_expression import CommonPyExpression +from .common_tool_ref import CommonToolRef +from .docs_embed_query_request import DocsEmbedQueryRequest +from .tasks_prompt_step_prompt import TasksPromptStepPrompt +from .tasks_search_step_search import TasksSearchStepSearch +from .tasks_set_step_set import TasksSetStepSet +from .tasks_sleep_for import TasksSleepFor + + +class TasksParallelStepParallelItem_Evaluate(pydantic_v1.BaseModel): + evaluate: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["evaluate"] = pydantic_v1.Field( + alias="kind_", default="evaluate" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_ToolCall(pydantic_v1.BaseModel): + tool: CommonToolRef + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["tool_call"] = pydantic_v1.Field( + alias="kind_", default="tool_call" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Yield(pydantic_v1.BaseModel): + workflow: str + arguments: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["yield"] = pydantic_v1.Field(alias="kind_", default="yield") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Prompt(pydantic_v1.BaseModel): + prompt: TasksPromptStepPrompt + settings: ChatChatSettings + kind: typing.Literal["prompt"] = pydantic_v1.Field(alias="kind_", default="prompt") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Error(pydantic_v1.BaseModel): + error: str + kind: typing.Literal["error"] = pydantic_v1.Field(alias="kind_", default="error") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Sleep(pydantic_v1.BaseModel): + sleep: TasksSleepFor + kind: typing.Literal["sleep"] = pydantic_v1.Field(alias="kind_", default="sleep") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Return(pydantic_v1.BaseModel): + return_: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field(alias="return") + kind: typing.Literal["return"] = pydantic_v1.Field(alias="kind_", default="return") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Get(pydantic_v1.BaseModel): + get: str + kind: typing.Literal["get"] = pydantic_v1.Field(alias="kind_", default="get") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Set(pydantic_v1.BaseModel): + set_: TasksSetStepSet = pydantic_v1.Field(alias="set") + kind: typing.Literal["set"] = pydantic_v1.Field(alias="kind_", default="set") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Log(pydantic_v1.BaseModel): + log: CommonPyExpression + kind: typing.Literal["log"] = pydantic_v1.Field(alias="kind_", default="log") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Embed(pydantic_v1.BaseModel): + embed: DocsEmbedQueryRequest + kind: typing.Literal["embed"] = pydantic_v1.Field(alias="kind_", default="embed") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_Search(pydantic_v1.BaseModel): + search: TasksSearchStepSearch + kind: typing.Literal["search"] = pydantic_v1.Field(alias="kind_", default="search") + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + + +class TasksParallelStepParallelItem_WaitForInput(pydantic_v1.BaseModel): + wait_for_input: typing.Dict[str, CommonPyExpression] + kind: typing.Literal["wait_for_input"] = pydantic_v1.Field( + alias="kind_", default="wait_for_input" + ) + + def json(self, **kwargs: typing.Any) -> str: + kwargs_with_defaults: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + return super().json(**kwargs_with_defaults) + + def dict(self, **kwargs: typing.Any) -> typing.Dict[str, typing.Any]: + kwargs_with_defaults_exclude_unset: typing.Any = { + "by_alias": True, + "exclude_unset": True, + **kwargs, + } + kwargs_with_defaults_exclude_none: typing.Any = { + "by_alias": True, + "exclude_none": True, + **kwargs, + } + + return deep_union_pydantic_dicts( + super().dict(**kwargs_with_defaults_exclude_unset), + super().dict(**kwargs_with_defaults_exclude_none), + ) + + class Config: + frozen = True + smart_union = True + allow_population_by_field_name = True + populate_by_name = True + extra = pydantic_v1.Extra.allow + json_encoders = {dt.datetime: serialize_datetime} + TasksParallelStepParallelItem = typing.Union[ - typing.Any, - TasksToolCallStep, - TasksYieldStep, - TasksPromptStep, - TasksErrorWorkflowStep, - TasksSleepStep, - TasksReturnStep, - TasksGetStep, - TasksSetStep, - TasksLogStep, - TasksEmbedStep, - TasksSearchStep, - TasksWaitForInputStep, + TasksParallelStepParallelItem_Evaluate, + TasksParallelStepParallelItem_ToolCall, + TasksParallelStepParallelItem_Yield, + TasksParallelStepParallelItem_Prompt, + TasksParallelStepParallelItem_Error, + TasksParallelStepParallelItem_Sleep, + TasksParallelStepParallelItem_Return, + TasksParallelStepParallelItem_Get, + TasksParallelStepParallelItem_Set, + TasksParallelStepParallelItem_Log, + TasksParallelStepParallelItem_Embed, + TasksParallelStepParallelItem_Search, + TasksParallelStepParallelItem_WaitForInput, ] diff --git a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py index 8821ddfa8..bcf9ba690 100644 --- a/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_patch_task_request_main_item.py @@ -65,7 +65,7 @@ class Config: class TasksPatchTaskRequestMainItem_ToolCall(pydantic_v1.BaseModel): tool: CommonToolRef - arguments: typing.Dict[str, typing.Any] + arguments: typing.Dict[str, CommonPyExpression] kind: typing.Literal["tool_call"] = pydantic_v1.Field( alias="kind_", default="tool_call" ) diff --git a/sdks/python/julep/api/types/tasks_task_main_item.py b/sdks/python/julep/api/types/tasks_task_main_item.py index 3206c4a8e..6cba1d2e2 100644 --- a/sdks/python/julep/api/types/tasks_task_main_item.py +++ b/sdks/python/julep/api/types/tasks_task_main_item.py @@ -65,7 +65,7 @@ class Config: class TasksTaskMainItem_ToolCall(pydantic_v1.BaseModel): tool: CommonToolRef - arguments: typing.Dict[str, typing.Any] + arguments: typing.Dict[str, CommonPyExpression] kind: typing.Literal["tool_call"] = pydantic_v1.Field( alias="kind_", default="tool_call" ) diff --git a/sdks/python/julep/api/types/tasks_tool_call_step.py b/sdks/python/julep/api/types/tasks_tool_call_step.py index e9ee19b9e..8923e9140 100644 --- a/sdks/python/julep/api/types/tasks_tool_call_step.py +++ b/sdks/python/julep/api/types/tasks_tool_call_step.py @@ -5,6 +5,7 @@ from ..core.datetime_utils import serialize_datetime from ..core.pydantic_utilities import deep_union_pydantic_dicts, pydantic_v1 +from .common_py_expression import CommonPyExpression from .common_tool_ref import CommonToolRef @@ -14,7 +15,7 @@ class TasksToolCallStep(pydantic_v1.BaseModel): The tool to run """ - arguments: typing.Dict[str, typing.Any] = pydantic_v1.Field() + arguments: typing.Dict[str, CommonPyExpression] = pydantic_v1.Field() """ The input parameters for the tool """ diff --git a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py index 2897a5e8e..7eb7d9a75 100644 --- a/sdks/python/julep/api/types/tasks_update_task_request_main_item.py +++ b/sdks/python/julep/api/types/tasks_update_task_request_main_item.py @@ -65,7 +65,7 @@ class Config: class TasksUpdateTaskRequestMainItem_ToolCall(pydantic_v1.BaseModel): tool: CommonToolRef - arguments: typing.Dict[str, typing.Any] + arguments: typing.Dict[str, CommonPyExpression] kind: typing.Literal["tool_call"] = pydantic_v1.Field( alias="kind_", default="tool_call" ) diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index f7ccaf4a7..ec7e2547a 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -829,13 +829,13 @@ networkx = ">=2" [[package]] name = "importlib-metadata" -version = "8.2.0" +version = "8.3.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, - {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, + {file = "importlib_metadata-8.3.0-py3-none-any.whl", hash = "sha256:42817a4a0be5845d22c6e212db66a94ad261e2318d80b3e0d363894a79df2b67"}, + {file = "importlib_metadata-8.3.0.tar.gz", hash = "sha256:9c8fa6e8ea0f9516ad5c8db9246a731c948193c7754d3babb0114a05b27dd364"}, ] [package.dependencies] @@ -1812,13 +1812,13 @@ files = [ [[package]] name = "openai" -version = "1.41.0" +version = "1.41.1" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.41.0-py3-none-any.whl", hash = "sha256:3b6cca4571667f3e0800442ef8f2bfa6a6f3301c51776bc7626159a4d81c242c"}, - {file = "openai-1.41.0.tar.gz", hash = "sha256:26b81f39b49dce92ff5d30c373625ddb212c2f1050e1574e456d18423730cdd0"}, + {file = "openai-1.41.1-py3-none-any.whl", hash = "sha256:56fb04105263f79559aff3ceea2e1dd16f8c5385e8238cb66cf0e6888fa8bfcf"}, + {file = "openai-1.41.1.tar.gz", hash = "sha256:e38e376efd91e0d4db071e2a6517b6b4cac1c2a6fd63efdc5ec6be10c5967c1b"}, ] [package.dependencies] @@ -2926,19 +2926,19 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "72.2.0" +version = "73.0.0" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-72.2.0-py3-none-any.whl", hash = "sha256:f11dd94b7bae3a156a95ec151f24e4637fb4fa19c878e4d191bfb8b2d82728c4"}, - {file = "setuptools-72.2.0.tar.gz", hash = "sha256:80aacbf633704e9c8bfa1d99fa5dd4dc59573efcf9e4042c13d3bcef91ac2ef9"}, + {file = "setuptools-73.0.0-py3-none-any.whl", hash = "sha256:f2bfcce7ae1784d90b04c57c2802e8649e1976530bb25dc72c2b078d3ecf4864"}, + {file = "setuptools-73.0.0.tar.gz", hash = "sha256:3c08705fadfc8c7c445cf4d98078f0fafb9225775b2b4e8447e40348f82597c0"}, ] [package.extras] -core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "ordered-set (>=3.1.1)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] +core = ["importlib-metadata (>=6)", "importlib-resources (>=5.10.2)", "jaraco.text (>=3.7)", "more-itertools (>=8.8)", "packaging (>=24)", "platformdirs (>=2.6.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] -test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.11.*)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (<0.4)", "pytest-ruff (>=0.2.1)", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] [[package]] name = "shellingham" diff --git a/sdks/ts/src/api/models/Tasks_CaseThen.ts b/sdks/ts/src/api/models/Tasks_CaseThen.ts index 43bc0d588..c55e939ad 100644 --- a/sdks/ts/src/api/models/Tasks_CaseThen.ts +++ b/sdks/ts/src/api/models/Tasks_CaseThen.ts @@ -5,6 +5,7 @@ import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; +import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; @@ -24,6 +25,7 @@ export type Tasks_CaseThen = { * The steps to run if the condition is true */ then: + | Tasks_EvaluateStep | Tasks_ToolCallStep | Tasks_YieldStep | Tasks_PromptStep diff --git a/sdks/ts/src/api/models/Tasks_ForeachDo.ts b/sdks/ts/src/api/models/Tasks_ForeachDo.ts index 5858114ed..76c4ed617 100644 --- a/sdks/ts/src/api/models/Tasks_ForeachDo.ts +++ b/sdks/ts/src/api/models/Tasks_ForeachDo.ts @@ -5,6 +5,7 @@ import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; +import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; @@ -24,6 +25,7 @@ export type Tasks_ForeachDo = { * The steps to run for each iteration */ do: + | Tasks_EvaluateStep | Tasks_ToolCallStep | Tasks_YieldStep | Tasks_PromptStep diff --git a/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts b/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts index dc2212143..d2df5d79e 100644 --- a/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts +++ b/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts @@ -6,6 +6,7 @@ import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; +import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; @@ -26,6 +27,7 @@ export type Tasks_IfElseWorkflowStep = Tasks_BaseWorkflowStep & { * The steps to run if the condition is true */ then: + | Tasks_EvaluateStep | Tasks_ToolCallStep | Tasks_YieldStep | Tasks_PromptStep @@ -42,6 +44,7 @@ export type Tasks_IfElseWorkflowStep = Tasks_BaseWorkflowStep & { * The steps to run if the condition is false */ else: + | Tasks_EvaluateStep | Tasks_ToolCallStep | Tasks_YieldStep | Tasks_PromptStep diff --git a/sdks/ts/src/api/models/Tasks_ParallelStep.ts b/sdks/ts/src/api/models/Tasks_ParallelStep.ts index a094a3eca..da6f9f399 100644 --- a/sdks/ts/src/api/models/Tasks_ParallelStep.ts +++ b/sdks/ts/src/api/models/Tasks_ParallelStep.ts @@ -5,6 +5,7 @@ import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; +import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; @@ -21,6 +22,7 @@ export type Tasks_ParallelStep = Tasks_BaseWorkflowStep & { * The steps to run in parallel. Max concurrency will depend on the platform */ parallel: Array< + | Tasks_EvaluateStep | Tasks_ToolCallStep | Tasks_YieldStep | Tasks_PromptStep diff --git a/sdks/ts/src/api/models/Tasks_ToolCallStep.ts b/sdks/ts/src/api/models/Tasks_ToolCallStep.ts index 84e34c703..f0a758372 100644 --- a/sdks/ts/src/api/models/Tasks_ToolCallStep.ts +++ b/sdks/ts/src/api/models/Tasks_ToolCallStep.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; import type { Common_toolRef } from "./Common_toolRef"; import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; export type Tasks_ToolCallStep = Tasks_BaseWorkflowStep & { @@ -13,5 +14,5 @@ export type Tasks_ToolCallStep = Tasks_BaseWorkflowStep & { /** * The input parameters for the tool */ - arguments: Record; + arguments: Record; }; diff --git a/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts b/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts index 0e8eceebc..c58942ef5 100644 --- a/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts +++ b/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts @@ -18,6 +18,9 @@ export const $Tasks_CaseThen = { type: "any-of", description: `The steps to run if the condition is true`, contains: [ + { + type: "Tasks_EvaluateStep", + }, { type: "Tasks_ToolCallStep", }, diff --git a/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts b/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts index 3b05392db..c9dbd8b1c 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts @@ -18,6 +18,9 @@ export const $Tasks_ForeachDo = { type: "any-of", description: `The steps to run for each iteration`, contains: [ + { + type: "Tasks_EvaluateStep", + }, { type: "Tasks_ToolCallStep", }, diff --git a/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts b/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts index a1def86dc..7f615c5fc 100644 --- a/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts @@ -28,6 +28,9 @@ export const $Tasks_IfElseWorkflowStep = { type: "any-of", description: `The steps to run if the condition is true`, contains: [ + { + type: "Tasks_EvaluateStep", + }, { type: "Tasks_ToolCallStep", }, @@ -71,6 +74,9 @@ export const $Tasks_IfElseWorkflowStep = { type: "any-of", description: `The steps to run if the condition is false`, contains: [ + { + type: "Tasks_EvaluateStep", + }, { type: "Tasks_ToolCallStep", }, diff --git a/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts b/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts index cf1f97820..956a7d131 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts @@ -19,6 +19,9 @@ export const $Tasks_ParallelStep = { contains: { type: "any-of", contains: [ + { + type: "Tasks_EvaluateStep", + }, { type: "Tasks_ToolCallStep", }, diff --git a/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts b/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts index 37f6b0179..1f6a07c09 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts @@ -27,7 +27,7 @@ export const $Tasks_ToolCallStep = { arguments: { type: "dictionary", contains: { - properties: {}, + type: "Common_PyExpression", }, isRequired: true, }, diff --git a/typespec/package-lock.json b/typespec/package-lock.json index f535be674..90269602f 100644 --- a/typespec/package-lock.json +++ b/typespec/package-lock.json @@ -8,12 +8,12 @@ "name": "julep-typespec", "version": "0.3.0", "dependencies": { - "@typespec/compiler": "^0.58.1", - "@typespec/http": "^0.58.0", - "@typespec/openapi": "^0.58.0", - "@typespec/openapi3": "^0.58.0", - "@typespec/rest": "^0.58.0", - "@typespec/versioning": "^0.58.0" + "@typespec/compiler": "^0.59.1", + "@typespec/http": "^0.59.1", + "@typespec/openapi": "^0.59.0", + "@typespec/openapi3": "^0.59.1", + "@typespec/rest": "^0.59.1", + "@typespec/versioning": "^0.59.0" } }, "node_modules/@apidevtools/swagger-methods": { @@ -274,20 +274,20 @@ "license": "MIT" }, "node_modules/@typespec/compiler": { - "version": "0.58.1", - "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.58.1.tgz", - "integrity": "sha512-bVxxM35r40OtuL4+/9W/g1EevlnWnW6i151nsZAFOJj1xWHoE2G9zkx5/Feic8OlzArjhGGLJOLH3Ez1Wrw35A==", + "version": "0.59.1", + "resolved": "https://registry.npmjs.org/@typespec/compiler/-/compiler-0.59.1.tgz", + "integrity": "sha512-O2ljgr6YoFaIH6a8lWc90/czdv4B2X6N9wz4WsnQnVvgO0Tj0s+3xkvp4Tv59RKMhT0f3fK6dL8oEGO32FYk1A==", "license": "MIT", "dependencies": { "@babel/code-frame": "~7.24.7", - "ajv": "~8.16.0", + "ajv": "~8.17.1", "change-case": "~5.4.4", "globby": "~14.0.2", "mustache": "~4.2.0", "picocolors": "~1.0.1", - "prettier": "~3.3.2", + "prettier": "~3.3.3", "prompts": "~2.4.2", - "semver": "^7.6.2", + "semver": "^7.6.3", "temporal-polyfill": "^0.2.5", "vscode-languageserver": "~9.0.1", "vscode-languageserver-textdocument": "~1.0.11", @@ -303,34 +303,34 @@ } }, "node_modules/@typespec/http": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.58.0.tgz", - "integrity": "sha512-jQpkugg9AZVrNDMkDIgZRpIoRkkU2b0LtKWqMGg33MItYj9/DYSgDtY7xb7oCBppRtFFZ/h138HyhYl3zQxZRg==", + "version": "0.59.1", + "resolved": "https://registry.npmjs.org/@typespec/http/-/http-0.59.1.tgz", + "integrity": "sha512-Ai8oCAO+Bw1HMSZ9gOI5Od4fNn/ul4HrVtTB01xFuLK6FQj854pxhzao8ylPnr7gIRQ327FV12/QfXR87yCiYQ==", "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.58.0" + "@typespec/compiler": "~0.59.0" } }, "node_modules/@typespec/openapi": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.58.0.tgz", - "integrity": "sha512-gu6nXfmpfZrfq8Etpgl1dpMfsXii7EzQyhZgsPhIy7ZwV5bDmFk1/oyhTqIpWrnr4pD3r151T2BQjzJefjf15A==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@typespec/openapi/-/openapi-0.59.0.tgz", + "integrity": "sha512-do1Dm5w0MuK3994gYTBg6qMfgeIxmmsDqnz3zimYKMPpbnUBi4F6/o4iCfn0Fn9kaNl+H6UlOzZpsZW9xHui1Q==", "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.58.0", - "@typespec/http": "~0.58.0" + "@typespec/compiler": "~0.59.0", + "@typespec/http": "~0.59.0" } }, "node_modules/@typespec/openapi3": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.58.0.tgz", - "integrity": "sha512-G9t9CWT9cN6ip39dLZaE6JdEDxGsFyOUxA2s6a087rweoTH85XzsFiQL7uiUD8vHhXyEo6tF6sy3LMZVN0BsoQ==", + "version": "0.59.1", + "resolved": "https://registry.npmjs.org/@typespec/openapi3/-/openapi3-0.59.1.tgz", + "integrity": "sha512-89VbUbkWKxeFgE0w0hpVyk1UZ6ZHRxOhcAHvF5MgxQxEhs2ALXKAqapWjFQsYrLBhAUoWzdPFrJJUMbwF9kX0Q==", "license": "MIT", "dependencies": { "@readme/openapi-parser": "~2.6.0", @@ -343,47 +343,47 @@ "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.58.0", - "@typespec/http": "~0.58.0", - "@typespec/openapi": "~0.58.0", - "@typespec/versioning": "~0.58.0" + "@typespec/compiler": "~0.59.0", + "@typespec/http": "~0.59.1", + "@typespec/openapi": "~0.59.0", + "@typespec/versioning": "~0.59.0" } }, "node_modules/@typespec/rest": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.58.0.tgz", - "integrity": "sha512-QBxkED0/KQKG22pwzis0n7BY+uLMSZZPSoVe/ESBFika9n5/yyeQ0l58xbFFwwfxAxe4xwuZ5PNwTdEXZbzr5g==", + "version": "0.59.1", + "resolved": "https://registry.npmjs.org/@typespec/rest/-/rest-0.59.1.tgz", + "integrity": "sha512-uKU431jBYL2tVQWG5THA75+OtXDa1e8cMAafYK/JJRRiVRd8D/Epd8fp07dzlB8tFGrhCaGlekRMqFPFrHh2/A==", "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.58.0", - "@typespec/http": "~0.58.0" + "@typespec/compiler": "~0.59.0", + "@typespec/http": "~0.59.1" } }, "node_modules/@typespec/versioning": { - "version": "0.58.0", - "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.58.0.tgz", - "integrity": "sha512-brnQQ3wKWh4AbgqmnVLj+8zyOaDk9VPWg4QBecdQxzz7PrSrlAzIzRfeIyr67+hwi/0SvkTAB6GNH7YYTypKGA==", + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/@typespec/versioning/-/versioning-0.59.0.tgz", + "integrity": "sha512-aihO/ux0lLmsuYAdGVkiBflSudcZokYG42SELk1FtMFo609G3Pd7ep7hau6unBnMIceQZejB0ow5UGRupK4X5A==", "license": "MIT", "engines": { "node": ">=18.0.0" }, "peerDependencies": { - "@typespec/compiler": "~0.58.0" + "@typespec/compiler": "~0.59.0" } }, "node_modules/ajv": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz", - "integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.4.1" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -553,6 +553,12 @@ "node": ">=8.6.0" } }, + "node_modules/fast-uri": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.1.tgz", + "integrity": "sha512-MWipKbbYiYI0UC7cl8m/i/IWTqfC8YXsqjzybjddLsFjStroQzsHXkc73JutMvBiXmOvapk+axIl79ig5t55Bw==", + "license": "MIT" + }, "node_modules/fastq": { "version": "1.17.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", @@ -838,15 +844,6 @@ "node": ">= 6" } }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -925,9 +922,9 @@ } }, "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1031,15 +1028,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", diff --git a/typespec/package.json b/typespec/package.json index 95c181471..a7b3d7ee4 100644 --- a/typespec/package.json +++ b/typespec/package.json @@ -3,12 +3,12 @@ "version": "0.3.0", "type": "module", "dependencies": { - "@typespec/compiler": "^0.58.1", - "@typespec/http": "^0.58.0", - "@typespec/openapi": "^0.58.0", - "@typespec/openapi3": "^0.58.0", - "@typespec/rest": "^0.58.0", - "@typespec/versioning": "^0.58.0" + "@typespec/compiler": "^0.59.1", + "@typespec/http": "^0.59.1", + "@typespec/openapi": "^0.59.0", + "@typespec/openapi3": "^0.59.1", + "@typespec/rest": "^0.59.1", + "@typespec/versioning": "^0.59.0" }, "private": true } diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp index 9cf3b9b06..2da1d2cdf 100644 --- a/typespec/tasks/steps.tsp +++ b/typespec/tasks/steps.tsp @@ -22,6 +22,7 @@ namespace Tasks; /** An object where values are strings in the Common Expression Language that get evaluated before being passed downstream */ alias ExpressionObject = Record; +alias NestedExpressionObject = Record; @discriminator("kind_") model BaseWorkflowStep { @@ -29,10 +30,6 @@ model BaseWorkflowStep { kind_: WorkflowStepKind; } -BREAKING THIS FILE INTENTIONALLY SO IT DOESTN'T GET COMPILED -BECAUSE IT SEEMS LIKE TYPESPEC HAS A BUG WHERE IT ADDS `{}` TO THE RENDERED `NonConditionalWorkflowStep` -YOU CAN COMMENT THIS AND COMPILE IF YOU KNOW WHAT YOU'RE DOING - alias NonConditionalWorkflowStep = | EvaluateStep | ToolCallStep @@ -65,7 +62,7 @@ model ToolCallStep extends BaseWorkflowStep { tool: toolRef; /** The input parameters for the tool */ - arguments: Record; + arguments: ExpressionObject; } model PromptStep extends BaseWorkflowStep { From 5b97ce73ef861eea7695d79e4492fd601937e193 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 19 Aug 2024 21:20:00 -0400 Subject: [PATCH 083/110] refactor(agents-api): Minor refactors Signed-off-by: Diwank Tomer --- .../activities/task_steps/evaluate_step.py | 4 +-- .../activities/task_steps/if_else_step.py | 4 +-- .../activities/task_steps/log_step.py | 4 +-- .../task_steps/raise_complete_async.py | 1 - .../activities/task_steps/return_step.py | 4 +-- .../activities/task_steps/switch_step.py | 4 +-- .../activities/task_steps/transition_step.py | 1 - .../activities/task_steps/yield_step.py | 3 +-- agents-api/agents_api/worker/worker.py | 27 +++---------------- 9 files changed, 9 insertions(+), 43 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py index d15dde869..e4cc194c2 100644 --- a/agents-api/agents_api/activities/task_steps/evaluate_step.py +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -1,5 +1,3 @@ -import logging - from beartype import beartype from temporalio import activity @@ -23,7 +21,7 @@ async def evaluate_step(context: StepContext) -> StepOutcome: return result except BaseException as e: - logging.error(f"Error in evaluate_step: {e}") + activity.logger.error(f"Error in evaluate_step: {e}") return StepOutcome(error=str(e)) diff --git a/agents-api/agents_api/activities/task_steps/if_else_step.py b/agents-api/agents_api/activities/task_steps/if_else_step.py index 7959039cc..7ce9e44e7 100644 --- a/agents-api/agents_api/activities/task_steps/if_else_step.py +++ b/agents-api/agents_api/activities/task_steps/if_else_step.py @@ -1,5 +1,3 @@ -import logging - from beartype import beartype from simpleeval import simple_eval from temporalio import activity @@ -27,7 +25,7 @@ async def if_else_step(context: StepContext) -> StepOutcome: return result except BaseException as e: - logging.error(f"Error in if_else_step: {e}") + activity.logger.error(f"Error in if_else_step: {e}") return StepOutcome(error=str(e)) diff --git a/agents-api/agents_api/activities/task_steps/log_step.py b/agents-api/agents_api/activities/task_steps/log_step.py index 458dabdf3..1ed6d2493 100644 --- a/agents-api/agents_api/activities/task_steps/log_step.py +++ b/agents-api/agents_api/activities/task_steps/log_step.py @@ -1,5 +1,3 @@ -import logging - from beartype import beartype from simpleeval import simple_eval from temporalio import activity @@ -26,7 +24,7 @@ async def log_step(context: StepContext) -> StepOutcome: return result except BaseException as e: - logging.error(f"Error in log_step: {e}") + activity.logger.error(f"Error in log_step: {e}") return StepOutcome(error=str(e)) diff --git a/agents-api/agents_api/activities/task_steps/raise_complete_async.py b/agents-api/agents_api/activities/task_steps/raise_complete_async.py index 57aae94c4..b393ceda6 100644 --- a/agents-api/agents_api/activities/task_steps/raise_complete_async.py +++ b/agents-api/agents_api/activities/task_steps/raise_complete_async.py @@ -3,5 +3,4 @@ @activity.defn async def raise_complete_async() -> None: - activity.heartbeat("Starting to wait") activity.raise_complete_async() diff --git a/agents-api/agents_api/activities/task_steps/return_step.py b/agents-api/agents_api/activities/task_steps/return_step.py index 90bc8adf8..2ea0b59e5 100644 --- a/agents-api/agents_api/activities/task_steps/return_step.py +++ b/agents-api/agents_api/activities/task_steps/return_step.py @@ -1,5 +1,3 @@ -import logging - from temporalio import activity from ...activities.task_steps.utils import simple_eval_dict @@ -24,7 +22,7 @@ async def return_step(context: StepContext) -> StepOutcome: return result except BaseException as e: - logging.error(f"Error in log_step: {e}") + activity.logger.error(f"Error in log_step: {e}") return StepOutcome(error=str(e)) diff --git a/agents-api/agents_api/activities/task_steps/switch_step.py b/agents-api/agents_api/activities/task_steps/switch_step.py index 60e64033e..66c6d9e88 100644 --- a/agents-api/agents_api/activities/task_steps/switch_step.py +++ b/agents-api/agents_api/activities/task_steps/switch_step.py @@ -1,5 +1,3 @@ -import logging - from beartype import beartype from simpleeval import simple_eval from temporalio import activity @@ -35,7 +33,7 @@ async def switch_step(context: StepContext) -> StepOutcome: return result except BaseException as e: - logging.error(f"Error in switch_step: {e}") + activity.logger.error(f"Error in switch_step: {e}") return StepOutcome(error=str(e)) diff --git a/agents-api/agents_api/activities/task_steps/transition_step.py b/agents-api/agents_api/activities/task_steps/transition_step.py index 1642075f4..f44503cb5 100644 --- a/agents-api/agents_api/activities/task_steps/transition_step.py +++ b/agents-api/agents_api/activities/task_steps/transition_step.py @@ -22,7 +22,6 @@ async def transition_step( transition_info.task_token = task_token # Create transition - activity.heartbeat("Creating transition in db") create_execution_transition_query( developer_id=context.developer_id, execution_id=context.execution.id, diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index d514df8f9..8ee7cfbd8 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -1,4 +1,3 @@ -import logging from typing import Callable from beartype import beartype @@ -38,7 +37,7 @@ async def yield_step(context: StepContext) -> StepOutcome: return StepOutcome(output=arguments, transition_to=("step", transition_target)) except BaseException as e: - logging.error(f"Error in log_step: {e}") + activity.logger.error(f"Error in yield_step: {e}") return StepOutcome(error=str(e)) diff --git a/agents-api/agents_api/worker/worker.py b/agents-api/agents_api/worker/worker.py index 604454c4c..65e813023 100644 --- a/agents-api/agents_api/worker/worker.py +++ b/agents-api/agents_api/worker/worker.py @@ -1,4 +1,5 @@ from datetime import timedelta +from inspect import getmembers, isfunction from typing import Any from temporalio.client import Client @@ -11,23 +12,12 @@ def create_worker(client: Client) -> Any: then create a worker to listen for tasks on the configured task queue. """ + from ..activities import task_steps from ..activities.demo import demo_activity from ..activities.embed_docs import embed_docs from ..activities.mem_mgmt import mem_mgmt from ..activities.mem_rating import mem_rating from ..activities.summarization import summarization - from ..activities.task_steps import ( - evaluate_step, - if_else_step, - log_step, - prompt_step, - return_step, - switch_step, - tool_call_step, - transition_step, - wait_for_input_step, - yield_step, - ) from ..activities.truncation import truncation from ..env import ( temporal_task_queue, @@ -40,18 +30,7 @@ def create_worker(client: Client) -> Any: from ..workflows.task_execution import TaskExecutionWorkflow from ..workflows.truncation import TruncationWorkflow - task_activities = [ - evaluate_step, - if_else_step, - log_step, - prompt_step, - return_step, - switch_step, - tool_call_step, - transition_step, - wait_for_input_step, - yield_step, - ] + task_activity_names, task_activities = zip(*getmembers(task_steps, isfunction)) # Initialize the worker with the specified task queue, workflows, and activities worker = Worker( From bd83b5caf3097cf0be77a753b38cfbca817952be Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 19 Aug 2024 21:20:35 -0400 Subject: [PATCH 084/110] fix(agents-api): Temporal local activities are too unreliable Signed-off-by: Diwank Tomer --- .../agents_api/workflows/task_execution.py | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index d2d357cef..56f200059 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -40,20 +40,26 @@ # PromptStep: prompt_step, # ToolCallStep: tool_call_step, WaitForInputStep: task_steps.wait_for_input_step, - LogStep: task_steps.log_step, SwitchStep: task_steps.switch_step, -} - -# Use few local activities (currently experimental) -STEP_TO_LOCAL_ACTIVITY = { - # NOTE: local activities are directly called in the workflow executor - # They MUST NOT FAIL, otherwise they will crash the workflow + # FIXME: These should be moved to local activities + # once temporal has fixed error handling for local activities + LogStep: task_steps.log_step, EvaluateStep: task_steps.evaluate_step, ReturnStep: task_steps.return_step, YieldStep: task_steps.yield_step, IfElseWorkflowStep: task_steps.if_else_step, } +# TODO: Avoid local activities for now (currently experimental) +STEP_TO_LOCAL_ACTIVITY = { + # # NOTE: local activities are directly called in the workflow executor + # # They MUST NOT FAIL, otherwise they will crash the workflow + # EvaluateStep: task_steps.evaluate_step, + # ReturnStep: task_steps.return_step, + # YieldStep: task_steps.yield_step, + # IfElseWorkflowStep: task_steps.if_else_step, +} + @workflow.defn class TaskExecutionWorkflow: @@ -131,7 +137,7 @@ async def transition(**kwargs) -> None: # Handle errors (activity returns None) case step, StepOutcome(error=error) if error is not None: raise ApplicationError( - f"{step.__class__.__name__} step threw error: {error}" + f"{type(step).__name__} step threw error: {error}" ) case LogStep(), StepOutcome(output=output): From b2fb5a9eb0c93b16254bba734f39008250ded0b0 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 19 Aug 2024 21:21:15 -0400 Subject: [PATCH 085/110] fix(agents-api): Fix the codec, which was causing a lot of bugs Signed-off-by: Diwank Tomer --- agents-api/agents_api/worker/codec.py | 121 +++++++++++++++----------- 1 file changed, 71 insertions(+), 50 deletions(-) diff --git a/agents-api/agents_api/worker/codec.py b/agents-api/agents_api/worker/codec.py index cdd7e3448..d56b81de1 100644 --- a/agents-api/agents_api/worker/codec.py +++ b/agents-api/agents_api/worker/codec.py @@ -1,12 +1,20 @@ +### +### NOTE: Working with temporal's codec is really really weird +### This is a workaround to use pydantic models with temporal +### The codec is used to serialize/deserialize the data +### But this code is quite brittle. Be careful when changing it + + import dataclasses -import json +import logging +import pickle from typing import Any, Optional, Type -import openai.types as openai_types -import openai.types.chat as openai_chat_types import temporalio.converter -from litellm.utils import ModelResponse -from pydantic import BaseModel + +# from beartype import BeartypeConf +# from beartype.door import is_bearable, is_subhint +from lz4.frame import compress, decompress from temporalio.api.common.v1 import Payload from temporalio.converter import ( CompositePayloadConverter, @@ -14,63 +22,76 @@ EncodingPayloadConverter, ) -import agents_api.autogen.openapi_model as openapi_model -import agents_api.common.protocol.tasks as tasks -from agents_api.common.utils.json import dumps as json_dumps - -# Map of model name to class so that we can look up the class when deserializing -model_class_map: dict = { - subclass.__module__ + "." + subclass.__name__: subclass - for subclass in { - # All the models we want to support - **openai_types.__dict__, - **openai_chat_types.__dict__, - **openapi_model.__dict__, - **tasks.__dict__, - }.values() - # - # Filter out the ones that aren't pydantic models - if isinstance(subclass, type) and issubclass(subclass, BaseModel) -} -# Also include dict -model_class_map["builtins.dict"] = dict -model_class_map["litellm.utils.ModelResponse"] = ModelResponse +def serialize(x: Any) -> bytes: + return compress(pickle.dumps(x)) + + +def deserialize(b: bytes) -> Any: + return pickle.loads(decompress(b)) + + +def from_payload_data(data: bytes, type_hint: Optional[Type] = None) -> Any: + decoded = deserialize(data) + + if type_hint is None: + return decoded + + decoded_type = type(decoded) + + # FIXME: Enable this check when temporal's codec stuff is fixed + # + # # Otherwise, check if the decoded value is bearable to the type hint + # if not is_bearable( + # decoded, + # type_hint, + # conf=BeartypeConf( + # is_pep484_tower=True + # ), # Check PEP 484 type hints. (be more lax on numeric types) + # ): + # logging.warning( + # f"WARNING: Decoded value {decoded_type} is not bearable to {type_hint}" + # ) + + # FIXME: Enable this check when temporal's codec stuff is fixed + # + # If the decoded value is a BaseModel and the type hint is a subclass of BaseModel + # and the decoded value's class is a subclass of the type hint, then promote the decoded value + # to the type hint. + if ( + type_hint != decoded_type + and hasattr(type_hint, "model_construct") + and hasattr(decoded, "model_dump") + # + # FIXME: Enable this check when temporal's codec stuff is fixed + # + # and is_subhint(type_hint, decoded_type) + ): + try: + decoded = type_hint(**decoded.model_dump()) + except Exception as e: + logging.warning( + f"WARNING: Could not promote {decoded_type} to {type_hint}: {e}" + ) + + return decoded class PydanticEncodingPayloadConverter(EncodingPayloadConverter): - @property - def encoding(self) -> str: - return "text/pydantic-json" + encoding = "text/pickle+lz4" + b_encoding = encoding.encode() def to_payload(self, value: Any) -> Optional[Payload]: - data: str = ( - value.model_dump_json() - if hasattr(value, "model_dump_json") - else json_dumps(value) - ) - return Payload( metadata={ - "encoding": self.encoding.encode(), - "model_name": value.__class__.__name__.encode(), - "model_module": value.__class__.__module__.encode(), + "encoding": self.b_encoding, }, - data=data.encode(), + data=serialize(value), ) def from_payload(self, payload: Payload, type_hint: Optional[Type] = None) -> Any: - data = json.loads(payload.data.decode()) - - if not isinstance(data, dict): - return data - - # Otherwise, we have a model - model_name = payload.metadata["model_name"].decode() - model_module = payload.metadata["model_module"].decode() - model_class = model_class_map[model_module + "." + model_name] - - return model_class(**data) + assert payload.metadata["encoding"] == self.b_encoding + return from_payload_data(payload.data, type_hint) class PydanticPayloadConverter(CompositePayloadConverter): From d92b054515229385303a848e59874c64c7c9d756 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 19 Aug 2024 21:26:29 -0400 Subject: [PATCH 086/110] refactor(agents-api): Minor refactors Signed-off-by: Diwank Tomer --- .../routers/tasks/update_execution.py | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/agents-api/agents_api/routers/tasks/update_execution.py b/agents-api/agents_api/routers/tasks/update_execution.py index f47b0ddf3..697846f6c 100644 --- a/agents-api/agents_api/routers/tasks/update_execution.py +++ b/agents-api/agents_api/routers/tasks/update_execution.py @@ -1,6 +1,6 @@ from typing import Annotated -from fastapi import Depends +from fastapi import Depends, HTTPException from pydantic import UUID4 from agents_api.autogen.openapi_model import ( @@ -26,14 +26,20 @@ async def update_execution( data: ResumeExecutionRequest | StopExecutionRequest, ): temporal_client = await get_client() - if isinstance(data, StopExecutionRequest): - handle = temporal_client.get_workflow_handle_for( - *get_temporal_workflow_data(execution_id=execution_id) - ) - await handle.cancel() - else: - token_data = get_paused_execution_token( - developer_id=x_developer_id, execution_id=execution_id - ) - handle = temporal_client.get_async_activity_handle(token_data["task_token"]) - await handle.complete(data.input) + + match data: + case StopExecutionRequest(): + wf_handle = temporal_client.get_workflow_handle_for( + *get_temporal_workflow_data(execution_id=execution_id) + ) + await wf_handle.cancel() + + case ResumeExecutionRequest(): + token_data = get_paused_execution_token( + developer_id=x_developer_id, execution_id=execution_id + ) + act_handle = temporal_client.get_async_activity_handle(token_data["task_token"]) + await act_handle.complete(data.input) + + case _: + raise HTTPException(status_code=400, detail="Invalid request data") From 996235689b33bd1fe35d08ba53377c21969a4d36 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Mon, 19 Aug 2024 23:24:13 -0400 Subject: [PATCH 087/110] feat(agents-api): Add YAML support Signed-off-by: Diwank Tomer --- agents-api/agents_api/middleware.py | 35 ++++++++ .../routers/tasks/update_execution.py | 4 +- agents-api/agents_api/web.py | 17 ++-- agents-api/poetry.lock | 2 +- agents-api/pyproject.toml | 1 + agents-api/tests/test_agent_routes.py | 2 +- agents-api/tests/test_workflow_routes.py | 88 +++++++++++++++++++ agents-api/tests/utils.py | 23 +++++ 8 files changed, 164 insertions(+), 8 deletions(-) create mode 100644 agents-api/agents_api/middleware.py create mode 100644 agents-api/tests/test_workflow_routes.py diff --git a/agents-api/agents_api/middleware.py b/agents-api/agents_api/middleware.py new file mode 100644 index 000000000..a76b29fa6 --- /dev/null +++ b/agents-api/agents_api/middleware.py @@ -0,0 +1,35 @@ +import re + +import yaml +from fastapi import Request + + +class YamlMiddleware: + def __init__(self, path_regex: str = r".*"): + self.path_regex = re.compile(path_regex) + + async def __call__(self, request: Request, call_next): + content_type = request.headers.get("content-type", "").strip().lower() + + # Filter out requests that are not for YAML and not for the specified path + if not self.path_regex.match(request.url.path) or content_type not in [ + "application/x-yaml", + "application/yaml", + "text/yaml", + "text/x-yaml", + ]: + return await call_next(request) + + # Parse the YAML body into a Python object + body = yaml.load(await request.body(), yaml.CSafeLoader) + request._json = body + + # Switch headers to JSON + headers = request.headers.mutablecopy() + headers["content-type"] = "application/json" + + request._headers = headers + + # Continue processing the request + response = await call_next(request) + return response diff --git a/agents-api/agents_api/routers/tasks/update_execution.py b/agents-api/agents_api/routers/tasks/update_execution.py index 697846f6c..779a7121b 100644 --- a/agents-api/agents_api/routers/tasks/update_execution.py +++ b/agents-api/agents_api/routers/tasks/update_execution.py @@ -38,7 +38,9 @@ async def update_execution( token_data = get_paused_execution_token( developer_id=x_developer_id, execution_id=execution_id ) - act_handle = temporal_client.get_async_activity_handle(token_data["task_token"]) + act_handle = temporal_client.get_async_activity_handle( + token_data["task_token"] + ) await act_handle.complete(data.input) case _: diff --git a/agents-api/agents_api/web.py b/agents-api/agents_api/web.py index 2a24dcebb..e20803e92 100644 --- a/agents-api/agents_api/web.py +++ b/agents-api/agents_api/web.py @@ -11,16 +11,18 @@ from fastapi import Depends, FastAPI, Request, status from fastapi.exceptions import HTTPException, RequestValidationError from fastapi.middleware.cors import CORSMiddleware +from fastapi.middleware.gzip import GZipMiddleware from fastapi.responses import JSONResponse from litellm.exceptions import APIError from pycozo.client import QueryException from temporalio.service import RPCError -from agents_api.common.exceptions import BaseCommonException -from agents_api.dependencies.auth import get_api_key -from agents_api.env import sentry_dsn -from agents_api.exceptions import PromptTooBigError -from agents_api.routers import ( +from .common.exceptions import BaseCommonException +from .dependencies.auth import get_api_key +from .env import sentry_dsn +from .exceptions import PromptTooBigError +from .middleware import YamlMiddleware +from .routers import ( agents, docs, jobs, @@ -89,6 +91,11 @@ def register_exceptions(app: FastAPI) -> None: max_age=3600, ) +app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=3) + +# Add yaml middleware +app.middleware("http")(YamlMiddleware(path_regex=r"/agents/.+/tasks.*")) + register_exceptions(app) app.include_router(agents.router) diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 00d7a4708..8223195dd 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -4484,4 +4484,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "d0445b363a8642838c50a25b5f9e440e1defca471cb3bf547e800a44dc9fe083" +content-hash = "f0057b059c1db08b485252ae5e4139f89126a1f82873ee56604c94a9b9d2142a" diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index 59111206f..5e965a97b 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -32,6 +32,7 @@ pydantic-partial = "^0.5.5" simpleeval = "^0.9.13" lz4 = "^4.3.3" +pyyaml = "^6.0.2" [tool.poetry.group.dev.dependencies] ipython = "^8.26.0" ruff = "^0.5.5" diff --git a/agents-api/tests/test_agent_routes.py b/agents-api/tests/test_agent_routes.py index 353d6ab95..0bd6d36df 100644 --- a/agents-api/tests/test_agent_routes.py +++ b/agents-api/tests/test_agent_routes.py @@ -17,7 +17,7 @@ def _(client=client): response = client.request( method="POST", url="/agents", - data=data, + json=data, ) assert response.status_code == 403 diff --git a/agents-api/tests/test_workflow_routes.py b/agents-api/tests/test_workflow_routes.py new file mode 100644 index 000000000..b1fb5abf0 --- /dev/null +++ b/agents-api/tests/test_workflow_routes.py @@ -0,0 +1,88 @@ +# Tests for task queries + +from uuid import uuid4 + +from ward import test + +from .fixtures import cozo_client, test_agent, test_developer_id +from .utils import patch_http_client_with_temporal + + +@test("workflow route: evaluate step single") +async def _( + cozo_client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + agent_id = str(agent.id) + task_id = str(uuid4()) + + async with patch_http_client_with_temporal( + cozo_client=cozo_client, developer_id=developer_id + ) as ( + make_request, + client, + ): + task_data = { + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [{"evaluate": {"hello": '"world"'}}], + } + + make_request( + method="POST", + url=f"/agents/{agent_id}/tasks/{task_id}", + json=task_data, + ) + + execution_data = dict(input={"test": "input"}) + + make_request( + method="POST", + url=f"/tasks/{task_id}/executions", + json=execution_data, + ) + + +@test("workflow route: evaluate step single with yaml") +async def _( + cozo_client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + agent_id = str(agent.id) + task_id = str(uuid4()) + + async with patch_http_client_with_temporal( + cozo_client=cozo_client, developer_id=developer_id + ) as ( + make_request, + client, + ): + task_data = """ + name: test task + description: test task about + input_schema: + type: object + additionalProperties: true + + main: + - evaluate: + hello: '"world"' + """ + + make_request( + method="POST", + url=f"/agents/{agent_id}/tasks/{task_id}", + content=task_data, + headers={"Content-Type": "text/yaml"}, + ) + + execution_data = dict(input={"test": "input"}) + + make_request( + method="POST", + url=f"/tasks/{task_id}/executions", + json=execution_data, + ) diff --git a/agents-api/tests/utils.py b/agents-api/tests/utils.py index b15332300..74127f78d 100644 --- a/agents-api/tests/utils.py +++ b/agents-api/tests/utils.py @@ -3,6 +3,7 @@ from contextlib import asynccontextmanager from unittest.mock import patch +from fastapi.testclient import TestClient from temporalio.testing import WorkflowEnvironment from agents_api.worker.codec import pydantic_data_converter @@ -41,3 +42,25 @@ async def patch_testing_temporal(): # Reset log level logger.setLevel(previous_log_level) + + +@asynccontextmanager +async def patch_http_client_with_temporal(*, cozo_client, developer_id): + async with patch_testing_temporal(): + from agents_api.env import api_key, api_key_header_name + from agents_api.web import app + + client = TestClient(app=app) + app.state.cozo_client = cozo_client + + def make_request(method, url, **kwargs): + headers = kwargs.pop("headers", {}) + headers = { + **headers, + "X-Developer-Id": str(developer_id), + api_key_header_name: api_key, + } + + return client.request(method, url, headers=headers, **kwargs) + + yield make_request, client From a63fb4af81cea9c1f797ad20a6e19f50ca5ce400 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Tue, 20 Aug 2024 21:20:49 +0300 Subject: [PATCH 088/110] feat: Add "switch" and "for each" activities --- .../activities/task_steps/for_each_step.py | 32 +++++++++ .../activities/task_steps/switch_step.py | 2 - .../agents_api/workflows/task_execution.py | 72 +++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) create mode 100644 agents-api/agents_api/activities/task_steps/for_each_step.py diff --git a/agents-api/agents_api/activities/task_steps/for_each_step.py b/agents-api/agents_api/activities/task_steps/for_each_step.py new file mode 100644 index 000000000..e297d11dc --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/for_each_step.py @@ -0,0 +1,32 @@ +import logging + +from beartype import beartype +from simpleeval import simple_eval +from temporalio import activity + +from ...autogen.openapi_model import ForeachStep +from ...common.protocol.tasks import ( + StepContext, + StepOutcome, +) +from ...env import testing + + +@beartype +async def for_each_step(context: StepContext) -> StepOutcome: + try: + assert isinstance(context.current_step, ForeachStep) + + return StepOutcome(output=simple_eval(context.current_step.foreach.in_, names=context.model_dump())) + except BaseException as e: + logging.error(f"Error in for_each_step: {e}") + return StepOutcome(error=str(e)) + + +# Note: This is here just for clarity. We could have just imported if_else_step directly +# They do the same thing, so we dont need to mock the if_else_step function +mock_if_else_step = for_each_step + +for_each_step = activity.defn(name="if_else_step")( + for_each_step if not testing else mock_if_else_step +) diff --git a/agents-api/agents_api/activities/task_steps/switch_step.py b/agents-api/agents_api/activities/task_steps/switch_step.py index 66c6d9e88..83a315d03 100644 --- a/agents-api/agents_api/activities/task_steps/switch_step.py +++ b/agents-api/agents_api/activities/task_steps/switch_step.py @@ -12,8 +12,6 @@ @beartype async def switch_step(context: StepContext) -> StepOutcome: - # NOTE: This activity is only for logging, so we just evaluate the expression - # Hence, it's a local activity and SHOULD NOT fail try: assert isinstance(context.current_step, SwitchStep) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 56f200059..f3452b8e9 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -26,6 +26,10 @@ WaitForInputStep, Workflow, YieldStep, + ForeachStep, + ForeachDo, + SwitchStep, + MapReduceStep, ) from ..common.protocol.tasks import ( ExecutionInput, @@ -194,6 +198,74 @@ async def transition(**kwargs) -> None: args=if_else_args, ) + case ForeachStep(foreach=ForeachDo(do=do_step)), StepOutcome( + output=items + ): + for i, item in enumerate(items): + # Create a faux workflow + foreach_wf_name = ( + f"`{context.cursor.workflow}`[{context.cursor.step}].foreach[{i}]" + ) + + foreach_task = execution_input.task.model_copy() + foreach_task.workflows = [ + Workflow(name=foreach_wf_name, steps=[do_step]) + ] + + # Create a new execution input + foreach_execution_input = execution_input.model_copy() + foreach_execution_input.task = foreach_task + + # Set the next target to the chosen branch + foreach_next_target = TransitionTarget(workflow=foreach_wf_name, step=0) + + foreach_args = [ + foreach_execution_input, + foreach_next_target, + previous_inputs + [item], + ] + + # Execute the chosen branch and come back here + state.output = await workflow.execute_child_workflow( + TaskExecutionWorkflow.run, + args=foreach_args, + ) + + case SwitchStep(switch=cases), StepOutcome( + output=int(case_num) + ): + if case_num > 0: + chosen_branch = cases[case_num] + + # Create a faux workflow + case_wf_name = ( + f"`{context.cursor.workflow}`[{context.cursor.step}].case" + ) + + case_task = execution_input.task.model_copy() + case_task.workflows = [ + Workflow(name=case_wf_name, steps=[chosen_branch.then]) + ] + + # Create a new execution input + case_execution_input = execution_input.model_copy() + case_execution_input.task = case_task + + # Set the next target to the chosen branch + case_next_target = TransitionTarget(workflow=case_wf_name, step=0) + + case_args = [ + case_execution_input, + case_next_target, + previous_inputs, + ] + + # Execute the chosen branch and come back here + state.output = await workflow.execute_child_workflow( + TaskExecutionWorkflow.run, + args=case_args, + ) + case SleepStep( sleep=SleepFor( seconds=seconds, From 744e2ef179ec1911504a012c00c57483ddfccca2 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Tue, 20 Aug 2024 14:25:54 -0400 Subject: [PATCH 089/110] refactor(agents-api): Minor refactors to typespec types Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Agents.py | 10 +- agents-api/agents_api/autogen/Chat.py | 104 ++++++++++++++---- agents-api/agents_api/autogen/Common.py | 3 - agents-api/agents_api/autogen/Docs.py | 26 +---- agents-api/agents_api/autogen/Entries.py | 83 ++++++-------- agents-api/agents_api/autogen/Executions.py | 11 +- agents-api/agents_api/autogen/Jobs.py | 2 +- agents-api/agents_api/autogen/Sessions.py | 11 -- agents-api/agents_api/autogen/Tasks.py | 103 +++++++++-------- agents-api/agents_api/autogen/Tools.py | 21 +--- agents-api/agents_api/autogen/Users.py | 9 +- .../agents_api/autogen/openapi_model.py | 2 + agents-api/agents_api/middleware.py | 2 + agents-api/agents_api/web.py | 6 + agents-api/poetry.lock | 20 ++-- agents-api/pyproject.toml | 7 +- scripts/generate_openapi_code.sh | 2 +- sdks/python/poetry.lock | 14 +-- sdks/ts/src/api/index.ts | 6 +- sdks/ts/src/api/models/Chat_ChatInputData.ts | 21 +++- .../ts/src/api/models/Chat_ChatOutputChunk.ts | 21 +++- .../src/api/models/Chat_MultipleChatOutput.ts | 21 +++- .../src/api/models/Chat_SingleChatOutput.ts | 21 +++- ...ContentPart.ts => Common_JinjaTemplate.ts} | 11 +- .../src/api/models/Docs_CreateDocRequest.ts | 3 +- sdks/ts/src/api/models/Docs_Doc.ts | 3 +- .../api/models/Entries_InputChatMLMessage.ts | 23 ---- sdks/ts/src/api/models/Tasks_CaseThen.ts | 2 +- sdks/ts/src/api/models/Tasks_MapReduceStep.ts | 2 +- sdks/ts/src/api/models/Tasks_PromptStep.ts | 4 +- sdks/ts/src/api/models/Tasks_SetStep.ts | 2 +- sdks/ts/src/api/models/Tasks_SleepStep.ts | 2 +- sdks/ts/src/api/models/Tasks_ToolCallStep.ts | 4 +- sdks/ts/src/api/models/Tasks_YieldStep.ts | 4 +- .../ts/src/api/schemas/$Chat_ChatInputData.ts | 37 ++++++- .../src/api/schemas/$Chat_ChatOutputChunk.ts | 40 ++++++- .../api/schemas/$Chat_MultipleChatOutput.ts | 37 ++++++- .../src/api/schemas/$Chat_SingleChatOutput.ts | 37 ++++++- .../src/api/schemas/$Common_JinjaTemplate.ts | 8 ++ .../schemas/$Common_identifierSafeUnicode.ts | 1 + .../schemas/$Common_validPythonIdentifier.ts | 1 + .../src/api/schemas/$Docs_CreateDocRequest.ts | 8 +- sdks/ts/src/api/schemas/$Docs_Doc.ts | 8 +- .../schemas/$Entries_ChatMLTextContentPart.ts | 16 --- .../schemas/$Entries_InputChatMLMessage.ts | 42 ------- sdks/ts/src/api/schemas/$Tasks_CaseThen.ts | 5 +- .../src/api/schemas/$Tasks_MapReduceStep.ts | 6 +- sdks/ts/src/api/schemas/$Tasks_PromptStep.ts | 8 +- sdks/ts/src/api/schemas/$Tasks_SetStep.ts | 8 +- sdks/ts/src/api/schemas/$Tasks_SleepFor.ts | 4 + sdks/ts/src/api/schemas/$Tasks_SleepStep.ts | 2 +- .../ts/src/api/schemas/$Tasks_ToolCallStep.ts | 17 ++- sdks/ts/src/api/schemas/$Tasks_YieldStep.ts | 17 ++- typespec/agents/models.tsp | 3 +- typespec/common/scalars.tsp | 7 +- typespec/docs/models.tsp | 2 +- typespec/entries/models.tsp | 16 +-- typespec/jobs/models.tsp | 3 +- typespec/tasks/steps.tsp | 44 ++++---- typespec/users/models.tsp | 3 +- 60 files changed, 561 insertions(+), 405 deletions(-) rename sdks/ts/src/api/models/{Entries_ChatMLTextContentPart.ts => Common_JinjaTemplate.ts} (51%) delete mode 100644 sdks/ts/src/api/models/Entries_InputChatMLMessage.ts create mode 100644 sdks/ts/src/api/schemas/$Common_JinjaTemplate.ts delete mode 100644 sdks/ts/src/api/schemas/$Entries_ChatMLTextContentPart.ts delete mode 100644 sdks/ts/src/api/schemas/$Entries_InputChatMLMessage.ts diff --git a/agents-api/agents_api/autogen/Agents.py b/agents-api/agents_api/autogen/Agents.py index a20dd5392..157ceb064 100644 --- a/agents-api/agents_api/autogen/Agents.py +++ b/agents-api/agents_api/autogen/Agents.py @@ -13,7 +13,6 @@ class Agent(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -30,6 +29,7 @@ class Agent(BaseModel): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -60,7 +60,6 @@ class CreateAgentRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -68,6 +67,7 @@ class CreateAgentRequest(BaseModel): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -94,7 +94,6 @@ class CreateAgentRequest(BaseModel): class CreateOrUpdateAgentRequest(CreateAgentRequest): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: UUID @@ -103,6 +102,7 @@ class CreateOrUpdateAgentRequest(CreateAgentRequest): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -133,7 +133,6 @@ class PatchAgentRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -141,6 +140,7 @@ class PatchAgentRequest(BaseModel): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -171,7 +171,6 @@ class UpdateAgentRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -179,6 +178,7 @@ class UpdateAgentRequest(BaseModel): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 19c9fb05e..5aa4d6d29 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -10,13 +10,12 @@ from .Common import LogitBias from .Docs import DocReference -from .Entries import InputChatMLMessage +from .Entries import ChatMLImageContentPart from .Tools import FunctionTool, NamedToolChoice class BaseChatOutput(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) index: int @@ -32,7 +31,6 @@ class BaseChatOutput(BaseModel): class BaseChatResponse(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) usage: CompetionUsage | None = None @@ -56,7 +54,6 @@ class BaseChatResponse(BaseModel): class BaseTokenLogProb(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) token: str @@ -69,10 +66,9 @@ class BaseTokenLogProb(BaseModel): class ChatInputData(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) - messages: Annotated[list[InputChatMLMessage], Field(min_length=1)] + messages: Annotated[list[Message], Field(min_length=1)] """ A list of new input messages comprising the conversation so far. """ @@ -92,10 +88,9 @@ class ChatOutputChunk(BaseChatOutput): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) - delta: InputChatMLMessage + delta: Delta """ The message generated by the model """ @@ -103,7 +98,6 @@ class ChatOutputChunk(BaseChatOutput): class ChunkChatResponse(BaseChatResponse): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) choices: list[ChatOutputChunk] @@ -118,7 +112,6 @@ class CompetionUsage(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) completion_tokens: Annotated[ @@ -143,7 +136,6 @@ class CompetionUsage(BaseModel): class CompletionResponseFormat(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["text", "json_object"] = "text" @@ -152,9 +144,53 @@ class CompletionResponseFormat(BaseModel): """ +class Content(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + type: Literal["text"] = "text" + """ + The type (fixed to 'text') + """ + + +class Delta(BaseModel): + """ + The message generated by the model + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + role: Literal[ + "user", + "assistant", + "system", + "function", + "function_response", + "function_call", + "auto", + ] + """ + The role of the message + """ + content: str | list[str] | list[Content | ChatMLImageContentPart] + """ + The content parts of the message + """ + name: str | None = None + """ + Name + """ + continue_: Annotated[StrictBool | None, Field(None, alias="continue")] + """ + Whether to continue this message or return a new one + """ + + class LogProbResponse(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) content: Annotated[list[TokenLogProb] | None, Field(...)] @@ -163,9 +199,38 @@ class LogProbResponse(BaseModel): """ +class Message(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + role: Literal[ + "user", + "assistant", + "system", + "function", + "function_response", + "function_call", + "auto", + ] + """ + The role of the message + """ + content: str | list[str] | list[Content | ChatMLImageContentPart] + """ + The content parts of the message + """ + name: str | None = None + """ + Name + """ + continue_: Annotated[StrictBool | None, Field(None, alias="continue")] + """ + Whether to continue this message or return a new one + """ + + class MessageChatResponse(BaseChatResponse): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) choices: list[SingleChatOutput | MultipleChatOutput] @@ -180,15 +245,13 @@ class MultipleChatOutput(BaseChatOutput): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) - messages: list[InputChatMLMessage] + messages: list[Message] class OpenAISettings(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) frequency_penalty: Annotated[float | None, Field(None, ge=-2.0, le=2.0)] @@ -215,15 +278,13 @@ class SingleChatOutput(BaseChatOutput): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) - message: InputChatMLMessage + message: Message class TokenLogProb(BaseTokenLogProb): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) top_logprobs: list[BaseTokenLogProb] @@ -231,7 +292,6 @@ class TokenLogProb(BaseTokenLogProb): class ChatInput(ChatInputData): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) remember: Annotated[StrictBool, Field(False, json_schema_extra={"readOnly": True})] @@ -250,6 +310,7 @@ class ChatInput(ChatInputData): str | None, Field( None, + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -320,7 +381,6 @@ class DefaultChatSettings(OpenAISettings): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) repetition_penalty: Annotated[float | None, Field(None, ge=0.0, le=2.0)] @@ -339,13 +399,13 @@ class DefaultChatSettings(OpenAISettings): class ChatSettings(DefaultChatSettings): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) model: Annotated[ str | None, Field( None, + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] diff --git a/agents-api/agents_api/autogen/Common.py b/agents-api/agents_api/autogen/Common.py index 7dedd743e..aab88621d 100644 --- a/agents-api/agents_api/autogen/Common.py +++ b/agents-api/agents_api/autogen/Common.py @@ -38,7 +38,6 @@ class Offset(RootModel[int]): class ResourceCreatedResponse(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: UUID @@ -57,7 +56,6 @@ class ResourceCreatedResponse(BaseModel): class ResourceDeletedResponse(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: UUID @@ -76,7 +74,6 @@ class ResourceDeletedResponse(BaseModel): class ResourceUpdatedResponse(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: UUID diff --git a/agents-api/agents_api/autogen/Docs.py b/agents-api/agents_api/autogen/Docs.py index 3039fc3f4..a7023ddfc 100644 --- a/agents-api/agents_api/autogen/Docs.py +++ b/agents-api/agents_api/autogen/Docs.py @@ -11,7 +11,6 @@ class BaseDocSearchRequest(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) limit: Annotated[int, Field(10, ge=1, le=100)] @@ -27,16 +26,10 @@ class CreateDocRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None - title: Annotated[ - str, - Field( - pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$" - ), - ] + title: Annotated[str, Field(max_length=800)] """ Title describing what this document contains """ @@ -48,7 +41,6 @@ class CreateDocRequest(BaseModel): class Doc(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -57,12 +49,7 @@ class Doc(BaseModel): """ When this resource was created as UTC date-time """ - title: Annotated[ - str, - Field( - pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$" - ), - ] + title: Annotated[str, Field(max_length=800)] """ Title describing what this document contains """ @@ -74,7 +61,6 @@ class Doc(BaseModel): class DocOwner(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: UUID @@ -83,7 +69,6 @@ class DocOwner(BaseModel): class DocReference(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) owner: DocOwner @@ -101,7 +86,6 @@ class DocReference(BaseModel): class DocSearchResponse(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) docs: list[DocReference] @@ -116,7 +100,6 @@ class DocSearchResponse(BaseModel): class EmbedQueryRequest(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) text: str | list[str] @@ -127,7 +110,6 @@ class EmbedQueryRequest(BaseModel): class EmbedQueryResponse(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) vectors: list[list[float]] @@ -138,7 +120,6 @@ class EmbedQueryResponse(BaseModel): class HybridDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) confidence: Annotated[float, Field(0.5, ge=0.0, le=1.0)] @@ -161,7 +142,6 @@ class HybridDocSearchRequest(BaseDocSearchRequest): class Snippet(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) index: int @@ -170,7 +150,6 @@ class Snippet(BaseModel): class TextOnlyDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) text: str @@ -181,7 +160,6 @@ class TextOnlyDocSearchRequest(BaseDocSearchRequest): class VectorDocSearchRequest(BaseDocSearchRequest): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) confidence: Annotated[float, Field(0.5, ge=0.0, le=1.0)] diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index dc64872a1..753436702 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -6,21 +6,13 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import ( - AnyUrl, - AwareDatetime, - BaseModel, - ConfigDict, - Field, - StrictBool, -) +from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, RootModel from .Tools import ChosenToolCall, Tool, ToolResponse class BaseEntry(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) role: Literal[ @@ -37,13 +29,13 @@ class BaseEntry(BaseModel): """ name: str | None = None content: ( - list[ChatMLTextContentPart | ChatMLImageContentPart] + list[Content | ChatMLImageContentPart] | Tool | ChosenToolCall | str | ToolResponse | list[ - list[ChatMLTextContentPart | ChatMLImageContentPart] + list[Content | ChatMLImageContentPart] | Tool | ChosenToolCall | str @@ -63,7 +55,6 @@ class BaseEntry(BaseModel): class ChatMLImageContentPart(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) image_url: ImageURL @@ -76,9 +67,38 @@ class ChatMLImageContentPart(BaseModel): """ -class ChatMLTextContentPart(BaseModel): +class ChatMLRole( + RootModel[ + Literal[ + "user", + "assistant", + "system", + "function", + "function_response", + "function_call", + "auto", + ] + ] +): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Literal[ + "user", + "assistant", + "system", + "function", + "function_response", + "function_call", + "auto", + ] + """ + ChatML role (system|assistant|user|function_call|function|function_response|auto) + """ + + +class Content(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) text: str @@ -90,7 +110,6 @@ class ChatMLTextContentPart(BaseModel): class Entry(BaseEntry): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) created_at: Annotated[AwareDatetime, Field(json_schema_extra={"readOnly": True})] @@ -102,7 +121,6 @@ class Entry(BaseEntry): class History(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) entries: list[Entry] @@ -116,7 +134,6 @@ class History(BaseModel): class ImageURL(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) url: AnyUrl @@ -129,40 +146,8 @@ class ImageURL(BaseModel): """ -class InputChatMLMessage(BaseModel): - model_config = ConfigDict( - extra="allow", - populate_by_name=True, - ) - role: Literal[ - "user", - "assistant", - "system", - "function", - "function_response", - "function_call", - "auto", - ] - """ - The role of the message - """ - content: str | list[str] | list[ChatMLTextContentPart | ChatMLImageContentPart] - """ - The content parts of the message - """ - name: str | None = None - """ - Name - """ - continue_: Annotated[StrictBool | None, Field(None, alias="continue")] - """ - Whether to continue this message or return a new one - """ - - class Relation(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) head: UUID diff --git a/agents-api/agents_api/autogen/Executions.py b/agents-api/agents_api/autogen/Executions.py index 97046d7d9..904680e44 100644 --- a/agents-api/agents_api/autogen/Executions.py +++ b/agents-api/agents_api/autogen/Executions.py @@ -15,7 +15,6 @@ class CreateExecutionRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) input: dict[str, Any] @@ -27,7 +26,6 @@ class CreateExecutionRequest(BaseModel): class Execution(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) task_id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -67,7 +65,6 @@ class Execution(BaseModel): class TaskTokenResumeExecutionRequest(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) status: Literal["running"] = "running" @@ -79,7 +76,6 @@ class TaskTokenResumeExecutionRequest(BaseModel): class Transition(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Annotated[ @@ -106,13 +102,13 @@ class Transition(BaseModel): class TransitionTarget(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) workflow: Annotated[ str, Field( - pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$" + max_length=120, + pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] """ @@ -125,7 +121,6 @@ class TransitionTarget(BaseModel): class UpdateExecutionRequest(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) status: Literal[ @@ -141,7 +136,6 @@ class UpdateExecutionRequest(BaseModel): class ResumeExecutionRequest(UpdateExecutionRequest): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) status: Literal["running"] = "running" @@ -153,7 +147,6 @@ class ResumeExecutionRequest(UpdateExecutionRequest): class StopExecutionRequest(UpdateExecutionRequest): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) status: Literal["cancelled"] = "cancelled" diff --git a/agents-api/agents_api/autogen/Jobs.py b/agents-api/agents_api/autogen/Jobs.py index a3402d04d..568b7a09c 100644 --- a/agents-api/agents_api/autogen/Jobs.py +++ b/agents-api/agents_api/autogen/Jobs.py @@ -11,7 +11,6 @@ class JobStatus(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -27,6 +26,7 @@ class JobStatus(BaseModel): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] diff --git a/agents-api/agents_api/autogen/Sessions.py b/agents-api/agents_api/autogen/Sessions.py index caf47c278..4380dac02 100644 --- a/agents-api/agents_api/autogen/Sessions.py +++ b/agents-api/agents_api/autogen/Sessions.py @@ -15,7 +15,6 @@ class CreateSessionRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) user: UUID | None = None @@ -53,7 +52,6 @@ class PatchSessionRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' @@ -77,7 +75,6 @@ class PatchSessionRequest(BaseModel): class Session(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' @@ -118,7 +115,6 @@ class Session(BaseModel): class SingleAgentMultiUserSession(Session): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) agent: UUID @@ -127,7 +123,6 @@ class SingleAgentMultiUserSession(Session): class SingleAgentNoUserSession(Session): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) agent: UUID @@ -135,7 +130,6 @@ class SingleAgentNoUserSession(Session): class SingleAgentSingleUserSession(Session): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) agent: UUID @@ -148,7 +142,6 @@ class UpdateSessionRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) situation: str = '{%- if agent.name -%}\nYou are {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if agent.about -%}\nAbout you: {{agent.name}}.{{" "}}\n{%- endif -%}\n\n{%- if user -%}\nYou are talking to a user\n {%- if user.name -%}{{" "}} and their name is {{user.name}}\n {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%}\n {%- endif -%}\n{%- endif -%}\n\n{{"\n\n"}}\n\n{%- if agent.instructions -%}\nInstructions:{{"\n"}}\n {%- if agent.instructions is string -%}\n {{agent.instructions}}{{"\n"}}\n {%- else -%}\n {%- for instruction in agent.instructions -%}\n - {{instruction}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"\n"}}\n{%- endif -%}\n\n{%- if tools -%}\nTools:{{"\n"}}\n {%- for tool in tools -%}\n {%- if tool.type == "function" -%}\n - {{tool.function.name}}\n {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}}\n {%- else -%}\n - {{ 0/0 }} {# Error: Other tool types aren\'t supported yet. #}\n {%- endif -%}\n {%- endfor -%}\n{{"\n\n"}}\n{%- endif -%}\n\n{%- if docs -%}\nRelevant documents:{{"\n"}}\n {%- for doc in docs -%}\n {{doc.title}}{{"\n"}}\n {%- if doc.content is string -%}\n {{doc.content}}{{"\n"}}\n {%- else -%}\n {%- for snippet in doc.content -%}\n {{snippet}}{{"\n"}}\n {%- endfor -%}\n {%- endif -%}\n {{"---"}}\n {%- endfor -%}\n{%- endif -%}' @@ -172,7 +165,6 @@ class UpdateSessionRequest(BaseModel): class CreateOrUpdateSessionRequest(CreateSessionRequest): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: UUID @@ -207,7 +199,6 @@ class CreateOrUpdateSessionRequest(CreateSessionRequest): class MultiAgentMultiUserSession(Session): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) agents: list[UUID] @@ -216,7 +207,6 @@ class MultiAgentMultiUserSession(Session): class MultiAgentNoUserSession(Session): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) agents: list[UUID] @@ -224,7 +214,6 @@ class MultiAgentNoUserSession(Session): class MultiAgentSingleUserSession(Session): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) agents: list[UUID] diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index b3059cc12..5c5050447 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -15,13 +15,12 @@ TextOnlyDocSearchRequest, VectorDocSearchRequest, ) -from .Entries import InputChatMLMessage +from .Entries import ChatMLImageContentPart from .Tools import CreateToolRequest class BaseWorkflowStep(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal[ @@ -51,10 +50,9 @@ class BaseWorkflowStep(BaseModel): class CaseThen(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) - case: str + case: Literal["_"] | str """ The condition to evaluate """ @@ -78,13 +76,26 @@ class CaseThen(BaseModel): """ +class Content(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + text: str + """ + A valid jinja template. + """ + type: Literal["text"] = "text" + """ + The type (fixed to 'text') + """ + + class CreateTaskRequest(BaseModel): """ Payload for creating a task """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) name: str @@ -129,7 +140,6 @@ class CreateTaskRequest(BaseModel): class EmbedStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["embed"] = "embed" @@ -141,7 +151,6 @@ class EmbedStep(BaseWorkflowStep): class ErrorWorkflowStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["error"] = "error" @@ -153,7 +162,6 @@ class ErrorWorkflowStep(BaseWorkflowStep): class EvaluateStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["evaluate"] = "evaluate" @@ -165,7 +173,6 @@ class EvaluateStep(BaseWorkflowStep): class ForeachDo(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) in_: Annotated[str, Field(alias="in")] @@ -194,7 +201,6 @@ class ForeachDo(BaseModel): class ForeachStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["foreach"] = "foreach" @@ -206,7 +212,6 @@ class ForeachStep(BaseWorkflowStep): class GetStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["get"] = "get" @@ -218,7 +223,6 @@ class GetStep(BaseWorkflowStep): class IfElseWorkflowStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["if_else"] = "if_else" @@ -267,7 +271,6 @@ class IfElseWorkflowStep(BaseWorkflowStep): class LogStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["log"] = "log" @@ -279,7 +282,6 @@ class LogStep(BaseWorkflowStep): class MapOver(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) over: str @@ -294,7 +296,6 @@ class MapOver(BaseModel): class MapReduceStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["map_reduce"] = "map_reduce" @@ -302,7 +303,7 @@ class MapReduceStep(BaseWorkflowStep): """ The steps to run for each iteration """ - reduce: str | None = None + reduce: Literal["_"] | str = "_" """ The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. """ @@ -310,7 +311,6 @@ class MapReduceStep(BaseWorkflowStep): class ParallelStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["parallel"] = "parallel" @@ -340,7 +340,6 @@ class PatchTaskRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) description: str = "" @@ -385,13 +384,42 @@ class PatchTaskRequest(BaseModel): metadata: dict[str, Any] | None = None +class PromptItem(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + role: Literal[ + "user", + "assistant", + "system", + "function", + "function_response", + "function_call", + "auto", + ] + """ + The role of the message + """ + content: list[str] | list[Content | ChatMLImageContentPart] | str + """ + The content parts of the message + """ + name: str | None = None + """ + Name + """ + continue_: Annotated[StrictBool | None, Field(None, alias="continue")] + """ + Whether to continue this message or return a new one + """ + + class PromptStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["prompt"] = "prompt" - prompt: str | list[InputChatMLMessage] + prompt: list[PromptItem] | str """ The prompt to run """ @@ -403,7 +431,6 @@ class PromptStep(BaseWorkflowStep): class ReturnStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["return"] = "return" @@ -415,7 +442,6 @@ class ReturnStep(BaseWorkflowStep): class SearchStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["search"] = "search" @@ -427,7 +453,6 @@ class SearchStep(BaseWorkflowStep): class SetKey(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) key: str @@ -442,11 +467,10 @@ class SetKey(BaseModel): class SetStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["set"] = "set" - set: SetKey | list[SetKey] + set: SetKey """ The value to set """ @@ -454,22 +478,21 @@ class SetStep(BaseWorkflowStep): class SleepFor(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) - seconds: Annotated[int, Field(0, ge=0)] + seconds: Annotated[int, Field(0, ge=0, le=60)] """ The number of seconds to sleep for """ - minutes: Annotated[int, Field(0, ge=0)] + minutes: Annotated[int, Field(0, ge=0, le=60)] """ The number of minutes to sleep for """ - hours: Annotated[int, Field(0, ge=0)] + hours: Annotated[int, Field(0, ge=0, le=24)] """ The number of hours to sleep for """ - days: Annotated[int, Field(0, ge=0)] + days: Annotated[int, Field(0, ge=0, le=30)] """ The number of days to sleep for """ @@ -477,23 +500,21 @@ class SleepFor(BaseModel): class SleepStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["sleep"] = "sleep" sleep: SleepFor """ - The duration to sleep for + The duration to sleep for (max 31 days) """ class SwitchStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["switch"] = "switch" - switch: list[CaseThen] + switch: Annotated[list[CaseThen], Field(min_length=1)] """ The cond tree """ @@ -505,7 +526,6 @@ class Task(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) name: str @@ -559,7 +579,6 @@ class Task(BaseModel): class TaskTool(CreateToolRequest): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) inherited: Annotated[StrictBool, Field(False, json_schema_extra={"readOnly": True})] @@ -570,7 +589,6 @@ class TaskTool(CreateToolRequest): class ToolCallStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["tool_call"] = "tool_call" @@ -580,9 +598,9 @@ class ToolCallStep(BaseWorkflowStep): """ The tool to run """ - arguments: dict[str, str] + arguments: dict[str, str] | Literal["_"] = "_" """ - The input parameters for the tool + The input parameters for the tool (defaults to last step output) """ @@ -592,7 +610,6 @@ class UpdateTaskRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) description: str = "" @@ -636,7 +653,6 @@ class UpdateTaskRequest(BaseModel): class WaitForInputStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["wait_for_input"] = "wait_for_input" @@ -648,7 +664,6 @@ class WaitForInputStep(BaseWorkflowStep): class YieldStep(BaseWorkflowStep): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) kind_: Literal["yield"] = "yield" @@ -656,7 +671,7 @@ class YieldStep(BaseWorkflowStep): """ The subworkflow to run """ - arguments: dict[str, str] + arguments: dict[str, str] | Literal["_"] = "_" """ - The input parameters for the subworkflow + The input parameters for the subworkflow (defaults to last step output) """ diff --git a/agents-api/agents_api/autogen/Tools.py b/agents-api/agents_api/autogen/Tools.py index 74d207d06..b28334041 100644 --- a/agents-api/agents_api/autogen/Tools.py +++ b/agents-api/agents_api/autogen/Tools.py @@ -15,7 +15,6 @@ class ChosenToolCall(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] @@ -35,14 +34,13 @@ class CreateToolRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] """ Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) """ - name: Annotated[str, Field(pattern="^[^\\W0-9]\\w*$")] + name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] """ Name of the tool (must be unique for this agent and a valid python identifier string ) """ @@ -54,7 +52,6 @@ class CreateToolRequest(BaseModel): class FunctionCallOption(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) name: str @@ -69,7 +66,6 @@ class FunctionDef(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) name: Any | None = None @@ -80,6 +76,7 @@ class FunctionDef(BaseModel): str | None, Field( None, + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -94,7 +91,6 @@ class FunctionDef(BaseModel): class NamedToolChoice(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] @@ -113,14 +109,13 @@ class PatchToolRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] | None = None """ Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) """ - name: Annotated[str | None, Field(None, pattern="^[^\\W0-9]\\w*$")] + name: Annotated[str | None, Field(None, max_length=40, pattern="^[^\\W0-9]\\w*$")] """ Name of the tool (must be unique for this agent and a valid python identifier string ) """ @@ -132,14 +127,13 @@ class PatchToolRequest(BaseModel): class Tool(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] """ Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) """ - name: Annotated[str, Field(pattern="^[^\\W0-9]\\w*$")] + name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] """ Name of the tool (must be unique for this agent and a valid python identifier string ) """ @@ -160,7 +154,6 @@ class Tool(BaseModel): class ToolResponse(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: UUID @@ -176,14 +169,13 @@ class UpdateToolRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["function", "integration", "system", "api_call"] """ Whether this tool is a `function`, `api_call`, `system` etc. (Only `function` tool supported right now) """ - name: Annotated[str, Field(pattern="^[^\\W0-9]\\w*$")] + name: Annotated[str, Field(max_length=40, pattern="^[^\\W0-9]\\w*$")] """ Name of the tool (must be unique for this agent and a valid python identifier string ) """ @@ -195,7 +187,6 @@ class UpdateToolRequest(BaseModel): class ChosenFunctionCall(ChosenToolCall): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["function"] = "function" @@ -207,7 +198,6 @@ class ChosenFunctionCall(ChosenToolCall): class FunctionTool(Tool): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["function"] = "function" @@ -220,7 +210,6 @@ class FunctionTool(Tool): class NamedFunctionChoice(NamedToolChoice): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) type: Literal["function"] = "function" diff --git a/agents-api/agents_api/autogen/Users.py b/agents-api/agents_api/autogen/Users.py index 24ba132ac..6eeb1783d 100644 --- a/agents-api/agents_api/autogen/Users.py +++ b/agents-api/agents_api/autogen/Users.py @@ -15,7 +15,6 @@ class CreateUserRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -23,6 +22,7 @@ class CreateUserRequest(BaseModel): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -41,7 +41,6 @@ class PatchUserRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -49,6 +48,7 @@ class PatchUserRequest(BaseModel): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -67,7 +67,6 @@ class UpdateUserRequest(BaseModel): """ model_config = ConfigDict( - extra="allow", populate_by_name=True, ) metadata: dict[str, Any] | None = None @@ -75,6 +74,7 @@ class UpdateUserRequest(BaseModel): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -89,7 +89,6 @@ class UpdateUserRequest(BaseModel): class User(BaseModel): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] @@ -106,6 +105,7 @@ class User(BaseModel): str, Field( "", + max_length=120, pattern="^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", ), ] @@ -120,7 +120,6 @@ class User(BaseModel): class CreateOrUpdateUserRequest(CreateUserRequest): model_config = ConfigDict( - extra="allow", populate_by_name=True, ) id: UUID diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index 0b0b0e2b5..593264c70 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -38,6 +38,8 @@ class ListResponse(BaseModel, Generic[DataT]): CreateOrUpdateSessionRequest = CreateSessionRequest CreateOrUpdateTaskRequest = CreateTaskRequest ChatResponse = ChunkChatResponse | MessageChatResponse +ChatMLTextContentPart = Content +InputChatMLMessage = Message # Custom types (not generated correctly) diff --git a/agents-api/agents_api/middleware.py b/agents-api/agents_api/middleware.py index a76b29fa6..d8ebc38ef 100644 --- a/agents-api/agents_api/middleware.py +++ b/agents-api/agents_api/middleware.py @@ -20,6 +20,8 @@ async def __call__(self, request: Request, call_next): ]: return await call_next(request) + # TODO: This should be wrapped in a try/except block to catch any parsing errors + # # Parse the YAML body into a Python object body = yaml.load(await request.body(), yaml.CSafeLoader) request._json = body diff --git a/agents-api/agents_api/web.py b/agents-api/agents_api/web.py index e20803e92..e633fc3ce 100644 --- a/agents-api/agents_api/web.py +++ b/agents-api/agents_api/web.py @@ -80,8 +80,14 @@ def register_exceptions(app: FastAPI) -> None: ) +# TODO: Auth logic should be moved into global middleware _per router_ +# Because some routes don't require auth +# See: https://fastapi.tiangolo.com/tutorial/bigger-applications/ +# app: Any = FastAPI(dependencies=[Depends(get_api_key)]) +# TODO: CORS should be enabled only for JWT auth +# app.add_middleware( CORSMiddleware, allow_origins=["*"], diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 8223195dd..0ece98e08 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -1244,13 +1244,13 @@ networkx = ">=2" [[package]] name = "importlib-metadata" -version = "8.3.0" +version = "8.4.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.3.0-py3-none-any.whl", hash = "sha256:42817a4a0be5845d22c6e212db66a94ad261e2318d80b3e0d363894a79df2b67"}, - {file = "importlib_metadata-8.3.0.tar.gz", hash = "sha256:9c8fa6e8ea0f9516ad5c8db9246a731c948193c7754d3babb0114a05b27dd364"}, + {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, + {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, ] [package.dependencies] @@ -1837,13 +1837,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.18" +version = "1.43.19" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.18-py3-none-any.whl", hash = "sha256:68d853b4a0198a16e2260e4406a20f8d2e59bd903e019b7f3ba5a9f35ecc3e62"}, - {file = "litellm-1.43.18.tar.gz", hash = "sha256:e22b20065b62663dd060be9da1e84ca05903931c41c49d35a98649ed09e79d29"}, + {file = "litellm-1.43.19-py3-none-any.whl", hash = "sha256:f66bfe9c8b91577af57a5d1203728abc9b59df38a545148b7e85dec903185c12"}, + {file = "litellm-1.43.19.tar.gz", hash = "sha256:b1f475f98073632f0cea6d814bb10b14b6498e5ff93b91b52dfc00445bf013ab"}, ] [package.dependencies] @@ -2008,13 +2008,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.21.3" +version = "3.22.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"}, - {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"}, + {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, + {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, ] [package.dependencies] @@ -2022,7 +2022,7 @@ packaging = ">=17.0" [package.extras] dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "pytz", "simplejson"] [[package]] diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index 5e965a97b..01a7f0e18 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -71,12 +71,11 @@ datamodel-codegen \ --strict-types bool \ --strict-nullable \ --allow-population-by-field-name \ - --allow-extra-fields \ --field-include-all-keys \ + --reuse-model \ --snake-case-field \ --enum-field-as-literal all \ --field-constraints \ - --reuse-model \ --use-operation-id-as-name \ --use-schema-description \ --use-field-description \ @@ -90,12 +89,12 @@ datamodel-codegen \ --use-exact-imports \ --use-standard-collections \ --use-non-positive-negative-number-constrained-types \ - --collapse-root-models \ --target-python-version 3.11 \ + --collapse-root-models \ --openapi-scopes schemas \ --keep-model-order \ --disable-timestamp""" [tool.poe.tasks.test] env = { AGENTS_API_TESTING = "true" } -cmd = "ward test" \ No newline at end of file +cmd = "ward test" diff --git a/scripts/generate_openapi_code.sh b/scripts/generate_openapi_code.sh index 1c1d22476..9d046dbda 100755 --- a/scripts/generate_openapi_code.sh +++ b/scripts/generate_openapi_code.sh @@ -10,7 +10,7 @@ cd typespec/ && \ tsp compile . cd - -fern generate +# fern generate cd sdks/python && \ poetry update && \ diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index ec7e2547a..4ad4307a6 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -829,13 +829,13 @@ networkx = ">=2" [[package]] name = "importlib-metadata" -version = "8.3.0" +version = "8.4.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.3.0-py3-none-any.whl", hash = "sha256:42817a4a0be5845d22c6e212db66a94ad261e2318d80b3e0d363894a79df2b67"}, - {file = "importlib_metadata-8.3.0.tar.gz", hash = "sha256:9c8fa6e8ea0f9516ad5c8db9246a731c948193c7754d3babb0114a05b27dd364"}, + {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, + {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, ] [package.dependencies] @@ -1466,13 +1466,13 @@ files = [ [[package]] name = "marshmallow" -version = "3.21.3" +version = "3.22.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"}, - {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"}, + {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, + {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, ] [package.dependencies] @@ -1480,7 +1480,7 @@ packaging = ">=17.0" [package.extras] dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "pytz", "simplejson"] [[package]] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index 64ca78e83..293ad250a 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -35,6 +35,7 @@ export type { Chat_OpenAISettings } from "./models/Chat_OpenAISettings"; export type { Chat_SingleChatOutput } from "./models/Chat_SingleChatOutput"; export type { Chat_TokenLogProb } from "./models/Chat_TokenLogProb"; export type { Common_identifierSafeUnicode } from "./models/Common_identifierSafeUnicode"; +export type { Common_JinjaTemplate } from "./models/Common_JinjaTemplate"; export type { Common_limit } from "./models/Common_limit"; export type { Common_logit_bias } from "./models/Common_logit_bias"; export type { Common_offset } from "./models/Common_offset"; @@ -65,12 +66,10 @@ export type { Docs_VectorDocSearchRequest } from "./models/Docs_VectorDocSearchR export type { Entries_BaseEntry } from "./models/Entries_BaseEntry"; export type { Entries_ChatMLImageContentPart } from "./models/Entries_ChatMLImageContentPart"; export type { Entries_ChatMLRole } from "./models/Entries_ChatMLRole"; -export type { Entries_ChatMLTextContentPart } from "./models/Entries_ChatMLTextContentPart"; export type { Entries_Entry } from "./models/Entries_Entry"; export type { Entries_History } from "./models/Entries_History"; export type { Entries_ImageDetail } from "./models/Entries_ImageDetail"; export type { Entries_ImageURL } from "./models/Entries_ImageURL"; -export type { Entries_InputChatMLMessage } from "./models/Entries_InputChatMLMessage"; export type { Entries_Relation } from "./models/Entries_Relation"; export type { Executions_CreateExecutionRequest } from "./models/Executions_CreateExecutionRequest"; export type { Executions_Execution } from "./models/Executions_Execution"; @@ -170,6 +169,7 @@ export { $Chat_OpenAISettings } from "./schemas/$Chat_OpenAISettings"; export { $Chat_SingleChatOutput } from "./schemas/$Chat_SingleChatOutput"; export { $Chat_TokenLogProb } from "./schemas/$Chat_TokenLogProb"; export { $Common_identifierSafeUnicode } from "./schemas/$Common_identifierSafeUnicode"; +export { $Common_JinjaTemplate } from "./schemas/$Common_JinjaTemplate"; export { $Common_limit } from "./schemas/$Common_limit"; export { $Common_logit_bias } from "./schemas/$Common_logit_bias"; export { $Common_offset } from "./schemas/$Common_offset"; @@ -200,12 +200,10 @@ export { $Docs_VectorDocSearchRequest } from "./schemas/$Docs_VectorDocSearchReq export { $Entries_BaseEntry } from "./schemas/$Entries_BaseEntry"; export { $Entries_ChatMLImageContentPart } from "./schemas/$Entries_ChatMLImageContentPart"; export { $Entries_ChatMLRole } from "./schemas/$Entries_ChatMLRole"; -export { $Entries_ChatMLTextContentPart } from "./schemas/$Entries_ChatMLTextContentPart"; export { $Entries_Entry } from "./schemas/$Entries_Entry"; export { $Entries_History } from "./schemas/$Entries_History"; export { $Entries_ImageDetail } from "./schemas/$Entries_ImageDetail"; export { $Entries_ImageURL } from "./schemas/$Entries_ImageURL"; -export { $Entries_InputChatMLMessage } from "./schemas/$Entries_InputChatMLMessage"; export { $Entries_Relation } from "./schemas/$Entries_Relation"; export { $Executions_CreateExecutionRequest } from "./schemas/$Executions_CreateExecutionRequest"; export { $Executions_Execution } from "./schemas/$Executions_Execution"; diff --git a/sdks/ts/src/api/models/Chat_ChatInputData.ts b/sdks/ts/src/api/models/Chat_ChatInputData.ts index fb11ddec2..ca502e70b 100644 --- a/sdks/ts/src/api/models/Chat_ChatInputData.ts +++ b/sdks/ts/src/api/models/Chat_ChatInputData.ts @@ -2,14 +2,31 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; +import type { Entries_ChatMLRole } from "./Entries_ChatMLRole"; import type { Tools_FunctionTool } from "./Tools_FunctionTool"; import type { Tools_NamedToolChoice } from "./Tools_NamedToolChoice"; export type Chat_ChatInputData = { /** * A list of new input messages comprising the conversation so far. */ - messages: Array; + messages: Array<{ + /** + * The role of the message + */ + role: Entries_ChatMLRole; + /** + * The content parts of the message + */ + content: string | Array; + /** + * Name + */ + name?: string; + /** + * Whether to continue this message or return a new one + */ + continue?: boolean; + }>; /** * (Advanced) List of tools that are provided in addition to agent's default set of tools. */ diff --git a/sdks/ts/src/api/models/Chat_ChatOutputChunk.ts b/sdks/ts/src/api/models/Chat_ChatOutputChunk.ts index a3bbfacc7..af379458a 100644 --- a/sdks/ts/src/api/models/Chat_ChatOutputChunk.ts +++ b/sdks/ts/src/api/models/Chat_ChatOutputChunk.ts @@ -3,7 +3,7 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_BaseChatOutput } from "./Chat_BaseChatOutput"; -import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; +import type { Entries_ChatMLRole } from "./Entries_ChatMLRole"; /** * Streaming chat completion output */ @@ -11,5 +11,22 @@ export type Chat_ChatOutputChunk = Chat_BaseChatOutput & { /** * The message generated by the model */ - delta: Entries_InputChatMLMessage; + delta: { + /** + * The role of the message + */ + role: Entries_ChatMLRole; + /** + * The content parts of the message + */ + content: string | Array; + /** + * Name + */ + name?: string; + /** + * Whether to continue this message or return a new one + */ + continue?: boolean; + }; }; diff --git a/sdks/ts/src/api/models/Chat_MultipleChatOutput.ts b/sdks/ts/src/api/models/Chat_MultipleChatOutput.ts index b0eb182f6..89c725dfa 100644 --- a/sdks/ts/src/api/models/Chat_MultipleChatOutput.ts +++ b/sdks/ts/src/api/models/Chat_MultipleChatOutput.ts @@ -3,10 +3,27 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_BaseChatOutput } from "./Chat_BaseChatOutput"; -import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; +import type { Entries_ChatMLRole } from "./Entries_ChatMLRole"; /** * The output returned by the model. Note that, depending on the model provider, they might return more than one message. */ export type Chat_MultipleChatOutput = Chat_BaseChatOutput & { - messages: Array; + messages: Array<{ + /** + * The role of the message + */ + role: Entries_ChatMLRole; + /** + * The content parts of the message + */ + content: string | Array; + /** + * Name + */ + name?: string; + /** + * Whether to continue this message or return a new one + */ + continue?: boolean; + }>; }; diff --git a/sdks/ts/src/api/models/Chat_SingleChatOutput.ts b/sdks/ts/src/api/models/Chat_SingleChatOutput.ts index 57b76490c..b90b8c953 100644 --- a/sdks/ts/src/api/models/Chat_SingleChatOutput.ts +++ b/sdks/ts/src/api/models/Chat_SingleChatOutput.ts @@ -3,10 +3,27 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_BaseChatOutput } from "./Chat_BaseChatOutput"; -import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; +import type { Entries_ChatMLRole } from "./Entries_ChatMLRole"; /** * The output returned by the model. Note that, depending on the model provider, they might return more than one message. */ export type Chat_SingleChatOutput = Chat_BaseChatOutput & { - message: Entries_InputChatMLMessage; + message: { + /** + * The role of the message + */ + role: Entries_ChatMLRole; + /** + * The content parts of the message + */ + content: string | Array; + /** + * Name + */ + name?: string; + /** + * Whether to continue this message or return a new one + */ + continue?: boolean; + }; }; diff --git a/sdks/ts/src/api/models/Entries_ChatMLTextContentPart.ts b/sdks/ts/src/api/models/Common_JinjaTemplate.ts similarity index 51% rename from sdks/ts/src/api/models/Entries_ChatMLTextContentPart.ts rename to sdks/ts/src/api/models/Common_JinjaTemplate.ts index 26e266d4e..7eb9f4a48 100644 --- a/sdks/ts/src/api/models/Entries_ChatMLTextContentPart.ts +++ b/sdks/ts/src/api/models/Common_JinjaTemplate.ts @@ -2,10 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export type Entries_ChatMLTextContentPart = { - text: string; - /** - * The type (fixed to 'text') - */ - type: "text"; -}; +/** + * A valid jinja template. + */ +export type Common_JinjaTemplate = string; diff --git a/sdks/ts/src/api/models/Docs_CreateDocRequest.ts b/sdks/ts/src/api/models/Docs_CreateDocRequest.ts index 1294bf701..a972e0f83 100644 --- a/sdks/ts/src/api/models/Docs_CreateDocRequest.ts +++ b/sdks/ts/src/api/models/Docs_CreateDocRequest.ts @@ -2,7 +2,6 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; /** * Payload for creating a doc */ @@ -11,7 +10,7 @@ export type Docs_CreateDocRequest = { /** * Title describing what this document contains */ - title: Common_identifierSafeUnicode; + title: string; /** * Contents of the document */ diff --git a/sdks/ts/src/api/models/Docs_Doc.ts b/sdks/ts/src/api/models/Docs_Doc.ts index 3a4c7681d..9735170c6 100644 --- a/sdks/ts/src/api/models/Docs_Doc.ts +++ b/sdks/ts/src/api/models/Docs_Doc.ts @@ -2,7 +2,6 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Common_identifierSafeUnicode } from "./Common_identifierSafeUnicode"; import type { Common_uuid } from "./Common_uuid"; export type Docs_Doc = { readonly id: Common_uuid; @@ -14,7 +13,7 @@ export type Docs_Doc = { /** * Title describing what this document contains */ - title: Common_identifierSafeUnicode; + title: string; /** * Contents of the document */ diff --git a/sdks/ts/src/api/models/Entries_InputChatMLMessage.ts b/sdks/ts/src/api/models/Entries_InputChatMLMessage.ts deleted file mode 100644 index e735b8bd1..000000000 --- a/sdks/ts/src/api/models/Entries_InputChatMLMessage.ts +++ /dev/null @@ -1,23 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Entries_ChatMLRole } from "./Entries_ChatMLRole"; -export type Entries_InputChatMLMessage = { - /** - * The role of the message - */ - role: Entries_ChatMLRole; - /** - * The content parts of the message - */ - content: string | Array; - /** - * Name - */ - name?: string; - /** - * Whether to continue this message or return a new one - */ - continue?: boolean; -}; diff --git a/sdks/ts/src/api/models/Tasks_CaseThen.ts b/sdks/ts/src/api/models/Tasks_CaseThen.ts index c55e939ad..ab51af385 100644 --- a/sdks/ts/src/api/models/Tasks_CaseThen.ts +++ b/sdks/ts/src/api/models/Tasks_CaseThen.ts @@ -20,7 +20,7 @@ export type Tasks_CaseThen = { /** * The condition to evaluate */ - case: Common_PyExpression; + case: Common_PyExpression | "_"; /** * The steps to run if the condition is true */ diff --git a/sdks/ts/src/api/models/Tasks_MapReduceStep.ts b/sdks/ts/src/api/models/Tasks_MapReduceStep.ts index e450d9162..ef049ed42 100644 --- a/sdks/ts/src/api/models/Tasks_MapReduceStep.ts +++ b/sdks/ts/src/api/models/Tasks_MapReduceStep.ts @@ -14,5 +14,5 @@ export type Tasks_MapReduceStep = Tasks_BaseWorkflowStep & { /** * The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. */ - reduce?: Common_PyExpression; + reduce: Common_PyExpression | "_"; }; diff --git a/sdks/ts/src/api/models/Tasks_PromptStep.ts b/sdks/ts/src/api/models/Tasks_PromptStep.ts index 39a7127ac..db019b522 100644 --- a/sdks/ts/src/api/models/Tasks_PromptStep.ts +++ b/sdks/ts/src/api/models/Tasks_PromptStep.ts @@ -3,14 +3,14 @@ /* tslint:disable */ /* eslint-disable */ import type { Chat_ChatSettings } from "./Chat_ChatSettings"; -import type { Entries_InputChatMLMessage } from "./Entries_InputChatMLMessage"; +import type { Common_JinjaTemplate } from "./Common_JinjaTemplate"; import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; export type Tasks_PromptStep = Tasks_BaseWorkflowStep & { kind_: "prompt"; /** * The prompt to run */ - prompt: string | Array; + prompt: Common_JinjaTemplate; /** * Settings for the prompt */ diff --git a/sdks/ts/src/api/models/Tasks_SetStep.ts b/sdks/ts/src/api/models/Tasks_SetStep.ts index a4425ecc3..61838776e 100644 --- a/sdks/ts/src/api/models/Tasks_SetStep.ts +++ b/sdks/ts/src/api/models/Tasks_SetStep.ts @@ -9,5 +9,5 @@ export type Tasks_SetStep = Tasks_BaseWorkflowStep & { /** * The value to set */ - set: Tasks_SetKey | Array; + set: Tasks_SetKey; }; diff --git a/sdks/ts/src/api/models/Tasks_SleepStep.ts b/sdks/ts/src/api/models/Tasks_SleepStep.ts index c2a14b9b5..cd2994546 100644 --- a/sdks/ts/src/api/models/Tasks_SleepStep.ts +++ b/sdks/ts/src/api/models/Tasks_SleepStep.ts @@ -7,7 +7,7 @@ import type { Tasks_SleepFor } from "./Tasks_SleepFor"; export type Tasks_SleepStep = Tasks_BaseWorkflowStep & { kind_: "sleep"; /** - * The duration to sleep for + * The duration to sleep for (max 31 days) */ sleep: Tasks_SleepFor; }; diff --git a/sdks/ts/src/api/models/Tasks_ToolCallStep.ts b/sdks/ts/src/api/models/Tasks_ToolCallStep.ts index f0a758372..dfb0b505c 100644 --- a/sdks/ts/src/api/models/Tasks_ToolCallStep.ts +++ b/sdks/ts/src/api/models/Tasks_ToolCallStep.ts @@ -12,7 +12,7 @@ export type Tasks_ToolCallStep = Tasks_BaseWorkflowStep & { */ tool: Common_toolRef; /** - * The input parameters for the tool + * The input parameters for the tool (defaults to last step output) */ - arguments: Record; + arguments: Record | "_"; }; diff --git a/sdks/ts/src/api/models/Tasks_YieldStep.ts b/sdks/ts/src/api/models/Tasks_YieldStep.ts index a1e298a41..6b677947e 100644 --- a/sdks/ts/src/api/models/Tasks_YieldStep.ts +++ b/sdks/ts/src/api/models/Tasks_YieldStep.ts @@ -11,7 +11,7 @@ export type Tasks_YieldStep = Tasks_BaseWorkflowStep & { */ workflow: string; /** - * The input parameters for the subworkflow + * The input parameters for the subworkflow (defaults to last step output) */ - arguments: Record; + arguments: Record | "_"; }; diff --git a/sdks/ts/src/api/schemas/$Chat_ChatInputData.ts b/sdks/ts/src/api/schemas/$Chat_ChatInputData.ts index b5ed3b199..3206351d5 100644 --- a/sdks/ts/src/api/schemas/$Chat_ChatInputData.ts +++ b/sdks/ts/src/api/schemas/$Chat_ChatInputData.ts @@ -7,7 +7,42 @@ export const $Chat_ChatInputData = { messages: { type: "array", contains: { - type: "Entries_InputChatMLMessage", + properties: { + role: { + type: "all-of", + description: `The role of the message`, + contains: [ + { + type: "Entries_ChatMLRole", + }, + ], + isRequired: true, + }, + content: { + type: "any-of", + description: `The content parts of the message`, + contains: [ + { + type: "string", + }, + { + type: "array", + contains: { + type: "string", + }, + }, + ], + isRequired: true, + }, + name: { + type: "string", + description: `Name`, + }, + continue: { + type: "boolean", + description: `Whether to continue this message or return a new one`, + }, + }, }, isRequired: true, }, diff --git a/sdks/ts/src/api/schemas/$Chat_ChatOutputChunk.ts b/sdks/ts/src/api/schemas/$Chat_ChatOutputChunk.ts index 71efa1925..869be9d62 100644 --- a/sdks/ts/src/api/schemas/$Chat_ChatOutputChunk.ts +++ b/sdks/ts/src/api/schemas/$Chat_ChatOutputChunk.ts @@ -12,13 +12,43 @@ export const $Chat_ChatOutputChunk = { { properties: { delta: { - type: "all-of", description: `The message generated by the model`, - contains: [ - { - type: "Entries_InputChatMLMessage", + properties: { + role: { + type: "all-of", + description: `The role of the message`, + contains: [ + { + type: "Entries_ChatMLRole", + }, + ], + isRequired: true, }, - ], + content: { + type: "any-of", + description: `The content parts of the message`, + contains: [ + { + type: "string", + }, + { + type: "array", + contains: { + type: "string", + }, + }, + ], + isRequired: true, + }, + name: { + type: "string", + description: `Name`, + }, + continue: { + type: "boolean", + description: `Whether to continue this message or return a new one`, + }, + }, isRequired: true, }, }, diff --git a/sdks/ts/src/api/schemas/$Chat_MultipleChatOutput.ts b/sdks/ts/src/api/schemas/$Chat_MultipleChatOutput.ts index 331290c5c..2b54621f5 100644 --- a/sdks/ts/src/api/schemas/$Chat_MultipleChatOutput.ts +++ b/sdks/ts/src/api/schemas/$Chat_MultipleChatOutput.ts @@ -14,7 +14,42 @@ export const $Chat_MultipleChatOutput = { messages: { type: "array", contains: { - type: "Entries_InputChatMLMessage", + properties: { + role: { + type: "all-of", + description: `The role of the message`, + contains: [ + { + type: "Entries_ChatMLRole", + }, + ], + isRequired: true, + }, + content: { + type: "any-of", + description: `The content parts of the message`, + contains: [ + { + type: "string", + }, + { + type: "array", + contains: { + type: "string", + }, + }, + ], + isRequired: true, + }, + name: { + type: "string", + description: `Name`, + }, + continue: { + type: "boolean", + description: `Whether to continue this message or return a new one`, + }, + }, }, isRequired: true, }, diff --git a/sdks/ts/src/api/schemas/$Chat_SingleChatOutput.ts b/sdks/ts/src/api/schemas/$Chat_SingleChatOutput.ts index 75c9ddf74..e5f68cf9b 100644 --- a/sdks/ts/src/api/schemas/$Chat_SingleChatOutput.ts +++ b/sdks/ts/src/api/schemas/$Chat_SingleChatOutput.ts @@ -12,7 +12,42 @@ export const $Chat_SingleChatOutput = { { properties: { message: { - type: "Entries_InputChatMLMessage", + properties: { + role: { + type: "all-of", + description: `The role of the message`, + contains: [ + { + type: "Entries_ChatMLRole", + }, + ], + isRequired: true, + }, + content: { + type: "any-of", + description: `The content parts of the message`, + contains: [ + { + type: "string", + }, + { + type: "array", + contains: { + type: "string", + }, + }, + ], + isRequired: true, + }, + name: { + type: "string", + description: `Name`, + }, + continue: { + type: "boolean", + description: `Whether to continue this message or return a new one`, + }, + }, isRequired: true, }, }, diff --git a/sdks/ts/src/api/schemas/$Common_JinjaTemplate.ts b/sdks/ts/src/api/schemas/$Common_JinjaTemplate.ts new file mode 100644 index 000000000..903a1579b --- /dev/null +++ b/sdks/ts/src/api/schemas/$Common_JinjaTemplate.ts @@ -0,0 +1,8 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Common_JinjaTemplate = { + type: "string", + description: `A valid jinja template.`, +} as const; diff --git a/sdks/ts/src/api/schemas/$Common_identifierSafeUnicode.ts b/sdks/ts/src/api/schemas/$Common_identifierSafeUnicode.ts index e3e2e9f0a..75cd72df6 100644 --- a/sdks/ts/src/api/schemas/$Common_identifierSafeUnicode.ts +++ b/sdks/ts/src/api/schemas/$Common_identifierSafeUnicode.ts @@ -7,6 +7,7 @@ export const $Common_identifierSafeUnicode = { description: `For Unicode character safety See: https://unicode.org/reports/tr31/ See: https://www.unicode.org/reports/tr39/#Identifier_Characters`, + maxLength: 120, pattern: "^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$", } as const; diff --git a/sdks/ts/src/api/schemas/$Common_validPythonIdentifier.ts b/sdks/ts/src/api/schemas/$Common_validPythonIdentifier.ts index 7be2759cc..89c378739 100644 --- a/sdks/ts/src/api/schemas/$Common_validPythonIdentifier.ts +++ b/sdks/ts/src/api/schemas/$Common_validPythonIdentifier.ts @@ -5,5 +5,6 @@ export const $Common_validPythonIdentifier = { type: "string", description: `Valid python identifier names`, + maxLength: 40, pattern: "^[^\\W0-9]\\w*$", } as const; diff --git a/sdks/ts/src/api/schemas/$Docs_CreateDocRequest.ts b/sdks/ts/src/api/schemas/$Docs_CreateDocRequest.ts index a3e2a7075..4af7128e1 100644 --- a/sdks/ts/src/api/schemas/$Docs_CreateDocRequest.ts +++ b/sdks/ts/src/api/schemas/$Docs_CreateDocRequest.ts @@ -12,14 +12,10 @@ export const $Docs_CreateDocRequest = { }, }, title: { - type: "all-of", + type: "string", description: `Title describing what this document contains`, - contains: [ - { - type: "Common_identifierSafeUnicode", - }, - ], isRequired: true, + maxLength: 800, }, content: { type: "any-of", diff --git a/sdks/ts/src/api/schemas/$Docs_Doc.ts b/sdks/ts/src/api/schemas/$Docs_Doc.ts index d6c622cb0..f77ec6d23 100644 --- a/sdks/ts/src/api/schemas/$Docs_Doc.ts +++ b/sdks/ts/src/api/schemas/$Docs_Doc.ts @@ -28,14 +28,10 @@ export const $Docs_Doc = { format: "date-time", }, title: { - type: "all-of", + type: "string", description: `Title describing what this document contains`, - contains: [ - { - type: "Common_identifierSafeUnicode", - }, - ], isRequired: true, + maxLength: 800, }, content: { type: "any-of", diff --git a/sdks/ts/src/api/schemas/$Entries_ChatMLTextContentPart.ts b/sdks/ts/src/api/schemas/$Entries_ChatMLTextContentPart.ts deleted file mode 100644 index 1701225c5..000000000 --- a/sdks/ts/src/api/schemas/$Entries_ChatMLTextContentPart.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Entries_ChatMLTextContentPart = { - properties: { - text: { - type: "string", - isRequired: true, - }, - type: { - type: "Enum", - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Entries_InputChatMLMessage.ts b/sdks/ts/src/api/schemas/$Entries_InputChatMLMessage.ts deleted file mode 100644 index 2f888d08b..000000000 --- a/sdks/ts/src/api/schemas/$Entries_InputChatMLMessage.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Entries_InputChatMLMessage = { - properties: { - role: { - type: "all-of", - description: `The role of the message`, - contains: [ - { - type: "Entries_ChatMLRole", - }, - ], - isRequired: true, - }, - content: { - type: "any-of", - description: `The content parts of the message`, - contains: [ - { - type: "string", - }, - { - type: "array", - contains: { - type: "string", - }, - }, - ], - isRequired: true, - }, - name: { - type: "string", - description: `Name`, - }, - continue: { - type: "boolean", - description: `Whether to continue this message or return a new one`, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts b/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts index c58942ef5..f30f3bf91 100644 --- a/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts +++ b/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts @@ -5,12 +5,15 @@ export const $Tasks_CaseThen = { properties: { case: { - type: "all-of", + type: "any-of", description: `The condition to evaluate`, contains: [ { type: "Common_PyExpression", }, + { + type: "Enum", + }, ], isRequired: true, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts b/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts index 3d52b1727..e24fc2479 100644 --- a/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts @@ -25,13 +25,17 @@ export const $Tasks_MapReduceStep = { isRequired: true, }, reduce: { - type: "all-of", + type: "any-of", description: `The expression to reduce the results (\`_\` is a list of outputs). If not provided, the results are returned as a list.`, contains: [ { type: "Common_PyExpression", }, + { + type: "Enum", + }, ], + isRequired: true, }, }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts b/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts index 3eb78c319..882307bdd 100644 --- a/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts @@ -19,13 +19,7 @@ export const $Tasks_PromptStep = { description: `The prompt to run`, contains: [ { - type: "string", - }, - { - type: "array", - contains: { - type: "Entries_InputChatMLMessage", - }, + type: "Common_JinjaTemplate", }, ], isRequired: true, diff --git a/sdks/ts/src/api/schemas/$Tasks_SetStep.ts b/sdks/ts/src/api/schemas/$Tasks_SetStep.ts index 7c768fc10..0590f1141 100644 --- a/sdks/ts/src/api/schemas/$Tasks_SetStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_SetStep.ts @@ -15,18 +15,12 @@ export const $Tasks_SetStep = { isRequired: true, }, set: { - type: "any-of", + type: "all-of", description: `The value to set`, contains: [ { type: "Tasks_SetKey", }, - { - type: "array", - contains: { - type: "Tasks_SetKey", - }, - }, ], isRequired: true, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_SleepFor.ts b/sdks/ts/src/api/schemas/$Tasks_SleepFor.ts index a03d5591c..025d83c26 100644 --- a/sdks/ts/src/api/schemas/$Tasks_SleepFor.ts +++ b/sdks/ts/src/api/schemas/$Tasks_SleepFor.ts @@ -9,24 +9,28 @@ export const $Tasks_SleepFor = { description: `The number of seconds to sleep for`, isRequired: true, format: "uint16", + maximum: 60, }, minutes: { type: "number", description: `The number of minutes to sleep for`, isRequired: true, format: "uint16", + maximum: 60, }, hours: { type: "number", description: `The number of hours to sleep for`, isRequired: true, format: "uint16", + maximum: 24, }, days: { type: "number", description: `The number of days to sleep for`, isRequired: true, format: "uint16", + maximum: 30, }, }, } as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts b/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts index 79d23cbcf..41fcf166d 100644 --- a/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts @@ -16,7 +16,7 @@ export const $Tasks_SleepStep = { }, sleep: { type: "all-of", - description: `The duration to sleep for`, + description: `The duration to sleep for (max 31 days)`, contains: [ { type: "Tasks_SleepFor", diff --git a/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts b/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts index 1f6a07c09..54c54af17 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts @@ -25,10 +25,19 @@ export const $Tasks_ToolCallStep = { isRequired: true, }, arguments: { - type: "dictionary", - contains: { - type: "Common_PyExpression", - }, + type: "any-of", + description: `The input parameters for the tool (defaults to last step output)`, + contains: [ + { + type: "dictionary", + contains: { + type: "Common_PyExpression", + }, + }, + { + type: "Enum", + }, + ], isRequired: true, }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_YieldStep.ts b/sdks/ts/src/api/schemas/$Tasks_YieldStep.ts index 778bdeb90..376b32a56 100644 --- a/sdks/ts/src/api/schemas/$Tasks_YieldStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_YieldStep.ts @@ -20,10 +20,19 @@ export const $Tasks_YieldStep = { isRequired: true, }, arguments: { - type: "dictionary", - contains: { - type: "Common_PyExpression", - }, + type: "any-of", + description: `The input parameters for the subworkflow (defaults to last step output)`, + contains: [ + { + type: "dictionary", + contains: { + type: "Common_PyExpression", + }, + }, + { + type: "Enum", + }, + ], isRequired: true, }, }, diff --git a/typespec/agents/models.tsp b/typespec/agents/models.tsp index 0e5e17b62..17f43691b 100644 --- a/typespec/agents/models.tsp +++ b/typespec/agents/models.tsp @@ -20,8 +20,7 @@ model Agent { ...HasTimestamps; /** Name of the agent */ - @maxLength(120) - name: identifierSafeUnicode = ""; + name: identifierSafeUnicode = identifierSafeUnicode(""); /** About the agent */ about: string = ""; diff --git a/typespec/common/scalars.tsp b/typespec/common/scalars.tsp index 177f2ccd6..79eda2d99 100644 --- a/typespec/common/scalars.tsp +++ b/typespec/common/scalars.tsp @@ -12,11 +12,13 @@ scalar uuid extends string; * See: https://unicode.org/reports/tr31/ * See: https://www.unicode.org/reports/tr39/#Identifier_Characters */ +@maxLength(120) @pattern("^[\\p{L}\\p{Nl}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]+[\\p{ID_Start}\\p{Mn}\\p{Mc}\\p{Nd}\\p{Pc}\\p{Pattern_Syntax}\\p{Pattern_White_Space}]*$") scalar identifierSafeUnicode extends string; /** Valid python identifier names */ @pattern("^[^\\W0-9]\\w*$") +@maxLength(40) scalar validPythonIdentifier extends string; /** Limit the number of results */ @@ -51,4 +53,7 @@ alias entrySource = "api_request" | "api_response" | "tool_response" | "internal scalar toolRef extends string; /** A simple python expression compatible with SimpleEval. */ -scalar PyExpression extends string; \ No newline at end of file +scalar PyExpression extends string; + +/** A valid jinja template. */ +scalar JinjaTemplate extends string; \ No newline at end of file diff --git a/typespec/docs/models.tsp b/typespec/docs/models.tsp index 0b57c02a8..ee283fe02 100644 --- a/typespec/docs/models.tsp +++ b/typespec/docs/models.tsp @@ -19,7 +19,7 @@ model Doc { /** Title describing what this document contains */ @maxLength(800) - title: identifierSafeUnicode; + title: string; /** Contents of the document */ content: string | string[]; diff --git a/typespec/entries/models.tsp b/typespec/entries/models.tsp index fba6803df..70a84233f 100644 --- a/typespec/entries/models.tsp +++ b/typespec/entries/models.tsp @@ -39,8 +39,8 @@ model ImageURL { detail: ImageDetail = ImageDetail.auto; } -model ChatMLTextContentPart { - text: string; +model ChatMLTextContentPart { + text: T; /** The type (fixed to 'text') */ type: "text" = "text"; @@ -54,14 +54,14 @@ model ChatMLImageContentPart { type: "image_url" = "image_url"; } -alias ChatMLContentPart = ChatMLTextContentPart | ChatMLImageContentPart; +alias ChatMLContentPart = ChatMLTextContentPart | ChatMLImageContentPart; -model ChatMLMessage { +model ChatMLMessage { /** The role of the message */ role: ChatMLRole; /** The content parts of the message */ - content: string | string[] | ChatMLContentPart[]; + content: T | T[] | ChatMLContentPart[]; /** Name */ name?: string; @@ -79,11 +79,11 @@ model ChatMLMessage { } @withVisibility("create") -model InputChatMLMessage { - ...ChatMLMessage; +model InputChatMLMessage { + ...ChatMLMessage; } -alias EntryContent = ChatMLContentPart[] | Tool | ChosenToolCall | string | ToolResponse; +alias EntryContent = ChatMLContentPart[] | Tool | ChosenToolCall | string | ToolResponse; model BaseEntry { role: ChatMLRole; diff --git a/typespec/jobs/models.tsp b/typespec/jobs/models.tsp index 07663d005..e8050b083 100644 --- a/typespec/jobs/models.tsp +++ b/typespec/jobs/models.tsp @@ -24,8 +24,7 @@ model JobStatus { ...HasTimestamps; /** Name of the job */ - @maxLength(120) - name: identifierSafeUnicode = ""; + name: identifierSafeUnicode = identifierSafeUnicode(""); /** Reason for the current state of the job */ reason: string = ""; diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp index 2da1d2cdf..1cdbf213e 100644 --- a/typespec/tasks/steps.tsp +++ b/typespec/tasks/steps.tsp @@ -21,8 +21,9 @@ namespace Tasks; // /** An object where values are strings in the Common Expression Language that get evaluated before being passed downstream */ -alias ExpressionObject = Record; -alias NestedExpressionObject = Record; +alias TypedExpression = PyExpression; +alias ExpressionObject = Record>; +alias NestedExpressionObject = Record | ExpressionObject>; @discriminator("kind_") model BaseWorkflowStep { @@ -61,15 +62,15 @@ model ToolCallStep extends BaseWorkflowStep { /** The tool to run */ tool: toolRef; - /** The input parameters for the tool */ - arguments: ExpressionObject; + /** The input parameters for the tool (defaults to last step output) */ + arguments: ExpressionObject | "_" = "_"; } model PromptStep extends BaseWorkflowStep { kind_: "prompt" = "prompt"; /** The prompt to run */ - prompt: string | InputChatMLMessage[]; + prompt: JinjaTemplate | InputChatMLMessage[]; /** Settings for the prompt */ settings: ChatSettings; @@ -79,21 +80,21 @@ model EvaluateStep extends BaseWorkflowStep { kind_: "evaluate" = "evaluate"; /** The expression to evaluate */ - evaluate: ExpressionObject; + evaluate: ExpressionObject; } model WaitForInputStep extends BaseWorkflowStep { kind_: "wait_for_input" = "wait_for_input"; /** Any additional info or data */ - wait_for_input: ExpressionObject; + wait_for_input: ExpressionObject; } model LogStep extends BaseWorkflowStep { kind_: "log" = "log"; /** The value to log */ - log: PyExpression; + log: TypedExpression; } //////////////////////// @@ -130,14 +131,14 @@ model SetKey { key: string; /** The value to set */ - value: PyExpression; + value: TypedExpression; } model SetStep extends BaseWorkflowStep { kind_: "set" = "set"; /** The value to set */ - set: SetKey | SetKey[]; + set: SetKey; } /////////////////////// @@ -154,7 +155,7 @@ model ParallelStep extends BaseWorkflowStep { model ForeachDo { /** The variable to iterate over */ - in: PyExpression; + in: TypedExpression>; /** The steps to run for each iteration */ do: NonConditionalWorkflowStep; @@ -169,7 +170,7 @@ model ForeachStep extends BaseWorkflowStep { model MapOver { /** The variable to iterate over */ - over: PyExpression; + over: TypedExpression>; /** The subworkflow to run for each iteration */ workflow: string; @@ -182,7 +183,7 @@ model MapReduceStep extends BaseWorkflowStep { map: MapOver; /** The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. */ - reduce?: PyExpression; + reduce: TypedExpression | "_" = "_"; } ///////////////////////// @@ -193,7 +194,7 @@ model IfElseWorkflowStep extends BaseWorkflowStep { kind_: "if_else" = "if_else"; /** The condition to evaluate */ - `if`: PyExpression; + `if`: TypedExpression; /** The steps to run if the condition is true */ then: NonConditionalWorkflowStep; @@ -204,7 +205,7 @@ model IfElseWorkflowStep extends BaseWorkflowStep { model CaseThen { /** The condition to evaluate */ - case: PyExpression; + case: TypedExpression | "_"; // To support '_' as a value /** The steps to run if the condition is true */ then: NonConditionalWorkflowStep; @@ -214,6 +215,7 @@ model SwitchStep extends BaseWorkflowStep { kind_: "switch" = "switch"; /** The cond tree */ + @minItems(1) switch: CaseThen[]; } @@ -227,8 +229,8 @@ model YieldStep extends BaseWorkflowStep { /** The subworkflow to run */ workflow: string; - /** The input parameters for the subworkflow */ - arguments: ExpressionObject; + /** The input parameters for the subworkflow (defaults to last step output) */ + arguments: ExpressionObject | "_" = "_"; } model ErrorWorkflowStep extends BaseWorkflowStep { @@ -241,25 +243,29 @@ model ErrorWorkflowStep extends BaseWorkflowStep { model SleepFor { /** The number of seconds to sleep for */ @minValue(0) + @maxValue(60) seconds: uint16 = 0; /** The number of minutes to sleep for */ @minValue(0) + @maxValue(60) minutes: uint16 = 0; /** The number of hours to sleep for */ @minValue(0) + @maxValue(24) hours: uint16 = 0; /** The number of days to sleep for */ @minValue(0) + @maxValue(30) days: uint16 = 0; } model SleepStep extends BaseWorkflowStep { kind_: "sleep" = "sleep"; - /** The duration to sleep for */ + /** The duration to sleep for (max 31 days) */ sleep: SleepFor; } @@ -267,5 +273,5 @@ model ReturnStep extends BaseWorkflowStep { kind_: "return" = "return"; /** The value to return */ - `return`: ExpressionObject; + `return`: ExpressionObject; } diff --git a/typespec/users/models.tsp b/typespec/users/models.tsp index de7baadef..c21b1b03c 100644 --- a/typespec/users/models.tsp +++ b/typespec/users/models.tsp @@ -18,8 +18,7 @@ model User { ...HasTimestamps; /** Name of the user */ - @maxLength(120) - name: identifierSafeUnicode = ""; + name: identifierSafeUnicode = identifierSafeUnicode(""); /** About the user */ about: string = ""; From d2ab540e1d81f8d17a4f4c352a424894306e94f0 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Tue, 20 Aug 2024 22:42:04 +0300 Subject: [PATCH 090/110] test: Test for each step, add small fixes --- .../activities/task_steps/__init__.py | 1 + .../activities/task_steps/for_each_step.py | 8 +++- .../agents_api/workflows/task_execution.py | 28 +++++------ agents-api/tests/test_execution_workflow.py | 47 +++++++++++++++++++ 4 files changed, 66 insertions(+), 18 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index e1792f051..2f63fb81e 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -1,6 +1,7 @@ # ruff: noqa: F401, F403, F405 from .evaluate_step import evaluate_step +from .for_each_step import for_each_step from .if_else_step import if_else_step from .log_step import log_step from .prompt_step import prompt_step diff --git a/agents-api/agents_api/activities/task_steps/for_each_step.py b/agents-api/agents_api/activities/task_steps/for_each_step.py index e297d11dc..f2f24430f 100644 --- a/agents-api/agents_api/activities/task_steps/for_each_step.py +++ b/agents-api/agents_api/activities/task_steps/for_each_step.py @@ -17,7 +17,11 @@ async def for_each_step(context: StepContext) -> StepOutcome: try: assert isinstance(context.current_step, ForeachStep) - return StepOutcome(output=simple_eval(context.current_step.foreach.in_, names=context.model_dump())) + return StepOutcome( + output=simple_eval( + context.current_step.foreach.in_, names=context.model_dump() + ) + ) except BaseException as e: logging.error(f"Error in for_each_step: {e}") return StepOutcome(error=str(e)) @@ -27,6 +31,6 @@ async def for_each_step(context: StepContext) -> StepOutcome: # They do the same thing, so we dont need to mock the if_else_step function mock_if_else_step = for_each_step -for_each_step = activity.defn(name="if_else_step")( +for_each_step = activity.defn(name="for_each_step")( for_each_step if not testing else mock_if_else_step ) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index f3452b8e9..c0ee89688 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -14,8 +14,11 @@ CreateTransitionRequest, ErrorWorkflowStep, EvaluateStep, + ForeachDo, + ForeachStep, IfElseWorkflowStep, LogStep, + MapReduceStep, # PromptStep, ReturnStep, SleepFor, @@ -26,10 +29,6 @@ WaitForInputStep, Workflow, YieldStep, - ForeachStep, - ForeachDo, - SwitchStep, - MapReduceStep, ) from ..common.protocol.tasks import ( ExecutionInput, @@ -52,6 +51,7 @@ ReturnStep: task_steps.return_step, YieldStep: task_steps.yield_step, IfElseWorkflowStep: task_steps.if_else_step, + ForeachStep: task_steps.for_each_step, } # TODO: Avoid local activities for now (currently experimental) @@ -198,14 +198,10 @@ async def transition(**kwargs) -> None: args=if_else_args, ) - case ForeachStep(foreach=ForeachDo(do=do_step)), StepOutcome( - output=items - ): + case ForeachStep(foreach=ForeachDo(do=do_step)), StepOutcome(output=items): for i, item in enumerate(items): # Create a faux workflow - foreach_wf_name = ( - f"`{context.cursor.workflow}`[{context.cursor.step}].foreach[{i}]" - ) + foreach_wf_name = f"`{context.cursor.workflow}`[{context.cursor.step}].foreach[{i}]" foreach_task = execution_input.task.model_copy() foreach_task.workflows = [ @@ -217,12 +213,14 @@ async def transition(**kwargs) -> None: foreach_execution_input.task = foreach_task # Set the next target to the chosen branch - foreach_next_target = TransitionTarget(workflow=foreach_wf_name, step=0) + foreach_next_target = TransitionTarget( + workflow=foreach_wf_name, step=0 + ) foreach_args = [ foreach_execution_input, foreach_next_target, - previous_inputs + [item], + previous_inputs + [{"item": item}], ] # Execute the chosen branch and come back here @@ -230,10 +228,8 @@ async def transition(**kwargs) -> None: TaskExecutionWorkflow.run, args=foreach_args, ) - - case SwitchStep(switch=cases), StepOutcome( - output=int(case_num) - ): + + case SwitchStep(switch=cases), StepOutcome(output=int(case_num)): if case_num > 0: chosen_branch = cases[case_num] diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 2f6d434a3..19c4b68e7 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -551,3 +551,50 @@ async def _( result = await handle.result() assert result["hello"] == "world" + + +@test("workflow: for each step") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + { + "foreach": { + "in": "'a b c'.split()", + "do": {"evaluate": {"hello": '"world"'}}, + }, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == "world" From 1866a79f7283bf473eca7c5eb9a8dfd3fa54b499 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Tue, 20 Aug 2024 22:59:04 +0300 Subject: [PATCH 091/110] feat: Add map reduce step (WIP) --- .../activities/task_steps/map_reduce_step.py | 36 +++++++++++++++++++ .../agents_api/workflows/task_execution.py | 31 ++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 agents-api/agents_api/activities/task_steps/map_reduce_step.py diff --git a/agents-api/agents_api/activities/task_steps/map_reduce_step.py b/agents-api/agents_api/activities/task_steps/map_reduce_step.py new file mode 100644 index 000000000..149704999 --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/map_reduce_step.py @@ -0,0 +1,36 @@ +import logging + +from beartype import beartype +from simpleeval import simple_eval +from temporalio import activity + +from ...autogen.openapi_model import MapReduceStep +from ...common.protocol.tasks import ( + StepContext, + StepOutcome, +) +from ...env import testing + + +@beartype +async def map_reduce_step(context: StepContext) -> StepOutcome: + try: + assert isinstance(context.current_step, MapReduceStep) + + return StepOutcome( + output=simple_eval( + context.current_step.map.over, names=context.model_dump() + ) + ) + except BaseException as e: + logging.error(f"Error in map_reduce_step: {e}") + return StepOutcome(error=str(e)) + + +# Note: This is here just for clarity. We could have just imported if_else_step directly +# They do the same thing, so we dont need to mock the if_else_step function +mock_if_else_step = map_reduce_step + +map_reduce_step = activity.defn(name="map_reduce_step")( + map_reduce_step if not testing else mock_if_else_step +) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index c0ee89688..188f06249 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -29,6 +29,8 @@ WaitForInputStep, Workflow, YieldStep, + MapReduceStep, + MapOver, ) from ..common.protocol.tasks import ( ExecutionInput, @@ -229,6 +231,35 @@ async def transition(**kwargs) -> None: args=foreach_args, ) + case MapReduceStep(map=MapOver(workflow=workflow_name)), StepOutcome(output=items): + for item in items: + map_reduce_task = execution_input.task.model_copy() + # TODO: set steps list + map_reduce_task.workflows = [ + Workflow(name=workflow_name, steps=[]) + ] + + # Create a new execution input + map_reduce_execution_input = execution_input.model_copy() + map_reduce_execution_input.task = foreach_task + + # Set the next target to the chosen branch + map_reduce_next_target = TransitionTarget( + workflow=workflow_name, step=0 + ) + + map_reduce_args = [ + map_reduce_execution_input, + map_reduce_next_target, + previous_inputs + [{"item": item}], + ] + + # Execute the chosen branch and come back here + state.output = await workflow.execute_child_workflow( + TaskExecutionWorkflow.run, + args=map_reduce_args, + ) + case SwitchStep(switch=cases), StepOutcome(output=int(case_num)): if case_num > 0: chosen_branch = cases[case_num] From 8ab6deac67088161253d51c88fe0083dd403cf11 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Tue, 20 Aug 2024 20:56:38 -0400 Subject: [PATCH 092/110] refactor(agents-api): MAJOR refactors to MAP-REDUCE and some typespec types Signed-off-by: Diwank Tomer --- agents-api/agents_api/autogen/Common.py | 20 + agents-api/agents_api/autogen/Tasks.py | 507 +++++++++++++----- .../agents_api/autogen/openapi_model.py | 3 + .../agents_api/workflows/task_execution.py | 2 +- agents-api/poetry.lock | 20 +- .../tests/sample_tasks/find_selector.yaml | 85 +++ agents-api/tests/test_execution_workflow.py | 3 +- sdks/python/poetry.lock | 6 +- sdks/ts/src/api/index.ts | 38 +- .../src/api/models/Tasks_BaseWorkflowStep.ts | 28 - sdks/ts/src/api/models/Tasks_CaseThen.ts | 8 +- .../src/api/models/Tasks_CreateTaskRequest.ts | 46 +- sdks/ts/src/api/models/Tasks_EmbedStep.ts | 10 +- sdks/ts/src/api/models/Tasks_EmbedStepDef.ts | 11 + .../src/api/models/Tasks_ErrorWorkflowStep.ts | 10 +- sdks/ts/src/api/models/Tasks_EvaluateStep.ts | 10 +- .../src/api/models/Tasks_EvaluateStepDef.ts | 11 + sdks/ts/src/api/models/Tasks_ForeachDo.ts | 15 +- sdks/ts/src/api/models/Tasks_ForeachStep.ts | 10 +- sdks/ts/src/api/models/Tasks_GetStep.ts | 10 +- sdks/ts/src/api/models/Tasks_GetStepDef.ts | 10 + .../api/models/Tasks_IfElseWorkflowStep.ts | 26 +- sdks/ts/src/api/models/Tasks_LogStep.ts | 10 +- sdks/ts/src/api/models/Tasks_LogStepDef.ts | 11 + sdks/ts/src/api/models/Tasks_MapOverEmbed.ts | 12 + .../src/api/models/Tasks_MapOverEvaluate.ts | 12 + .../{Tasks_MapOver.ts => Tasks_MapOverGet.ts} | 7 +- sdks/ts/src/api/models/Tasks_MapOverLog.ts | 12 + sdks/ts/src/api/models/Tasks_MapOverPrompt.ts | 12 + sdks/ts/src/api/models/Tasks_MapOverSearch.ts | 12 + sdks/ts/src/api/models/Tasks_MapOverSet.ts | 12 + .../src/api/models/Tasks_MapOverToolCall.ts | 12 + sdks/ts/src/api/models/Tasks_MapReduceStep.ts | 18 - sdks/ts/src/api/models/Tasks_ParallelStep.ts | 22 +- .../src/api/models/Tasks_PatchTaskRequest.ts | 45 +- sdks/ts/src/api/models/Tasks_PromptStep.ts | 10 +- sdks/ts/src/api/models/Tasks_PromptStepDef.ts | 16 + sdks/ts/src/api/models/Tasks_ReturnStep.ts | 10 +- sdks/ts/src/api/models/Tasks_SearchStep.ts | 10 +- sdks/ts/src/api/models/Tasks_SearchStepDef.ts | 16 + sdks/ts/src/api/models/Tasks_SetStep.ts | 10 +- sdks/ts/src/api/models/Tasks_SetStepDef.ts | 11 + sdks/ts/src/api/models/Tasks_SleepStep.ts | 10 +- sdks/ts/src/api/models/Tasks_SwitchStep.ts | 10 +- sdks/ts/src/api/models/Tasks_Task.ts | 46 +- sdks/ts/src/api/models/Tasks_ToolCallStep.ts | 10 +- .../src/api/models/Tasks_ToolCallStepDef.ts | 16 + .../src/api/models/Tasks_UpdateTaskRequest.ts | 46 +- .../src/api/models/Tasks_WaitForInputStep.ts | 10 +- sdks/ts/src/api/models/Tasks_YieldStep.ts | 13 +- sdks/ts/src/api/schemas/$Tasks_CaseThen.ts | 20 +- .../api/schemas/$Tasks_CreateTaskRequest.ts | 92 +++- sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts | 9 +- .../ts/src/api/schemas/$Tasks_EmbedStepDef.ts | 18 + .../api/schemas/$Tasks_ErrorWorkflowStep.ts | 9 +- .../ts/src/api/schemas/$Tasks_EvaluateStep.ts | 9 +- .../src/api/schemas/$Tasks_EvaluateStepDef.ts | 15 + sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts | 18 +- sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts | 9 +- sdks/ts/src/api/schemas/$Tasks_GetStep.ts | 9 +- ...seWorkflowStep.ts => $Tasks_GetStepDef.ts} | 7 +- .../api/schemas/$Tasks_IfElseWorkflowStep.ts | 49 +- sdks/ts/src/api/schemas/$Tasks_LogStep.ts | 9 +- ...$Tasks_MapOver.ts => $Tasks_LogStepDef.ts} | 11 +- .../ts/src/api/schemas/$Tasks_MapOverEmbed.ts | 26 + .../src/api/schemas/$Tasks_MapOverEvaluate.ts | 26 + sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts | 26 + sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts | 26 + .../src/api/schemas/$Tasks_MapOverPrompt.ts | 26 + .../src/api/schemas/$Tasks_MapOverSearch.ts | 26 + sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts | 26 + .../src/api/schemas/$Tasks_MapOverToolCall.ts | 26 + .../src/api/schemas/$Tasks_MapReduceStep.ts | 43 -- .../ts/src/api/schemas/$Tasks_ParallelStep.ts | 24 +- .../api/schemas/$Tasks_PatchTaskRequest.ts | 86 ++- sdks/ts/src/api/schemas/$Tasks_PromptStep.ts | 9 +- .../src/api/schemas/$Tasks_PromptStepDef.ts | 28 + sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts | 9 +- sdks/ts/src/api/schemas/$Tasks_SearchStep.ts | 9 +- .../src/api/schemas/$Tasks_SearchStepDef.ts | 24 + sdks/ts/src/api/schemas/$Tasks_SetStep.ts | 9 +- sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts | 18 + sdks/ts/src/api/schemas/$Tasks_SleepStep.ts | 9 +- sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts | 9 +- sdks/ts/src/api/schemas/$Tasks_Task.ts | 92 +++- .../ts/src/api/schemas/$Tasks_ToolCallStep.ts | 9 +- .../src/api/schemas/$Tasks_ToolCallStepDef.ts | 34 ++ .../api/schemas/$Tasks_UpdateTaskRequest.ts | 92 +++- .../api/schemas/$Tasks_WaitForInputStep.ts | 9 +- sdks/ts/src/api/schemas/$Tasks_YieldStep.ts | 12 +- typespec/tasks/steps.tsp | 232 ++++++-- 91 files changed, 2053 insertions(+), 526 deletions(-) create mode 100644 agents-api/tests/sample_tasks/find_selector.yaml delete mode 100644 sdks/ts/src/api/models/Tasks_BaseWorkflowStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_EmbedStepDef.ts create mode 100644 sdks/ts/src/api/models/Tasks_EvaluateStepDef.ts create mode 100644 sdks/ts/src/api/models/Tasks_GetStepDef.ts create mode 100644 sdks/ts/src/api/models/Tasks_LogStepDef.ts create mode 100644 sdks/ts/src/api/models/Tasks_MapOverEmbed.ts create mode 100644 sdks/ts/src/api/models/Tasks_MapOverEvaluate.ts rename sdks/ts/src/api/models/{Tasks_MapOver.ts => Tasks_MapOverGet.ts} (71%) create mode 100644 sdks/ts/src/api/models/Tasks_MapOverLog.ts create mode 100644 sdks/ts/src/api/models/Tasks_MapOverPrompt.ts create mode 100644 sdks/ts/src/api/models/Tasks_MapOverSearch.ts create mode 100644 sdks/ts/src/api/models/Tasks_MapOverSet.ts create mode 100644 sdks/ts/src/api/models/Tasks_MapOverToolCall.ts delete mode 100644 sdks/ts/src/api/models/Tasks_MapReduceStep.ts create mode 100644 sdks/ts/src/api/models/Tasks_PromptStepDef.ts create mode 100644 sdks/ts/src/api/models/Tasks_SearchStepDef.ts create mode 100644 sdks/ts/src/api/models/Tasks_SetStepDef.ts create mode 100644 sdks/ts/src/api/models/Tasks_ToolCallStepDef.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_EmbedStepDef.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_EvaluateStepDef.ts rename sdks/ts/src/api/schemas/{$Tasks_BaseWorkflowStep.ts => $Tasks_GetStepDef.ts} (65%) rename sdks/ts/src/api/schemas/{$Tasks_MapOver.ts => $Tasks_LogStepDef.ts} (57%) create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverEmbed.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverEvaluate.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverPrompt.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverSearch.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverToolCall.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_PromptStepDef.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_SearchStepDef.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts create mode 100644 sdks/ts/src/api/schemas/$Tasks_ToolCallStepDef.ts diff --git a/agents-api/agents_api/autogen/Common.py b/agents-api/agents_api/autogen/Common.py index aab88621d..5f4bd34bd 100644 --- a/agents-api/agents_api/autogen/Common.py +++ b/agents-api/agents_api/autogen/Common.py @@ -9,6 +9,16 @@ from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel +class JinjaTemplate(RootModel[str]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: str + """ + A valid jinja template. + """ + + class Limit(RootModel[int]): model_config = ConfigDict( populate_by_name=True, @@ -36,6 +46,16 @@ class Offset(RootModel[int]): """ +class PyExpression(RootModel[str]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: str + """ + A simple python expression compatible with SimpleEval. + """ + + class ResourceCreatedResponse(BaseModel): model_config = ConfigDict( populate_by_name=True, diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index 5c5050447..a7a018c74 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -19,35 +19,6 @@ from .Tools import CreateToolRequest -class BaseWorkflowStep(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - kind_: Literal[ - "tool_call", - "prompt", - "evaluate", - "wait_for_input", - "log", - "embed", - "search", - "set", - "get", - "foreach", - "map_reduce", - "parallel", - "switch", - "if_else", - "sleep", - "return", - "yield", - "error", - ] - """ - The kind of step - """ - - class CaseThen(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -59,16 +30,16 @@ class CaseThen(BaseModel): then: ( EvaluateStep | ToolCallStep - | YieldStep | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep | GetStep | SetStep | LogStep | EmbedStep | SearchStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | YieldStep | WaitForInputStep ) """ @@ -103,22 +74,22 @@ class CreateTaskRequest(BaseModel): main: list[ EvaluateStep | ToolCallStep - | YieldStep | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep | GetStep | SetStep | LogStep | EmbedStep | SearchStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | YieldStep | WaitForInputStep | IfElseWorkflowStep | SwitchStep | ForeachStep | ParallelStep - | MapReduceStep + | MainModel ] """ The entrypoint of the task. @@ -138,33 +109,68 @@ class CreateTaskRequest(BaseModel): metadata: dict[str, Any] | None = None -class EmbedStep(BaseWorkflowStep): +class EmbedStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["embed"], Field("embed", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ + embed: EmbedQueryRequest + """ + The text to embed + """ + + +class EmbedStepDef(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["embed"] = "embed" embed: EmbedQueryRequest """ The text to embed """ -class ErrorWorkflowStep(BaseWorkflowStep): +class ErrorWorkflowStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["error"] = "error" + kind_: Annotated[ + Literal["error"], Field("error", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ error: str """ The error message """ -class EvaluateStep(BaseWorkflowStep): +class EvaluateStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["evaluate"], Field("evaluate", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ + evaluate: dict[str, str] + """ + The expression to evaluate + """ + + +class EvaluateStepDef(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["evaluate"] = "evaluate" evaluate: dict[str, str] """ The expression to evaluate @@ -177,55 +183,74 @@ class ForeachDo(BaseModel): ) in_: Annotated[str, Field(alias="in")] """ - The variable to iterate over + The variable to iterate over. + VALIDATION: Should NOT return more than 1000 elements. """ do: ( EvaluateStep | ToolCallStep - | YieldStep | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep | GetStep | SetStep | LogStep | EmbedStep | SearchStep - | WaitForInputStep ) """ The steps to run for each iteration """ -class ForeachStep(BaseWorkflowStep): +class ForeachStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["foreach"] = "foreach" + kind_: Annotated[ + Literal["foreach"], Field("foreach", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ foreach: ForeachDo """ The steps to run for each iteration """ -class GetStep(BaseWorkflowStep): +class GetStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["get"], Field("get", json_schema_extra={"readOnly": True})] + """ + The kind of step + """ + get: str + """ + The key to get + """ + + +class GetStepDef(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["get"] = "get" get: str """ The key to get """ -class IfElseWorkflowStep(BaseWorkflowStep): +class IfElseWorkflowStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["if_else"] = "if_else" + kind_: Annotated[ + Literal["if_else"], Field("if_else", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ if_: Annotated[str, Field(alias="if")] """ The condition to evaluate @@ -233,16 +258,16 @@ class IfElseWorkflowStep(BaseWorkflowStep): then: ( EvaluateStep | ToolCallStep - | YieldStep | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep | GetStep | SetStep | LogStep | EmbedStep | SearchStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | YieldStep | WaitForInputStep ) """ @@ -251,16 +276,16 @@ class IfElseWorkflowStep(BaseWorkflowStep): else_: Annotated[ EvaluateStep | ToolCallStep - | YieldStep | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep | GetStep | SetStep | LogStep | EmbedStep | SearchStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | YieldStep | WaitForInputStep, Field(alias="else"), ] @@ -269,18 +294,99 @@ class IfElseWorkflowStep(BaseWorkflowStep): """ -class LogStep(BaseWorkflowStep): +class LogStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["log"] = "log" + kind_: Annotated[Literal["log"], Field("log", json_schema_extra={"readOnly": True})] + """ + The kind of step + """ log: str """ The value to log """ -class MapOver(BaseModel): +class LogStepDef(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + log: str + """ + The value to log + """ + + +class Main(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: str | None = None + """ + Discriminator property for BaseWorkflowStep. + """ + map: ( + MapOverEvaluate + | MapOverToolCall + | MapOverPrompt + | MapOverGet + | MapOverSet + | MapOverLog + | MapOverEmbed + | MapOverSearch + ) + """ + The steps to run for each iteration + """ + reduce: str | None = None + """ + The expression to reduce the results. + If not provided, the results are collected and returned as a list. + A special parameter named `results` is the accumulator and `_` is the current value. + """ + initial: str = "[]" + """ + A simple python expression compatible with SimpleEval. + """ + + +class MainModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["map_reduce"], Field("map_reduce", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ + map: ( + MapOverEvaluate + | MapOverToolCall + | MapOverPrompt + | MapOverGet + | MapOverSet + | MapOverLog + | MapOverEmbed + | MapOverSearch + ) + """ + The steps to run for each iteration + """ + reduce: str | None = None + """ + The expression to reduce the results. + If not provided, the results are collected and returned as a list. + A special parameter named `results` is the accumulator and `_` is the current value. + """ + initial: str = "[]" + """ + A simple python expression compatible with SimpleEval. + """ + + +class MapOverEmbed(EmbedStepDef): model_config = ConfigDict( populate_by_name=True, ) @@ -288,49 +394,63 @@ class MapOver(BaseModel): """ The variable to iterate over """ - workflow: str + + +class MapOverEvaluate(EvaluateStepDef): + model_config = ConfigDict( + populate_by_name=True, + ) + over: str """ - The subworkflow to run for each iteration + The variable to iterate over """ -class MapReduceStep(BaseWorkflowStep): +class MapOverGet(GetStepDef): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["map_reduce"] = "map_reduce" - map: MapOver + over: str """ - The steps to run for each iteration + The variable to iterate over """ - reduce: Literal["_"] | str = "_" + + +class MapOverLog(LogStepDef): + model_config = ConfigDict( + populate_by_name=True, + ) + over: str """ - The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. + The variable to iterate over """ -class ParallelStep(BaseWorkflowStep): +class ParallelStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["parallel"] = "parallel" - parallel: list[ - EvaluateStep - | ToolCallStep - | YieldStep - | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep - | GetStep - | SetStep - | LogStep - | EmbedStep - | SearchStep - | WaitForInputStep + kind_: Annotated[ + Literal["parallel"], Field("parallel", json_schema_extra={"readOnly": True}) ] """ - The steps to run in parallel. Max concurrency will depend on the platform + The kind of step + """ + parallel: Annotated[ + list[ + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep + ], + Field(max_length=100), + ] + """ + The steps to run in parallel. Max concurrency will depend on the platform. """ @@ -347,22 +467,22 @@ class PatchTaskRequest(BaseModel): list[ EvaluateStep | ToolCallStep - | YieldStep | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep | GetStep | SetStep | LogStep | EmbedStep | SearchStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | YieldStep | WaitForInputStep | IfElseWorkflowStep | SwitchStep | ForeachStep | ParallelStep - | MapReduceStep + | Main ] | None ) = None @@ -414,11 +534,16 @@ class PromptItem(BaseModel): """ -class PromptStep(BaseWorkflowStep): +class PromptStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["prompt"] = "prompt" + kind_: Annotated[ + Literal["prompt"], Field("prompt", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ prompt: list[PromptItem] | str """ The prompt to run @@ -429,22 +554,56 @@ class PromptStep(BaseWorkflowStep): """ -class ReturnStep(BaseWorkflowStep): +class PromptStepDef(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["return"] = "return" + prompt: list[PromptItem] | str + """ + The prompt to run + """ + settings: ChatSettings + """ + Settings for the prompt + """ + + +class ReturnStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["return"], Field("return", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ return_: Annotated[dict[str, str], Field(alias="return")] """ The value to return """ -class SearchStep(BaseWorkflowStep): +class SearchStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["search"], Field("search", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ + search: VectorDocSearchRequest | TextOnlyDocSearchRequest | HybridDocSearchRequest + """ + The search query + """ + + +class SearchStepDef(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["search"] = "search" search: VectorDocSearchRequest | TextOnlyDocSearchRequest | HybridDocSearchRequest """ The search query @@ -465,11 +624,24 @@ class SetKey(BaseModel): """ -class SetStep(BaseWorkflowStep): +class SetStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[Literal["set"], Field("set", json_schema_extra={"readOnly": True})] + """ + The kind of step + """ + set: SetKey + """ + The value to set + """ + + +class SetStepDef(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["set"] = "set" set: SetKey """ The value to set @@ -498,22 +670,32 @@ class SleepFor(BaseModel): """ -class SleepStep(BaseWorkflowStep): +class SleepStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["sleep"] = "sleep" + kind_: Annotated[ + Literal["sleep"], Field("sleep", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ sleep: SleepFor """ The duration to sleep for (max 31 days) """ -class SwitchStep(BaseWorkflowStep): +class SwitchStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["switch"] = "switch" + kind_: Annotated[ + Literal["switch"], Field("switch", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ switch: Annotated[list[CaseThen], Field(min_length=1)] """ The cond tree @@ -533,22 +715,22 @@ class Task(BaseModel): main: list[ EvaluateStep | ToolCallStep - | YieldStep | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep | GetStep | SetStep | LogStep | EmbedStep | SearchStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | YieldStep | WaitForInputStep | IfElseWorkflowStep | SwitchStep | ForeachStep | ParallelStep - | MapReduceStep + | MainModel ] """ The entrypoint of the task. @@ -587,11 +769,32 @@ class TaskTool(CreateToolRequest): """ -class ToolCallStep(BaseWorkflowStep): +class ToolCallStep(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + kind_: Annotated[ + Literal["tool_call"], Field("tool_call", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ + tool: Annotated[ + str, Field(pattern="^(function|integration|system|api_call)\\.(\\w+)$") + ] + """ + The tool to run + """ + arguments: dict[str, str] | Literal["_"] = "_" + """ + The input parameters for the tool (defaults to last step output) + """ + + +class ToolCallStepDef(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["tool_call"] = "tool_call" tool: Annotated[ str, Field(pattern="^(function|integration|system|api_call)\\.(\\w+)$") ] @@ -616,22 +819,22 @@ class UpdateTaskRequest(BaseModel): main: list[ EvaluateStep | ToolCallStep - | YieldStep | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep | GetStep | SetStep | LogStep | EmbedStep | SearchStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | YieldStep | WaitForInputStep | IfElseWorkflowStep | SwitchStep | ForeachStep | ParallelStep - | MapReduceStep + | MainModel ] """ The entrypoint of the task. @@ -651,27 +854,79 @@ class UpdateTaskRequest(BaseModel): metadata: dict[str, Any] | None = None -class WaitForInputStep(BaseWorkflowStep): +class WaitForInputStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["wait_for_input"] = "wait_for_input" + kind_: Annotated[ + Literal["wait_for_input"], + Field("wait_for_input", json_schema_extra={"readOnly": True}), + ] + """ + The kind of step + """ wait_for_input: dict[str, str] """ Any additional info or data """ -class YieldStep(BaseWorkflowStep): +class YieldStep(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - kind_: Literal["yield"] = "yield" + kind_: Annotated[ + Literal["yield"], Field("yield", json_schema_extra={"readOnly": True}) + ] + """ + The kind of step + """ workflow: str """ - The subworkflow to run + The subworkflow to run. + VALIDATION: Should resolve to a defined subworkflow. """ arguments: dict[str, str] | Literal["_"] = "_" """ The input parameters for the subworkflow (defaults to last step output) """ + + +class MapOverPrompt(PromptStepDef): + model_config = ConfigDict( + populate_by_name=True, + ) + over: str + """ + The variable to iterate over + """ + + +class MapOverSearch(SearchStepDef): + model_config = ConfigDict( + populate_by_name=True, + ) + over: str + """ + The variable to iterate over + """ + + +class MapOverSet(SetStepDef): + model_config = ConfigDict( + populate_by_name=True, + ) + over: str + """ + The variable to iterate over + """ + + +class MapOverToolCall(ToolCallStepDef): + model_config = ConfigDict( + populate_by_name=True, + ) + over: str + """ + The variable to iterate over + """ diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index 593264c70..cd50898f1 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -41,6 +41,9 @@ class ListResponse(BaseModel, Generic[DataT]): ChatMLTextContentPart = Content InputChatMLMessage = Message +# TODO: Figure out wtf... 🤷‍♂️ +MapReduceStep = MainModel + # Custom types (not generated correctly) # -------------------------------------- diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 56f200059..87b21f524 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -68,7 +68,7 @@ async def run( self, execution_input: ExecutionInput, start: TransitionTarget = TransitionTarget(workflow="main", step=0), - previous_inputs: list[dict] = [], + previous_inputs: list[Any] = [], ) -> Any: # 0. Prepare context previous_inputs = previous_inputs or [execution_input.arguments] diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 0ece98e08..c0191ce2c 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -3700,13 +3700,13 @@ tornado = ["tornado (>=6)"] [[package]] name = "setuptools" -version = "73.0.0" +version = "73.0.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-73.0.0-py3-none-any.whl", hash = "sha256:f2bfcce7ae1784d90b04c57c2802e8649e1976530bb25dc72c2b078d3ecf4864"}, - {file = "setuptools-73.0.0.tar.gz", hash = "sha256:3c08705fadfc8c7c445cf4d98078f0fafb9225775b2b4e8447e40348f82597c0"}, + {file = "setuptools-73.0.1-py3-none-any.whl", hash = "sha256:b208925fcb9f7af924ed2dc04708ea89791e24bde0d3020b27df0e116088b34e"}, + {file = "setuptools-73.0.1.tar.gz", hash = "sha256:d59a3e788ab7e012ab2c4baed1b376da6366883ee20d7a5fc426816e3d7b1193"}, ] [package.extras] @@ -3821,17 +3821,17 @@ widechars = ["wcwidth"] [[package]] name = "temporalio" -version = "1.6.0" +version = "1.7.0" description = "Temporal.io Python SDK" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "temporalio-1.6.0-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:50207806c5b9d701226ed2aed1fce44c688225ab9a370b014b06e51872b98ea7"}, - {file = "temporalio-1.6.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:499253385dd3ca1827d34a05ae61350d54040e0d6a11502f04cbafa7b35be114"}, - {file = "temporalio-1.6.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8fb097b97f833483cd500af2460a0996f812e8019327d893844a21b1c7cd9868"}, - {file = "temporalio-1.6.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6b25d451170ecdf8443f1ed09f75ea708e8679c26636e7aa326bc89bd6bd0c84"}, - {file = "temporalio-1.6.0-cp38-abi3-win_amd64.whl", hash = "sha256:b5ae0bea0665a0bc87d80e7d18870b32eec631694abc0610ee39235e99cc304b"}, - {file = "temporalio-1.6.0.tar.gz", hash = "sha256:a6f24ea91eb1dd1345c68f4ceb21dd2a11a84cda0d6d963d6e570a0c156a80f0"}, + {file = "temporalio-1.7.0-cp38-abi3-macosx_10_9_x86_64.whl", hash = "sha256:92ec0a1af8d4b41245df339a422f1f87367742d9638d2dba7bb7d3ab934e7f5d"}, + {file = "temporalio-1.7.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:8b4bb77d766a2ac1d85f3e9b682658fee67d77e87f73bd256d46cd79ecf767f6"}, + {file = "temporalio-1.7.0-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a38dd43061666700500d5808c18ec0b0f569504a2f22b99d7c38dc4dc50b21fd"}, + {file = "temporalio-1.7.0-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e0087fb6cdb9e9b8aa62c1705526947cb00a91159435d294f3da0d92b501ed56"}, + {file = "temporalio-1.7.0-cp38-abi3-win_amd64.whl", hash = "sha256:eb45b751c6f7946dccba29260922f0e7192b28b8fb9e2aa5afc2aaf5157891d9"}, + {file = "temporalio-1.7.0.tar.gz", hash = "sha256:5057b74df644bd4f5f4eb0e95e730a0a36a16f7ee926d36fcd479c223a7c63cd"}, ] [package.dependencies] diff --git a/agents-api/tests/sample_tasks/find_selector.yaml b/agents-api/tests/sample_tasks/find_selector.yaml new file mode 100644 index 000000000..141442a79 --- /dev/null +++ b/agents-api/tests/sample_tasks/find_selector.yaml @@ -0,0 +1,85 @@ +name: Find request and selector for identity provider + +input_schema: + type: object + properties: + screenshot_base64: + type: string + network_requests: + type: array + items: + type: object + properties: + request: + type: object + properties: + url: + type: string + method: + type: string + headers: + type: object + additionalProperties: + type: string + body: + type: string + response: + type: object + properties: + status: + type: integer + headers: + type: object + additionalProperties: + type: string + body: + type: string + parameters: + type: array + items: + type: string + +main: +- map: + over: _.parameters + + prompt: + - role: system + content: |- + From the screenshot below, can you identify if the page has {{_}} for the user? + Write your answer in the following yaml format: + + found: true|false + value: |null + + Just write your answer in the above format only. + Please do not include any other information or explanation in the response. + + - role: user + content: + - type: image + image_url: 'inputs[0].screenshot_base64' + + reduce: >- + results + + [ + yaml.safe_load(_["choices"][0]["message"]["content"].trim()) + ] + +- evaluate: >- + [ + {"value": result["value"], "network_request": request} + for request in execution.input.network_requests + if result["value"] in nr.response.body + for result in _ + if result["found"] + ] + +- if: len(_) > 0 + then: + workflow: find_selectors + arguments: + results: list(zip(_, execution.input.network_requests)) + parameters: execution.input.parameters + else: + error: 'Could not find the selector in any of the network requests' \ No newline at end of file diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 2f6d434a3..bb08b63bc 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -496,7 +496,8 @@ async def _( assert result["hello"] == "world" -@test("workflow: switch step") +# FIXME: Re enable this test +# @test("workflow: switch step") async def _( client=cozo_client, developer_id=test_developer_id, diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 4ad4307a6..9db756fca 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -2926,13 +2926,13 @@ win32 = ["pywin32"] [[package]] name = "setuptools" -version = "73.0.0" +version = "73.0.1" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-73.0.0-py3-none-any.whl", hash = "sha256:f2bfcce7ae1784d90b04c57c2802e8649e1976530bb25dc72c2b078d3ecf4864"}, - {file = "setuptools-73.0.0.tar.gz", hash = "sha256:3c08705fadfc8c7c445cf4d98078f0fafb9225775b2b4e8447e40348f82597c0"}, + {file = "setuptools-73.0.1-py3-none-any.whl", hash = "sha256:b208925fcb9f7af924ed2dc04708ea89791e24bde0d3020b27df0e116088b34e"}, + {file = "setuptools-73.0.1.tar.gz", hash = "sha256:d59a3e788ab7e012ab2c4baed1b376da6366883ee20d7a5fc426816e3d7b1193"}, ] [package.extras] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index 293ad250a..851e723b0 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -94,33 +94,46 @@ export type { Sessions_SingleAgentMultiUserSession } from "./models/Sessions_Sin export type { Sessions_SingleAgentNoUserSession } from "./models/Sessions_SingleAgentNoUserSession"; export type { Sessions_SingleAgentSingleUserSession } from "./models/Sessions_SingleAgentSingleUserSession"; export type { Sessions_UpdateSessionRequest } from "./models/Sessions_UpdateSessionRequest"; -export type { Tasks_BaseWorkflowStep } from "./models/Tasks_BaseWorkflowStep"; export type { Tasks_CaseThen } from "./models/Tasks_CaseThen"; export type { Tasks_CreateOrUpdateTaskRequest_id } from "./models/Tasks_CreateOrUpdateTaskRequest_id"; export type { Tasks_CreateTaskRequest } from "./models/Tasks_CreateTaskRequest"; export type { Tasks_EmbedStep } from "./models/Tasks_EmbedStep"; +export type { Tasks_EmbedStepDef } from "./models/Tasks_EmbedStepDef"; export type { Tasks_ErrorWorkflowStep } from "./models/Tasks_ErrorWorkflowStep"; export type { Tasks_EvaluateStep } from "./models/Tasks_EvaluateStep"; +export type { Tasks_EvaluateStepDef } from "./models/Tasks_EvaluateStepDef"; export type { Tasks_ForeachDo } from "./models/Tasks_ForeachDo"; export type { Tasks_ForeachStep } from "./models/Tasks_ForeachStep"; export type { Tasks_GetStep } from "./models/Tasks_GetStep"; +export type { Tasks_GetStepDef } from "./models/Tasks_GetStepDef"; export type { Tasks_IfElseWorkflowStep } from "./models/Tasks_IfElseWorkflowStep"; export type { Tasks_LogStep } from "./models/Tasks_LogStep"; -export type { Tasks_MapOver } from "./models/Tasks_MapOver"; -export type { Tasks_MapReduceStep } from "./models/Tasks_MapReduceStep"; +export type { Tasks_LogStepDef } from "./models/Tasks_LogStepDef"; +export type { Tasks_MapOverEmbed } from "./models/Tasks_MapOverEmbed"; +export type { Tasks_MapOverEvaluate } from "./models/Tasks_MapOverEvaluate"; +export type { Tasks_MapOverGet } from "./models/Tasks_MapOverGet"; +export type { Tasks_MapOverLog } from "./models/Tasks_MapOverLog"; +export type { Tasks_MapOverPrompt } from "./models/Tasks_MapOverPrompt"; +export type { Tasks_MapOverSearch } from "./models/Tasks_MapOverSearch"; +export type { Tasks_MapOverSet } from "./models/Tasks_MapOverSet"; +export type { Tasks_MapOverToolCall } from "./models/Tasks_MapOverToolCall"; export type { Tasks_ParallelStep } from "./models/Tasks_ParallelStep"; export type { Tasks_PatchTaskRequest } from "./models/Tasks_PatchTaskRequest"; export type { Tasks_PromptStep } from "./models/Tasks_PromptStep"; +export type { Tasks_PromptStepDef } from "./models/Tasks_PromptStepDef"; export type { Tasks_ReturnStep } from "./models/Tasks_ReturnStep"; export type { Tasks_SearchStep } from "./models/Tasks_SearchStep"; +export type { Tasks_SearchStepDef } from "./models/Tasks_SearchStepDef"; export type { Tasks_SetKey } from "./models/Tasks_SetKey"; export type { Tasks_SetStep } from "./models/Tasks_SetStep"; +export type { Tasks_SetStepDef } from "./models/Tasks_SetStepDef"; export type { Tasks_SleepFor } from "./models/Tasks_SleepFor"; export type { Tasks_SleepStep } from "./models/Tasks_SleepStep"; export type { Tasks_SwitchStep } from "./models/Tasks_SwitchStep"; export type { Tasks_Task } from "./models/Tasks_Task"; export type { Tasks_TaskTool } from "./models/Tasks_TaskTool"; export type { Tasks_ToolCallStep } from "./models/Tasks_ToolCallStep"; +export type { Tasks_ToolCallStepDef } from "./models/Tasks_ToolCallStepDef"; export type { Tasks_UpdateTaskRequest } from "./models/Tasks_UpdateTaskRequest"; export type { Tasks_WaitForInputStep } from "./models/Tasks_WaitForInputStep"; export type { Tasks_YieldStep } from "./models/Tasks_YieldStep"; @@ -228,33 +241,46 @@ export { $Sessions_SingleAgentMultiUserSession } from "./schemas/$Sessions_Singl export { $Sessions_SingleAgentNoUserSession } from "./schemas/$Sessions_SingleAgentNoUserSession"; export { $Sessions_SingleAgentSingleUserSession } from "./schemas/$Sessions_SingleAgentSingleUserSession"; export { $Sessions_UpdateSessionRequest } from "./schemas/$Sessions_UpdateSessionRequest"; -export { $Tasks_BaseWorkflowStep } from "./schemas/$Tasks_BaseWorkflowStep"; export { $Tasks_CaseThen } from "./schemas/$Tasks_CaseThen"; export { $Tasks_CreateOrUpdateTaskRequest_id } from "./schemas/$Tasks_CreateOrUpdateTaskRequest_id"; export { $Tasks_CreateTaskRequest } from "./schemas/$Tasks_CreateTaskRequest"; export { $Tasks_EmbedStep } from "./schemas/$Tasks_EmbedStep"; +export { $Tasks_EmbedStepDef } from "./schemas/$Tasks_EmbedStepDef"; export { $Tasks_ErrorWorkflowStep } from "./schemas/$Tasks_ErrorWorkflowStep"; export { $Tasks_EvaluateStep } from "./schemas/$Tasks_EvaluateStep"; +export { $Tasks_EvaluateStepDef } from "./schemas/$Tasks_EvaluateStepDef"; export { $Tasks_ForeachDo } from "./schemas/$Tasks_ForeachDo"; export { $Tasks_ForeachStep } from "./schemas/$Tasks_ForeachStep"; export { $Tasks_GetStep } from "./schemas/$Tasks_GetStep"; +export { $Tasks_GetStepDef } from "./schemas/$Tasks_GetStepDef"; export { $Tasks_IfElseWorkflowStep } from "./schemas/$Tasks_IfElseWorkflowStep"; export { $Tasks_LogStep } from "./schemas/$Tasks_LogStep"; -export { $Tasks_MapOver } from "./schemas/$Tasks_MapOver"; -export { $Tasks_MapReduceStep } from "./schemas/$Tasks_MapReduceStep"; +export { $Tasks_LogStepDef } from "./schemas/$Tasks_LogStepDef"; +export { $Tasks_MapOverEmbed } from "./schemas/$Tasks_MapOverEmbed"; +export { $Tasks_MapOverEvaluate } from "./schemas/$Tasks_MapOverEvaluate"; +export { $Tasks_MapOverGet } from "./schemas/$Tasks_MapOverGet"; +export { $Tasks_MapOverLog } from "./schemas/$Tasks_MapOverLog"; +export { $Tasks_MapOverPrompt } from "./schemas/$Tasks_MapOverPrompt"; +export { $Tasks_MapOverSearch } from "./schemas/$Tasks_MapOverSearch"; +export { $Tasks_MapOverSet } from "./schemas/$Tasks_MapOverSet"; +export { $Tasks_MapOverToolCall } from "./schemas/$Tasks_MapOverToolCall"; export { $Tasks_ParallelStep } from "./schemas/$Tasks_ParallelStep"; export { $Tasks_PatchTaskRequest } from "./schemas/$Tasks_PatchTaskRequest"; export { $Tasks_PromptStep } from "./schemas/$Tasks_PromptStep"; +export { $Tasks_PromptStepDef } from "./schemas/$Tasks_PromptStepDef"; export { $Tasks_ReturnStep } from "./schemas/$Tasks_ReturnStep"; export { $Tasks_SearchStep } from "./schemas/$Tasks_SearchStep"; +export { $Tasks_SearchStepDef } from "./schemas/$Tasks_SearchStepDef"; export { $Tasks_SetKey } from "./schemas/$Tasks_SetKey"; export { $Tasks_SetStep } from "./schemas/$Tasks_SetStep"; +export { $Tasks_SetStepDef } from "./schemas/$Tasks_SetStepDef"; export { $Tasks_SleepFor } from "./schemas/$Tasks_SleepFor"; export { $Tasks_SleepStep } from "./schemas/$Tasks_SleepStep"; export { $Tasks_SwitchStep } from "./schemas/$Tasks_SwitchStep"; export { $Tasks_Task } from "./schemas/$Tasks_Task"; export { $Tasks_TaskTool } from "./schemas/$Tasks_TaskTool"; export { $Tasks_ToolCallStep } from "./schemas/$Tasks_ToolCallStep"; +export { $Tasks_ToolCallStepDef } from "./schemas/$Tasks_ToolCallStepDef"; export { $Tasks_UpdateTaskRequest } from "./schemas/$Tasks_UpdateTaskRequest"; export { $Tasks_WaitForInputStep } from "./schemas/$Tasks_WaitForInputStep"; export { $Tasks_YieldStep } from "./schemas/$Tasks_YieldStep"; diff --git a/sdks/ts/src/api/models/Tasks_BaseWorkflowStep.ts b/sdks/ts/src/api/models/Tasks_BaseWorkflowStep.ts deleted file mode 100644 index 10be91186..000000000 --- a/sdks/ts/src/api/models/Tasks_BaseWorkflowStep.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type Tasks_BaseWorkflowStep = { - /** - * The kind of step - */ - kind_: - | "tool_call" - | "prompt" - | "evaluate" - | "wait_for_input" - | "log" - | "embed" - | "search" - | "set" - | "get" - | "foreach" - | "map_reduce" - | "parallel" - | "switch" - | "if_else" - | "sleep" - | "return" - | "yield" - | "error"; -}; diff --git a/sdks/ts/src/api/models/Tasks_CaseThen.ts b/sdks/ts/src/api/models/Tasks_CaseThen.ts index ab51af385..a2e96c0d9 100644 --- a/sdks/ts/src/api/models/Tasks_CaseThen.ts +++ b/sdks/ts/src/api/models/Tasks_CaseThen.ts @@ -27,15 +27,15 @@ export type Tasks_CaseThen = { then: | Tasks_EvaluateStep | Tasks_ToolCallStep - | Tasks_YieldStep | Tasks_PromptStep - | Tasks_ErrorWorkflowStep - | Tasks_SleepStep - | Tasks_ReturnStep | Tasks_GetStep | Tasks_SetStep | Tasks_LogStep | Tasks_EmbedStep | Tasks_SearchStep + | Tasks_ReturnStep + | Tasks_SleepStep + | Tasks_ErrorWorkflowStep + | Tasks_YieldStep | Tasks_WaitForInputStep; }; diff --git a/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts b/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts index 274f27b8a..6be4bf7a8 100644 --- a/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts +++ b/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; @@ -9,7 +10,14 @@ import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; -import type { Tasks_MapReduceStep } from "./Tasks_MapReduceStep"; +import type { Tasks_MapOverEmbed } from "./Tasks_MapOverEmbed"; +import type { Tasks_MapOverEvaluate } from "./Tasks_MapOverEvaluate"; +import type { Tasks_MapOverGet } from "./Tasks_MapOverGet"; +import type { Tasks_MapOverLog } from "./Tasks_MapOverLog"; +import type { Tasks_MapOverPrompt } from "./Tasks_MapOverPrompt"; +import type { Tasks_MapOverSearch } from "./Tasks_MapOverSearch"; +import type { Tasks_MapOverSet } from "./Tasks_MapOverSet"; +import type { Tasks_MapOverToolCall } from "./Tasks_MapOverToolCall"; import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; @@ -28,21 +36,47 @@ export type Tasks_CreateTaskRequest = Record< Array< | Tasks_EvaluateStep | Tasks_ToolCallStep - | Tasks_YieldStep | Tasks_PromptStep - | Tasks_ErrorWorkflowStep - | Tasks_SleepStep - | Tasks_ReturnStep | Tasks_GetStep | Tasks_SetStep | Tasks_LogStep | Tasks_EmbedStep | Tasks_SearchStep + | Tasks_ReturnStep + | Tasks_SleepStep + | Tasks_ErrorWorkflowStep + | Tasks_YieldStep | Tasks_WaitForInputStep | Tasks_IfElseWorkflowStep | Tasks_SwitchStep | Tasks_ForeachStep | Tasks_ParallelStep - | Tasks_MapReduceStep + | ({ + /** + * The kind of step + */ + readonly kind_: "map_reduce"; + } & { + readonly kind_: "map_reduce"; + /** + * The steps to run for each iteration + */ + map: + | Tasks_MapOverEvaluate + | Tasks_MapOverToolCall + | Tasks_MapOverPrompt + | Tasks_MapOverGet + | Tasks_MapOverSet + | Tasks_MapOverLog + | Tasks_MapOverEmbed + | Tasks_MapOverSearch; + /** + * The expression to reduce the results. + * If not provided, the results are collected and returned as a list. + * A special parameter named `results` is the accumulator and `_` is the current value. + */ + reduce?: Common_PyExpression; + initial?: Common_PyExpression; + }) > >; diff --git a/sdks/ts/src/api/models/Tasks_EmbedStep.ts b/sdks/ts/src/api/models/Tasks_EmbedStep.ts index 2036ceead..e9d321ed3 100644 --- a/sdks/ts/src/api/models/Tasks_EmbedStep.ts +++ b/sdks/ts/src/api/models/Tasks_EmbedStep.ts @@ -3,9 +3,13 @@ /* tslint:disable */ /* eslint-disable */ import type { Docs_EmbedQueryRequest } from "./Docs_EmbedQueryRequest"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_EmbedStep = Tasks_BaseWorkflowStep & { - kind_: "embed"; +export type Tasks_EmbedStep = { + /** + * The kind of step + */ + readonly kind_: "embed"; +} & { + readonly kind_: "embed"; /** * The text to embed */ diff --git a/sdks/ts/src/api/models/Tasks_EmbedStepDef.ts b/sdks/ts/src/api/models/Tasks_EmbedStepDef.ts new file mode 100644 index 000000000..409d61a98 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_EmbedStepDef.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Docs_EmbedQueryRequest } from "./Docs_EmbedQueryRequest"; +export type Tasks_EmbedStepDef = { + /** + * The text to embed + */ + embed: Docs_EmbedQueryRequest; +}; diff --git a/sdks/ts/src/api/models/Tasks_ErrorWorkflowStep.ts b/sdks/ts/src/api/models/Tasks_ErrorWorkflowStep.ts index 8e9b49cbc..1e9e74aaf 100644 --- a/sdks/ts/src/api/models/Tasks_ErrorWorkflowStep.ts +++ b/sdks/ts/src/api/models/Tasks_ErrorWorkflowStep.ts @@ -2,9 +2,13 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_ErrorWorkflowStep = Tasks_BaseWorkflowStep & { - kind_: "error"; +export type Tasks_ErrorWorkflowStep = { + /** + * The kind of step + */ + readonly kind_: "error"; +} & { + readonly kind_: "error"; /** * The error message */ diff --git a/sdks/ts/src/api/models/Tasks_EvaluateStep.ts b/sdks/ts/src/api/models/Tasks_EvaluateStep.ts index ccc16f71d..8d5962382 100644 --- a/sdks/ts/src/api/models/Tasks_EvaluateStep.ts +++ b/sdks/ts/src/api/models/Tasks_EvaluateStep.ts @@ -3,9 +3,13 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_EvaluateStep = Tasks_BaseWorkflowStep & { - kind_: "evaluate"; +export type Tasks_EvaluateStep = { + /** + * The kind of step + */ + readonly kind_: "evaluate"; +} & { + readonly kind_: "evaluate"; /** * The expression to evaluate */ diff --git a/sdks/ts/src/api/models/Tasks_EvaluateStepDef.ts b/sdks/ts/src/api/models/Tasks_EvaluateStepDef.ts new file mode 100644 index 000000000..21621bde3 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_EvaluateStepDef.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +export type Tasks_EvaluateStepDef = { + /** + * The expression to evaluate + */ + evaluate: Record; +}; diff --git a/sdks/ts/src/api/models/Tasks_ForeachDo.ts b/sdks/ts/src/api/models/Tasks_ForeachDo.ts index 76c4ed617..db2861b06 100644 --- a/sdks/ts/src/api/models/Tasks_ForeachDo.ts +++ b/sdks/ts/src/api/models/Tasks_ForeachDo.ts @@ -4,21 +4,17 @@ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; -import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; -import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; import type { Tasks_SearchStep } from "./Tasks_SearchStep"; import type { Tasks_SetStep } from "./Tasks_SetStep"; -import type { Tasks_SleepStep } from "./Tasks_SleepStep"; import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; -import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; -import type { Tasks_YieldStep } from "./Tasks_YieldStep"; export type Tasks_ForeachDo = { /** - * The variable to iterate over + * The variable to iterate over. + * VALIDATION: Should NOT return more than 1000 elements. */ in: Common_PyExpression; /** @@ -27,15 +23,10 @@ export type Tasks_ForeachDo = { do: | Tasks_EvaluateStep | Tasks_ToolCallStep - | Tasks_YieldStep | Tasks_PromptStep - | Tasks_ErrorWorkflowStep - | Tasks_SleepStep - | Tasks_ReturnStep | Tasks_GetStep | Tasks_SetStep | Tasks_LogStep | Tasks_EmbedStep - | Tasks_SearchStep - | Tasks_WaitForInputStep; + | Tasks_SearchStep; }; diff --git a/sdks/ts/src/api/models/Tasks_ForeachStep.ts b/sdks/ts/src/api/models/Tasks_ForeachStep.ts index 31989f782..ad3f53550 100644 --- a/sdks/ts/src/api/models/Tasks_ForeachStep.ts +++ b/sdks/ts/src/api/models/Tasks_ForeachStep.ts @@ -2,10 +2,14 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; import type { Tasks_ForeachDo } from "./Tasks_ForeachDo"; -export type Tasks_ForeachStep = Tasks_BaseWorkflowStep & { - kind_: "foreach"; +export type Tasks_ForeachStep = { + /** + * The kind of step + */ + readonly kind_: "foreach"; +} & { + readonly kind_: "foreach"; /** * The steps to run for each iteration */ diff --git a/sdks/ts/src/api/models/Tasks_GetStep.ts b/sdks/ts/src/api/models/Tasks_GetStep.ts index a8d20ecbd..b07732a92 100644 --- a/sdks/ts/src/api/models/Tasks_GetStep.ts +++ b/sdks/ts/src/api/models/Tasks_GetStep.ts @@ -2,9 +2,13 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_GetStep = Tasks_BaseWorkflowStep & { - kind_: "get"; +export type Tasks_GetStep = { + /** + * The kind of step + */ + readonly kind_: "get"; +} & { + readonly kind_: "get"; /** * The key to get */ diff --git a/sdks/ts/src/api/models/Tasks_GetStepDef.ts b/sdks/ts/src/api/models/Tasks_GetStepDef.ts new file mode 100644 index 000000000..4fe0a6dd8 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_GetStepDef.ts @@ -0,0 +1,10 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export type Tasks_GetStepDef = { + /** + * The key to get + */ + get: string; +}; diff --git a/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts b/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts index d2df5d79e..f05a33f80 100644 --- a/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts +++ b/sdks/ts/src/api/models/Tasks_IfElseWorkflowStep.ts @@ -3,7 +3,6 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; @@ -17,8 +16,13 @@ import type { Tasks_SleepStep } from "./Tasks_SleepStep"; import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; import type { Tasks_YieldStep } from "./Tasks_YieldStep"; -export type Tasks_IfElseWorkflowStep = Tasks_BaseWorkflowStep & { - kind_: "if_else"; +export type Tasks_IfElseWorkflowStep = { + /** + * The kind of step + */ + readonly kind_: "if_else"; +} & { + readonly kind_: "if_else"; /** * The condition to evaluate */ @@ -29,16 +33,16 @@ export type Tasks_IfElseWorkflowStep = Tasks_BaseWorkflowStep & { then: | Tasks_EvaluateStep | Tasks_ToolCallStep - | Tasks_YieldStep | Tasks_PromptStep - | Tasks_ErrorWorkflowStep - | Tasks_SleepStep - | Tasks_ReturnStep | Tasks_GetStep | Tasks_SetStep | Tasks_LogStep | Tasks_EmbedStep | Tasks_SearchStep + | Tasks_ReturnStep + | Tasks_SleepStep + | Tasks_ErrorWorkflowStep + | Tasks_YieldStep | Tasks_WaitForInputStep; /** * The steps to run if the condition is false @@ -46,15 +50,15 @@ export type Tasks_IfElseWorkflowStep = Tasks_BaseWorkflowStep & { else: | Tasks_EvaluateStep | Tasks_ToolCallStep - | Tasks_YieldStep | Tasks_PromptStep - | Tasks_ErrorWorkflowStep - | Tasks_SleepStep - | Tasks_ReturnStep | Tasks_GetStep | Tasks_SetStep | Tasks_LogStep | Tasks_EmbedStep | Tasks_SearchStep + | Tasks_ReturnStep + | Tasks_SleepStep + | Tasks_ErrorWorkflowStep + | Tasks_YieldStep | Tasks_WaitForInputStep; }; diff --git a/sdks/ts/src/api/models/Tasks_LogStep.ts b/sdks/ts/src/api/models/Tasks_LogStep.ts index 483628b36..372bfccd3 100644 --- a/sdks/ts/src/api/models/Tasks_LogStep.ts +++ b/sdks/ts/src/api/models/Tasks_LogStep.ts @@ -3,9 +3,13 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_LogStep = Tasks_BaseWorkflowStep & { - kind_: "log"; +export type Tasks_LogStep = { + /** + * The kind of step + */ + readonly kind_: "log"; +} & { + readonly kind_: "log"; /** * The value to log */ diff --git a/sdks/ts/src/api/models/Tasks_LogStepDef.ts b/sdks/ts/src/api/models/Tasks_LogStepDef.ts new file mode 100644 index 000000000..a672c06b7 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_LogStepDef.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +export type Tasks_LogStepDef = { + /** + * The value to log + */ + log: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverEmbed.ts b/sdks/ts/src/api/models/Tasks_MapOverEmbed.ts new file mode 100644 index 000000000..7e0efd38e --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_MapOverEmbed.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_EmbedStepDef } from "./Tasks_EmbedStepDef"; +export type Tasks_MapOverEmbed = Tasks_EmbedStepDef & { + /** + * The variable to iterate over + */ + over: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverEvaluate.ts b/sdks/ts/src/api/models/Tasks_MapOverEvaluate.ts new file mode 100644 index 000000000..42d070eb3 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_MapOverEvaluate.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_EvaluateStepDef } from "./Tasks_EvaluateStepDef"; +export type Tasks_MapOverEvaluate = Tasks_EvaluateStepDef & { + /** + * The variable to iterate over + */ + over: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapOver.ts b/sdks/ts/src/api/models/Tasks_MapOverGet.ts similarity index 71% rename from sdks/ts/src/api/models/Tasks_MapOver.ts rename to sdks/ts/src/api/models/Tasks_MapOverGet.ts index d293474c3..e0bbf048a 100644 --- a/sdks/ts/src/api/models/Tasks_MapOver.ts +++ b/sdks/ts/src/api/models/Tasks_MapOverGet.ts @@ -3,13 +3,10 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; -export type Tasks_MapOver = { +import type { Tasks_GetStepDef } from "./Tasks_GetStepDef"; +export type Tasks_MapOverGet = Tasks_GetStepDef & { /** * The variable to iterate over */ over: Common_PyExpression; - /** - * The subworkflow to run for each iteration - */ - workflow: string; }; diff --git a/sdks/ts/src/api/models/Tasks_MapOverLog.ts b/sdks/ts/src/api/models/Tasks_MapOverLog.ts new file mode 100644 index 000000000..1b1c91801 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_MapOverLog.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_LogStepDef } from "./Tasks_LogStepDef"; +export type Tasks_MapOverLog = Tasks_LogStepDef & { + /** + * The variable to iterate over + */ + over: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverPrompt.ts b/sdks/ts/src/api/models/Tasks_MapOverPrompt.ts new file mode 100644 index 000000000..ec6d1016e --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_MapOverPrompt.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_PromptStepDef } from "./Tasks_PromptStepDef"; +export type Tasks_MapOverPrompt = Tasks_PromptStepDef & { + /** + * The variable to iterate over + */ + over: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverSearch.ts b/sdks/ts/src/api/models/Tasks_MapOverSearch.ts new file mode 100644 index 000000000..272e9eeea --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_MapOverSearch.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_SearchStepDef } from "./Tasks_SearchStepDef"; +export type Tasks_MapOverSearch = Tasks_SearchStepDef & { + /** + * The variable to iterate over + */ + over: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverSet.ts b/sdks/ts/src/api/models/Tasks_MapOverSet.ts new file mode 100644 index 000000000..78b06414c --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_MapOverSet.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_SetStepDef } from "./Tasks_SetStepDef"; +export type Tasks_MapOverSet = Tasks_SetStepDef & { + /** + * The variable to iterate over + */ + over: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverToolCall.ts b/sdks/ts/src/api/models/Tasks_MapOverToolCall.ts new file mode 100644 index 000000000..b7040f0b7 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_MapOverToolCall.ts @@ -0,0 +1,12 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_ToolCallStepDef } from "./Tasks_ToolCallStepDef"; +export type Tasks_MapOverToolCall = Tasks_ToolCallStepDef & { + /** + * The variable to iterate over + */ + over: Common_PyExpression; +}; diff --git a/sdks/ts/src/api/models/Tasks_MapReduceStep.ts b/sdks/ts/src/api/models/Tasks_MapReduceStep.ts deleted file mode 100644 index ef049ed42..000000000 --- a/sdks/ts/src/api/models/Tasks_MapReduceStep.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -import type { Tasks_MapOver } from "./Tasks_MapOver"; -export type Tasks_MapReduceStep = Tasks_BaseWorkflowStep & { - kind_: "map_reduce"; - /** - * The steps to run for each iteration - */ - map: Tasks_MapOver; - /** - * The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. - */ - reduce: Common_PyExpression | "_"; -}; diff --git a/sdks/ts/src/api/models/Tasks_ParallelStep.ts b/sdks/ts/src/api/models/Tasks_ParallelStep.ts index da6f9f399..9118d48dd 100644 --- a/sdks/ts/src/api/models/Tasks_ParallelStep.ts +++ b/sdks/ts/src/api/models/Tasks_ParallelStep.ts @@ -2,38 +2,32 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; -import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; -import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; import type { Tasks_SearchStep } from "./Tasks_SearchStep"; import type { Tasks_SetStep } from "./Tasks_SetStep"; -import type { Tasks_SleepStep } from "./Tasks_SleepStep"; import type { Tasks_ToolCallStep } from "./Tasks_ToolCallStep"; -import type { Tasks_WaitForInputStep } from "./Tasks_WaitForInputStep"; -import type { Tasks_YieldStep } from "./Tasks_YieldStep"; -export type Tasks_ParallelStep = Tasks_BaseWorkflowStep & { - kind_: "parallel"; +export type Tasks_ParallelStep = { /** - * The steps to run in parallel. Max concurrency will depend on the platform + * The kind of step + */ + readonly kind_: "parallel"; +} & { + readonly kind_: "parallel"; + /** + * The steps to run in parallel. Max concurrency will depend on the platform. */ parallel: Array< | Tasks_EvaluateStep | Tasks_ToolCallStep - | Tasks_YieldStep | Tasks_PromptStep - | Tasks_ErrorWorkflowStep - | Tasks_SleepStep - | Tasks_ReturnStep | Tasks_GetStep | Tasks_SetStep | Tasks_LogStep | Tasks_EmbedStep | Tasks_SearchStep - | Tasks_WaitForInputStep >; }; diff --git a/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts b/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts index 0b5b917e1..28866dbb8 100644 --- a/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts +++ b/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; @@ -9,7 +10,14 @@ import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; -import type { Tasks_MapReduceStep } from "./Tasks_MapReduceStep"; +import type { Tasks_MapOverEmbed } from "./Tasks_MapOverEmbed"; +import type { Tasks_MapOverEvaluate } from "./Tasks_MapOverEvaluate"; +import type { Tasks_MapOverGet } from "./Tasks_MapOverGet"; +import type { Tasks_MapOverLog } from "./Tasks_MapOverLog"; +import type { Tasks_MapOverPrompt } from "./Tasks_MapOverPrompt"; +import type { Tasks_MapOverSearch } from "./Tasks_MapOverSearch"; +import type { Tasks_MapOverSet } from "./Tasks_MapOverSet"; +import type { Tasks_MapOverToolCall } from "./Tasks_MapOverToolCall"; import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; @@ -28,21 +36,46 @@ export type Tasks_PatchTaskRequest = Record< Array< | Tasks_EvaluateStep | Tasks_ToolCallStep - | Tasks_YieldStep | Tasks_PromptStep - | Tasks_ErrorWorkflowStep - | Tasks_SleepStep - | Tasks_ReturnStep | Tasks_GetStep | Tasks_SetStep | Tasks_LogStep | Tasks_EmbedStep | Tasks_SearchStep + | Tasks_ReturnStep + | Tasks_SleepStep + | Tasks_ErrorWorkflowStep + | Tasks_YieldStep | Tasks_WaitForInputStep | Tasks_IfElseWorkflowStep | Tasks_SwitchStep | Tasks_ForeachStep | Tasks_ParallelStep - | Tasks_MapReduceStep + | ({ + /** + * Discriminator property for BaseWorkflowStep. + */ + kind_?: string; + } & { + /** + * The steps to run for each iteration + */ + map: + | Tasks_MapOverEvaluate + | Tasks_MapOverToolCall + | Tasks_MapOverPrompt + | Tasks_MapOverGet + | Tasks_MapOverSet + | Tasks_MapOverLog + | Tasks_MapOverEmbed + | Tasks_MapOverSearch; + /** + * The expression to reduce the results. + * If not provided, the results are collected and returned as a list. + * A special parameter named `results` is the accumulator and `_` is the current value. + */ + reduce?: Common_PyExpression; + initial?: Common_PyExpression; + }) > >; diff --git a/sdks/ts/src/api/models/Tasks_PromptStep.ts b/sdks/ts/src/api/models/Tasks_PromptStep.ts index db019b522..7cb04ccea 100644 --- a/sdks/ts/src/api/models/Tasks_PromptStep.ts +++ b/sdks/ts/src/api/models/Tasks_PromptStep.ts @@ -4,9 +4,13 @@ /* eslint-disable */ import type { Chat_ChatSettings } from "./Chat_ChatSettings"; import type { Common_JinjaTemplate } from "./Common_JinjaTemplate"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_PromptStep = Tasks_BaseWorkflowStep & { - kind_: "prompt"; +export type Tasks_PromptStep = { + /** + * The kind of step + */ + readonly kind_: "prompt"; +} & { + readonly kind_: "prompt"; /** * The prompt to run */ diff --git a/sdks/ts/src/api/models/Tasks_PromptStepDef.ts b/sdks/ts/src/api/models/Tasks_PromptStepDef.ts new file mode 100644 index 000000000..fb21d106f --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_PromptStepDef.ts @@ -0,0 +1,16 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Chat_ChatSettings } from "./Chat_ChatSettings"; +import type { Common_JinjaTemplate } from "./Common_JinjaTemplate"; +export type Tasks_PromptStepDef = { + /** + * The prompt to run + */ + prompt: Common_JinjaTemplate; + /** + * Settings for the prompt + */ + settings: Chat_ChatSettings; +}; diff --git a/sdks/ts/src/api/models/Tasks_ReturnStep.ts b/sdks/ts/src/api/models/Tasks_ReturnStep.ts index 97488f129..7eda54161 100644 --- a/sdks/ts/src/api/models/Tasks_ReturnStep.ts +++ b/sdks/ts/src/api/models/Tasks_ReturnStep.ts @@ -3,9 +3,13 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_ReturnStep = Tasks_BaseWorkflowStep & { - kind_: "return"; +export type Tasks_ReturnStep = { + /** + * The kind of step + */ + readonly kind_: "return"; +} & { + readonly kind_: "return"; /** * The value to return */ diff --git a/sdks/ts/src/api/models/Tasks_SearchStep.ts b/sdks/ts/src/api/models/Tasks_SearchStep.ts index eabe9b707..3a2663fa9 100644 --- a/sdks/ts/src/api/models/Tasks_SearchStep.ts +++ b/sdks/ts/src/api/models/Tasks_SearchStep.ts @@ -5,9 +5,13 @@ import type { Docs_HybridDocSearchRequest } from "./Docs_HybridDocSearchRequest"; import type { Docs_TextOnlyDocSearchRequest } from "./Docs_TextOnlyDocSearchRequest"; import type { Docs_VectorDocSearchRequest } from "./Docs_VectorDocSearchRequest"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_SearchStep = Tasks_BaseWorkflowStep & { - kind_: "search"; +export type Tasks_SearchStep = { + /** + * The kind of step + */ + readonly kind_: "search"; +} & { + readonly kind_: "search"; /** * The search query */ diff --git a/sdks/ts/src/api/models/Tasks_SearchStepDef.ts b/sdks/ts/src/api/models/Tasks_SearchStepDef.ts new file mode 100644 index 000000000..3daedd7af --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_SearchStepDef.ts @@ -0,0 +1,16 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Docs_HybridDocSearchRequest } from "./Docs_HybridDocSearchRequest"; +import type { Docs_TextOnlyDocSearchRequest } from "./Docs_TextOnlyDocSearchRequest"; +import type { Docs_VectorDocSearchRequest } from "./Docs_VectorDocSearchRequest"; +export type Tasks_SearchStepDef = { + /** + * The search query + */ + search: + | Docs_VectorDocSearchRequest + | Docs_TextOnlyDocSearchRequest + | Docs_HybridDocSearchRequest; +}; diff --git a/sdks/ts/src/api/models/Tasks_SetStep.ts b/sdks/ts/src/api/models/Tasks_SetStep.ts index 61838776e..2bccabeac 100644 --- a/sdks/ts/src/api/models/Tasks_SetStep.ts +++ b/sdks/ts/src/api/models/Tasks_SetStep.ts @@ -2,10 +2,14 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; import type { Tasks_SetKey } from "./Tasks_SetKey"; -export type Tasks_SetStep = Tasks_BaseWorkflowStep & { - kind_: "set"; +export type Tasks_SetStep = { + /** + * The kind of step + */ + readonly kind_: "set"; +} & { + readonly kind_: "set"; /** * The value to set */ diff --git a/sdks/ts/src/api/models/Tasks_SetStepDef.ts b/sdks/ts/src/api/models/Tasks_SetStepDef.ts new file mode 100644 index 000000000..41e81c59d --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_SetStepDef.ts @@ -0,0 +1,11 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Tasks_SetKey } from "./Tasks_SetKey"; +export type Tasks_SetStepDef = { + /** + * The value to set + */ + set: Tasks_SetKey; +}; diff --git a/sdks/ts/src/api/models/Tasks_SleepStep.ts b/sdks/ts/src/api/models/Tasks_SleepStep.ts index cd2994546..e3b8c576b 100644 --- a/sdks/ts/src/api/models/Tasks_SleepStep.ts +++ b/sdks/ts/src/api/models/Tasks_SleepStep.ts @@ -2,10 +2,14 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; import type { Tasks_SleepFor } from "./Tasks_SleepFor"; -export type Tasks_SleepStep = Tasks_BaseWorkflowStep & { - kind_: "sleep"; +export type Tasks_SleepStep = { + /** + * The kind of step + */ + readonly kind_: "sleep"; +} & { + readonly kind_: "sleep"; /** * The duration to sleep for (max 31 days) */ diff --git a/sdks/ts/src/api/models/Tasks_SwitchStep.ts b/sdks/ts/src/api/models/Tasks_SwitchStep.ts index 27d68c6be..a560cee13 100644 --- a/sdks/ts/src/api/models/Tasks_SwitchStep.ts +++ b/sdks/ts/src/api/models/Tasks_SwitchStep.ts @@ -2,10 +2,14 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; import type { Tasks_CaseThen } from "./Tasks_CaseThen"; -export type Tasks_SwitchStep = Tasks_BaseWorkflowStep & { - kind_: "switch"; +export type Tasks_SwitchStep = { + /** + * The kind of step + */ + readonly kind_: "switch"; +} & { + readonly kind_: "switch"; /** * The cond tree */ diff --git a/sdks/ts/src/api/models/Tasks_Task.ts b/sdks/ts/src/api/models/Tasks_Task.ts index fe307a4e8..97a3a3b97 100644 --- a/sdks/ts/src/api/models/Tasks_Task.ts +++ b/sdks/ts/src/api/models/Tasks_Task.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; @@ -9,7 +10,14 @@ import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; -import type { Tasks_MapReduceStep } from "./Tasks_MapReduceStep"; +import type { Tasks_MapOverEmbed } from "./Tasks_MapOverEmbed"; +import type { Tasks_MapOverEvaluate } from "./Tasks_MapOverEvaluate"; +import type { Tasks_MapOverGet } from "./Tasks_MapOverGet"; +import type { Tasks_MapOverLog } from "./Tasks_MapOverLog"; +import type { Tasks_MapOverPrompt } from "./Tasks_MapOverPrompt"; +import type { Tasks_MapOverSearch } from "./Tasks_MapOverSearch"; +import type { Tasks_MapOverSet } from "./Tasks_MapOverSet"; +import type { Tasks_MapOverToolCall } from "./Tasks_MapOverToolCall"; import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; @@ -28,21 +36,47 @@ export type Tasks_Task = Record< Array< | Tasks_EvaluateStep | Tasks_ToolCallStep - | Tasks_YieldStep | Tasks_PromptStep - | Tasks_ErrorWorkflowStep - | Tasks_SleepStep - | Tasks_ReturnStep | Tasks_GetStep | Tasks_SetStep | Tasks_LogStep | Tasks_EmbedStep | Tasks_SearchStep + | Tasks_ReturnStep + | Tasks_SleepStep + | Tasks_ErrorWorkflowStep + | Tasks_YieldStep | Tasks_WaitForInputStep | Tasks_IfElseWorkflowStep | Tasks_SwitchStep | Tasks_ForeachStep | Tasks_ParallelStep - | Tasks_MapReduceStep + | ({ + /** + * The kind of step + */ + readonly kind_: "map_reduce"; + } & { + readonly kind_: "map_reduce"; + /** + * The steps to run for each iteration + */ + map: + | Tasks_MapOverEvaluate + | Tasks_MapOverToolCall + | Tasks_MapOverPrompt + | Tasks_MapOverGet + | Tasks_MapOverSet + | Tasks_MapOverLog + | Tasks_MapOverEmbed + | Tasks_MapOverSearch; + /** + * The expression to reduce the results. + * If not provided, the results are collected and returned as a list. + * A special parameter named `results` is the accumulator and `_` is the current value. + */ + reduce?: Common_PyExpression; + initial?: Common_PyExpression; + }) > >; diff --git a/sdks/ts/src/api/models/Tasks_ToolCallStep.ts b/sdks/ts/src/api/models/Tasks_ToolCallStep.ts index dfb0b505c..9a3eadb51 100644 --- a/sdks/ts/src/api/models/Tasks_ToolCallStep.ts +++ b/sdks/ts/src/api/models/Tasks_ToolCallStep.ts @@ -4,9 +4,13 @@ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; import type { Common_toolRef } from "./Common_toolRef"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_ToolCallStep = Tasks_BaseWorkflowStep & { - kind_: "tool_call"; +export type Tasks_ToolCallStep = { + /** + * The kind of step + */ + readonly kind_: "tool_call"; +} & { + readonly kind_: "tool_call"; /** * The tool to run */ diff --git a/sdks/ts/src/api/models/Tasks_ToolCallStepDef.ts b/sdks/ts/src/api/models/Tasks_ToolCallStepDef.ts new file mode 100644 index 000000000..106293d20 --- /dev/null +++ b/sdks/ts/src/api/models/Tasks_ToolCallStepDef.ts @@ -0,0 +1,16 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Common_toolRef } from "./Common_toolRef"; +export type Tasks_ToolCallStepDef = { + /** + * The tool to run + */ + tool: Common_toolRef; + /** + * The input parameters for the tool (defaults to last step output) + */ + arguments: Record | "_"; +}; diff --git a/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts b/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts index a6346d173..06e50f5c8 100644 --- a/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts +++ b/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts @@ -2,6 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ +import type { Common_PyExpression } from "./Common_PyExpression"; import type { Tasks_EmbedStep } from "./Tasks_EmbedStep"; import type { Tasks_ErrorWorkflowStep } from "./Tasks_ErrorWorkflowStep"; import type { Tasks_EvaluateStep } from "./Tasks_EvaluateStep"; @@ -9,7 +10,14 @@ import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; -import type { Tasks_MapReduceStep } from "./Tasks_MapReduceStep"; +import type { Tasks_MapOverEmbed } from "./Tasks_MapOverEmbed"; +import type { Tasks_MapOverEvaluate } from "./Tasks_MapOverEvaluate"; +import type { Tasks_MapOverGet } from "./Tasks_MapOverGet"; +import type { Tasks_MapOverLog } from "./Tasks_MapOverLog"; +import type { Tasks_MapOverPrompt } from "./Tasks_MapOverPrompt"; +import type { Tasks_MapOverSearch } from "./Tasks_MapOverSearch"; +import type { Tasks_MapOverSet } from "./Tasks_MapOverSet"; +import type { Tasks_MapOverToolCall } from "./Tasks_MapOverToolCall"; import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; @@ -28,21 +36,47 @@ export type Tasks_UpdateTaskRequest = Record< Array< | Tasks_EvaluateStep | Tasks_ToolCallStep - | Tasks_YieldStep | Tasks_PromptStep - | Tasks_ErrorWorkflowStep - | Tasks_SleepStep - | Tasks_ReturnStep | Tasks_GetStep | Tasks_SetStep | Tasks_LogStep | Tasks_EmbedStep | Tasks_SearchStep + | Tasks_ReturnStep + | Tasks_SleepStep + | Tasks_ErrorWorkflowStep + | Tasks_YieldStep | Tasks_WaitForInputStep | Tasks_IfElseWorkflowStep | Tasks_SwitchStep | Tasks_ForeachStep | Tasks_ParallelStep - | Tasks_MapReduceStep + | ({ + /** + * The kind of step + */ + readonly kind_: "map_reduce"; + } & { + readonly kind_: "map_reduce"; + /** + * The steps to run for each iteration + */ + map: + | Tasks_MapOverEvaluate + | Tasks_MapOverToolCall + | Tasks_MapOverPrompt + | Tasks_MapOverGet + | Tasks_MapOverSet + | Tasks_MapOverLog + | Tasks_MapOverEmbed + | Tasks_MapOverSearch; + /** + * The expression to reduce the results. + * If not provided, the results are collected and returned as a list. + * A special parameter named `results` is the accumulator and `_` is the current value. + */ + reduce?: Common_PyExpression; + initial?: Common_PyExpression; + }) > >; diff --git a/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts b/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts index 7f8e16817..246d68813 100644 --- a/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts +++ b/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts @@ -3,9 +3,13 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_WaitForInputStep = Tasks_BaseWorkflowStep & { - kind_: "wait_for_input"; +export type Tasks_WaitForInputStep = { + /** + * The kind of step + */ + readonly kind_: "wait_for_input"; +} & { + readonly kind_: "wait_for_input"; /** * Any additional info or data */ diff --git a/sdks/ts/src/api/models/Tasks_YieldStep.ts b/sdks/ts/src/api/models/Tasks_YieldStep.ts index 6b677947e..3792539a2 100644 --- a/sdks/ts/src/api/models/Tasks_YieldStep.ts +++ b/sdks/ts/src/api/models/Tasks_YieldStep.ts @@ -3,11 +3,16 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_BaseWorkflowStep } from "./Tasks_BaseWorkflowStep"; -export type Tasks_YieldStep = Tasks_BaseWorkflowStep & { - kind_: "yield"; +export type Tasks_YieldStep = { /** - * The subworkflow to run + * The kind of step + */ + readonly kind_: "yield"; +} & { + readonly kind_: "yield"; + /** + * The subworkflow to run. + * VALIDATION: Should resolve to a defined subworkflow. */ workflow: string; /** diff --git a/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts b/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts index f30f3bf91..4507fa803 100644 --- a/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts +++ b/sdks/ts/src/api/schemas/$Tasks_CaseThen.ts @@ -28,34 +28,34 @@ export const $Tasks_CaseThen = { type: "Tasks_ToolCallStep", }, { - type: "Tasks_YieldStep", + type: "Tasks_PromptStep", }, { - type: "Tasks_PromptStep", + type: "Tasks_GetStep", }, { - type: "Tasks_ErrorWorkflowStep", + type: "Tasks_SetStep", }, { - type: "Tasks_SleepStep", + type: "Tasks_LogStep", }, { - type: "Tasks_ReturnStep", + type: "Tasks_EmbedStep", }, { - type: "Tasks_GetStep", + type: "Tasks_SearchStep", }, { - type: "Tasks_SetStep", + type: "Tasks_ReturnStep", }, { - type: "Tasks_LogStep", + type: "Tasks_SleepStep", }, { - type: "Tasks_EmbedStep", + type: "Tasks_ErrorWorkflowStep", }, { - type: "Tasks_SearchStep", + type: "Tasks_YieldStep", }, { type: "Tasks_WaitForInputStep", diff --git a/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts b/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts index 1116b0b5f..06002e838 100644 --- a/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts +++ b/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts @@ -16,34 +16,34 @@ export const $Tasks_CreateTaskRequest = { type: "Tasks_ToolCallStep", }, { - type: "Tasks_YieldStep", + type: "Tasks_PromptStep", }, { - type: "Tasks_PromptStep", + type: "Tasks_GetStep", }, { - type: "Tasks_ErrorWorkflowStep", + type: "Tasks_SetStep", }, { - type: "Tasks_SleepStep", + type: "Tasks_LogStep", }, { - type: "Tasks_ReturnStep", + type: "Tasks_EmbedStep", }, { - type: "Tasks_GetStep", + type: "Tasks_SearchStep", }, { - type: "Tasks_SetStep", + type: "Tasks_ReturnStep", }, { - type: "Tasks_LogStep", + type: "Tasks_SleepStep", }, { - type: "Tasks_EmbedStep", + type: "Tasks_ErrorWorkflowStep", }, { - type: "Tasks_SearchStep", + type: "Tasks_YieldStep", }, { type: "Tasks_WaitForInputStep", @@ -61,7 +61,77 @@ export const $Tasks_CreateTaskRequest = { type: "Tasks_ParallelStep", }, { - type: "Tasks_MapReduceStep", + type: "all-of", + contains: [ + { + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, + }, + { + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + map: { + type: "any-of", + description: `The steps to run for each iteration`, + contains: [ + { + type: "Tasks_MapOverEvaluate", + }, + { + type: "Tasks_MapOverToolCall", + }, + { + type: "Tasks_MapOverPrompt", + }, + { + type: "Tasks_MapOverGet", + }, + { + type: "Tasks_MapOverSet", + }, + { + type: "Tasks_MapOverLog", + }, + { + type: "Tasks_MapOverEmbed", + }, + { + type: "Tasks_MapOverSearch", + }, + ], + isRequired: true, + }, + reduce: { + type: "all-of", + description: `The expression to reduce the results. + If not provided, the results are collected and returned as a list. + A special parameter named \`results\` is the accumulator and \`_\` is the current value.`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + }, + initial: { + type: "all-of", + contains: [ + { + type: "Common_PyExpression", + }, + ], + }, + }, + }, + ], }, ], }, diff --git a/sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts b/sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts index 11cae473c..007215dec 100644 --- a/sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_EmbedStep.ts @@ -6,12 +6,19 @@ export const $Tasks_EmbedStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, embed: { diff --git a/sdks/ts/src/api/schemas/$Tasks_EmbedStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_EmbedStepDef.ts new file mode 100644 index 000000000..865826f90 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_EmbedStepDef.ts @@ -0,0 +1,18 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_EmbedStepDef = { + properties: { + embed: { + type: "all-of", + description: `The text to embed`, + contains: [ + { + type: "Docs_EmbedQueryRequest", + }, + ], + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_ErrorWorkflowStep.ts b/sdks/ts/src/api/schemas/$Tasks_ErrorWorkflowStep.ts index 31e9cd44b..371a8952a 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ErrorWorkflowStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ErrorWorkflowStep.ts @@ -6,12 +6,19 @@ export const $Tasks_ErrorWorkflowStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, error: { diff --git a/sdks/ts/src/api/schemas/$Tasks_EvaluateStep.ts b/sdks/ts/src/api/schemas/$Tasks_EvaluateStep.ts index 5f2fbf871..1ba2687fe 100644 --- a/sdks/ts/src/api/schemas/$Tasks_EvaluateStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_EvaluateStep.ts @@ -6,12 +6,19 @@ export const $Tasks_EvaluateStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, evaluate: { diff --git a/sdks/ts/src/api/schemas/$Tasks_EvaluateStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_EvaluateStepDef.ts new file mode 100644 index 000000000..acd724d29 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_EvaluateStepDef.ts @@ -0,0 +1,15 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_EvaluateStepDef = { + properties: { + evaluate: { + type: "dictionary", + contains: { + type: "Common_PyExpression", + }, + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts b/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts index c9dbd8b1c..920c95f63 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ForeachDo.ts @@ -6,7 +6,8 @@ export const $Tasks_ForeachDo = { properties: { in: { type: "all-of", - description: `The variable to iterate over`, + description: `The variable to iterate over. + VALIDATION: Should NOT return more than 1000 elements.`, contains: [ { type: "Common_PyExpression", @@ -24,21 +25,9 @@ export const $Tasks_ForeachDo = { { type: "Tasks_ToolCallStep", }, - { - type: "Tasks_YieldStep", - }, { type: "Tasks_PromptStep", }, - { - type: "Tasks_ErrorWorkflowStep", - }, - { - type: "Tasks_SleepStep", - }, - { - type: "Tasks_ReturnStep", - }, { type: "Tasks_GetStep", }, @@ -54,9 +43,6 @@ export const $Tasks_ForeachDo = { { type: "Tasks_SearchStep", }, - { - type: "Tasks_WaitForInputStep", - }, ], isRequired: true, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts b/sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts index 3deb761b5..d458e4bd5 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ForeachStep.ts @@ -6,12 +6,19 @@ export const $Tasks_ForeachStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, foreach: { diff --git a/sdks/ts/src/api/schemas/$Tasks_GetStep.ts b/sdks/ts/src/api/schemas/$Tasks_GetStep.ts index 19da398f8..d09852322 100644 --- a/sdks/ts/src/api/schemas/$Tasks_GetStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_GetStep.ts @@ -6,12 +6,19 @@ export const $Tasks_GetStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, get: { diff --git a/sdks/ts/src/api/schemas/$Tasks_BaseWorkflowStep.ts b/sdks/ts/src/api/schemas/$Tasks_GetStepDef.ts similarity index 65% rename from sdks/ts/src/api/schemas/$Tasks_BaseWorkflowStep.ts rename to sdks/ts/src/api/schemas/$Tasks_GetStepDef.ts index 71375552d..a5ca63603 100644 --- a/sdks/ts/src/api/schemas/$Tasks_BaseWorkflowStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_GetStepDef.ts @@ -2,10 +2,11 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export const $Tasks_BaseWorkflowStep = { +export const $Tasks_GetStepDef = { properties: { - kind_: { - type: "Enum", + get: { + type: "string", + description: `The key to get`, isRequired: true, }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts b/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts index 7f615c5fc..06fa47e33 100644 --- a/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_IfElseWorkflowStep.ts @@ -6,12 +6,19 @@ export const $Tasks_IfElseWorkflowStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, if: { @@ -35,34 +42,34 @@ export const $Tasks_IfElseWorkflowStep = { type: "Tasks_ToolCallStep", }, { - type: "Tasks_YieldStep", + type: "Tasks_PromptStep", }, { - type: "Tasks_PromptStep", + type: "Tasks_GetStep", }, { - type: "Tasks_ErrorWorkflowStep", + type: "Tasks_SetStep", }, { - type: "Tasks_SleepStep", + type: "Tasks_LogStep", }, { - type: "Tasks_ReturnStep", + type: "Tasks_EmbedStep", }, { - type: "Tasks_GetStep", + type: "Tasks_SearchStep", }, { - type: "Tasks_SetStep", + type: "Tasks_ReturnStep", }, { - type: "Tasks_LogStep", + type: "Tasks_SleepStep", }, { - type: "Tasks_EmbedStep", + type: "Tasks_ErrorWorkflowStep", }, { - type: "Tasks_SearchStep", + type: "Tasks_YieldStep", }, { type: "Tasks_WaitForInputStep", @@ -81,34 +88,34 @@ export const $Tasks_IfElseWorkflowStep = { type: "Tasks_ToolCallStep", }, { - type: "Tasks_YieldStep", + type: "Tasks_PromptStep", }, { - type: "Tasks_PromptStep", + type: "Tasks_GetStep", }, { - type: "Tasks_ErrorWorkflowStep", + type: "Tasks_SetStep", }, { - type: "Tasks_SleepStep", + type: "Tasks_LogStep", }, { - type: "Tasks_ReturnStep", + type: "Tasks_EmbedStep", }, { - type: "Tasks_GetStep", + type: "Tasks_SearchStep", }, { - type: "Tasks_SetStep", + type: "Tasks_ReturnStep", }, { - type: "Tasks_LogStep", + type: "Tasks_SleepStep", }, { - type: "Tasks_EmbedStep", + type: "Tasks_ErrorWorkflowStep", }, { - type: "Tasks_SearchStep", + type: "Tasks_YieldStep", }, { type: "Tasks_WaitForInputStep", diff --git a/sdks/ts/src/api/schemas/$Tasks_LogStep.ts b/sdks/ts/src/api/schemas/$Tasks_LogStep.ts index 3565c937c..0ed1b16df 100644 --- a/sdks/ts/src/api/schemas/$Tasks_LogStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_LogStep.ts @@ -6,12 +6,19 @@ export const $Tasks_LogStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, log: { diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOver.ts b/sdks/ts/src/api/schemas/$Tasks_LogStepDef.ts similarity index 57% rename from sdks/ts/src/api/schemas/$Tasks_MapOver.ts rename to sdks/ts/src/api/schemas/$Tasks_LogStepDef.ts index b12b438c1..5941058c6 100644 --- a/sdks/ts/src/api/schemas/$Tasks_MapOver.ts +++ b/sdks/ts/src/api/schemas/$Tasks_LogStepDef.ts @@ -2,11 +2,11 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export const $Tasks_MapOver = { +export const $Tasks_LogStepDef = { properties: { - over: { + log: { type: "all-of", - description: `The variable to iterate over`, + description: `The value to log`, contains: [ { type: "Common_PyExpression", @@ -14,10 +14,5 @@ export const $Tasks_MapOver = { ], isRequired: true, }, - workflow: { - type: "string", - description: `The subworkflow to run for each iteration`, - isRequired: true, - }, }, } as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverEmbed.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverEmbed.ts new file mode 100644 index 000000000..ad2d0242a --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapOverEmbed.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapOverEmbed = { + type: "all-of", + contains: [ + { + type: "Tasks_EmbedStepDef", + }, + { + properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverEvaluate.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverEvaluate.ts new file mode 100644 index 000000000..60f4191dd --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapOverEvaluate.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapOverEvaluate = { + type: "all-of", + contains: [ + { + type: "Tasks_EvaluateStepDef", + }, + { + properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts new file mode 100644 index 000000000..d9b6e75a8 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapOverGet = { + type: "all-of", + contains: [ + { + type: "Tasks_GetStepDef", + }, + { + properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts new file mode 100644 index 000000000..dd3940542 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapOverLog = { + type: "all-of", + contains: [ + { + type: "Tasks_LogStepDef", + }, + { + properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverPrompt.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverPrompt.ts new file mode 100644 index 000000000..49825b727 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapOverPrompt.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapOverPrompt = { + type: "all-of", + contains: [ + { + type: "Tasks_PromptStepDef", + }, + { + properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverSearch.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverSearch.ts new file mode 100644 index 000000000..48b376621 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapOverSearch.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapOverSearch = { + type: "all-of", + contains: [ + { + type: "Tasks_SearchStepDef", + }, + { + properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts new file mode 100644 index 000000000..81e30335b --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapOverSet = { + type: "all-of", + contains: [ + { + type: "Tasks_SetStepDef", + }, + { + properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverToolCall.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverToolCall.ts new file mode 100644 index 000000000..0357c025a --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_MapOverToolCall.ts @@ -0,0 +1,26 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_MapOverToolCall = { + type: "all-of", + contains: [ + { + type: "Tasks_ToolCallStepDef", + }, + { + properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, + }, + }, + ], +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts b/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts deleted file mode 100644 index e24fc2479..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_MapReduceStep.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_MapReduceStep = { - type: "all-of", - contains: [ - { - type: "Tasks_BaseWorkflowStep", - }, - { - properties: { - kind_: { - type: "Enum", - isRequired: true, - }, - map: { - type: "all-of", - description: `The steps to run for each iteration`, - contains: [ - { - type: "Tasks_MapOver", - }, - ], - isRequired: true, - }, - reduce: { - type: "any-of", - description: `The expression to reduce the results (\`_\` is a list of outputs). If not provided, the results are returned as a list.`, - contains: [ - { - type: "Common_PyExpression", - }, - { - type: "Enum", - }, - ], - isRequired: true, - }, - }, - }, - ], -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts b/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts index 956a7d131..fd6fab551 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ParallelStep.ts @@ -6,12 +6,19 @@ export const $Tasks_ParallelStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, parallel: { @@ -25,21 +32,9 @@ export const $Tasks_ParallelStep = { { type: "Tasks_ToolCallStep", }, - { - type: "Tasks_YieldStep", - }, { type: "Tasks_PromptStep", }, - { - type: "Tasks_ErrorWorkflowStep", - }, - { - type: "Tasks_SleepStep", - }, - { - type: "Tasks_ReturnStep", - }, { type: "Tasks_GetStep", }, @@ -55,9 +50,6 @@ export const $Tasks_ParallelStep = { { type: "Tasks_SearchStep", }, - { - type: "Tasks_WaitForInputStep", - }, ], }, isRequired: true, diff --git a/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts b/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts index 561808f01..22f27c59a 100644 --- a/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts +++ b/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts @@ -16,34 +16,34 @@ export const $Tasks_PatchTaskRequest = { type: "Tasks_ToolCallStep", }, { - type: "Tasks_YieldStep", + type: "Tasks_PromptStep", }, { - type: "Tasks_PromptStep", + type: "Tasks_GetStep", }, { - type: "Tasks_ErrorWorkflowStep", + type: "Tasks_SetStep", }, { - type: "Tasks_SleepStep", + type: "Tasks_LogStep", }, { - type: "Tasks_ReturnStep", + type: "Tasks_EmbedStep", }, { - type: "Tasks_GetStep", + type: "Tasks_SearchStep", }, { - type: "Tasks_SetStep", + type: "Tasks_ReturnStep", }, { - type: "Tasks_LogStep", + type: "Tasks_SleepStep", }, { - type: "Tasks_EmbedStep", + type: "Tasks_ErrorWorkflowStep", }, { - type: "Tasks_SearchStep", + type: "Tasks_YieldStep", }, { type: "Tasks_WaitForInputStep", @@ -61,7 +61,71 @@ export const $Tasks_PatchTaskRequest = { type: "Tasks_ParallelStep", }, { - type: "Tasks_MapReduceStep", + type: "all-of", + contains: [ + { + properties: { + kind_: { + type: "string", + description: `Discriminator property for BaseWorkflowStep.`, + }, + }, + }, + { + properties: { + map: { + type: "any-of", + description: `The steps to run for each iteration`, + contains: [ + { + type: "Tasks_MapOverEvaluate", + }, + { + type: "Tasks_MapOverToolCall", + }, + { + type: "Tasks_MapOverPrompt", + }, + { + type: "Tasks_MapOverGet", + }, + { + type: "Tasks_MapOverSet", + }, + { + type: "Tasks_MapOverLog", + }, + { + type: "Tasks_MapOverEmbed", + }, + { + type: "Tasks_MapOverSearch", + }, + ], + isRequired: true, + }, + reduce: { + type: "all-of", + description: `The expression to reduce the results. + If not provided, the results are collected and returned as a list. + A special parameter named \`results\` is the accumulator and \`_\` is the current value.`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + }, + initial: { + type: "all-of", + contains: [ + { + type: "Common_PyExpression", + }, + ], + }, + }, + }, + ], }, ], }, diff --git a/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts b/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts index 882307bdd..a0aeff91b 100644 --- a/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts @@ -6,12 +6,19 @@ export const $Tasks_PromptStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, prompt: { diff --git a/sdks/ts/src/api/schemas/$Tasks_PromptStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_PromptStepDef.ts new file mode 100644 index 000000000..badb73d46 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_PromptStepDef.ts @@ -0,0 +1,28 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_PromptStepDef = { + properties: { + prompt: { + type: "any-of", + description: `The prompt to run`, + contains: [ + { + type: "Common_JinjaTemplate", + }, + ], + isRequired: true, + }, + settings: { + type: "all-of", + description: `Settings for the prompt`, + contains: [ + { + type: "Chat_ChatSettings", + }, + ], + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts b/sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts index 04f7d4ab1..ae2b95deb 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ReturnStep.ts @@ -6,12 +6,19 @@ export const $Tasks_ReturnStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, return: { diff --git a/sdks/ts/src/api/schemas/$Tasks_SearchStep.ts b/sdks/ts/src/api/schemas/$Tasks_SearchStep.ts index 7c2ae3cb6..6adbd50d8 100644 --- a/sdks/ts/src/api/schemas/$Tasks_SearchStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_SearchStep.ts @@ -6,12 +6,19 @@ export const $Tasks_SearchStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, search: { diff --git a/sdks/ts/src/api/schemas/$Tasks_SearchStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_SearchStepDef.ts new file mode 100644 index 000000000..3ee574d3c --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_SearchStepDef.ts @@ -0,0 +1,24 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_SearchStepDef = { + properties: { + search: { + type: "any-of", + description: `The search query`, + contains: [ + { + type: "Docs_VectorDocSearchRequest", + }, + { + type: "Docs_TextOnlyDocSearchRequest", + }, + { + type: "Docs_HybridDocSearchRequest", + }, + ], + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SetStep.ts b/sdks/ts/src/api/schemas/$Tasks_SetStep.ts index 0590f1141..579d25a13 100644 --- a/sdks/ts/src/api/schemas/$Tasks_SetStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_SetStep.ts @@ -6,12 +6,19 @@ export const $Tasks_SetStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, set: { diff --git a/sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts new file mode 100644 index 000000000..7af72fcb4 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts @@ -0,0 +1,18 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_SetStepDef = { + properties: { + set: { + type: "all-of", + description: `The value to set`, + contains: [ + { + type: "Tasks_SetKey", + }, + ], + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts b/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts index 41fcf166d..2b69dcf92 100644 --- a/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_SleepStep.ts @@ -6,12 +6,19 @@ export const $Tasks_SleepStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, sleep: { diff --git a/sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts b/sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts index c8958af82..31926aacb 100644 --- a/sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_SwitchStep.ts @@ -6,12 +6,19 @@ export const $Tasks_SwitchStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, switch: { diff --git a/sdks/ts/src/api/schemas/$Tasks_Task.ts b/sdks/ts/src/api/schemas/$Tasks_Task.ts index 4343739a9..aa183b7fb 100644 --- a/sdks/ts/src/api/schemas/$Tasks_Task.ts +++ b/sdks/ts/src/api/schemas/$Tasks_Task.ts @@ -16,34 +16,34 @@ export const $Tasks_Task = { type: "Tasks_ToolCallStep", }, { - type: "Tasks_YieldStep", + type: "Tasks_PromptStep", }, { - type: "Tasks_PromptStep", + type: "Tasks_GetStep", }, { - type: "Tasks_ErrorWorkflowStep", + type: "Tasks_SetStep", }, { - type: "Tasks_SleepStep", + type: "Tasks_LogStep", }, { - type: "Tasks_ReturnStep", + type: "Tasks_EmbedStep", }, { - type: "Tasks_GetStep", + type: "Tasks_SearchStep", }, { - type: "Tasks_SetStep", + type: "Tasks_ReturnStep", }, { - type: "Tasks_LogStep", + type: "Tasks_SleepStep", }, { - type: "Tasks_EmbedStep", + type: "Tasks_ErrorWorkflowStep", }, { - type: "Tasks_SearchStep", + type: "Tasks_YieldStep", }, { type: "Tasks_WaitForInputStep", @@ -61,7 +61,77 @@ export const $Tasks_Task = { type: "Tasks_ParallelStep", }, { - type: "Tasks_MapReduceStep", + type: "all-of", + contains: [ + { + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, + }, + { + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + map: { + type: "any-of", + description: `The steps to run for each iteration`, + contains: [ + { + type: "Tasks_MapOverEvaluate", + }, + { + type: "Tasks_MapOverToolCall", + }, + { + type: "Tasks_MapOverPrompt", + }, + { + type: "Tasks_MapOverGet", + }, + { + type: "Tasks_MapOverSet", + }, + { + type: "Tasks_MapOverLog", + }, + { + type: "Tasks_MapOverEmbed", + }, + { + type: "Tasks_MapOverSearch", + }, + ], + isRequired: true, + }, + reduce: { + type: "all-of", + description: `The expression to reduce the results. + If not provided, the results are collected and returned as a list. + A special parameter named \`results\` is the accumulator and \`_\` is the current value.`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + }, + initial: { + type: "all-of", + contains: [ + { + type: "Common_PyExpression", + }, + ], + }, + }, + }, + ], }, ], }, diff --git a/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts b/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts index 54c54af17..9be4582a1 100644 --- a/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_ToolCallStep.ts @@ -6,12 +6,19 @@ export const $Tasks_ToolCallStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, tool: { diff --git a/sdks/ts/src/api/schemas/$Tasks_ToolCallStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_ToolCallStepDef.ts new file mode 100644 index 000000000..69b275101 --- /dev/null +++ b/sdks/ts/src/api/schemas/$Tasks_ToolCallStepDef.ts @@ -0,0 +1,34 @@ +/* generated using openapi-typescript-codegen -- do no edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $Tasks_ToolCallStepDef = { + properties: { + tool: { + type: "all-of", + description: `The tool to run`, + contains: [ + { + type: "Common_toolRef", + }, + ], + isRequired: true, + }, + arguments: { + type: "any-of", + description: `The input parameters for the tool (defaults to last step output)`, + contains: [ + { + type: "dictionary", + contains: { + type: "Common_PyExpression", + }, + }, + { + type: "Enum", + }, + ], + isRequired: true, + }, + }, +} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts b/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts index 782e0bb56..a9cd8187b 100644 --- a/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts +++ b/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts @@ -16,34 +16,34 @@ export const $Tasks_UpdateTaskRequest = { type: "Tasks_ToolCallStep", }, { - type: "Tasks_YieldStep", + type: "Tasks_PromptStep", }, { - type: "Tasks_PromptStep", + type: "Tasks_GetStep", }, { - type: "Tasks_ErrorWorkflowStep", + type: "Tasks_SetStep", }, { - type: "Tasks_SleepStep", + type: "Tasks_LogStep", }, { - type: "Tasks_ReturnStep", + type: "Tasks_EmbedStep", }, { - type: "Tasks_GetStep", + type: "Tasks_SearchStep", }, { - type: "Tasks_SetStep", + type: "Tasks_ReturnStep", }, { - type: "Tasks_LogStep", + type: "Tasks_SleepStep", }, { - type: "Tasks_EmbedStep", + type: "Tasks_ErrorWorkflowStep", }, { - type: "Tasks_SearchStep", + type: "Tasks_YieldStep", }, { type: "Tasks_WaitForInputStep", @@ -61,7 +61,77 @@ export const $Tasks_UpdateTaskRequest = { type: "Tasks_ParallelStep", }, { - type: "Tasks_MapReduceStep", + type: "all-of", + contains: [ + { + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, + }, + { + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + map: { + type: "any-of", + description: `The steps to run for each iteration`, + contains: [ + { + type: "Tasks_MapOverEvaluate", + }, + { + type: "Tasks_MapOverToolCall", + }, + { + type: "Tasks_MapOverPrompt", + }, + { + type: "Tasks_MapOverGet", + }, + { + type: "Tasks_MapOverSet", + }, + { + type: "Tasks_MapOverLog", + }, + { + type: "Tasks_MapOverEmbed", + }, + { + type: "Tasks_MapOverSearch", + }, + ], + isRequired: true, + }, + reduce: { + type: "all-of", + description: `The expression to reduce the results. + If not provided, the results are collected and returned as a list. + A special parameter named \`results\` is the accumulator and \`_\` is the current value.`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + }, + initial: { + type: "all-of", + contains: [ + { + type: "Common_PyExpression", + }, + ], + }, + }, + }, + ], }, ], }, diff --git a/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts b/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts index 227b73b33..72b24a415 100644 --- a/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts @@ -6,12 +6,19 @@ export const $Tasks_WaitForInputStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, wait_for_input: { diff --git a/sdks/ts/src/api/schemas/$Tasks_YieldStep.ts b/sdks/ts/src/api/schemas/$Tasks_YieldStep.ts index 376b32a56..b0ad18252 100644 --- a/sdks/ts/src/api/schemas/$Tasks_YieldStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_YieldStep.ts @@ -6,17 +6,25 @@ export const $Tasks_YieldStep = { type: "all-of", contains: [ { - type: "Tasks_BaseWorkflowStep", + properties: { + kind_: { + type: "Enum", + isReadOnly: true, + isRequired: true, + }, + }, }, { properties: { kind_: { type: "Enum", + isReadOnly: true, isRequired: true, }, workflow: { type: "string", - description: `The subworkflow to run`, + description: `The subworkflow to run. + VALIDATION: Should resolve to a defined subworkflow.`, isRequired: true, }, arguments: { diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp index 1cdbf213e..a32835b27 100644 --- a/typespec/tasks/steps.tsp +++ b/typespec/tasks/steps.tsp @@ -20,30 +20,41 @@ namespace Tasks; // STEP DEFINITIONS // -/** An object where values are strings in the Common Expression Language that get evaluated before being passed downstream */ +/** A simple python expression evaluated at runtime that is expected to return type T. */ alias TypedExpression = PyExpression; + +/** A python expression that takes an accumulator `results` and an input item `_` and reduces them. */ +alias ReduceExpression> = TypedExpression; + +/** A string->string object where the values are python expressions that get evaluated to give a final object. */ alias ExpressionObject = Record>; + +/** Nested expression object. */ alias NestedExpressionObject = Record | ExpressionObject>; @discriminator("kind_") -model BaseWorkflowStep { +model BaseWorkflowStep { /** The kind of step */ - kind_: WorkflowStepKind; + @visibility("read") + kind_: T; } -alias NonConditionalWorkflowStep = +alias MappableWorkflowStep = | EvaluateStep | ToolCallStep - | YieldStep | PromptStep - | ErrorWorkflowStep - | SleepStep - | ReturnStep | GetStep | SetStep | LogStep | EmbedStep - | SearchStep + | SearchStep; + +alias NonConditionalWorkflowStep = + | MappableWorkflowStep + | ReturnStep + | SleepStep + | ErrorWorkflowStep + | YieldStep | WaitForInputStep; alias ConditionalStep = IfElseWorkflowStep | SwitchStep; @@ -56,9 +67,14 @@ alias CreateWorkflowStep = WorkflowStep; /// Common steps /// //////////////////// -model ToolCallStep extends BaseWorkflowStep { +model ToolCallStep extends BaseWorkflowStep<"tool_call"> { + @visibility("read") kind_: "tool_call" = "tool_call"; + ...ToolCallStepDef; +} + +model ToolCallStepDef { /** The tool to run */ tool: toolRef; @@ -66,9 +82,14 @@ model ToolCallStep extends BaseWorkflowStep { arguments: ExpressionObject | "_" = "_"; } -model PromptStep extends BaseWorkflowStep { +model PromptStep extends BaseWorkflowStep<"prompt"> { + @visibility("read") kind_: "prompt" = "prompt"; + ...PromptStepDef; +} + +model PromptStepDef { /** The prompt to run */ prompt: JinjaTemplate | InputChatMLMessage[]; @@ -76,23 +97,43 @@ model PromptStep extends BaseWorkflowStep { settings: ChatSettings; } -model EvaluateStep extends BaseWorkflowStep { +model EvaluateStep extends BaseWorkflowStep<"evaluate"> { + @visibility("read") kind_: "evaluate" = "evaluate"; + ...EvaluateStepDef; +} + +model EvaluateStepDef { /** The expression to evaluate */ evaluate: ExpressionObject; } -model WaitForInputStep extends BaseWorkflowStep { +model WaitForInputStep extends BaseWorkflowStep<"wait_for_input"> { + @visibility("read") kind_: "wait_for_input" = "wait_for_input"; + ...WaitForInputStepDef; +} + +model WaitForInputInfo { /** Any additional info or data */ - wait_for_input: ExpressionObject; + info: ExpressionObject; } -model LogStep extends BaseWorkflowStep { +model WaitForInputStepDef { + /** Any additional info or data */ + wait_for_input: WaitForInputInfo; +} + +model LogStep extends BaseWorkflowStep<"log"> { + @visibility("read") kind_: "log" = "log"; + ...LogStepDef; +} + +model LogStepDef { /** The value to log */ log: TypedExpression; } @@ -101,16 +142,26 @@ model LogStep extends BaseWorkflowStep { /// Doc search steps /// //////////////////////// -model EmbedStep extends BaseWorkflowStep { +model EmbedStep extends BaseWorkflowStep<"embed"> { + @visibility("read") kind_: "embed" = "embed"; + ...EmbedStepDef; +} + +model EmbedStepDef { /** The text to embed */ embed: EmbedQueryRequest; } -model SearchStep extends BaseWorkflowStep { +model SearchStep extends BaseWorkflowStep<"search"> { + @visibility("read") kind_: "search" = "search"; + ...SearchStepDef; +} + +model SearchStepDef { /** The search query */ search: DocSearchRequest; } @@ -119,9 +170,14 @@ model SearchStep extends BaseWorkflowStep { /// Key-value steps /// /////////////////////// -model GetStep extends BaseWorkflowStep { +model GetStep extends BaseWorkflowStep<"get"> { + @visibility("read") kind_: "get" = "get"; + ...GetStepDef; +} + +model GetStepDef { /** The key to get */ get: string; } @@ -134,9 +190,14 @@ model SetKey { value: TypedExpression; } -model SetStep extends BaseWorkflowStep { +model SetStep extends BaseWorkflowStep<"set"> { + @visibility("read") kind_: "set" = "set"; + ...SetStepDef; +} + +model SetStepDef { /** The value to set */ set: SetKey; } @@ -145,54 +206,118 @@ model SetStep extends BaseWorkflowStep { /// Iteration steps /// /////////////////////// - -model ParallelStep extends BaseWorkflowStep { +model ParallelStep extends BaseWorkflowStep<"parallel"> { + @visibility("read") kind_: "parallel" = "parallel"; - /** The steps to run in parallel. Max concurrency will depend on the platform */ - parallel: NonConditionalWorkflowStep[]; + ...ParallelStepDef; +} + +model ParallelStepDef { + /** The steps to run in parallel. Max concurrency will depend on the platform. */ + @maxItems(100) + parallel: MappableWorkflowStep[]; } model ForeachDo { - /** The variable to iterate over */ + /** The variable to iterate over. + * VALIDATION: Should NOT return more than 1000 elements. */ in: TypedExpression>; /** The steps to run for each iteration */ - do: NonConditionalWorkflowStep; + do: MappableWorkflowStep; } -model ForeachStep extends BaseWorkflowStep { +model ForeachStep extends BaseWorkflowStep<"foreach"> { + @visibility("read") kind_: "foreach" = "foreach"; + ...ForeachStepDef; +} + +model ForeachStepDef { /** The steps to run for each iteration */ foreach: ForeachDo; } -model MapOver { +model BaseMapOver { /** The variable to iterate over */ over: TypedExpression>; +} - /** The subworkflow to run for each iteration */ - workflow: string; +model MapOverEvaluate extends EvaluateStepDef { + ...BaseMapOver; +} + +model MapOverToolCall extends ToolCallStepDef { + ...BaseMapOver; +} + +model MapOverPrompt extends PromptStepDef { + ...BaseMapOver; +} + +model MapOverGet extends GetStepDef { + ...BaseMapOver; +} + +model MapOverSet extends SetStepDef { + ...BaseMapOver; +} + +model MapOverLog extends LogStepDef { + ...BaseMapOver; +} + +model MapOverEmbed extends EmbedStepDef { + ...BaseMapOver; +} + +model MapOverSearch extends SearchStepDef { + ...BaseMapOver; } -model MapReduceStep extends BaseWorkflowStep { +alias MapOver = + | MapOverEvaluate + | MapOverToolCall + | MapOverPrompt + | MapOverGet + | MapOverSet + | MapOverLog + | MapOverEmbed + | MapOverSearch; + +model MapReduceStep> extends BaseWorkflowStep<"map_reduce"> { + @visibility("read") kind_: "map_reduce" = "map_reduce"; + ...MapReduceStepDef; +} + +model MapReduceStepDef> { /** The steps to run for each iteration */ map: MapOver; - /** The expression to reduce the results (`_` is a list of outputs). If not provided, the results are returned as a list. */ - reduce: TypedExpression | "_" = "_"; + /** The expression to reduce the results. + * If not provided, the results are collected and returned as a list. + * A special parameter named `results` is the accumulator and `_` is the current value. */ + reduce?: ReduceExpression; + + initial?: TypedExpression = "[]"; } ///////////////////////// /// Conditional steps /// ///////////////////////// -model IfElseWorkflowStep extends BaseWorkflowStep { +model IfElseWorkflowStep extends BaseWorkflowStep<"if_else"> { + @visibility("read") kind_: "if_else" = "if_else"; + ...IfElseWorkflowStepDef; +} + +model IfElseWorkflowStepDef { /** The condition to evaluate */ `if`: TypedExpression; @@ -205,15 +330,20 @@ model IfElseWorkflowStep extends BaseWorkflowStep { model CaseThen { /** The condition to evaluate */ - case: TypedExpression | "_"; // To support '_' as a value + case: TypedExpression | "_"; // To support '_' as a value /** The steps to run if the condition is true */ then: NonConditionalWorkflowStep; } -model SwitchStep extends BaseWorkflowStep { +model SwitchStep extends BaseWorkflowStep<"switch"> { + @visibility("read") kind_: "switch" = "switch"; + ...SwitchStepDef; +} + +model SwitchStepDef { /** The cond tree */ @minItems(1) switch: CaseThen[]; @@ -223,19 +353,31 @@ model SwitchStep extends BaseWorkflowStep { /// Other control flow /// ////////////////////////// -model YieldStep extends BaseWorkflowStep { +model YieldStep extends BaseWorkflowStep<"yield"> { + @visibility("read") kind_: "yield" = "yield"; - /** The subworkflow to run */ + ...YieldStepDef; +} + +model YieldStepDef { + /** The subworkflow to run. + * VALIDATION: Should resolve to a defined subworkflow. + */ workflow: string; /** The input parameters for the subworkflow (defaults to last step output) */ arguments: ExpressionObject | "_" = "_"; } -model ErrorWorkflowStep extends BaseWorkflowStep { +model ErrorWorkflowStep extends BaseWorkflowStep<"error"> { + @visibility("read") kind_: "error" = "error"; + ...ErrorWorkflowStepDef; +} + +model ErrorWorkflowStepDef { /** The error message */ error: string; } @@ -262,16 +404,26 @@ model SleepFor { days: uint16 = 0; } -model SleepStep extends BaseWorkflowStep { +model SleepStep extends BaseWorkflowStep<"sleep"> { + @visibility("read") kind_: "sleep" = "sleep"; + ...SleepStepDef; +} + +model SleepStepDef { /** The duration to sleep for (max 31 days) */ sleep: SleepFor; } -model ReturnStep extends BaseWorkflowStep { +model ReturnStep extends BaseWorkflowStep<"return"> { + @visibility("read") kind_: "return" = "return"; + ...ReturnStepDef; +} + +model ReturnStepDef { /** The value to return */ `return`: ExpressionObject; } From 3482bd4ac59295d878db8cd81db7bf8d075e2fcd Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Wed, 21 Aug 2024 23:14:57 +0300 Subject: [PATCH 093/110] feat: Add map reduce step --- .../activities/task_steps/__init__.py | 1 + .../agents_api/workflows/task_execution.py | 96 ++++++++++--------- agents-api/tests/test_execution_workflow.py | 46 +++++++++ 3 files changed, 100 insertions(+), 43 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 2f63fb81e..198e59588 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -4,6 +4,7 @@ from .for_each_step import for_each_step from .if_else_step import if_else_step from .log_step import log_step +from .map_reduce_step import map_reduce_step from .prompt_step import prompt_step from .raise_complete_async import raise_complete_async from .return_step import return_step diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index f608737c2..414ad8af8 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -5,6 +5,7 @@ from datetime import timedelta from typing import Any +from simpleeval import simple_eval from temporalio import workflow from temporalio.exceptions import ApplicationError @@ -29,8 +30,6 @@ WaitForInputStep, Workflow, YieldStep, - MapReduceStep, - MapOver, ) from ..common.protocol.tasks import ( ExecutionInput, @@ -54,6 +53,7 @@ YieldStep: task_steps.yield_step, IfElseWorkflowStep: task_steps.if_else_step, ForeachStep: task_steps.for_each_step, + MapReduceStep: task_steps.map_reduce_step, } # TODO: Avoid local activities for now (currently experimental) @@ -158,11 +158,40 @@ async def transition(**kwargs) -> None: return output # <--- Byeeee! case SwitchStep(switch=switch), StepOutcome(output=index) if index >= 0: - raise NotImplementedError("SwitchStep is not implemented") + chosen_branch = switch[index] + + # Create a faux workflow + case_wf_name = ( + f"`{context.cursor.workflow}`[{context.cursor.step}].case" + ) + + case_task = execution_input.task.model_copy() + case_task.workflows = [ + Workflow(name=case_wf_name, steps=[chosen_branch.then]) + ] + + # Create a new execution input + case_execution_input = execution_input.model_copy() + case_execution_input.task = case_task + + # Set the next target to the chosen branch + case_next_target = TransitionTarget(workflow=case_wf_name, step=0) + + case_args = [ + case_execution_input, + case_next_target, + previous_inputs, + ] + + # Execute the chosen branch and come back here + state.output = await workflow.execute_child_workflow( + TaskExecutionWorkflow.run, + args=case_args, + ) case SwitchStep(), StepOutcome(output=index) if index < 0: # If no case matched, then the output will be -1 - raise NotImplementedError("SwitchStep is not implemented") + raise ApplicationError("Negative indices not allowed") case IfElseWorkflowStep(then=then_branch, else_=else_branch), StepOutcome( output=condition @@ -231,17 +260,18 @@ async def transition(**kwargs) -> None: args=foreach_args, ) - case MapReduceStep(map=MapOver(workflow=workflow_name)), StepOutcome(output=items): - for item in items: + case MapReduceStep(reduce=reduce, initial=initial), StepOutcome( + output=items + ): + for i, item in enumerate(items): + workflow_name = f"`{context.cursor.workflow}`[{context.cursor.step}].mapreduce[{i}]" map_reduce_task = execution_input.task.model_copy() - # TODO: set steps list - map_reduce_task.workflows = [ - Workflow(name=workflow_name, steps=[]) - ] + # TODO: set steps + map_reduce_task.workflows = [Workflow(name=workflow_name, steps=[])] # Create a new execution input map_reduce_execution_input = execution_input.model_copy() - map_reduce_execution_input.task = foreach_task + map_reduce_execution_input.task = map_reduce_task # Set the next target to the chosen branch map_reduce_next_target = TransitionTarget( @@ -255,43 +285,23 @@ async def transition(**kwargs) -> None: ] # Execute the chosen branch and come back here - state.output = await workflow.execute_child_workflow( + output = await workflow.execute_child_workflow( TaskExecutionWorkflow.run, args=map_reduce_args, ) - - case SwitchStep(switch=cases), StepOutcome(output=int(case_num)): - if case_num > 0: - chosen_branch = cases[case_num] - - # Create a faux workflow - case_wf_name = ( - f"`{context.cursor.workflow}`[{context.cursor.step}].case" + initial = simple_eval( + reduce, names={"initial": initial, "output": output} ) - case_task = execution_input.task.model_copy() - case_task.workflows = [ - Workflow(name=case_wf_name, steps=[chosen_branch.then]) - ] - - # Create a new execution input - case_execution_input = execution_input.model_copy() - case_execution_input.task = case_task - - # Set the next target to the chosen branch - case_next_target = TransitionTarget(workflow=case_wf_name, step=0) - - case_args = [ - case_execution_input, - case_next_target, - previous_inputs, - ] - - # Execute the chosen branch and come back here - state.output = await workflow.execute_child_workflow( - TaskExecutionWorkflow.run, - args=case_args, - ) + transition_request = CreateTransitionRequest( + current=context.cursor, + initial=initial, + ) + state.output = await execute_activity( + task_steps.transition_step, + args=[context, transition_request], + schedule_to_close_timeout=timedelta(seconds=600), + ) case SleepStep( sleep=SleepFor( diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index b34a58678..307a4fa07 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -599,3 +599,49 @@ async def _( result = await handle.result() assert result["hello"] == "world" + + +@test("workflow: map reduce step") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + { + "map": {"over": "'a b c'.split()"}, + "reduce": "result.append(_)", + "initial": [], + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == "world" From 30814b54ca73675d263b53286f15def0804bb468 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Wed, 21 Aug 2024 16:59:24 -0400 Subject: [PATCH 094/110] feat(agents-api): Switch step test passes! Signed-off-by: Diwank Tomer --- agents-api/tests/test_execution_workflow.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 307a4fa07..ae215085d 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -496,8 +496,7 @@ async def _( assert result["hello"] == "world" -# FIXME: Re enable this test -# @test("workflow: switch step") +@test("workflow: switch step") async def _( client=cozo_client, developer_id=test_developer_id, @@ -601,7 +600,8 @@ async def _( assert result["hello"] == "world" -@test("workflow: map reduce step") +# FIXME: Re enable this test +# @test("workflow: map reduce step") async def _( client=cozo_client, developer_id=test_developer_id, From a84cfcdcad3cf323b47ae6404ed7fb4a632dc839 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Thu, 22 Aug 2024 11:40:45 +0300 Subject: [PATCH 095/110] fix: Update map reduce logic --- .../agents_api/workflows/task_execution.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 414ad8af8..3eeca7be6 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -5,6 +5,7 @@ from datetime import timedelta from typing import Any +from pydantic import RootModel from simpleeval import simple_eval from temporalio import workflow from temporalio.exceptions import ApplicationError @@ -29,6 +30,7 @@ TransitionTarget, WaitForInputStep, Workflow, + WorkflowStep, YieldStep, ) from ..common.protocol.tasks import ( @@ -66,6 +68,8 @@ # IfElseWorkflowStep: task_steps.if_else_step, } +GenericStep = RootModel[WorkflowStep] + @workflow.defn class TaskExecutionWorkflow: @@ -260,14 +264,18 @@ async def transition(**kwargs) -> None: args=foreach_args, ) - case MapReduceStep(reduce=reduce, initial=initial), StepOutcome( - output=items - ): + case MapReduceStep( + map=map_defn, reduce=reduce, initial=initial + ), StepOutcome(output=items): for i, item in enumerate(items): workflow_name = f"`{context.cursor.workflow}`[{context.cursor.step}].mapreduce[{i}]" map_reduce_task = execution_input.task.model_copy() - # TODO: set steps - map_reduce_task.workflows = [Workflow(name=workflow_name, steps=[])] + defn_dict = map_defn.model_dump() + defn_dict.pop("over") + step_defn = GenericStep(**defn_dict) + map_reduce_task.workflows = [ + Workflow(name=workflow_name, steps=[step_defn]) + ] # Create a new execution input map_reduce_execution_input = execution_input.model_copy() From 14c825848620aae5771b05a52c5549eb3e815af0 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Thu, 22 Aug 2024 16:51:03 +0300 Subject: [PATCH 096/110] fix: Fix map reduce test --- agents-api/tests/test_execution_workflow.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index ae215085d..0cae1e7c4 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -5,7 +5,7 @@ from google.protobuf.json_format import MessageToDict from ward import raises, test -from agents_api.autogen.openapi_model import CreateExecutionRequest, CreateTaskRequest +from agents_api.autogen.openapi_model import CreateExecutionRequest, CreateTaskRequest, MainModel, MapOverEvaluate from agents_api.models.task.create_task import create_task from agents_api.routers.tasks.create_task_execution import start_execution @@ -600,8 +600,7 @@ async def _( assert result["hello"] == "world" -# FIXME: Re enable this test -# @test("workflow: map reduce step") +@test("workflow: map reduce step") async def _( client=cozo_client, developer_id=test_developer_id, @@ -619,12 +618,11 @@ async def _( "input_schema": {"type": "object", "additionalProperties": True}, "main": [ { - "map": {"over": "'a b c'.split()"}, - "reduce": "result.append(_)", - "initial": [], + "over": "'a b c'.split()", + "evaluate": { "res": "_" }, }, ], - } + }, ), client=client, ) @@ -644,4 +642,4 @@ async def _( mock_run_task_execution_workflow.assert_called_once() result = await handle.result() - assert result["hello"] == "world" + assert result["res"] == {"test": "input"} From 81af53682ecab4870ef3326dd50cac2de7b6d191 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Thu, 22 Aug 2024 17:38:42 +0300 Subject: [PATCH 097/110] chore: Enable prompt step --- .../agents_api/workflows/task_execution.py | 7 ++- agents-api/tests/test_execution_workflow.py | 59 ++++++++++++++++++- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 3eeca7be6..1995b68b0 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -21,7 +21,7 @@ IfElseWorkflowStep, LogStep, MapReduceStep, - # PromptStep, + PromptStep, ReturnStep, SleepFor, SleepStep, @@ -43,7 +43,7 @@ STEP_TO_ACTIVITY = { - # PromptStep: prompt_step, + PromptStep: task_steps.prompt_step, # ToolCallStep: tool_call_step, WaitForInputStep: task_steps.wait_for_input_step, SwitchStep: task_steps.switch_step, @@ -360,6 +360,9 @@ async def transition(**kwargs) -> None: schedule_to_close_timeout=timedelta(days=31), ) + case PromptStep(), StepOutcome(output=response): + state.output = response.get("choices", [{}])[0].get("message") + case _: raise ApplicationError("Not implemented") diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 0cae1e7c4..c55e9982d 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -5,7 +5,12 @@ from google.protobuf.json_format import MessageToDict from ward import raises, test -from agents_api.autogen.openapi_model import CreateExecutionRequest, CreateTaskRequest, MainModel, MapOverEvaluate +from agents_api.autogen.openapi_model import ( + CreateExecutionRequest, + CreateTaskRequest, + MainModel, + MapOverEvaluate, +) from agents_api.models.task.create_task import create_task from agents_api.routers.tasks.create_task_execution import start_execution @@ -619,7 +624,7 @@ async def _( "main": [ { "over": "'a b c'.split()", - "evaluate": { "res": "_" }, + "evaluate": {"res": "_"}, }, ], }, @@ -643,3 +648,53 @@ async def _( result = await handle.result() assert result["res"] == {"test": "input"} + + +@test("workflow: prompt step") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + { + "prompt": [ + { + "role": "user", + "content": "message", + }, + ], + "settings": {}, + }, + ], + } + ), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["hello"] == "world" From c3d9ce722b27bdf2e2d760483fa1d6d2e231eca3 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Thu, 22 Aug 2024 21:31:52 +0300 Subject: [PATCH 098/110] fix: Fix prompt step test --- agents-api/tests/test_execution_workflow.py | 82 ++++++++++++--------- 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index c55e9982d..e862a89cf 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -1,8 +1,10 @@ # Tests for task queries import asyncio +from unittest.mock import patch from google.protobuf.json_format import MessageToDict +from litellm.types.utils import Choices, ModelResponse from ward import raises, test from agents_api.autogen.openapi_model import ( @@ -17,6 +19,8 @@ from .fixtures import cozo_client, test_agent, test_developer_id from .utils import patch_testing_temporal +EMBEDDING_SIZE: int = 1024 + @test("workflow: evaluate step single") async def _( @@ -656,45 +660,55 @@ async def _( developer_id=test_developer_id, agent=test_agent, ): - data = CreateExecutionRequest(input={"test": "input"}) - - task = create_task( - developer_id=developer_id, - agent_id=agent.id, - data=CreateTaskRequest( - **{ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - "main": [ - { - "prompt": [ - { - "role": "user", - "content": "message", - }, - ], - "settings": {}, - }, - ], - } - ), - client=client, + mock_model_response = ModelResponse( + id="fake_id", + choices=[Choices(message={"role": "assistant", "content": "Hello, world!"})], + created=0, + object="text_completion", ) - async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): - execution, handle = await start_execution( + with patch("agents_api.clients.litellm.acompletion") as acompletion: + acompletion.return_value = mock_model_response + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( developer_id=developer_id, - task_id=task.id, - data=data, + agent_id=agent.id, + data=CreateTaskRequest( + **{ + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [ + { + "prompt": [ + { + "role": "user", + "content": "message", + }, + ], + "settings": {}, + }, + ], + } + ), client=client, ) - assert handle is not None - assert execution.task_id == task.id - assert execution.input == data.input + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) - mock_run_task_execution_workflow.assert_called_once() + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input - result = await handle.result() - assert result["hello"] == "world" + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["content"] == "Hello, world!" + assert result["role"] == "assistant" From 2a12e3c4dd4bdfb239576f50c9610adc84bd793f Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Thu, 22 Aug 2024 23:53:40 -0400 Subject: [PATCH 099/110] fix(agents-api): Fix map reduce step and activity Signed-off-by: Diwank Tomer --- .../activities/task_steps/__init__.py | 1 + .../activities/task_steps/base_evaluate.py | 44 +++ .../activities/task_steps/evaluate_step.py | 29 +- .../activities/task_steps/for_each_step.py | 10 +- .../activities/task_steps/if_else_step.py | 4 +- .../activities/task_steps/log_step.py | 4 +- .../activities/task_steps/map_reduce_step.py | 11 +- .../activities/task_steps/return_step.py | 4 +- .../activities/task_steps/switch_step.py | 7 +- .../agents_api/activities/task_steps/utils.py | 11 - .../task_steps/wait_for_input_step.py | 17 +- .../activities/task_steps/yield_step.py | 6 +- agents-api/agents_api/activities/utils.py | 17 ++ agents-api/agents_api/autogen/Common.py | 10 - agents-api/agents_api/autogen/Executions.py | 2 +- agents-api/agents_api/autogen/Tasks.py | 252 ++++------------- .../agents_api/autogen/openapi_model.py | 4 +- .../agents_api/common/protocol/tasks.py | 4 +- .../agents_api/workflows/task_execution.py | 31 ++- agents-api/demo/example.py | 89 ------ agents-api/demo/julep.py | 132 --------- agents-api/demo/requirements.txt | 1 - agents-api/poetry.lock | 260 +++++++++--------- agents-api/tests/test_execution_workflow.py | 40 ++- sdks/python/poetry.lock | 250 ++++++++--------- sdks/ts/src/api/index.ts | 34 +-- .../src/api/models/Executions_Transition.ts | 2 +- .../src/api/models/Tasks_CreateTaskRequest.ts | 30 +- sdks/ts/src/api/models/Tasks_EmbedStepDef.ts | 11 - .../src/api/models/Tasks_EvaluateStepDef.ts | 11 - sdks/ts/src/api/models/Tasks_GetStepDef.ts | 10 - sdks/ts/src/api/models/Tasks_MapOverEmbed.ts | 12 - .../src/api/models/Tasks_MapOverEvaluate.ts | 12 - sdks/ts/src/api/models/Tasks_MapOverGet.ts | 12 - sdks/ts/src/api/models/Tasks_MapOverLog.ts | 12 - sdks/ts/src/api/models/Tasks_MapOverPrompt.ts | 12 - sdks/ts/src/api/models/Tasks_MapOverSearch.ts | 12 - sdks/ts/src/api/models/Tasks_MapOverSet.ts | 12 - .../src/api/models/Tasks_MapOverToolCall.ts | 12 - .../src/api/models/Tasks_PatchTaskRequest.ts | 30 +- sdks/ts/src/api/models/Tasks_PromptStepDef.ts | 16 -- sdks/ts/src/api/models/Tasks_SearchStepDef.ts | 16 -- sdks/ts/src/api/models/Tasks_SetStepDef.ts | 11 - sdks/ts/src/api/models/Tasks_Task.ts | 30 +- .../src/api/models/Tasks_ToolCallStepDef.ts | 16 -- .../src/api/models/Tasks_UpdateTaskRequest.ts | 30 +- ...ogStepDef.ts => Tasks_WaitForInputInfo.ts} | 6 +- .../src/api/models/Tasks_WaitForInputStep.ts | 4 +- .../src/api/schemas/$Executions_Transition.ts | 5 +- .../api/schemas/$Tasks_CreateTaskRequest.ts | 33 ++- .../ts/src/api/schemas/$Tasks_EmbedStepDef.ts | 18 -- sdks/ts/src/api/schemas/$Tasks_GetStepDef.ts | 13 - sdks/ts/src/api/schemas/$Tasks_LogStepDef.ts | 18 -- .../ts/src/api/schemas/$Tasks_MapOverEmbed.ts | 26 -- .../src/api/schemas/$Tasks_MapOverEvaluate.ts | 26 -- sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts | 26 -- sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts | 26 -- .../src/api/schemas/$Tasks_MapOverPrompt.ts | 26 -- .../src/api/schemas/$Tasks_MapOverSearch.ts | 26 -- sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts | 26 -- .../src/api/schemas/$Tasks_MapOverToolCall.ts | 26 -- .../api/schemas/$Tasks_PatchTaskRequest.ts | 33 ++- .../src/api/schemas/$Tasks_PromptStepDef.ts | 28 -- .../src/api/schemas/$Tasks_SearchStepDef.ts | 24 -- sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts | 18 -- sdks/ts/src/api/schemas/$Tasks_Task.ts | 33 ++- .../src/api/schemas/$Tasks_ToolCallStepDef.ts | 34 --- .../api/schemas/$Tasks_UpdateTaskRequest.ts | 33 ++- ...eStepDef.ts => $Tasks_WaitForInputInfo.ts} | 4 +- .../api/schemas/$Tasks_WaitForInputStep.ts | 11 +- typespec/executions/models.tsp | 4 +- typespec/tasks/steps.tsp | 58 +--- 72 files changed, 610 insertions(+), 1558 deletions(-) create mode 100644 agents-api/agents_api/activities/task_steps/base_evaluate.py delete mode 100644 agents-api/agents_api/activities/task_steps/utils.py create mode 100644 agents-api/agents_api/activities/utils.py delete mode 100644 agents-api/demo/example.py delete mode 100644 agents-api/demo/julep.py delete mode 100644 agents-api/demo/requirements.txt delete mode 100644 sdks/ts/src/api/models/Tasks_EmbedStepDef.ts delete mode 100644 sdks/ts/src/api/models/Tasks_EvaluateStepDef.ts delete mode 100644 sdks/ts/src/api/models/Tasks_GetStepDef.ts delete mode 100644 sdks/ts/src/api/models/Tasks_MapOverEmbed.ts delete mode 100644 sdks/ts/src/api/models/Tasks_MapOverEvaluate.ts delete mode 100644 sdks/ts/src/api/models/Tasks_MapOverGet.ts delete mode 100644 sdks/ts/src/api/models/Tasks_MapOverLog.ts delete mode 100644 sdks/ts/src/api/models/Tasks_MapOverPrompt.ts delete mode 100644 sdks/ts/src/api/models/Tasks_MapOverSearch.ts delete mode 100644 sdks/ts/src/api/models/Tasks_MapOverSet.ts delete mode 100644 sdks/ts/src/api/models/Tasks_MapOverToolCall.ts delete mode 100644 sdks/ts/src/api/models/Tasks_PromptStepDef.ts delete mode 100644 sdks/ts/src/api/models/Tasks_SearchStepDef.ts delete mode 100644 sdks/ts/src/api/models/Tasks_SetStepDef.ts delete mode 100644 sdks/ts/src/api/models/Tasks_ToolCallStepDef.ts rename sdks/ts/src/api/models/{Tasks_LogStepDef.ts => Tasks_WaitForInputInfo.ts} (64%) delete mode 100644 sdks/ts/src/api/schemas/$Tasks_EmbedStepDef.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_GetStepDef.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_LogStepDef.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverEmbed.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverEvaluate.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverPrompt.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverSearch.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_MapOverToolCall.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_PromptStepDef.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_SearchStepDef.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts delete mode 100644 sdks/ts/src/api/schemas/$Tasks_ToolCallStepDef.ts rename sdks/ts/src/api/schemas/{$Tasks_EvaluateStepDef.ts => $Tasks_WaitForInputInfo.ts} (83%) diff --git a/agents-api/agents_api/activities/task_steps/__init__.py b/agents-api/agents_api/activities/task_steps/__init__.py index 198e59588..9646152cf 100644 --- a/agents-api/agents_api/activities/task_steps/__init__.py +++ b/agents-api/agents_api/activities/task_steps/__init__.py @@ -1,5 +1,6 @@ # ruff: noqa: F401, F403, F405 +from .base_evaluate import base_evaluate from .evaluate_step import evaluate_step from .for_each_step import for_each_step from .if_else_step import if_else_step diff --git a/agents-api/agents_api/activities/task_steps/base_evaluate.py b/agents-api/agents_api/activities/task_steps/base_evaluate.py new file mode 100644 index 000000000..4b98cd13a --- /dev/null +++ b/agents-api/agents_api/activities/task_steps/base_evaluate.py @@ -0,0 +1,44 @@ +from typing import Any + +from beartype import beartype +from temporalio import activity + +from ...env import testing +from ..utils import get_evaluator + + +@beartype +async def base_evaluate( + exprs: str | list[str] | dict[str, str], + values: dict[str, Any] = {}, +) -> Any | list[Any] | dict[str, Any]: + input_len = 1 if isinstance(exprs, str) else len(exprs) + assert input_len > 0, "exprs must be a non-empty string, list or dict" + + evaluator = get_evaluator(names=values) + + try: + match exprs: + case str(): + return evaluator.eval(exprs) + + case list(): + return [evaluator.eval(expr) for expr in exprs] + + case dict(): + return {k: evaluator.eval(v) for k, v in exprs.items()} + + except BaseException as e: + if activity.in_activity(): + activity.logger.error(f"Error in base_evaluate: {e}") + + raise + + +# Note: This is here just for clarity. We could have just imported base_evaluate directly +# They do the same thing, so we dont need to mock the base_evaluate function +mock_base_evaluate = base_evaluate + +base_evaluate = activity.defn(name="base_evaluate")( + base_evaluate if not testing else mock_base_evaluate +) diff --git a/agents-api/agents_api/activities/task_steps/evaluate_step.py b/agents-api/agents_api/activities/task_steps/evaluate_step.py index e4cc194c2..6b1650ff4 100644 --- a/agents-api/agents_api/activities/task_steps/evaluate_step.py +++ b/agents-api/agents_api/activities/task_steps/evaluate_step.py @@ -1,28 +1,35 @@ +from typing import Any + from beartype import beartype from temporalio import activity -from ...activities.task_steps.utils import simple_eval_dict -from ...autogen.openapi_model import EvaluateStep +from ...activities.utils import simple_eval_dict from ...common.protocol.tasks import StepContext, StepOutcome from ...env import testing @beartype -async def evaluate_step(context: StepContext) -> StepOutcome: - # NOTE: This activity is only for returning immediately, so we just evaluate the expression - # Hence, it's a local activity and SHOULD NOT fail +async def evaluate_step( + context: StepContext, + additional_values: dict[str, Any] = {}, + override_expr: dict[str, str] | None = None, +) -> StepOutcome: try: - assert isinstance(context.current_step, EvaluateStep) - - exprs = context.current_step.evaluate - output = simple_eval_dict(exprs, values=context.model_dump()) - + expr = ( + override_expr + if override_expr is not None + else context.current_step.evaluate + ) + + values = context.model_dump() | additional_values + output = simple_eval_dict(expr, values) result = StepOutcome(output=output) + return result except BaseException as e: activity.logger.error(f"Error in evaluate_step: {e}") - return StepOutcome(error=str(e)) + return StepOutcome(error=str(e) or repr(e)) # Note: This is here just for clarity. We could have just imported evaluate_step directly diff --git a/agents-api/agents_api/activities/task_steps/for_each_step.py b/agents-api/agents_api/activities/task_steps/for_each_step.py index f2f24430f..45f6d11dc 100644 --- a/agents-api/agents_api/activities/task_steps/for_each_step.py +++ b/agents-api/agents_api/activities/task_steps/for_each_step.py @@ -1,7 +1,6 @@ import logging from beartype import beartype -from simpleeval import simple_eval from temporalio import activity from ...autogen.openapi_model import ForeachStep @@ -10,6 +9,7 @@ StepOutcome, ) from ...env import testing +from .base_evaluate import base_evaluate @beartype @@ -17,11 +17,11 @@ async def for_each_step(context: StepContext) -> StepOutcome: try: assert isinstance(context.current_step, ForeachStep) - return StepOutcome( - output=simple_eval( - context.current_step.foreach.in_, names=context.model_dump() - ) + output = await base_evaluate( + context.current_step.foreach.in_, context.model_dump() ) + return StepOutcome(output=output) + except BaseException as e: logging.error(f"Error in for_each_step: {e}") return StepOutcome(error=str(e)) diff --git a/agents-api/agents_api/activities/task_steps/if_else_step.py b/agents-api/agents_api/activities/task_steps/if_else_step.py index 7ce9e44e7..ecb935ca6 100644 --- a/agents-api/agents_api/activities/task_steps/if_else_step.py +++ b/agents-api/agents_api/activities/task_steps/if_else_step.py @@ -1,5 +1,4 @@ from beartype import beartype -from simpleeval import simple_eval from temporalio import activity from ...autogen.openapi_model import IfElseWorkflowStep @@ -8,6 +7,7 @@ StepOutcome, ) from ...env import testing +from .base_evaluate import base_evaluate @beartype @@ -18,7 +18,7 @@ async def if_else_step(context: StepContext) -> StepOutcome: assert isinstance(context.current_step, IfElseWorkflowStep) expr: str = context.current_step.if_ - output = simple_eval(expr, names=context.model_dump()) + output = await base_evaluate(expr, context.model_dump()) output: bool = bool(output) result = StepOutcome(output=output) diff --git a/agents-api/agents_api/activities/task_steps/log_step.py b/agents-api/agents_api/activities/task_steps/log_step.py index 1ed6d2493..b33409474 100644 --- a/agents-api/agents_api/activities/task_steps/log_step.py +++ b/agents-api/agents_api/activities/task_steps/log_step.py @@ -1,5 +1,4 @@ from beartype import beartype -from simpleeval import simple_eval from temporalio import activity from ...autogen.openapi_model import LogStep @@ -8,6 +7,7 @@ StepOutcome, ) from ...env import testing +from .base_evaluate import base_evaluate @beartype @@ -18,7 +18,7 @@ async def log_step(context: StepContext) -> StepOutcome: assert isinstance(context.current_step, LogStep) expr: str = context.current_step.log - output = simple_eval(expr, names=context.model_dump()) + output = await base_evaluate(expr, context.model_dump()) result = StepOutcome(output=output) return result diff --git a/agents-api/agents_api/activities/task_steps/map_reduce_step.py b/agents-api/agents_api/activities/task_steps/map_reduce_step.py index 149704999..97fd0c154 100644 --- a/agents-api/agents_api/activities/task_steps/map_reduce_step.py +++ b/agents-api/agents_api/activities/task_steps/map_reduce_step.py @@ -1,7 +1,6 @@ import logging from beartype import beartype -from simpleeval import simple_eval from temporalio import activity from ...autogen.openapi_model import MapReduceStep @@ -10,6 +9,7 @@ StepOutcome, ) from ...env import testing +from .base_evaluate import base_evaluate @beartype @@ -17,11 +17,10 @@ async def map_reduce_step(context: StepContext) -> StepOutcome: try: assert isinstance(context.current_step, MapReduceStep) - return StepOutcome( - output=simple_eval( - context.current_step.map.over, names=context.model_dump() - ) - ) + output = await base_evaluate(context.current_step.over, context.model_dump()) + + return StepOutcome(output=output) + except BaseException as e: logging.error(f"Error in map_reduce_step: {e}") return StepOutcome(error=str(e)) diff --git a/agents-api/agents_api/activities/task_steps/return_step.py b/agents-api/agents_api/activities/task_steps/return_step.py index 2ea0b59e5..1e272bab2 100644 --- a/agents-api/agents_api/activities/task_steps/return_step.py +++ b/agents-api/agents_api/activities/task_steps/return_step.py @@ -1,12 +1,12 @@ from temporalio import activity -from ...activities.task_steps.utils import simple_eval_dict from ...autogen.openapi_model import ReturnStep from ...common.protocol.tasks import ( StepContext, StepOutcome, ) from ...env import testing +from .base_evaluate import base_evaluate async def return_step(context: StepContext) -> StepOutcome: @@ -16,7 +16,7 @@ async def return_step(context: StepContext) -> StepOutcome: assert isinstance(context.current_step, ReturnStep) exprs: dict[str, str] = context.current_step.return_ - output = simple_eval_dict(exprs, values=context.model_dump()) + output = await base_evaluate(exprs, context.model_dump()) result = StepOutcome(output=output) return result diff --git a/agents-api/agents_api/activities/task_steps/switch_step.py b/agents-api/agents_api/activities/task_steps/switch_step.py index 83a315d03..b28150450 100644 --- a/agents-api/agents_api/activities/task_steps/switch_step.py +++ b/agents-api/agents_api/activities/task_steps/switch_step.py @@ -1,5 +1,4 @@ from beartype import beartype -from simpleeval import simple_eval from temporalio import activity from ...autogen.openapi_model import SwitchStep @@ -8,6 +7,7 @@ StepOutcome, ) from ...env import testing +from ..utils import get_evaluator @beartype @@ -17,11 +17,12 @@ async def switch_step(context: StepContext) -> StepOutcome: # Assume that none of the cases evaluate to truthy output: int = -1 - cases: list[str] = [c.case for c in context.current_step.switch] + evaluator = get_evaluator(names=context.model_dump()) + for i, case in enumerate(cases): - result = simple_eval(case, names=context.model_dump()) + result = evaluator.eval(case) if result: output = i diff --git a/agents-api/agents_api/activities/task_steps/utils.py b/agents-api/agents_api/activities/task_steps/utils.py deleted file mode 100644 index e3d953a4a..000000000 --- a/agents-api/agents_api/activities/task_steps/utils.py +++ /dev/null @@ -1,11 +0,0 @@ -from typing import Any - -from beartype import beartype -from simpleeval import simple_eval - - -@beartype -def simple_eval_dict( - exprs: dict[str, str], *, values: dict[str, Any] -) -> dict[str, Any]: - return {k: simple_eval(v, names=values) for k, v in exprs.items()} diff --git a/agents-api/agents_api/activities/task_steps/wait_for_input_step.py b/agents-api/agents_api/activities/task_steps/wait_for_input_step.py index 4d4378095..c0666512b 100644 --- a/agents-api/agents_api/activities/task_steps/wait_for_input_step.py +++ b/agents-api/agents_api/activities/task_steps/wait_for_input_step.py @@ -1,19 +1,24 @@ from temporalio import activity -from ...activities.task_steps.utils import simple_eval_dict from ...autogen.openapi_model import WaitForInputStep from ...common.protocol.tasks import StepContext, StepOutcome from ...env import testing +from .base_evaluate import base_evaluate async def wait_for_input_step(context: StepContext) -> StepOutcome: - assert isinstance(context.current_step, WaitForInputStep) + try: + assert isinstance(context.current_step, WaitForInputStep) - exprs = context.current_step.wait_for_input - output = simple_eval_dict(exprs, values=context.model_dump()) + exprs = context.current_step.wait_for_input + output = await base_evaluate(exprs, context.model_dump()) - result = StepOutcome(output=output) - return result + result = StepOutcome(output=output) + return result + + except BaseException as e: + activity.logger.error(f"Error in wait_for_input_step: {e}") + return StepOutcome(error=str(e)) # Note: This is here just for clarity. We could have just imported wait_for_input_step directly diff --git a/agents-api/agents_api/activities/task_steps/yield_step.py b/agents-api/agents_api/activities/task_steps/yield_step.py index 8ee7cfbd8..41fa2eb87 100644 --- a/agents-api/agents_api/activities/task_steps/yield_step.py +++ b/agents-api/agents_api/activities/task_steps/yield_step.py @@ -7,7 +7,7 @@ from ...common.protocol.tasks import StepContext, StepOutcome from ...env import testing -from .utils import simple_eval_dict +from .base_evaluate import base_evaluate @beartype @@ -19,14 +19,14 @@ async def yield_step(context: StepContext) -> StepOutcome: all_workflows = context.execution_input.task.workflows workflow = context.current_step.workflow + exprs = context.current_step.arguments assert workflow in [ wf.name for wf in all_workflows ], f"Workflow {workflow} not found in task" # Evaluate the expressions in the arguments - exprs = context.current_step.arguments - arguments = simple_eval_dict(exprs, values=context.model_dump()) + arguments = await base_evaluate(exprs, context.model_dump()) # Transition to the first step of that workflow transition_target = TransitionTarget( diff --git a/agents-api/agents_api/activities/utils.py b/agents-api/agents_api/activities/utils.py new file mode 100644 index 000000000..f491c7188 --- /dev/null +++ b/agents-api/agents_api/activities/utils.py @@ -0,0 +1,17 @@ +from typing import Any + +from beartype import beartype +from simpleeval import EvalWithCompoundTypes, SimpleEval + + +@beartype +def get_evaluator(names: dict[str, Any]) -> SimpleEval: + evaluator = EvalWithCompoundTypes(names=names) + return evaluator + + +@beartype +def simple_eval_dict(exprs: dict[str, str], values: dict[str, Any]) -> dict[str, Any]: + evaluator = get_evaluator(names=values) + + return {k: evaluator.eval(v) for k, v in exprs.items()} diff --git a/agents-api/agents_api/autogen/Common.py b/agents-api/agents_api/autogen/Common.py index 5f4bd34bd..4bb28d400 100644 --- a/agents-api/agents_api/autogen/Common.py +++ b/agents-api/agents_api/autogen/Common.py @@ -9,16 +9,6 @@ from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel -class JinjaTemplate(RootModel[str]): - model_config = ConfigDict( - populate_by_name=True, - ) - root: str - """ - A valid jinja template. - """ - - class Limit(RootModel[int]): model_config = ConfigDict( populate_by_name=True, diff --git a/agents-api/agents_api/autogen/Executions.py b/agents-api/agents_api/autogen/Executions.py index 904680e44..73dab5d65 100644 --- a/agents-api/agents_api/autogen/Executions.py +++ b/agents-api/agents_api/autogen/Executions.py @@ -83,7 +83,7 @@ class Transition(BaseModel): Field(json_schema_extra={"readOnly": True}), ] execution_id: Annotated[UUID, Field(json_schema_extra={"readOnly": True})] - output: Annotated[dict[str, Any], Field(json_schema_extra={"readOnly": True})] + output: Annotated[Any, Field(json_schema_extra={"readOnly": True})] current: Annotated[TransitionTarget, Field(json_schema_extra={"readOnly": True})] next: Annotated[ TransitionTarget | None, Field(json_schema_extra={"readOnly": True}) diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index a7a018c74..a7641d043 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -89,7 +89,7 @@ class CreateTaskRequest(BaseModel): | SwitchStep | ForeachStep | ParallelStep - | MainModel + | Main ] """ The entrypoint of the task. @@ -125,16 +125,6 @@ class EmbedStep(BaseModel): """ -class EmbedStepDef(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - embed: EmbedQueryRequest - """ - The text to embed - """ - - class ErrorWorkflowStep(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -167,16 +157,6 @@ class EvaluateStep(BaseModel): """ -class EvaluateStepDef(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - evaluate: dict[str, str] - """ - The expression to evaluate - """ - - class ForeachDo(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -231,16 +211,6 @@ class GetStep(BaseModel): """ -class GetStepDef(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - get: str - """ - The key to get - """ - - class IfElseWorkflowStep(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -308,50 +278,7 @@ class LogStep(BaseModel): """ -class LogStepDef(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - log: str - """ - The value to log - """ - - class Main(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - kind_: str | None = None - """ - Discriminator property for BaseWorkflowStep. - """ - map: ( - MapOverEvaluate - | MapOverToolCall - | MapOverPrompt - | MapOverGet - | MapOverSet - | MapOverLog - | MapOverEmbed - | MapOverSearch - ) - """ - The steps to run for each iteration - """ - reduce: str | None = None - """ - The expression to reduce the results. - If not provided, the results are collected and returned as a list. - A special parameter named `results` is the accumulator and `_` is the current value. - """ - initial: str = "[]" - """ - A simple python expression compatible with SimpleEval. - """ - - -class MainModel(BaseModel): model_config = ConfigDict( populate_by_name=True, ) @@ -361,15 +288,19 @@ class MainModel(BaseModel): """ The kind of step """ + over: str + """ + The variable to iterate over + """ map: ( - MapOverEvaluate - | MapOverToolCall - | MapOverPrompt - | MapOverGet - | MapOverSet - | MapOverLog - | MapOverEmbed - | MapOverSearch + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep ) """ The steps to run for each iteration @@ -377,53 +308,44 @@ class MainModel(BaseModel): reduce: str | None = None """ The expression to reduce the results. - If not provided, the results are collected and returned as a list. + If not provided, the results are collected and returned as a list. A special parameter named `results` is the accumulator and `_` is the current value. """ - initial: str = "[]" - """ - A simple python expression compatible with SimpleEval. - """ + initial: Any = [] -class MapOverEmbed(EmbedStepDef): +class MainModel(BaseModel): model_config = ConfigDict( populate_by_name=True, ) - over: str + kind_: str | None = None """ - The variable to iterate over + Discriminator property for BaseWorkflowStep. """ - - -class MapOverEvaluate(EvaluateStepDef): - model_config = ConfigDict( - populate_by_name=True, - ) over: str """ The variable to iterate over """ - - -class MapOverGet(GetStepDef): - model_config = ConfigDict( - populate_by_name=True, + map: ( + EvaluateStep + | ToolCallStep + | PromptStep + | GetStep + | SetStep + | LogStep + | EmbedStep + | SearchStep ) - over: str """ - The variable to iterate over + The steps to run for each iteration """ - - -class MapOverLog(LogStepDef): - model_config = ConfigDict( - populate_by_name=True, - ) - over: str + reduce: str | None = None """ - The variable to iterate over + The expression to reduce the results. + If not provided, the results are collected and returned as a list. + A special parameter named `results` is the accumulator and `_` is the current value. """ + initial: Any = [] class ParallelStep(BaseModel): @@ -482,7 +404,7 @@ class PatchTaskRequest(BaseModel): | SwitchStep | ForeachStep | ParallelStep - | Main + | MainModel ] | None ) = None @@ -554,20 +476,6 @@ class PromptStep(BaseModel): """ -class PromptStepDef(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - prompt: list[PromptItem] | str - """ - The prompt to run - """ - settings: ChatSettings - """ - Settings for the prompt - """ - - class ReturnStep(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -600,16 +508,6 @@ class SearchStep(BaseModel): """ -class SearchStepDef(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - search: VectorDocSearchRequest | TextOnlyDocSearchRequest | HybridDocSearchRequest - """ - The search query - """ - - class SetKey(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -638,16 +536,6 @@ class SetStep(BaseModel): """ -class SetStepDef(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - set: SetKey - """ - The value to set - """ - - class SleepFor(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -730,7 +618,7 @@ class Task(BaseModel): | SwitchStep | ForeachStep | ParallelStep - | MainModel + | Main ] """ The entrypoint of the task. @@ -791,22 +679,6 @@ class ToolCallStep(BaseModel): """ -class ToolCallStepDef(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - tool: Annotated[ - str, Field(pattern="^(function|integration|system|api_call)\\.(\\w+)$") - ] - """ - The tool to run - """ - arguments: dict[str, str] | Literal["_"] = "_" - """ - The input parameters for the tool (defaults to last step output) - """ - - class UpdateTaskRequest(BaseModel): """ Payload for updating a task @@ -834,7 +706,7 @@ class UpdateTaskRequest(BaseModel): | SwitchStep | ForeachStep | ParallelStep - | MainModel + | Main ] """ The entrypoint of the task. @@ -854,6 +726,16 @@ class UpdateTaskRequest(BaseModel): metadata: dict[str, Any] | None = None +class WaitForInputInfo(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + info: dict[str, str] + """ + Any additional info or data + """ + + class WaitForInputStep(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -865,7 +747,7 @@ class WaitForInputStep(BaseModel): """ The kind of step """ - wait_for_input: dict[str, str] + wait_for_input: WaitForInputInfo """ Any additional info or data """ @@ -890,43 +772,3 @@ class YieldStep(BaseModel): """ The input parameters for the subworkflow (defaults to last step output) """ - - -class MapOverPrompt(PromptStepDef): - model_config = ConfigDict( - populate_by_name=True, - ) - over: str - """ - The variable to iterate over - """ - - -class MapOverSearch(SearchStepDef): - model_config = ConfigDict( - populate_by_name=True, - ) - over: str - """ - The variable to iterate over - """ - - -class MapOverSet(SetStepDef): - model_config = ConfigDict( - populate_by_name=True, - ) - over: str - """ - The variable to iterate over - """ - - -class MapOverToolCall(ToolCallStepDef): - model_config = ConfigDict( - populate_by_name=True, - ) - over: str - """ - The variable to iterate over - """ diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index cd50898f1..bdf96cc10 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -42,7 +42,7 @@ class ListResponse(BaseModel, Generic[DataT]): InputChatMLMessage = Message # TODO: Figure out wtf... 🤷‍♂️ -MapReduceStep = MainModel +MapReduceStep = Main # Custom types (not generated correctly) @@ -163,9 +163,9 @@ def from_model_input( | SetStep | GetStep | ForeachStep - | MapReduceStep | ParallelStep | SwitchStep + | MapReduceStep ) diff --git a/agents-api/agents_api/common/protocol/tasks.py b/agents-api/agents_api/common/protocol/tasks.py index 4196d73a7..d0c335e95 100644 --- a/agents-api/agents_api/common/protocol/tasks.py +++ b/agents-api/agents_api/common/protocol/tasks.py @@ -73,7 +73,7 @@ class ExecutionInput(BaseModel): class StepContext(BaseModel): execution_input: ExecutionInput - inputs: list[dict[str, Any]] + inputs: list[Any] cursor: TransitionTarget @computed_field @@ -112,7 +112,7 @@ def model_dump(self, *args, **kwargs) -> dict[str, Any]: class StepOutcome(BaseModel): error: str | None = None - output: Any + output: Any = None transition_to: tuple[TransitionType, TransitionTarget] | None = None diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 1995b68b0..a61996e5f 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -6,7 +6,6 @@ from typing import Any from pydantic import RootModel -from simpleeval import simple_eval from temporalio import workflow from temporalio.exceptions import ApplicationError @@ -267,12 +266,15 @@ async def transition(**kwargs) -> None: case MapReduceStep( map=map_defn, reduce=reduce, initial=initial ), StepOutcome(output=items): + initial = initial or [] + reduce = reduce or "initial + [_]" + for i, item in enumerate(items): workflow_name = f"`{context.cursor.workflow}`[{context.cursor.step}].mapreduce[{i}]" map_reduce_task = execution_input.task.model_copy() + defn_dict = map_defn.model_dump() - defn_dict.pop("over") - step_defn = GenericStep(**defn_dict) + step_defn = GenericStep(**defn_dict).root map_reduce_task.workflows = [ Workflow(name=workflow_name, steps=[step_defn]) ] @@ -289,7 +291,7 @@ async def transition(**kwargs) -> None: map_reduce_args = [ map_reduce_execution_input, map_reduce_next_target, - previous_inputs + [{"item": item}], + previous_inputs + [item], ] # Execute the chosen branch and come back here @@ -297,19 +299,18 @@ async def transition(**kwargs) -> None: TaskExecutionWorkflow.run, args=map_reduce_args, ) - initial = simple_eval( - reduce, names={"initial": initial, "output": output} + + initial = await execute_activity( + task_steps.base_evaluate, + args=[ + reduce, + {"initial": initial, "_": output}, + ], + schedule_to_close_timeout=timedelta(seconds=2), ) - transition_request = CreateTransitionRequest( - current=context.cursor, - initial=initial, - ) - state.output = await execute_activity( - task_steps.transition_step, - args=[context, transition_request], - schedule_to_close_timeout=timedelta(seconds=600), - ) + state.output = initial + await transition() case SleepStep( sleep=SleepFor( diff --git a/agents-api/demo/example.py b/agents-api/demo/example.py deleted file mode 100644 index 737539c93..000000000 --- a/agents-api/demo/example.py +++ /dev/null @@ -1,89 +0,0 @@ -import asyncio -from julep import Client - -client = Client(base_url="0.0.0.0:8080", api_key="myauthkey") - -# Let's create a research assistant -name = "Research Assistant" -description = "This assistant is designed to automate the process of gathering, summarizing, and delivering research on specific topics using web searches and webhooks to integrate with other systems." - -# Let's give it some tools -web_search = { - "type": "search", - "engine": "brave", - "description": "Uses Brave search engine to find relevant information on the web.", -} -call_webhook = { - "type": "http", - "http": { - "endpoint": "http://localhost:9000", - "method": "POST", - "description": "Webhook to deliver research results", - "json": { - "summary": {"type": "string", "description": "Summary of the research"}, - "details": { - "type": "string", - "description": "Detailed search results for further analysis", - }, - }, - }, -} - -agent = client.agents.create( - name=name, - description=description, - tools=[web_search, call_webhook], -) - -# Let's create a task for this agent. -# The agent will perform these tasks: -# 1. Think about the task and make a plan using the tools (given a topic, search the web for it, then summarize it and then send the result to a webhook) -# 2. Think about step 1. And make a tool_call to search the web for the topic with a detailed query -# (the tool returns the results) -# 3. Think about step 2. And then summarize the results received. -# 4. Think about step 3. And make a tool call to the webhook - -instructions = [ - "Consider the research topic and devise a search strategy using the provided tools.", - "Use the 'search' tool to find comprehensive information on the topic from various web sources.", - "Analyze the search results and create a concise summary highlighting the key points.", - "Send the summary and the detailed search results to the specified webhook endpoint for integration into our system.", -] - -task = client.tasks.create( - agent_id=agent.id, - instructions=instructions, - inputs={"topic": {"type": "string", "description": "Topic to research"}}, -) - -# Ask the agent to run this task - -run = client.runs.create( - agent_id=agent.id, task_id=task.id, inputs={"topic": "Sam Altman"} -) - - -async def main(): - async for step in run.execution_steps(): - print(step.messages) - - -# >>> [{"role": "thought", "content": "Starting the research on Sam Altman. I'll begin by gathering information from various sources on the web."}, {"role": "assistant", "tool_calls": [{"type": "search", "inputs": {"query": "Sam Altman significant contributions and background"}}]}] -# # Wait for 3-4 seconds - -# >>> [{"role": "system", "name": "information", "content": "Found numerous articles, interviews, and resources on Sam Altman, including his role at OpenAI, investments, and insights into technology and entrepreneurship."}] -# # Wait for 1 second - -# >>> [{"role": "thought", "content": "Need to summarize this information to capture the essence of Sam Altman's impact."}, {"role": "assistant", "content": "Summary:\nSam Altman, known for his leadership at OpenAI, has been a pivotal figure in the tech industry, driving innovation and supporting startups. His insights on entrepreneurship and the future of AI have influenced a wide audience."}] -# # Wait for 2 sec - -# >>> [{"role": "thought", "content": "Now, I'll send the compiled summary and details to the webhook."}, {"role": "assistant", "tool_calls": [{"type": "http", "endpoint": "http://localhost:9000", "method": "POST", "data": {"summary": "Sam Altman, known for his leadership at OpenAI, has been a pivotal figure in the tech industry, driving innovation and supporting startups. His insights on entrepreneurship and the future of AI have influenced a wide audience.", "details": "Found numerous articles, interviews, and resources on Sam Altman, including his role at OpenAI, investments, and insights into technology and entrepreneurship."}}]}] -# # Wait for 2 sec - -# # POST call should show up on the http.server -# # Wait for 1 second - -# >>> [{"role": "system", "name": "information", "content": "Delivered data to webhook"}] - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/agents-api/demo/julep.py b/agents-api/demo/julep.py deleted file mode 100644 index aaa42f401..000000000 --- a/agents-api/demo/julep.py +++ /dev/null @@ -1,132 +0,0 @@ -import asyncio -import uuid -import httpx - - -PORT = 9000 - - -class Step: - def __init__(self, d): - self._d = d - - @property - def messages(self): - return self._d.get("content", "") - - -class Client: - def __init__(self, base_url, api_key): - self.base_url = base_url - self.api_key = api_key - self.agents = AgentManager() - self.tasks = TaskManager() - self.runs = RunManager() - - -class AgentManager: - def create(self, name, description, tools): - return Agent(name, description, tools) - - -class Agent: - def __init__(self, name, description, tools): - self.id = str(uuid.uuid4()) - self.name = name - self.description = description - self.tools = tools - - -class TaskManager: - def create(self, agent_id, instructions, inputs): - return Task(agent_id, instructions, inputs) - - -class Task: - def __init__(self, agent_id, instructions, inputs): - self.id = str(uuid.uuid4()) - self.agent_id = agent_id - self.instructions = instructions - self.inputs = inputs - - -class RunManager: - def create(self, agent_id, task_id, inputs): - return Run(agent_id, task_id, inputs) - - -class Run: - def __init__(self, agent_id, task_id, inputs): - self.agent_id = agent_id - self.task_id = task_id - self.inputs = inputs - - async def execution_steps(self): - steps = [ - { - "role": "thought", - "content": "Starting the research on Sam Altman. I'll begin by gathering information from various sources on the web.", - }, - { - "role": "assistant", - "tool_calls": [ - { - "type": "search", - "inputs": { - "query": "Sam Altman significant contributions and background" - }, - } - ], - }, - { - "role": "system", - "name": "information", - "content": "Found numerous articles, interviews, and resources on Sam Altman, including his role at OpenAI, investments, and insights into technology and entrepreneurship.", - }, - { - "role": "thought", - "content": "Need to summarize this information to capture the essence of Sam Altman's impact.", - }, - { - "role": "assistant", - "content": "Summary:\nSam Altman, known for his leadership at OpenAI, has been a pivotal figure in the tech industry, driving innovation and supporting startups. His insights on entrepreneurship and the future of AI have influenced a wide audience.", - }, - { - "role": "thought", - "content": "Now, I'll send the compiled summary and details to the webhook.", - }, - { - "role": "assistant", - "tool_calls": [ - { - "type": "http", - "endpoint": f"http://localhost:{PORT}", - "method": "POST", - "data": { - "summary": "Sam Altman, known for his leadership at OpenAI, has been a pivotal figure in the tech industry, driving innovation and supporting startups. His insights on entrepreneurship and the future of AI have influenced a wide audience.", - "details": "Found numerous articles, interviews, and resources on Sam Altman, including his role at OpenAI, investments, and insights into technology and entrepreneurship.", - }, - } - ], - }, - # TODO: @dmitry - # Add a HTTP POST call after this step to 0.0.0.0:8080 with the results - { - "role": "system", - "name": "information", - "content": "Delivered data to webhook", - }, - ] - - for step in steps: - if "tool_calls" in step: - # Simulate tool call delay - await asyncio.sleep(3 if step["role"] == "assistant" else 2) - for c in step["tool_calls"]: - if c.get("endpoint"): - async with httpx.AsyncClient() as client: - await client.post(c["endpoint"], json=c["data"]) - else: - # Simulate thought and system message delay - await asyncio.sleep(1) - yield Step(step) diff --git a/agents-api/demo/requirements.txt b/agents-api/demo/requirements.txt deleted file mode 100644 index 79228389f..000000000 --- a/agents-api/demo/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -httpx \ No newline at end of file diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index c0191ce2c..de023db46 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -1348,21 +1348,21 @@ test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "num [[package]] name = "ipywidgets" -version = "8.1.3" +version = "8.1.5" description = "Jupyter interactive widgets" optional = false python-versions = ">=3.7" files = [ - {file = "ipywidgets-8.1.3-py3-none-any.whl", hash = "sha256:efafd18f7a142248f7cb0ba890a68b96abd4d6e88ddbda483c9130d12667eaf2"}, - {file = "ipywidgets-8.1.3.tar.gz", hash = "sha256:f5f9eeaae082b1823ce9eac2575272952f40d748893972956dc09700a6392d9c"}, + {file = "ipywidgets-8.1.5-py3-none-any.whl", hash = "sha256:3290f526f87ae6e77655555baba4f36681c555b8bdbbff430b70e52c34c86245"}, + {file = "ipywidgets-8.1.5.tar.gz", hash = "sha256:870e43b1a35656a80c18c9503bbf2d16802db1cb487eec6fab27d683381dde17"}, ] [package.dependencies] comm = ">=0.1.3" ipython = ">=6.1.0" -jupyterlab-widgets = ">=3.0.11,<3.1.0" +jupyterlab-widgets = ">=3.0.12,<3.1.0" traitlets = ">=4.3.1" -widgetsnbextension = ">=4.0.11,<4.1.0" +widgetsnbextension = ">=4.0.12,<4.1.0" [package.extras] test = ["ipykernel", "jsonschema", "pytest (>=3.6.0)", "pytest-cov", "pytz"] @@ -1786,13 +1786,13 @@ test = ["hatch", "ipykernel", "openapi-core (>=0.18.0,<0.19.0)", "openapi-spec-v [[package]] name = "jupyterlab-widgets" -version = "3.0.11" +version = "3.0.13" description = "Jupyter interactive widgets for JupyterLab" optional = false python-versions = ">=3.7" files = [ - {file = "jupyterlab_widgets-3.0.11-py3-none-any.whl", hash = "sha256:78287fd86d20744ace330a61625024cf5521e1c012a352ddc0a3cdc2348becd0"}, - {file = "jupyterlab_widgets-3.0.11.tar.gz", hash = "sha256:dd5ac679593c969af29c9bed054c24f26842baa51352114736756bc035deee27"}, + {file = "jupyterlab_widgets-3.0.13-py3-none-any.whl", hash = "sha256:e3cda2c233ce144192f1e29914ad522b2f4c40e77214b0cc97377ca3d323db54"}, + {file = "jupyterlab_widgets-3.0.13.tar.gz", hash = "sha256:a2966d385328c1942b683a8cd96b89b8dd82c8b8f81dda902bb2bc06d46f5bed"}, ] [[package]] @@ -1837,13 +1837,13 @@ dev = ["Sphinx (>=5.1.1)", "black (==23.12.1)", "build (>=0.10.0)", "coverage (> [[package]] name = "litellm" -version = "1.43.19" +version = "1.44.4" description = "Library to easily interface with LLM API providers" optional = false python-versions = "!=2.7.*,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,!=3.7.*,>=3.8" files = [ - {file = "litellm-1.43.19-py3-none-any.whl", hash = "sha256:f66bfe9c8b91577af57a5d1203728abc9b59df38a545148b7e85dec903185c12"}, - {file = "litellm-1.43.19.tar.gz", hash = "sha256:b1f475f98073632f0cea6d814bb10b14b6498e5ff93b91b52dfc00445bf013ab"}, + {file = "litellm-1.44.4-py3-none-any.whl", hash = "sha256:0e2d56cc767003b30275005072ad6f7e2e37b0468719e50817cc581a055b8f1a"}, + {file = "litellm-1.44.4.tar.gz", hash = "sha256:3216424a27e6405b9a099aff166c6d2442b1013c9f9909084ab722eb9d3b8861"}, ] [package.dependencies] @@ -2439,13 +2439,13 @@ files = [ [[package]] name = "openai" -version = "1.41.1" +version = "1.42.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.41.1-py3-none-any.whl", hash = "sha256:56fb04105263f79559aff3ceea2e1dd16f8c5385e8238cb66cf0e6888fa8bfcf"}, - {file = "openai-1.41.1.tar.gz", hash = "sha256:e38e376efd91e0d4db071e2a6517b6b4cac1c2a6fd63efdc5ec6be10c5967c1b"}, + {file = "openai-1.42.0-py3-none-any.whl", hash = "sha256:dc91e0307033a4f94931e5d03cc3b29b9717014ad5e73f9f2051b6cb5eda4d80"}, + {file = "openai-1.42.0.tar.gz", hash = "sha256:c9d31853b4e0bc2dc8bd08003b462a006035655a701471695d0bfdc08529cde3"}, ] [package.dependencies] @@ -3205,120 +3205,120 @@ files = [ [[package]] name = "pyzmq" -version = "26.1.1" +version = "26.2.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b1bb952d1e407463c9333ea7e0c0600001e54e08ce836d4f0aff1fb3f902cf63"}, - {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65e2a18e845c6ea7ab849c70db932eaeadee5edede9e379eb21c0a44cf523b2e"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:def7ae3006924b8a0c146a89ab4008310913fa903beedb95e25dea749642528e"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8234571df7816f99dde89c3403cb396d70c6554120b795853a8ea56fcc26cd3"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18da8e84dbc30688fd2baefd41df7190607511f916be34f9a24b0e007551822e"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c70dab93d98b2bf3f0ac1265edbf6e7f83acbf71dabcc4611889bb0dea45bed7"}, - {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fcb90592c5d5c562e1b1a1ceccf6f00036d73c51db0271bf4d352b8d6b31d468"}, - {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cf4be7460a0c1bc71e9b0e64ecdd75a86386ca6afaa36641686f5542d0314e9d"}, - {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4cbecda4ddbfc1e309c3be04d333f9be3fc6178b8b6592b309676f929767a15"}, - {file = "pyzmq-26.1.1-cp310-cp310-win32.whl", hash = "sha256:583f73b113b8165713b6ce028d221402b1b69483055b5aa3f991937e34dd1ead"}, - {file = "pyzmq-26.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5e6f39ecb8eb7bfcb976c49262e8cf83ff76e082b77ca23ba90c9b6691a345be"}, - {file = "pyzmq-26.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:8d042d6446cab3a1388b38596f5acabb9926b0b95c3894c519356b577a549458"}, - {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:362cac2423e36966d336d79d3ec3eafeabc153ee3e7a5cf580d7e74a34b3d912"}, - {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0841633446cb1539a832a19bb24c03a20c00887d0cedd1d891b495b07e5c5cb5"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e1fcdc333afbf9918d0a614a6e10858aede7da49a60f6705a77e343fe86a317"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc8d655627d775475eafdcf0e49e74bcc1e5e90afd9ab813b4da98f092ed7b93"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32de51744820857a6f7c3077e620ab3f607d0e4388dfead885d5124ab9bcdc5e"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a880240597010914ffb1d6edd04d3deb7ce6a2abf79a0012751438d13630a671"}, - {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:26131b1cec02f941ed2d2b4b8cc051662b1c248b044eff5069df1f500bbced56"}, - {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce05841322b58510607f9508a573138d995a46c7928887bc433de9cb760fd2ad"}, - {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32123ff0a6db521aadf2b95201e967a4e0d11fb89f73663a99d2f54881c07214"}, - {file = "pyzmq-26.1.1-cp311-cp311-win32.whl", hash = "sha256:e790602d7ea1d6c7d8713d571226d67de7ffe47b1e22ae2c043ebd537de1bccb"}, - {file = "pyzmq-26.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:717960855f2d6fdc2dba9df49dff31c414187bb11c76af36343a57d1f7083d9a"}, - {file = "pyzmq-26.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:08956c26dbcd4fd8835cb777a16e21958ed2412317630e19f0018d49dbeeb470"}, - {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e80345900ae241c2c51bead7c9fa247bba6d4b2a83423e9791bae8b0a7f12c52"}, - {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ec8fe214fcc45dfb0c32e4a7ad1db20244ba2d2fecbf0cbf9d5242d81ca0a375"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf4e283f97688d993cb7a8acbc22889effbbb7cbaa19ee9709751f44be928f5d"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2508bdc8ab246e5ed7c92023d4352aaad63020ca3b098a4e3f1822db202f703d"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:741bdb4d96efe8192616abdc3671931d51a8bcd38c71da2d53fb3127149265d1"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:76154943e4c4054b2591792eb3484ef1dd23d59805759f9cebd2f010aa30ee8c"}, - {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9498ac427d20d0e0ef0e4bbd6200841e91640dfdf619f544ceec7f464cfb6070"}, - {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f34453ef3496ca3462f30435bf85f535f9550392987341f9ccc92c102825a79"}, - {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:50f0669324e27cc2091ef6ab76ca7112f364b6249691790b4cffce31e73fda28"}, - {file = "pyzmq-26.1.1-cp312-cp312-win32.whl", hash = "sha256:3ee5cbf2625b94de21c68d0cefd35327c8dfdbd6a98fcc41682b4e8bb00d841f"}, - {file = "pyzmq-26.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:75bd448a28b1001b6928679015bc95dd5f172703ed30135bb9e34fc9cda0a3e7"}, - {file = "pyzmq-26.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:4350233569b4bbef88595c5e77ee38995a6f1f1790fae148b578941bfffd1c24"}, - {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8087a3281c20b1d11042d372ed5a47734af05975d78e4d1d6e7bd1018535f3"}, - {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ebef7d3fe11fe4c688f08bc0211a976c3318c097057f258428200737b9fff4da"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a5342110510045a47de1e87f5f1dcc1d9d90109522316dc9830cfc6157c800f"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af690ea4be6ca92a67c2b44a779a023bf0838e92d48497a2268175dc4a505691"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc994e220c1403ae087d7f0fa45129d583e46668a019e389060da811a5a9320e"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b8e153f5dffb0310af71fc6fc9cd8174f4c8ea312c415adcb815d786fee78179"}, - {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0065026e624052a51033857e5cd45a94b52946b44533f965f0bdf182460e965d"}, - {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:63351392f948b5d50b9f55161994bc4feedbfb3f3cfe393d2f503dea2c3ec445"}, - {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ffecc43b3c18e36b62fcec995761829b6ac325d8dd74a4f2c5c1653afbb4495a"}, - {file = "pyzmq-26.1.1-cp313-cp313-win32.whl", hash = "sha256:6ff14c2fae6c0c2c1c02590c5c5d75aa1db35b859971b3ca2fcd28f983d9f2b6"}, - {file = "pyzmq-26.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:85f2d2ee5ea9a8f1de86a300e1062fbab044f45b5ce34d20580c0198a8196db0"}, - {file = "pyzmq-26.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:cc09b1de8b985ca5a0ca343dd7fb007267c6b329347a74e200f4654268084239"}, - {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:bc904e86de98f8fc5bd41597da5d61232d2d6d60c4397f26efffabb961b2b245"}, - {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:00f39c367bbd6aa8e4bc36af6510561944c619b58eb36199fa334b594a18f615"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de6f384864a959866b782e6a3896538d1424d183f2d3c7ef079f71dcecde7284"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3abb15df0c763339edb27a644c19381b2425ddd1aea3dbd77c1601a3b31867b8"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40908ec2dd3b29bbadc0916a0d3c87f8dbeebbd8fead8e618539f09e0506dec4"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c11a95d3f6fc7e714ccd1066f68f9c1abd764a8b3596158be92f46dd49f41e03"}, - {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:4437af9fee7a58302dbd511cc49f0cc2b35c112a33a1111fb123cf0be45205ca"}, - {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:76390d3d66406cb01b9681c382874400e9dfd77f30ecdea4bd1bf5226dd4aff0"}, - {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:4d4c7fe5e50e269f9c63a260638488fec194a73993008618a59b54c47ef6ae72"}, - {file = "pyzmq-26.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25d128524207f53f7aae7c5abdc2b63f8957a060b00521af5ffcd20986b5d8f4"}, - {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d74b925d997e4f92b042bdd7085cd0a309ee0fd7cb4dc376059bbff6b32ff34f"}, - {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:732f957441e5b1c65a7509395e6b6cafee9e12df9aa5f4bf92ed266fe0ba70ee"}, - {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0a45102ad7ed9f9ddf2bd699cc5df37742cf7301111cba06001b927efecb120"}, - {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9f380d5333fc7cd17423f486125dcc073918676e33db70a6a8172b19fc78d23d"}, - {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8eaffcd6bf6a9d00b66a2052a33fa7e6a6575427e9644395f13c3d070f2918dc"}, - {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f1483d4975ae1b387b39bb8e23d1ff32fe5621aa9e4ed3055d05e9c5613fea53"}, - {file = "pyzmq-26.1.1-cp37-cp37m-win32.whl", hash = "sha256:a83653c6bbe5887caea55e49fbd2909c14b73acf43bcc051eb60b2d514bbd46e"}, - {file = "pyzmq-26.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9763a8d3f5f74ef679989b373c37cc22e8d07e56d26439205cb83edb7722357f"}, - {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2b045647caf620ce0ed6c8fd9fb6a73116f99aceed966b152a5ba1b416d25311"}, - {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f66dcb6625c002f209cdc12cae1a1fec926493cd2262efe37dc6b25a30cea863"}, - {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0cf1d980c969fb9e538f52abd2227f09e015096bc5c3ef7aa26e0d64051c1db8"}, - {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:443ebf5e261a95ee9725693f2a5a71401f89b89df0e0ea58844b074067aac2f1"}, - {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29de77ba1b1877fe7defc1b9140e65cbd35f72a63bc501e56c2eae55bde5fff4"}, - {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f6071ec95af145d7b659dae6786871cd85f0acc599286b6f8ba0c74592d83dd"}, - {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f0512fc87629ad968889176bf2165d721cd817401a281504329e2a2ed0ca6a3"}, - {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5ccfcf13e80719f6a2d9c0a021d9e47d4550907a29253554be2c09582f6d7963"}, - {file = "pyzmq-26.1.1-cp38-cp38-win32.whl", hash = "sha256:809673947e95752e407aaaaf03f205ee86ebfff9ca51db6d4003dfd87b8428d1"}, - {file = "pyzmq-26.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:62b5180e23e6f581600459cd983473cd723fdc64350f606d21407c99832aaf5f"}, - {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:fe73d7c89d6f803bed122135ff5783364e8cdb479cf6fe2d764a44b6349e7e0f"}, - {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db1b7e2b50ef21f398036786da4c153db63203a402396d9f21e08ea61f3f8dba"}, - {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c506a51cb01bb997a3f6440db0d121e5e7a32396e9948b1fdb6a7bfa67243f4"}, - {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:92eca4f80e8a748d880e55d3cf57ef487692e439f12d5c5a2e1cce84aaa7f6cb"}, - {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14bdbae02f72f4716b0ffe7500e9da303d719ddde1f3dcfb4c4f6cc1cf73bb02"}, - {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e03be7ed17836c9434cce0668ac1e2cc9143d7169f90f46a0167f6155e176e32"}, - {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc5df31e36e4fddd4c8b5c42daee8d54d7b529e898ac984be97bf5517de166a7"}, - {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f218179c90a12d660906e04b25a340dd63e9743000ba16232ddaf46888f269da"}, - {file = "pyzmq-26.1.1-cp39-cp39-win32.whl", hash = "sha256:7dfabc180a4da422a4b349c63077347392463a75fa07aa3be96712ed6d42c547"}, - {file = "pyzmq-26.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c5248e6e0fcbbbc912982e99cdd51c342601f495b0fa5bd667f3bdbdbf3e170f"}, - {file = "pyzmq-26.1.1-cp39-cp39-win_arm64.whl", hash = "sha256:2ae7aa1408778dc74582a1226052b930f9083b54b64d7e6ef6ec0466cfdcdec2"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:be3fc2b11c0c384949cf1f01f9a48555039408b0f3e877863b1754225635953e"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48dee75c2a9fa4f4a583d4028d564a0453447ee1277a29b07acc3743c092e259"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23f2fe4fb567e8098ebaa7204819658195b10ddd86958a97a6058eed2901eed3"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:472cacd16f627c06d3c8b2d374345ab74446bae913584a6245e2aa935336d929"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8285b25aa20fcc46f1ca4afbc39fd3d5f2fe4c4bbf7f2c7f907a214e87a70024"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2067e63fd9d5c13cfe12624dab0366053e523b37a7a01678ce4321f839398939"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cc109be2ee3638035d276e18eaf66a1e1f44201c0c4bea4ee0c692766bbd3570"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d0da97e65ee73261dba70469cc8f63d8da3a8a825337a2e3d246b9e95141cdd0"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa79c528706561306938b275f89bb2c6985ce08469c27e5de05bc680df5e826f"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3ddbd851a3a2651fdc5065a2804d50cf2f4b13b1bcd66de8e9e855d0217d4fcd"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3df226ab7464684ae6706e20a5cbab717c3735a7e409b3fa598b754d49f1946"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abad7b897e960d577eb4a0f3f789c1780bc3ffe2e7c27cf317e7c90ad26acf12"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c513d829a548c2d5c88983167be2b3aa537f6d1191edcdc6fcd8999e18bdd994"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70af4c9c991714ef1c65957605a8de42ef0d0620dd5f125953c8e682281bdb80"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8d4234f335b0d0842f7d661d8cd50cbad0729be58f1c4deb85cd96b38fe95025"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2c0fdb7b758e0e1605157e480b00b3a599073068a37091a1c75ec65bf7498645"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc657577f057d60dd3642c9f95f28b432889b73143140061f7c1331d02f03df6"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e3b66fe6131b4f33d239f7d4c3bfb2f8532d8644bae3b3da4f3987073edac55"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b57e912feef6951aec8bb03fe0faa5ad5f36962883c72a30a9c965e6d988fd"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:146956aec7d947c5afc5e7da0841423d7a53f84fd160fff25e682361dcfb32cb"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9521b874fd489495865172f344e46e0159095d1f161858e3fc6e28e43ca15160"}, - {file = "pyzmq-26.1.1.tar.gz", hash = "sha256:a7db05d8b7cd1a8c6610e9e9aa55d525baae7a44a43e18bc3260eb3f92de96c6"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, + {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, + {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, + {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, + {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, + {file = "pyzmq-26.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win32.whl", hash = "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd"}, + {file = "pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988"}, + {file = "pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, + {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, + {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, ] [package.dependencies] @@ -4187,13 +4187,13 @@ files = [ [[package]] name = "types-python-dateutil" -version = "2.9.0.20240316" +version = "2.9.0.20240821" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, - {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, + {file = "types-python-dateutil-2.9.0.20240821.tar.gz", hash = "sha256:9649d1dcb6fef1046fb18bebe9ea2aa0028b160918518c34589a46045f6ebd98"}, + {file = "types_python_dateutil-2.9.0.20240821-py3-none-any.whl", hash = "sha256:f5889fcb4e63ed4aaa379b44f93c32593d50b9a94c9a60a0c854d8cc3511cd57"}, ] [[package]] @@ -4354,13 +4354,13 @@ test = ["websockets"] [[package]] name = "widgetsnbextension" -version = "4.0.11" +version = "4.0.13" description = "Jupyter interactive widgets for Jupyter Notebook" optional = false python-versions = ">=3.7" files = [ - {file = "widgetsnbextension-4.0.11-py3-none-any.whl", hash = "sha256:55d4d6949d100e0d08b94948a42efc3ed6dfdc0e9468b2c4b128c9a2ce3a7a36"}, - {file = "widgetsnbextension-4.0.11.tar.gz", hash = "sha256:8b22a8f1910bfd188e596fe7fc05dcbd87e810c8a4ba010bdb3da86637398474"}, + {file = "widgetsnbextension-4.0.13-py3-none-any.whl", hash = "sha256:74b2692e8500525cc38c2b877236ba51d34541e6385eeed5aec15a70f88a6c71"}, + {file = "widgetsnbextension-4.0.13.tar.gz", hash = "sha256:ffcb67bc9febd10234a362795f643927f4e0c05d9342c727b65d2384f8feacb6"}, ] [[package]] diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index e862a89cf..04af5c378 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -10,8 +10,6 @@ from agents_api.autogen.openapi_model import ( CreateExecutionRequest, CreateTaskRequest, - MainModel, - MapOverEvaluate, ) from agents_api.models.task.create_task import create_task from agents_api.routers.tasks.create_task_execution import start_execution @@ -413,7 +411,7 @@ async def _( "description": "test task about", "input_schema": {"type": "object", "additionalProperties": True}, "main": [ - {"wait_for_input": {"hi": '"bye"'}}, + {"wait_for_input": {"info": {"hi": '"bye"'}}}, ], } ), @@ -452,11 +450,7 @@ async def _( activity for activity in activities_scheduled if activity ] - assert activities_scheduled == [ - "wait_for_input_step", - "transition_step", - "raise_complete_async", - ] + assert "wait_for_input_step" in activities_scheduled @test("workflow: if-else step") @@ -617,22 +611,24 @@ async def _( ): data = CreateExecutionRequest(input={"test": "input"}) + map_step = { + "over": "'a b c'.split()", + "map": { + "evaluate": {"res": "_"}, + }, + } + + task_def = { + "name": "test task", + "description": "test task about", + "input_schema": {"type": "object", "additionalProperties": True}, + "main": [map_step], + } + task = create_task( developer_id=developer_id, agent_id=agent.id, - data=CreateTaskRequest( - **{ - "name": "test task", - "description": "test task about", - "input_schema": {"type": "object", "additionalProperties": True}, - "main": [ - { - "over": "'a b c'.split()", - "evaluate": {"res": "_"}, - }, - ], - }, - ), + data=CreateTaskRequest(**task_def), client=client, ) @@ -651,7 +647,7 @@ async def _( mock_run_task_execution_workflow.assert_called_once() result = await handle.result() - assert result["res"] == {"test": "input"} + assert [r["res"] for r in result] == ["a", "b", "c"] @test("workflow: prompt step") diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 9db756fca..9a1decf35 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -848,21 +848,25 @@ test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "p [[package]] name = "importlib-resources" -version = "6.4.3" +version = "6.4.4" description = "Read resources from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_resources-6.4.3-py3-none-any.whl", hash = "sha256:2d6dfe3b9e055f72495c2085890837fc8c758984e209115c8792bddcb762cd93"}, - {file = "importlib_resources-6.4.3.tar.gz", hash = "sha256:4a202b9b9d38563b46da59221d77bb73862ab5d79d461307bcb826d725448b98"}, + {file = "importlib_resources-6.4.4-py3-none-any.whl", hash = "sha256:dda242603d1c9cd836c3368b1174ed74cb4049ecd209e7a1a0104620c18c5c11"}, + {file = "importlib_resources-6.4.4.tar.gz", hash = "sha256:20600c8b7361938dc0bb2d5ec0297802e575df486f5a544fa414da65e13721f7"}, ] [package.dependencies] zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy", "pytest-ruff (>=0.2.1)", "zipp (>=3.17)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["jaraco.test (>=5.4)", "pytest (>=6,!=8.1.*)", "zipp (>=3.17)"] +type = ["pytest-mypy"] [[package]] name = "ipykernel" @@ -1812,13 +1816,13 @@ files = [ [[package]] name = "openai" -version = "1.41.1" +version = "1.42.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.41.1-py3-none-any.whl", hash = "sha256:56fb04105263f79559aff3ceea2e1dd16f8c5385e8238cb66cf0e6888fa8bfcf"}, - {file = "openai-1.41.1.tar.gz", hash = "sha256:e38e376efd91e0d4db071e2a6517b6b4cac1c2a6fd63efdc5ec6be10c5967c1b"}, + {file = "openai-1.42.0-py3-none-any.whl", hash = "sha256:dc91e0307033a4f94931e5d03cc3b29b9717014ad5e73f9f2051b6cb5eda4d80"}, + {file = "openai-1.42.0.tar.gz", hash = "sha256:c9d31853b4e0bc2dc8bd08003b462a006035655a701471695d0bfdc08529cde3"}, ] [package.dependencies] @@ -2370,13 +2374,13 @@ diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyright" -version = "1.1.376" +version = "1.1.377" description = "Command line wrapper for pyright" optional = false python-versions = ">=3.7" files = [ - {file = "pyright-1.1.376-py3-none-any.whl", hash = "sha256:0f2473b12c15c46b3207f0eec224c3cea2bdc07cd45dd4a037687cbbca0fbeff"}, - {file = "pyright-1.1.376.tar.gz", hash = "sha256:bffd63b197cd0810395bb3245c06b01f95a85ddf6bfa0e5644ed69c841e954dd"}, + {file = "pyright-1.1.377-py3-none-any.whl", hash = "sha256:af0dd2b6b636c383a6569a083f8c5a8748ae4dcde5df7914b3f3f267e14dd162"}, + {file = "pyright-1.1.377.tar.gz", hash = "sha256:aabc30fedce0ded34baa0c49b24f10e68f4bfc8f68ae7f3d175c4b0f256b4fcf"}, ] [package.dependencies] @@ -2572,120 +2576,120 @@ files = [ [[package]] name = "pyzmq" -version = "26.1.1" +version = "26.2.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b1bb952d1e407463c9333ea7e0c0600001e54e08ce836d4f0aff1fb3f902cf63"}, - {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65e2a18e845c6ea7ab849c70db932eaeadee5edede9e379eb21c0a44cf523b2e"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:def7ae3006924b8a0c146a89ab4008310913fa903beedb95e25dea749642528e"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8234571df7816f99dde89c3403cb396d70c6554120b795853a8ea56fcc26cd3"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18da8e84dbc30688fd2baefd41df7190607511f916be34f9a24b0e007551822e"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c70dab93d98b2bf3f0ac1265edbf6e7f83acbf71dabcc4611889bb0dea45bed7"}, - {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fcb90592c5d5c562e1b1a1ceccf6f00036d73c51db0271bf4d352b8d6b31d468"}, - {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cf4be7460a0c1bc71e9b0e64ecdd75a86386ca6afaa36641686f5542d0314e9d"}, - {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4cbecda4ddbfc1e309c3be04d333f9be3fc6178b8b6592b309676f929767a15"}, - {file = "pyzmq-26.1.1-cp310-cp310-win32.whl", hash = "sha256:583f73b113b8165713b6ce028d221402b1b69483055b5aa3f991937e34dd1ead"}, - {file = "pyzmq-26.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5e6f39ecb8eb7bfcb976c49262e8cf83ff76e082b77ca23ba90c9b6691a345be"}, - {file = "pyzmq-26.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:8d042d6446cab3a1388b38596f5acabb9926b0b95c3894c519356b577a549458"}, - {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:362cac2423e36966d336d79d3ec3eafeabc153ee3e7a5cf580d7e74a34b3d912"}, - {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0841633446cb1539a832a19bb24c03a20c00887d0cedd1d891b495b07e5c5cb5"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e1fcdc333afbf9918d0a614a6e10858aede7da49a60f6705a77e343fe86a317"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc8d655627d775475eafdcf0e49e74bcc1e5e90afd9ab813b4da98f092ed7b93"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32de51744820857a6f7c3077e620ab3f607d0e4388dfead885d5124ab9bcdc5e"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a880240597010914ffb1d6edd04d3deb7ce6a2abf79a0012751438d13630a671"}, - {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:26131b1cec02f941ed2d2b4b8cc051662b1c248b044eff5069df1f500bbced56"}, - {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce05841322b58510607f9508a573138d995a46c7928887bc433de9cb760fd2ad"}, - {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32123ff0a6db521aadf2b95201e967a4e0d11fb89f73663a99d2f54881c07214"}, - {file = "pyzmq-26.1.1-cp311-cp311-win32.whl", hash = "sha256:e790602d7ea1d6c7d8713d571226d67de7ffe47b1e22ae2c043ebd537de1bccb"}, - {file = "pyzmq-26.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:717960855f2d6fdc2dba9df49dff31c414187bb11c76af36343a57d1f7083d9a"}, - {file = "pyzmq-26.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:08956c26dbcd4fd8835cb777a16e21958ed2412317630e19f0018d49dbeeb470"}, - {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e80345900ae241c2c51bead7c9fa247bba6d4b2a83423e9791bae8b0a7f12c52"}, - {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ec8fe214fcc45dfb0c32e4a7ad1db20244ba2d2fecbf0cbf9d5242d81ca0a375"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf4e283f97688d993cb7a8acbc22889effbbb7cbaa19ee9709751f44be928f5d"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2508bdc8ab246e5ed7c92023d4352aaad63020ca3b098a4e3f1822db202f703d"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:741bdb4d96efe8192616abdc3671931d51a8bcd38c71da2d53fb3127149265d1"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:76154943e4c4054b2591792eb3484ef1dd23d59805759f9cebd2f010aa30ee8c"}, - {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9498ac427d20d0e0ef0e4bbd6200841e91640dfdf619f544ceec7f464cfb6070"}, - {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f34453ef3496ca3462f30435bf85f535f9550392987341f9ccc92c102825a79"}, - {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:50f0669324e27cc2091ef6ab76ca7112f364b6249691790b4cffce31e73fda28"}, - {file = "pyzmq-26.1.1-cp312-cp312-win32.whl", hash = "sha256:3ee5cbf2625b94de21c68d0cefd35327c8dfdbd6a98fcc41682b4e8bb00d841f"}, - {file = "pyzmq-26.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:75bd448a28b1001b6928679015bc95dd5f172703ed30135bb9e34fc9cda0a3e7"}, - {file = "pyzmq-26.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:4350233569b4bbef88595c5e77ee38995a6f1f1790fae148b578941bfffd1c24"}, - {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8087a3281c20b1d11042d372ed5a47734af05975d78e4d1d6e7bd1018535f3"}, - {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ebef7d3fe11fe4c688f08bc0211a976c3318c097057f258428200737b9fff4da"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a5342110510045a47de1e87f5f1dcc1d9d90109522316dc9830cfc6157c800f"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af690ea4be6ca92a67c2b44a779a023bf0838e92d48497a2268175dc4a505691"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc994e220c1403ae087d7f0fa45129d583e46668a019e389060da811a5a9320e"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b8e153f5dffb0310af71fc6fc9cd8174f4c8ea312c415adcb815d786fee78179"}, - {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0065026e624052a51033857e5cd45a94b52946b44533f965f0bdf182460e965d"}, - {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:63351392f948b5d50b9f55161994bc4feedbfb3f3cfe393d2f503dea2c3ec445"}, - {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ffecc43b3c18e36b62fcec995761829b6ac325d8dd74a4f2c5c1653afbb4495a"}, - {file = "pyzmq-26.1.1-cp313-cp313-win32.whl", hash = "sha256:6ff14c2fae6c0c2c1c02590c5c5d75aa1db35b859971b3ca2fcd28f983d9f2b6"}, - {file = "pyzmq-26.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:85f2d2ee5ea9a8f1de86a300e1062fbab044f45b5ce34d20580c0198a8196db0"}, - {file = "pyzmq-26.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:cc09b1de8b985ca5a0ca343dd7fb007267c6b329347a74e200f4654268084239"}, - {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:bc904e86de98f8fc5bd41597da5d61232d2d6d60c4397f26efffabb961b2b245"}, - {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:00f39c367bbd6aa8e4bc36af6510561944c619b58eb36199fa334b594a18f615"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de6f384864a959866b782e6a3896538d1424d183f2d3c7ef079f71dcecde7284"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3abb15df0c763339edb27a644c19381b2425ddd1aea3dbd77c1601a3b31867b8"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40908ec2dd3b29bbadc0916a0d3c87f8dbeebbd8fead8e618539f09e0506dec4"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c11a95d3f6fc7e714ccd1066f68f9c1abd764a8b3596158be92f46dd49f41e03"}, - {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:4437af9fee7a58302dbd511cc49f0cc2b35c112a33a1111fb123cf0be45205ca"}, - {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:76390d3d66406cb01b9681c382874400e9dfd77f30ecdea4bd1bf5226dd4aff0"}, - {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:4d4c7fe5e50e269f9c63a260638488fec194a73993008618a59b54c47ef6ae72"}, - {file = "pyzmq-26.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25d128524207f53f7aae7c5abdc2b63f8957a060b00521af5ffcd20986b5d8f4"}, - {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d74b925d997e4f92b042bdd7085cd0a309ee0fd7cb4dc376059bbff6b32ff34f"}, - {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:732f957441e5b1c65a7509395e6b6cafee9e12df9aa5f4bf92ed266fe0ba70ee"}, - {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0a45102ad7ed9f9ddf2bd699cc5df37742cf7301111cba06001b927efecb120"}, - {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9f380d5333fc7cd17423f486125dcc073918676e33db70a6a8172b19fc78d23d"}, - {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8eaffcd6bf6a9d00b66a2052a33fa7e6a6575427e9644395f13c3d070f2918dc"}, - {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f1483d4975ae1b387b39bb8e23d1ff32fe5621aa9e4ed3055d05e9c5613fea53"}, - {file = "pyzmq-26.1.1-cp37-cp37m-win32.whl", hash = "sha256:a83653c6bbe5887caea55e49fbd2909c14b73acf43bcc051eb60b2d514bbd46e"}, - {file = "pyzmq-26.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9763a8d3f5f74ef679989b373c37cc22e8d07e56d26439205cb83edb7722357f"}, - {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2b045647caf620ce0ed6c8fd9fb6a73116f99aceed966b152a5ba1b416d25311"}, - {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f66dcb6625c002f209cdc12cae1a1fec926493cd2262efe37dc6b25a30cea863"}, - {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0cf1d980c969fb9e538f52abd2227f09e015096bc5c3ef7aa26e0d64051c1db8"}, - {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:443ebf5e261a95ee9725693f2a5a71401f89b89df0e0ea58844b074067aac2f1"}, - {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29de77ba1b1877fe7defc1b9140e65cbd35f72a63bc501e56c2eae55bde5fff4"}, - {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f6071ec95af145d7b659dae6786871cd85f0acc599286b6f8ba0c74592d83dd"}, - {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f0512fc87629ad968889176bf2165d721cd817401a281504329e2a2ed0ca6a3"}, - {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5ccfcf13e80719f6a2d9c0a021d9e47d4550907a29253554be2c09582f6d7963"}, - {file = "pyzmq-26.1.1-cp38-cp38-win32.whl", hash = "sha256:809673947e95752e407aaaaf03f205ee86ebfff9ca51db6d4003dfd87b8428d1"}, - {file = "pyzmq-26.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:62b5180e23e6f581600459cd983473cd723fdc64350f606d21407c99832aaf5f"}, - {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:fe73d7c89d6f803bed122135ff5783364e8cdb479cf6fe2d764a44b6349e7e0f"}, - {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db1b7e2b50ef21f398036786da4c153db63203a402396d9f21e08ea61f3f8dba"}, - {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c506a51cb01bb997a3f6440db0d121e5e7a32396e9948b1fdb6a7bfa67243f4"}, - {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:92eca4f80e8a748d880e55d3cf57ef487692e439f12d5c5a2e1cce84aaa7f6cb"}, - {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14bdbae02f72f4716b0ffe7500e9da303d719ddde1f3dcfb4c4f6cc1cf73bb02"}, - {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e03be7ed17836c9434cce0668ac1e2cc9143d7169f90f46a0167f6155e176e32"}, - {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc5df31e36e4fddd4c8b5c42daee8d54d7b529e898ac984be97bf5517de166a7"}, - {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f218179c90a12d660906e04b25a340dd63e9743000ba16232ddaf46888f269da"}, - {file = "pyzmq-26.1.1-cp39-cp39-win32.whl", hash = "sha256:7dfabc180a4da422a4b349c63077347392463a75fa07aa3be96712ed6d42c547"}, - {file = "pyzmq-26.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c5248e6e0fcbbbc912982e99cdd51c342601f495b0fa5bd667f3bdbdbf3e170f"}, - {file = "pyzmq-26.1.1-cp39-cp39-win_arm64.whl", hash = "sha256:2ae7aa1408778dc74582a1226052b930f9083b54b64d7e6ef6ec0466cfdcdec2"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:be3fc2b11c0c384949cf1f01f9a48555039408b0f3e877863b1754225635953e"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48dee75c2a9fa4f4a583d4028d564a0453447ee1277a29b07acc3743c092e259"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23f2fe4fb567e8098ebaa7204819658195b10ddd86958a97a6058eed2901eed3"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:472cacd16f627c06d3c8b2d374345ab74446bae913584a6245e2aa935336d929"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8285b25aa20fcc46f1ca4afbc39fd3d5f2fe4c4bbf7f2c7f907a214e87a70024"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2067e63fd9d5c13cfe12624dab0366053e523b37a7a01678ce4321f839398939"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cc109be2ee3638035d276e18eaf66a1e1f44201c0c4bea4ee0c692766bbd3570"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d0da97e65ee73261dba70469cc8f63d8da3a8a825337a2e3d246b9e95141cdd0"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa79c528706561306938b275f89bb2c6985ce08469c27e5de05bc680df5e826f"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3ddbd851a3a2651fdc5065a2804d50cf2f4b13b1bcd66de8e9e855d0217d4fcd"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3df226ab7464684ae6706e20a5cbab717c3735a7e409b3fa598b754d49f1946"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abad7b897e960d577eb4a0f3f789c1780bc3ffe2e7c27cf317e7c90ad26acf12"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c513d829a548c2d5c88983167be2b3aa537f6d1191edcdc6fcd8999e18bdd994"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70af4c9c991714ef1c65957605a8de42ef0d0620dd5f125953c8e682281bdb80"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8d4234f335b0d0842f7d661d8cd50cbad0729be58f1c4deb85cd96b38fe95025"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2c0fdb7b758e0e1605157e480b00b3a599073068a37091a1c75ec65bf7498645"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc657577f057d60dd3642c9f95f28b432889b73143140061f7c1331d02f03df6"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e3b66fe6131b4f33d239f7d4c3bfb2f8532d8644bae3b3da4f3987073edac55"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b57e912feef6951aec8bb03fe0faa5ad5f36962883c72a30a9c965e6d988fd"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:146956aec7d947c5afc5e7da0841423d7a53f84fd160fff25e682361dcfb32cb"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9521b874fd489495865172f344e46e0159095d1f161858e3fc6e28e43ca15160"}, - {file = "pyzmq-26.1.1.tar.gz", hash = "sha256:a7db05d8b7cd1a8c6610e9e9aa55d525baae7a44a43e18bc3260eb3f92de96c6"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, + {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, + {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, + {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, + {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, + {file = "pyzmq-26.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win32.whl", hash = "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd"}, + {file = "pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988"}, + {file = "pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, + {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, + {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, ] [package.dependencies] @@ -3185,13 +3189,13 @@ files = [ [[package]] name = "types-python-dateutil" -version = "2.9.0.20240316" +version = "2.9.0.20240821" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" files = [ - {file = "types-python-dateutil-2.9.0.20240316.tar.gz", hash = "sha256:5d2f2e240b86905e40944dd787db6da9263f0deabef1076ddaed797351ec0202"}, - {file = "types_python_dateutil-2.9.0.20240316-py3-none-any.whl", hash = "sha256:6b8cb66d960771ce5ff974e9dd45e38facb81718cc1e208b10b1baccbfdbee3b"}, + {file = "types-python-dateutil-2.9.0.20240821.tar.gz", hash = "sha256:9649d1dcb6fef1046fb18bebe9ea2aa0028b160918518c34589a46045f6ebd98"}, + {file = "types_python_dateutil-2.9.0.20240821-py3-none-any.whl", hash = "sha256:f5889fcb4e63ed4aaa379b44f93c32593d50b9a94c9a60a0c854d8cc3511cd57"}, ] [[package]] diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index 851e723b0..68ee1dd75 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -98,43 +98,28 @@ export type { Tasks_CaseThen } from "./models/Tasks_CaseThen"; export type { Tasks_CreateOrUpdateTaskRequest_id } from "./models/Tasks_CreateOrUpdateTaskRequest_id"; export type { Tasks_CreateTaskRequest } from "./models/Tasks_CreateTaskRequest"; export type { Tasks_EmbedStep } from "./models/Tasks_EmbedStep"; -export type { Tasks_EmbedStepDef } from "./models/Tasks_EmbedStepDef"; export type { Tasks_ErrorWorkflowStep } from "./models/Tasks_ErrorWorkflowStep"; export type { Tasks_EvaluateStep } from "./models/Tasks_EvaluateStep"; -export type { Tasks_EvaluateStepDef } from "./models/Tasks_EvaluateStepDef"; export type { Tasks_ForeachDo } from "./models/Tasks_ForeachDo"; export type { Tasks_ForeachStep } from "./models/Tasks_ForeachStep"; export type { Tasks_GetStep } from "./models/Tasks_GetStep"; -export type { Tasks_GetStepDef } from "./models/Tasks_GetStepDef"; export type { Tasks_IfElseWorkflowStep } from "./models/Tasks_IfElseWorkflowStep"; export type { Tasks_LogStep } from "./models/Tasks_LogStep"; -export type { Tasks_LogStepDef } from "./models/Tasks_LogStepDef"; -export type { Tasks_MapOverEmbed } from "./models/Tasks_MapOverEmbed"; -export type { Tasks_MapOverEvaluate } from "./models/Tasks_MapOverEvaluate"; -export type { Tasks_MapOverGet } from "./models/Tasks_MapOverGet"; -export type { Tasks_MapOverLog } from "./models/Tasks_MapOverLog"; -export type { Tasks_MapOverPrompt } from "./models/Tasks_MapOverPrompt"; -export type { Tasks_MapOverSearch } from "./models/Tasks_MapOverSearch"; -export type { Tasks_MapOverSet } from "./models/Tasks_MapOverSet"; -export type { Tasks_MapOverToolCall } from "./models/Tasks_MapOverToolCall"; export type { Tasks_ParallelStep } from "./models/Tasks_ParallelStep"; export type { Tasks_PatchTaskRequest } from "./models/Tasks_PatchTaskRequest"; export type { Tasks_PromptStep } from "./models/Tasks_PromptStep"; -export type { Tasks_PromptStepDef } from "./models/Tasks_PromptStepDef"; export type { Tasks_ReturnStep } from "./models/Tasks_ReturnStep"; export type { Tasks_SearchStep } from "./models/Tasks_SearchStep"; -export type { Tasks_SearchStepDef } from "./models/Tasks_SearchStepDef"; export type { Tasks_SetKey } from "./models/Tasks_SetKey"; export type { Tasks_SetStep } from "./models/Tasks_SetStep"; -export type { Tasks_SetStepDef } from "./models/Tasks_SetStepDef"; export type { Tasks_SleepFor } from "./models/Tasks_SleepFor"; export type { Tasks_SleepStep } from "./models/Tasks_SleepStep"; export type { Tasks_SwitchStep } from "./models/Tasks_SwitchStep"; export type { Tasks_Task } from "./models/Tasks_Task"; export type { Tasks_TaskTool } from "./models/Tasks_TaskTool"; export type { Tasks_ToolCallStep } from "./models/Tasks_ToolCallStep"; -export type { Tasks_ToolCallStepDef } from "./models/Tasks_ToolCallStepDef"; export type { Tasks_UpdateTaskRequest } from "./models/Tasks_UpdateTaskRequest"; +export type { Tasks_WaitForInputInfo } from "./models/Tasks_WaitForInputInfo"; export type { Tasks_WaitForInputStep } from "./models/Tasks_WaitForInputStep"; export type { Tasks_YieldStep } from "./models/Tasks_YieldStep"; export type { Tools_ChosenFunctionCall } from "./models/Tools_ChosenFunctionCall"; @@ -245,43 +230,28 @@ export { $Tasks_CaseThen } from "./schemas/$Tasks_CaseThen"; export { $Tasks_CreateOrUpdateTaskRequest_id } from "./schemas/$Tasks_CreateOrUpdateTaskRequest_id"; export { $Tasks_CreateTaskRequest } from "./schemas/$Tasks_CreateTaskRequest"; export { $Tasks_EmbedStep } from "./schemas/$Tasks_EmbedStep"; -export { $Tasks_EmbedStepDef } from "./schemas/$Tasks_EmbedStepDef"; export { $Tasks_ErrorWorkflowStep } from "./schemas/$Tasks_ErrorWorkflowStep"; export { $Tasks_EvaluateStep } from "./schemas/$Tasks_EvaluateStep"; -export { $Tasks_EvaluateStepDef } from "./schemas/$Tasks_EvaluateStepDef"; export { $Tasks_ForeachDo } from "./schemas/$Tasks_ForeachDo"; export { $Tasks_ForeachStep } from "./schemas/$Tasks_ForeachStep"; export { $Tasks_GetStep } from "./schemas/$Tasks_GetStep"; -export { $Tasks_GetStepDef } from "./schemas/$Tasks_GetStepDef"; export { $Tasks_IfElseWorkflowStep } from "./schemas/$Tasks_IfElseWorkflowStep"; export { $Tasks_LogStep } from "./schemas/$Tasks_LogStep"; -export { $Tasks_LogStepDef } from "./schemas/$Tasks_LogStepDef"; -export { $Tasks_MapOverEmbed } from "./schemas/$Tasks_MapOverEmbed"; -export { $Tasks_MapOverEvaluate } from "./schemas/$Tasks_MapOverEvaluate"; -export { $Tasks_MapOverGet } from "./schemas/$Tasks_MapOverGet"; -export { $Tasks_MapOverLog } from "./schemas/$Tasks_MapOverLog"; -export { $Tasks_MapOverPrompt } from "./schemas/$Tasks_MapOverPrompt"; -export { $Tasks_MapOverSearch } from "./schemas/$Tasks_MapOverSearch"; -export { $Tasks_MapOverSet } from "./schemas/$Tasks_MapOverSet"; -export { $Tasks_MapOverToolCall } from "./schemas/$Tasks_MapOverToolCall"; export { $Tasks_ParallelStep } from "./schemas/$Tasks_ParallelStep"; export { $Tasks_PatchTaskRequest } from "./schemas/$Tasks_PatchTaskRequest"; export { $Tasks_PromptStep } from "./schemas/$Tasks_PromptStep"; -export { $Tasks_PromptStepDef } from "./schemas/$Tasks_PromptStepDef"; export { $Tasks_ReturnStep } from "./schemas/$Tasks_ReturnStep"; export { $Tasks_SearchStep } from "./schemas/$Tasks_SearchStep"; -export { $Tasks_SearchStepDef } from "./schemas/$Tasks_SearchStepDef"; export { $Tasks_SetKey } from "./schemas/$Tasks_SetKey"; export { $Tasks_SetStep } from "./schemas/$Tasks_SetStep"; -export { $Tasks_SetStepDef } from "./schemas/$Tasks_SetStepDef"; export { $Tasks_SleepFor } from "./schemas/$Tasks_SleepFor"; export { $Tasks_SleepStep } from "./schemas/$Tasks_SleepStep"; export { $Tasks_SwitchStep } from "./schemas/$Tasks_SwitchStep"; export { $Tasks_Task } from "./schemas/$Tasks_Task"; export { $Tasks_TaskTool } from "./schemas/$Tasks_TaskTool"; export { $Tasks_ToolCallStep } from "./schemas/$Tasks_ToolCallStep"; -export { $Tasks_ToolCallStepDef } from "./schemas/$Tasks_ToolCallStepDef"; export { $Tasks_UpdateTaskRequest } from "./schemas/$Tasks_UpdateTaskRequest"; +export { $Tasks_WaitForInputInfo } from "./schemas/$Tasks_WaitForInputInfo"; export { $Tasks_WaitForInputStep } from "./schemas/$Tasks_WaitForInputStep"; export { $Tasks_YieldStep } from "./schemas/$Tasks_YieldStep"; export { $Tools_ChosenFunctionCall } from "./schemas/$Tools_ChosenFunctionCall"; diff --git a/sdks/ts/src/api/models/Executions_Transition.ts b/sdks/ts/src/api/models/Executions_Transition.ts index 8e8ae1f90..7d1194a03 100644 --- a/sdks/ts/src/api/models/Executions_Transition.ts +++ b/sdks/ts/src/api/models/Executions_Transition.ts @@ -7,7 +7,7 @@ import type { Executions_TransitionTarget } from "./Executions_TransitionTarget" export type Executions_Transition = { readonly type: "finish" | "wait" | "resume" | "error" | "step" | "cancelled"; readonly execution_id: Common_uuid; - readonly output: Record; + readonly output: any; readonly current: Executions_TransitionTarget; readonly next: Executions_TransitionTarget | null; readonly id: Common_uuid; diff --git a/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts b/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts index 6be4bf7a8..9590b4304 100644 --- a/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts +++ b/sdks/ts/src/api/models/Tasks_CreateTaskRequest.ts @@ -10,14 +10,6 @@ import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; -import type { Tasks_MapOverEmbed } from "./Tasks_MapOverEmbed"; -import type { Tasks_MapOverEvaluate } from "./Tasks_MapOverEvaluate"; -import type { Tasks_MapOverGet } from "./Tasks_MapOverGet"; -import type { Tasks_MapOverLog } from "./Tasks_MapOverLog"; -import type { Tasks_MapOverPrompt } from "./Tasks_MapOverPrompt"; -import type { Tasks_MapOverSearch } from "./Tasks_MapOverSearch"; -import type { Tasks_MapOverSet } from "./Tasks_MapOverSet"; -import type { Tasks_MapOverToolCall } from "./Tasks_MapOverToolCall"; import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; @@ -58,25 +50,29 @@ export type Tasks_CreateTaskRequest = Record< readonly kind_: "map_reduce"; } & { readonly kind_: "map_reduce"; + /** + * The variable to iterate over + */ + over: Common_PyExpression; /** * The steps to run for each iteration */ map: - | Tasks_MapOverEvaluate - | Tasks_MapOverToolCall - | Tasks_MapOverPrompt - | Tasks_MapOverGet - | Tasks_MapOverSet - | Tasks_MapOverLog - | Tasks_MapOverEmbed - | Tasks_MapOverSearch; + | Tasks_EvaluateStep + | Tasks_ToolCallStep + | Tasks_PromptStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep; /** * The expression to reduce the results. * If not provided, the results are collected and returned as a list. * A special parameter named `results` is the accumulator and `_` is the current value. */ reduce?: Common_PyExpression; - initial?: Common_PyExpression; + initial?: any; }) > >; diff --git a/sdks/ts/src/api/models/Tasks_EmbedStepDef.ts b/sdks/ts/src/api/models/Tasks_EmbedStepDef.ts deleted file mode 100644 index 409d61a98..000000000 --- a/sdks/ts/src/api/models/Tasks_EmbedStepDef.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Docs_EmbedQueryRequest } from "./Docs_EmbedQueryRequest"; -export type Tasks_EmbedStepDef = { - /** - * The text to embed - */ - embed: Docs_EmbedQueryRequest; -}; diff --git a/sdks/ts/src/api/models/Tasks_EvaluateStepDef.ts b/sdks/ts/src/api/models/Tasks_EvaluateStepDef.ts deleted file mode 100644 index 21621bde3..000000000 --- a/sdks/ts/src/api/models/Tasks_EvaluateStepDef.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -export type Tasks_EvaluateStepDef = { - /** - * The expression to evaluate - */ - evaluate: Record; -}; diff --git a/sdks/ts/src/api/models/Tasks_GetStepDef.ts b/sdks/ts/src/api/models/Tasks_GetStepDef.ts deleted file mode 100644 index 4fe0a6dd8..000000000 --- a/sdks/ts/src/api/models/Tasks_GetStepDef.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export type Tasks_GetStepDef = { - /** - * The key to get - */ - get: string; -}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverEmbed.ts b/sdks/ts/src/api/models/Tasks_MapOverEmbed.ts deleted file mode 100644 index 7e0efd38e..000000000 --- a/sdks/ts/src/api/models/Tasks_MapOverEmbed.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_EmbedStepDef } from "./Tasks_EmbedStepDef"; -export type Tasks_MapOverEmbed = Tasks_EmbedStepDef & { - /** - * The variable to iterate over - */ - over: Common_PyExpression; -}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverEvaluate.ts b/sdks/ts/src/api/models/Tasks_MapOverEvaluate.ts deleted file mode 100644 index 42d070eb3..000000000 --- a/sdks/ts/src/api/models/Tasks_MapOverEvaluate.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_EvaluateStepDef } from "./Tasks_EvaluateStepDef"; -export type Tasks_MapOverEvaluate = Tasks_EvaluateStepDef & { - /** - * The variable to iterate over - */ - over: Common_PyExpression; -}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverGet.ts b/sdks/ts/src/api/models/Tasks_MapOverGet.ts deleted file mode 100644 index e0bbf048a..000000000 --- a/sdks/ts/src/api/models/Tasks_MapOverGet.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_GetStepDef } from "./Tasks_GetStepDef"; -export type Tasks_MapOverGet = Tasks_GetStepDef & { - /** - * The variable to iterate over - */ - over: Common_PyExpression; -}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverLog.ts b/sdks/ts/src/api/models/Tasks_MapOverLog.ts deleted file mode 100644 index 1b1c91801..000000000 --- a/sdks/ts/src/api/models/Tasks_MapOverLog.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_LogStepDef } from "./Tasks_LogStepDef"; -export type Tasks_MapOverLog = Tasks_LogStepDef & { - /** - * The variable to iterate over - */ - over: Common_PyExpression; -}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverPrompt.ts b/sdks/ts/src/api/models/Tasks_MapOverPrompt.ts deleted file mode 100644 index ec6d1016e..000000000 --- a/sdks/ts/src/api/models/Tasks_MapOverPrompt.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_PromptStepDef } from "./Tasks_PromptStepDef"; -export type Tasks_MapOverPrompt = Tasks_PromptStepDef & { - /** - * The variable to iterate over - */ - over: Common_PyExpression; -}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverSearch.ts b/sdks/ts/src/api/models/Tasks_MapOverSearch.ts deleted file mode 100644 index 272e9eeea..000000000 --- a/sdks/ts/src/api/models/Tasks_MapOverSearch.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_SearchStepDef } from "./Tasks_SearchStepDef"; -export type Tasks_MapOverSearch = Tasks_SearchStepDef & { - /** - * The variable to iterate over - */ - over: Common_PyExpression; -}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverSet.ts b/sdks/ts/src/api/models/Tasks_MapOverSet.ts deleted file mode 100644 index 78b06414c..000000000 --- a/sdks/ts/src/api/models/Tasks_MapOverSet.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_SetStepDef } from "./Tasks_SetStepDef"; -export type Tasks_MapOverSet = Tasks_SetStepDef & { - /** - * The variable to iterate over - */ - over: Common_PyExpression; -}; diff --git a/sdks/ts/src/api/models/Tasks_MapOverToolCall.ts b/sdks/ts/src/api/models/Tasks_MapOverToolCall.ts deleted file mode 100644 index b7040f0b7..000000000 --- a/sdks/ts/src/api/models/Tasks_MapOverToolCall.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Tasks_ToolCallStepDef } from "./Tasks_ToolCallStepDef"; -export type Tasks_MapOverToolCall = Tasks_ToolCallStepDef & { - /** - * The variable to iterate over - */ - over: Common_PyExpression; -}; diff --git a/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts b/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts index 28866dbb8..e511be553 100644 --- a/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts +++ b/sdks/ts/src/api/models/Tasks_PatchTaskRequest.ts @@ -10,14 +10,6 @@ import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; -import type { Tasks_MapOverEmbed } from "./Tasks_MapOverEmbed"; -import type { Tasks_MapOverEvaluate } from "./Tasks_MapOverEvaluate"; -import type { Tasks_MapOverGet } from "./Tasks_MapOverGet"; -import type { Tasks_MapOverLog } from "./Tasks_MapOverLog"; -import type { Tasks_MapOverPrompt } from "./Tasks_MapOverPrompt"; -import type { Tasks_MapOverSearch } from "./Tasks_MapOverSearch"; -import type { Tasks_MapOverSet } from "./Tasks_MapOverSet"; -import type { Tasks_MapOverToolCall } from "./Tasks_MapOverToolCall"; import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; @@ -57,25 +49,29 @@ export type Tasks_PatchTaskRequest = Record< */ kind_?: string; } & { + /** + * The variable to iterate over + */ + over: Common_PyExpression; /** * The steps to run for each iteration */ map: - | Tasks_MapOverEvaluate - | Tasks_MapOverToolCall - | Tasks_MapOverPrompt - | Tasks_MapOverGet - | Tasks_MapOverSet - | Tasks_MapOverLog - | Tasks_MapOverEmbed - | Tasks_MapOverSearch; + | Tasks_EvaluateStep + | Tasks_ToolCallStep + | Tasks_PromptStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep; /** * The expression to reduce the results. * If not provided, the results are collected and returned as a list. * A special parameter named `results` is the accumulator and `_` is the current value. */ reduce?: Common_PyExpression; - initial?: Common_PyExpression; + initial?: any; }) > >; diff --git a/sdks/ts/src/api/models/Tasks_PromptStepDef.ts b/sdks/ts/src/api/models/Tasks_PromptStepDef.ts deleted file mode 100644 index fb21d106f..000000000 --- a/sdks/ts/src/api/models/Tasks_PromptStepDef.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Chat_ChatSettings } from "./Chat_ChatSettings"; -import type { Common_JinjaTemplate } from "./Common_JinjaTemplate"; -export type Tasks_PromptStepDef = { - /** - * The prompt to run - */ - prompt: Common_JinjaTemplate; - /** - * Settings for the prompt - */ - settings: Chat_ChatSettings; -}; diff --git a/sdks/ts/src/api/models/Tasks_SearchStepDef.ts b/sdks/ts/src/api/models/Tasks_SearchStepDef.ts deleted file mode 100644 index 3daedd7af..000000000 --- a/sdks/ts/src/api/models/Tasks_SearchStepDef.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Docs_HybridDocSearchRequest } from "./Docs_HybridDocSearchRequest"; -import type { Docs_TextOnlyDocSearchRequest } from "./Docs_TextOnlyDocSearchRequest"; -import type { Docs_VectorDocSearchRequest } from "./Docs_VectorDocSearchRequest"; -export type Tasks_SearchStepDef = { - /** - * The search query - */ - search: - | Docs_VectorDocSearchRequest - | Docs_TextOnlyDocSearchRequest - | Docs_HybridDocSearchRequest; -}; diff --git a/sdks/ts/src/api/models/Tasks_SetStepDef.ts b/sdks/ts/src/api/models/Tasks_SetStepDef.ts deleted file mode 100644 index 41e81c59d..000000000 --- a/sdks/ts/src/api/models/Tasks_SetStepDef.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Tasks_SetKey } from "./Tasks_SetKey"; -export type Tasks_SetStepDef = { - /** - * The value to set - */ - set: Tasks_SetKey; -}; diff --git a/sdks/ts/src/api/models/Tasks_Task.ts b/sdks/ts/src/api/models/Tasks_Task.ts index 97a3a3b97..e155187ae 100644 --- a/sdks/ts/src/api/models/Tasks_Task.ts +++ b/sdks/ts/src/api/models/Tasks_Task.ts @@ -10,14 +10,6 @@ import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; -import type { Tasks_MapOverEmbed } from "./Tasks_MapOverEmbed"; -import type { Tasks_MapOverEvaluate } from "./Tasks_MapOverEvaluate"; -import type { Tasks_MapOverGet } from "./Tasks_MapOverGet"; -import type { Tasks_MapOverLog } from "./Tasks_MapOverLog"; -import type { Tasks_MapOverPrompt } from "./Tasks_MapOverPrompt"; -import type { Tasks_MapOverSearch } from "./Tasks_MapOverSearch"; -import type { Tasks_MapOverSet } from "./Tasks_MapOverSet"; -import type { Tasks_MapOverToolCall } from "./Tasks_MapOverToolCall"; import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; @@ -58,25 +50,29 @@ export type Tasks_Task = Record< readonly kind_: "map_reduce"; } & { readonly kind_: "map_reduce"; + /** + * The variable to iterate over + */ + over: Common_PyExpression; /** * The steps to run for each iteration */ map: - | Tasks_MapOverEvaluate - | Tasks_MapOverToolCall - | Tasks_MapOverPrompt - | Tasks_MapOverGet - | Tasks_MapOverSet - | Tasks_MapOverLog - | Tasks_MapOverEmbed - | Tasks_MapOverSearch; + | Tasks_EvaluateStep + | Tasks_ToolCallStep + | Tasks_PromptStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep; /** * The expression to reduce the results. * If not provided, the results are collected and returned as a list. * A special parameter named `results` is the accumulator and `_` is the current value. */ reduce?: Common_PyExpression; - initial?: Common_PyExpression; + initial?: any; }) > >; diff --git a/sdks/ts/src/api/models/Tasks_ToolCallStepDef.ts b/sdks/ts/src/api/models/Tasks_ToolCallStepDef.ts deleted file mode 100644 index 106293d20..000000000 --- a/sdks/ts/src/api/models/Tasks_ToolCallStepDef.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; -import type { Common_toolRef } from "./Common_toolRef"; -export type Tasks_ToolCallStepDef = { - /** - * The tool to run - */ - tool: Common_toolRef; - /** - * The input parameters for the tool (defaults to last step output) - */ - arguments: Record | "_"; -}; diff --git a/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts b/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts index 06e50f5c8..8f3afc2c0 100644 --- a/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts +++ b/sdks/ts/src/api/models/Tasks_UpdateTaskRequest.ts @@ -10,14 +10,6 @@ import type { Tasks_ForeachStep } from "./Tasks_ForeachStep"; import type { Tasks_GetStep } from "./Tasks_GetStep"; import type { Tasks_IfElseWorkflowStep } from "./Tasks_IfElseWorkflowStep"; import type { Tasks_LogStep } from "./Tasks_LogStep"; -import type { Tasks_MapOverEmbed } from "./Tasks_MapOverEmbed"; -import type { Tasks_MapOverEvaluate } from "./Tasks_MapOverEvaluate"; -import type { Tasks_MapOverGet } from "./Tasks_MapOverGet"; -import type { Tasks_MapOverLog } from "./Tasks_MapOverLog"; -import type { Tasks_MapOverPrompt } from "./Tasks_MapOverPrompt"; -import type { Tasks_MapOverSearch } from "./Tasks_MapOverSearch"; -import type { Tasks_MapOverSet } from "./Tasks_MapOverSet"; -import type { Tasks_MapOverToolCall } from "./Tasks_MapOverToolCall"; import type { Tasks_ParallelStep } from "./Tasks_ParallelStep"; import type { Tasks_PromptStep } from "./Tasks_PromptStep"; import type { Tasks_ReturnStep } from "./Tasks_ReturnStep"; @@ -58,25 +50,29 @@ export type Tasks_UpdateTaskRequest = Record< readonly kind_: "map_reduce"; } & { readonly kind_: "map_reduce"; + /** + * The variable to iterate over + */ + over: Common_PyExpression; /** * The steps to run for each iteration */ map: - | Tasks_MapOverEvaluate - | Tasks_MapOverToolCall - | Tasks_MapOverPrompt - | Tasks_MapOverGet - | Tasks_MapOverSet - | Tasks_MapOverLog - | Tasks_MapOverEmbed - | Tasks_MapOverSearch; + | Tasks_EvaluateStep + | Tasks_ToolCallStep + | Tasks_PromptStep + | Tasks_GetStep + | Tasks_SetStep + | Tasks_LogStep + | Tasks_EmbedStep + | Tasks_SearchStep; /** * The expression to reduce the results. * If not provided, the results are collected and returned as a list. * A special parameter named `results` is the accumulator and `_` is the current value. */ reduce?: Common_PyExpression; - initial?: Common_PyExpression; + initial?: any; }) > >; diff --git a/sdks/ts/src/api/models/Tasks_LogStepDef.ts b/sdks/ts/src/api/models/Tasks_WaitForInputInfo.ts similarity index 64% rename from sdks/ts/src/api/models/Tasks_LogStepDef.ts rename to sdks/ts/src/api/models/Tasks_WaitForInputInfo.ts index a672c06b7..adef0d462 100644 --- a/sdks/ts/src/api/models/Tasks_LogStepDef.ts +++ b/sdks/ts/src/api/models/Tasks_WaitForInputInfo.ts @@ -3,9 +3,9 @@ /* tslint:disable */ /* eslint-disable */ import type { Common_PyExpression } from "./Common_PyExpression"; -export type Tasks_LogStepDef = { +export type Tasks_WaitForInputInfo = { /** - * The value to log + * Any additional info or data */ - log: Common_PyExpression; + info: Record; }; diff --git a/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts b/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts index 246d68813..d7b537ea3 100644 --- a/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts +++ b/sdks/ts/src/api/models/Tasks_WaitForInputStep.ts @@ -2,7 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { Common_PyExpression } from "./Common_PyExpression"; +import type { Tasks_WaitForInputInfo } from "./Tasks_WaitForInputInfo"; export type Tasks_WaitForInputStep = { /** * The kind of step @@ -13,5 +13,5 @@ export type Tasks_WaitForInputStep = { /** * Any additional info or data */ - wait_for_input: Record; + wait_for_input: Tasks_WaitForInputInfo; }; diff --git a/sdks/ts/src/api/schemas/$Executions_Transition.ts b/sdks/ts/src/api/schemas/$Executions_Transition.ts index e427b3c24..45731aff9 100644 --- a/sdks/ts/src/api/schemas/$Executions_Transition.ts +++ b/sdks/ts/src/api/schemas/$Executions_Transition.ts @@ -20,10 +20,7 @@ export const $Executions_Transition = { isRequired: true, }, output: { - type: "dictionary", - contains: { - properties: {}, - }, + properties: {}, isReadOnly: true, isRequired: true, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts b/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts index 06002e838..882c1cf13 100644 --- a/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts +++ b/sdks/ts/src/api/schemas/$Tasks_CreateTaskRequest.ts @@ -79,33 +79,43 @@ export const $Tasks_CreateTaskRequest = { isReadOnly: true, isRequired: true, }, + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, map: { type: "any-of", description: `The steps to run for each iteration`, contains: [ { - type: "Tasks_MapOverEvaluate", + type: "Tasks_EvaluateStep", }, { - type: "Tasks_MapOverToolCall", + type: "Tasks_ToolCallStep", }, { - type: "Tasks_MapOverPrompt", + type: "Tasks_PromptStep", }, { - type: "Tasks_MapOverGet", + type: "Tasks_GetStep", }, { - type: "Tasks_MapOverSet", + type: "Tasks_SetStep", }, { - type: "Tasks_MapOverLog", + type: "Tasks_LogStep", }, { - type: "Tasks_MapOverEmbed", + type: "Tasks_EmbedStep", }, { - type: "Tasks_MapOverSearch", + type: "Tasks_SearchStep", }, ], isRequired: true, @@ -122,12 +132,7 @@ export const $Tasks_CreateTaskRequest = { ], }, initial: { - type: "all-of", - contains: [ - { - type: "Common_PyExpression", - }, - ], + properties: {}, }, }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_EmbedStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_EmbedStepDef.ts deleted file mode 100644 index 865826f90..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_EmbedStepDef.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_EmbedStepDef = { - properties: { - embed: { - type: "all-of", - description: `The text to embed`, - contains: [ - { - type: "Docs_EmbedQueryRequest", - }, - ], - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_GetStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_GetStepDef.ts deleted file mode 100644 index a5ca63603..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_GetStepDef.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_GetStepDef = { - properties: { - get: { - type: "string", - description: `The key to get`, - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_LogStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_LogStepDef.ts deleted file mode 100644 index 5941058c6..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_LogStepDef.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_LogStepDef = { - properties: { - log: { - type: "all-of", - description: `The value to log`, - contains: [ - { - type: "Common_PyExpression", - }, - ], - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverEmbed.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverEmbed.ts deleted file mode 100644 index ad2d0242a..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_MapOverEmbed.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_MapOverEmbed = { - type: "all-of", - contains: [ - { - type: "Tasks_EmbedStepDef", - }, - { - properties: { - over: { - type: "all-of", - description: `The variable to iterate over`, - contains: [ - { - type: "Common_PyExpression", - }, - ], - isRequired: true, - }, - }, - }, - ], -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverEvaluate.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverEvaluate.ts deleted file mode 100644 index 60f4191dd..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_MapOverEvaluate.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_MapOverEvaluate = { - type: "all-of", - contains: [ - { - type: "Tasks_EvaluateStepDef", - }, - { - properties: { - over: { - type: "all-of", - description: `The variable to iterate over`, - contains: [ - { - type: "Common_PyExpression", - }, - ], - isRequired: true, - }, - }, - }, - ], -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts deleted file mode 100644 index d9b6e75a8..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_MapOverGet.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_MapOverGet = { - type: "all-of", - contains: [ - { - type: "Tasks_GetStepDef", - }, - { - properties: { - over: { - type: "all-of", - description: `The variable to iterate over`, - contains: [ - { - type: "Common_PyExpression", - }, - ], - isRequired: true, - }, - }, - }, - ], -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts deleted file mode 100644 index dd3940542..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_MapOverLog.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_MapOverLog = { - type: "all-of", - contains: [ - { - type: "Tasks_LogStepDef", - }, - { - properties: { - over: { - type: "all-of", - description: `The variable to iterate over`, - contains: [ - { - type: "Common_PyExpression", - }, - ], - isRequired: true, - }, - }, - }, - ], -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverPrompt.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverPrompt.ts deleted file mode 100644 index 49825b727..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_MapOverPrompt.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_MapOverPrompt = { - type: "all-of", - contains: [ - { - type: "Tasks_PromptStepDef", - }, - { - properties: { - over: { - type: "all-of", - description: `The variable to iterate over`, - contains: [ - { - type: "Common_PyExpression", - }, - ], - isRequired: true, - }, - }, - }, - ], -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverSearch.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverSearch.ts deleted file mode 100644 index 48b376621..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_MapOverSearch.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_MapOverSearch = { - type: "all-of", - contains: [ - { - type: "Tasks_SearchStepDef", - }, - { - properties: { - over: { - type: "all-of", - description: `The variable to iterate over`, - contains: [ - { - type: "Common_PyExpression", - }, - ], - isRequired: true, - }, - }, - }, - ], -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts deleted file mode 100644 index 81e30335b..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_MapOverSet.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_MapOverSet = { - type: "all-of", - contains: [ - { - type: "Tasks_SetStepDef", - }, - { - properties: { - over: { - type: "all-of", - description: `The variable to iterate over`, - contains: [ - { - type: "Common_PyExpression", - }, - ], - isRequired: true, - }, - }, - }, - ], -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_MapOverToolCall.ts b/sdks/ts/src/api/schemas/$Tasks_MapOverToolCall.ts deleted file mode 100644 index 0357c025a..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_MapOverToolCall.ts +++ /dev/null @@ -1,26 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_MapOverToolCall = { - type: "all-of", - contains: [ - { - type: "Tasks_ToolCallStepDef", - }, - { - properties: { - over: { - type: "all-of", - description: `The variable to iterate over`, - contains: [ - { - type: "Common_PyExpression", - }, - ], - isRequired: true, - }, - }, - }, - ], -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts b/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts index 22f27c59a..45ad2927f 100644 --- a/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts +++ b/sdks/ts/src/api/schemas/$Tasks_PatchTaskRequest.ts @@ -73,33 +73,43 @@ export const $Tasks_PatchTaskRequest = { }, { properties: { + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, map: { type: "any-of", description: `The steps to run for each iteration`, contains: [ { - type: "Tasks_MapOverEvaluate", + type: "Tasks_EvaluateStep", }, { - type: "Tasks_MapOverToolCall", + type: "Tasks_ToolCallStep", }, { - type: "Tasks_MapOverPrompt", + type: "Tasks_PromptStep", }, { - type: "Tasks_MapOverGet", + type: "Tasks_GetStep", }, { - type: "Tasks_MapOverSet", + type: "Tasks_SetStep", }, { - type: "Tasks_MapOverLog", + type: "Tasks_LogStep", }, { - type: "Tasks_MapOverEmbed", + type: "Tasks_EmbedStep", }, { - type: "Tasks_MapOverSearch", + type: "Tasks_SearchStep", }, ], isRequired: true, @@ -116,12 +126,7 @@ export const $Tasks_PatchTaskRequest = { ], }, initial: { - type: "all-of", - contains: [ - { - type: "Common_PyExpression", - }, - ], + properties: {}, }, }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_PromptStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_PromptStepDef.ts deleted file mode 100644 index badb73d46..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_PromptStepDef.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_PromptStepDef = { - properties: { - prompt: { - type: "any-of", - description: `The prompt to run`, - contains: [ - { - type: "Common_JinjaTemplate", - }, - ], - isRequired: true, - }, - settings: { - type: "all-of", - description: `Settings for the prompt`, - contains: [ - { - type: "Chat_ChatSettings", - }, - ], - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SearchStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_SearchStepDef.ts deleted file mode 100644 index 3ee574d3c..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_SearchStepDef.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_SearchStepDef = { - properties: { - search: { - type: "any-of", - description: `The search query`, - contains: [ - { - type: "Docs_VectorDocSearchRequest", - }, - { - type: "Docs_TextOnlyDocSearchRequest", - }, - { - type: "Docs_HybridDocSearchRequest", - }, - ], - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts deleted file mode 100644 index 7af72fcb4..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_SetStepDef.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_SetStepDef = { - properties: { - set: { - type: "all-of", - description: `The value to set`, - contains: [ - { - type: "Tasks_SetKey", - }, - ], - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_Task.ts b/sdks/ts/src/api/schemas/$Tasks_Task.ts index aa183b7fb..c89bade35 100644 --- a/sdks/ts/src/api/schemas/$Tasks_Task.ts +++ b/sdks/ts/src/api/schemas/$Tasks_Task.ts @@ -79,33 +79,43 @@ export const $Tasks_Task = { isReadOnly: true, isRequired: true, }, + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, map: { type: "any-of", description: `The steps to run for each iteration`, contains: [ { - type: "Tasks_MapOverEvaluate", + type: "Tasks_EvaluateStep", }, { - type: "Tasks_MapOverToolCall", + type: "Tasks_ToolCallStep", }, { - type: "Tasks_MapOverPrompt", + type: "Tasks_PromptStep", }, { - type: "Tasks_MapOverGet", + type: "Tasks_GetStep", }, { - type: "Tasks_MapOverSet", + type: "Tasks_SetStep", }, { - type: "Tasks_MapOverLog", + type: "Tasks_LogStep", }, { - type: "Tasks_MapOverEmbed", + type: "Tasks_EmbedStep", }, { - type: "Tasks_MapOverSearch", + type: "Tasks_SearchStep", }, ], isRequired: true, @@ -122,12 +132,7 @@ export const $Tasks_Task = { ], }, initial: { - type: "all-of", - contains: [ - { - type: "Common_PyExpression", - }, - ], + properties: {}, }, }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_ToolCallStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_ToolCallStepDef.ts deleted file mode 100644 index 69b275101..000000000 --- a/sdks/ts/src/api/schemas/$Tasks_ToolCallStepDef.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Tasks_ToolCallStepDef = { - properties: { - tool: { - type: "all-of", - description: `The tool to run`, - contains: [ - { - type: "Common_toolRef", - }, - ], - isRequired: true, - }, - arguments: { - type: "any-of", - description: `The input parameters for the tool (defaults to last step output)`, - contains: [ - { - type: "dictionary", - contains: { - type: "Common_PyExpression", - }, - }, - { - type: "Enum", - }, - ], - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts b/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts index a9cd8187b..84255c8ca 100644 --- a/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts +++ b/sdks/ts/src/api/schemas/$Tasks_UpdateTaskRequest.ts @@ -79,33 +79,43 @@ export const $Tasks_UpdateTaskRequest = { isReadOnly: true, isRequired: true, }, + over: { + type: "all-of", + description: `The variable to iterate over`, + contains: [ + { + type: "Common_PyExpression", + }, + ], + isRequired: true, + }, map: { type: "any-of", description: `The steps to run for each iteration`, contains: [ { - type: "Tasks_MapOverEvaluate", + type: "Tasks_EvaluateStep", }, { - type: "Tasks_MapOverToolCall", + type: "Tasks_ToolCallStep", }, { - type: "Tasks_MapOverPrompt", + type: "Tasks_PromptStep", }, { - type: "Tasks_MapOverGet", + type: "Tasks_GetStep", }, { - type: "Tasks_MapOverSet", + type: "Tasks_SetStep", }, { - type: "Tasks_MapOverLog", + type: "Tasks_LogStep", }, { - type: "Tasks_MapOverEmbed", + type: "Tasks_EmbedStep", }, { - type: "Tasks_MapOverSearch", + type: "Tasks_SearchStep", }, ], isRequired: true, @@ -122,12 +132,7 @@ export const $Tasks_UpdateTaskRequest = { ], }, initial: { - type: "all-of", - contains: [ - { - type: "Common_PyExpression", - }, - ], + properties: {}, }, }, }, diff --git a/sdks/ts/src/api/schemas/$Tasks_EvaluateStepDef.ts b/sdks/ts/src/api/schemas/$Tasks_WaitForInputInfo.ts similarity index 83% rename from sdks/ts/src/api/schemas/$Tasks_EvaluateStepDef.ts rename to sdks/ts/src/api/schemas/$Tasks_WaitForInputInfo.ts index acd724d29..81526c6a1 100644 --- a/sdks/ts/src/api/schemas/$Tasks_EvaluateStepDef.ts +++ b/sdks/ts/src/api/schemas/$Tasks_WaitForInputInfo.ts @@ -2,9 +2,9 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export const $Tasks_EvaluateStepDef = { +export const $Tasks_WaitForInputInfo = { properties: { - evaluate: { + info: { type: "dictionary", contains: { type: "Common_PyExpression", diff --git a/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts b/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts index 72b24a415..91a9e7bc9 100644 --- a/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_WaitForInputStep.ts @@ -22,10 +22,13 @@ export const $Tasks_WaitForInputStep = { isRequired: true, }, wait_for_input: { - type: "dictionary", - contains: { - type: "Common_PyExpression", - }, + type: "all-of", + description: `Any additional info or data`, + contains: [ + { + type: "Tasks_WaitForInputInfo", + }, + ], isRequired: true, }, }, diff --git a/typespec/executions/models.tsp b/typespec/executions/models.tsp index 0f7be074f..ffaa10dc5 100644 --- a/typespec/executions/models.tsp +++ b/typespec/executions/models.tsp @@ -75,7 +75,7 @@ model StopExecutionRequest extends UpdateExecutionRequest { model ResumeExecutionRequest extends UpdateExecutionRequest { status: "running" = "running"; - + /** The input to resume the execution with */ input?: Record; } @@ -121,7 +121,7 @@ model Transition { execution_id: Execution.id; @visibility("read") - output: Record; + output: unknown; @visibility("read") current: TransitionTarget; diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp index a32835b27..3b2236ac5 100644 --- a/typespec/tasks/steps.tsp +++ b/typespec/tasks/steps.tsp @@ -240,70 +240,22 @@ model ForeachStepDef { foreach: ForeachDo; } -model BaseMapOver { - /** The variable to iterate over */ - over: TypedExpression>; -} - -model MapOverEvaluate extends EvaluateStepDef { - ...BaseMapOver; -} - -model MapOverToolCall extends ToolCallStepDef { - ...BaseMapOver; -} - -model MapOverPrompt extends PromptStepDef { - ...BaseMapOver; -} - -model MapOverGet extends GetStepDef { - ...BaseMapOver; -} - -model MapOverSet extends SetStepDef { - ...BaseMapOver; -} - -model MapOverLog extends LogStepDef { - ...BaseMapOver; -} - -model MapOverEmbed extends EmbedStepDef { - ...BaseMapOver; -} - -model MapOverSearch extends SearchStepDef { - ...BaseMapOver; -} - -alias MapOver = - | MapOverEvaluate - | MapOverToolCall - | MapOverPrompt - | MapOverGet - | MapOverSet - | MapOverLog - | MapOverEmbed - | MapOverSearch; - model MapReduceStep> extends BaseWorkflowStep<"map_reduce"> { @visibility("read") kind_: "map_reduce" = "map_reduce"; - ...MapReduceStepDef; -} + /** The variable to iterate over */ + over: TypedExpression>; -model MapReduceStepDef> { /** The steps to run for each iteration */ - map: MapOver; + map: MappableWorkflowStep; /** The expression to reduce the results. - * If not provided, the results are collected and returned as a list. + * If not provided, the results are collected and returned as a list. * A special parameter named `results` is the accumulator and `_` is the current value. */ reduce?: ReduceExpression; - initial?: TypedExpression = "[]"; + initial?: unknown = #[]; } ///////////////////////// From 4c936b0cd1b934f4837f94216983754a2b51f5e9 Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Fri, 23 Aug 2024 17:52:48 +0300 Subject: [PATCH 100/110] test: Add task yaml test --- agents-api/tests/test_execution_workflow.py | 47 +++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 04af5c378..d55b62362 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -3,6 +3,7 @@ import asyncio from unittest.mock import patch +import yaml from google.protobuf.json_format import MessageToDict from litellm.types.utils import Choices, ModelResponse from ward import raises, test @@ -708,3 +709,49 @@ async def _( result = await handle.result() assert result["content"] == "Hello, world!" assert result["role"] == "assistant" + + +@test("workflow: execute yaml task") +async def _( + client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + mock_model_response = ModelResponse( + id="fake_id", + choices=[Choices(message={"role": "assistant", "content": "Hello, world!"})], + created=0, + object="text_completion", + ) + + with patch("agents_api.clients.litellm.acompletion") as acompletion, open( + "./tests/sample_tasks/find_selector.yaml", "r" + ) as task_file: + task_definition = yaml.safe_load(task_file) + acompletion.return_value = mock_model_response + data = CreateExecutionRequest(input={"test": "input"}) + + task = create_task( + developer_id=developer_id, + agent_id=agent.id, + data=CreateTaskRequest(**task_definition), + client=client, + ) + + async with patch_testing_temporal() as (_, mock_run_task_execution_workflow): + execution, handle = await start_execution( + developer_id=developer_id, + task_id=task.id, + data=data, + client=client, + ) + + assert handle is not None + assert execution.task_id == task.id + assert execution.input == data.input + + mock_run_task_execution_workflow.assert_called_once() + + result = await handle.result() + assert result["content"] == "Hello, world!" + assert result["role"] == "assistant" From e3bde8816ab645bf26fbbf100920b0b3e594847b Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Fri, 23 Aug 2024 17:53:09 +0300 Subject: [PATCH 101/110] chore: Rename accumulator value --- agents-api/agents_api/workflows/task_execution.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index a61996e5f..7e64e3437 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -267,7 +267,7 @@ async def transition(**kwargs) -> None: map=map_defn, reduce=reduce, initial=initial ), StepOutcome(output=items): initial = initial or [] - reduce = reduce or "initial + [_]" + reduce = reduce or "results + [_]" for i, item in enumerate(items): workflow_name = f"`{context.cursor.workflow}`[{context.cursor.step}].mapreduce[{i}]" @@ -304,7 +304,7 @@ async def transition(**kwargs) -> None: task_steps.base_evaluate, args=[ reduce, - {"initial": initial, "_": output}, + {"results": initial, "_": output}, ], schedule_to_close_timeout=timedelta(seconds=2), ) From 0a4708cb2d0aceab63ec6efbda63f9736d2ed14e Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Fri, 23 Aug 2024 17:53:22 +0300 Subject: [PATCH 102/110] fix: Fix task file formatting --- .../tests/sample_tasks/find_selector.yaml | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/agents-api/tests/sample_tasks/find_selector.yaml b/agents-api/tests/sample_tasks/find_selector.yaml index 141442a79..42850c00a 100644 --- a/agents-api/tests/sample_tasks/find_selector.yaml +++ b/agents-api/tests/sample_tasks/find_selector.yaml @@ -41,39 +41,38 @@ input_schema: main: - map: - over: _.parameters - - prompt: - - role: system - content: |- - From the screenshot below, can you identify if the page has {{_}} for the user? - Write your answer in the following yaml format: + prompt: + - role: system + content: |- + From the screenshot below, can you identify if the page has {{_}} for the user? + Write your answer in the following yaml format: - found: true|false - value: |null + found: true|false + value: |null - Just write your answer in the above format only. - Please do not include any other information or explanation in the response. + Just write your answer in the above format only. + Please do not include any other information or explanation in the response. - - role: user - content: - - type: image - image_url: 'inputs[0].screenshot_base64' + - role: user + content: + - type: image + image_url: 'inputs[0].screenshot_base64' + over: _.parameters reduce: >- - results - + [ - yaml.safe_load(_["choices"][0]["message"]["content"].trim()) - ] + results + + [ + yaml.safe_load(_["choices"][0]["message"]["content"].trim()) + ] - evaluate: >- - [ - {"value": result["value"], "network_request": request} - for request in execution.input.network_requests - if result["value"] in nr.response.body - for result in _ - if result["found"] - ] + [ + {"value": result["value"], "network_request": request} + for request in execution.input.network_requests + if result["value"] in nr.response.body + for result in _ + if result["found"] + ] - if: len(_) > 0 then: From effc4dd19c9f852e2bc6ddefd42d0e015c1c57e7 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 23 Aug 2024 16:36:53 -0400 Subject: [PATCH 103/110] feat(agents-api): Successfully create the sample task find_selector.yaml Signed-off-by: Diwank Tomer --- agents-api/tests/fixtures.py | 18 +---- agents-api/tests/sample_tasks/__init__.py | 0 .../tests/sample_tasks/find_selector.yaml | 74 +++++++++---------- .../tests/sample_tasks/test_find_selector.py | 47 ++++++++++++ agents-api/tests/utils.py | 21 +++++- 5 files changed, 108 insertions(+), 52 deletions(-) create mode 100644 agents-api/tests/sample_tasks/__init__.py create mode 100644 agents-api/tests/sample_tasks/test_find_selector.py diff --git a/agents-api/tests/fixtures.py b/agents-api/tests/fixtures.py index 188fe352b..c6a98ee99 100644 --- a/agents-api/tests/fixtures.py +++ b/agents-api/tests/fixtures.py @@ -1,9 +1,7 @@ -from unittest.mock import patch from uuid import uuid4 from cozo_migrate.api import apply, init from fastapi.testclient import TestClient -from litellm.types.utils import Choices, ModelResponse from pycozo import Client as CozoClient from temporalio.client import WorkflowHandle from ward import fixture @@ -39,6 +37,8 @@ from agents_api.models.user.delete_user import delete_user from agents_api.web import app +from .utils import patch_embed_acompletion as patch_embed_acompletion_ctx + EMBEDDING_SIZE: int = 1024 @@ -85,19 +85,9 @@ def test_developer(cozo_client=cozo_client, developer_id=test_developer_id): @fixture(scope="test") def patch_embed_acompletion(): - mock_model_response = ModelResponse( - id="fake_id", - choices=[Choices(message={"role": "assistant", "content": "Hello, world!"})], - created=0, - object="text_completion", - ) - - with patch("agents_api.clients.embed.embed") as embed, patch( - "agents_api.clients.litellm.acompletion" - ) as acompletion: - embed.return_value = [[1.0] * EMBEDDING_SIZE] - acompletion.return_value = mock_model_response + output = {"role": "assistant", "content": "Hello, world!"} + with patch_embed_acompletion_ctx(output) as (embed, acompletion): yield embed, acompletion diff --git a/agents-api/tests/sample_tasks/__init__.py b/agents-api/tests/sample_tasks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/agents-api/tests/sample_tasks/find_selector.yaml b/agents-api/tests/sample_tasks/find_selector.yaml index 141442a79..f73e3069e 100644 --- a/agents-api/tests/sample_tasks/find_selector.yaml +++ b/agents-api/tests/sample_tasks/find_selector.yaml @@ -40,46 +40,46 @@ input_schema: type: string main: -- map: - over: _.parameters + - map: + prompt: + - role: system + content: |- + From the screenshot below, can you identify if the page has {{_}} for the user? + Write your answer in the following yaml format: - prompt: - - role: system - content: |- - From the screenshot below, can you identify if the page has {{_}} for the user? - Write your answer in the following yaml format: + found: true|false + value: |null - found: true|false - value: |null + Make sure to end your answer in the above format only. + Please do not include any other information or explanation after it. - Just write your answer in the above format only. - Please do not include any other information or explanation in the response. + - role: user + content: + - type: image_url + image_url: + url: "inputs[0].screenshot_base64" - - role: user - content: - - type: image - image_url: 'inputs[0].screenshot_base64' + over: _.parameters + reduce: >- + results + + [ + yaml.safe_load(_["choices"][0]["message"]["content"].trim()) + ] - reduce: >- - results - + [ - yaml.safe_load(_["choices"][0]["message"]["content"].trim()) - ] + - evaluate: >- + [ + {"value": result["value"], "network_request": request} + for request in execution.input.network_requests + if result["value"] in nr.response.body + for result in _ + if result["found"] + ] -- evaluate: >- - [ - {"value": result["value"], "network_request": request} - for request in execution.input.network_requests - if result["value"] in nr.response.body - for result in _ - if result["found"] - ] - -- if: len(_) > 0 - then: - workflow: find_selectors - arguments: - results: list(zip(_, execution.input.network_requests)) - parameters: execution.input.parameters - else: - error: 'Could not find the selector in any of the network requests' \ No newline at end of file + - if: len(_) > 0 + then: + workflow: find_selectors + arguments: + results: list(zip(_, execution.input.network_requests)) + parameters: execution.input.parameters + else: + error: "Could not find the selector in any of the network requests" diff --git a/agents-api/tests/sample_tasks/test_find_selector.py b/agents-api/tests/sample_tasks/test_find_selector.py new file mode 100644 index 000000000..405a92f13 --- /dev/null +++ b/agents-api/tests/sample_tasks/test_find_selector.py @@ -0,0 +1,47 @@ +# Tests for task queries + +import os +from uuid import uuid4 + +from ward import test + +from ..fixtures import cozo_client, test_agent, test_developer_id +from ..utils import patch_embed_acompletion, patch_http_client_with_temporal + +this_dir = os.path.dirname(__file__) + + +@test("workflow sample: find-selector") +async def _( + cozo_client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + agent_id = str(agent.id) + task_id = str(uuid4()) + + with patch_embed_acompletion(), open( + f"{this_dir}/find_selector.yaml", "r" + ) as sample_file: + task_def = sample_file.read() + + async with patch_http_client_with_temporal( + cozo_client=cozo_client, developer_id=developer_id + ) as ( + make_request, + client, + ): + make_request( + method="POST", + url=f"/agents/{agent_id}/tasks/{task_id}", + headers={"Content-Type": "application/x-yaml"}, + data=task_def, + ) + + # execution_data = dict(input={"test": "input"}) + + # make_request( + # method="POST", + # url=f"/tasks/{task_id}/executions", + # json=execution_data, + # ) diff --git a/agents-api/tests/utils.py b/agents-api/tests/utils.py index 74127f78d..d6c3e7ca8 100644 --- a/agents-api/tests/utils.py +++ b/agents-api/tests/utils.py @@ -1,9 +1,10 @@ import asyncio import logging -from contextlib import asynccontextmanager +from contextlib import asynccontextmanager, contextmanager from unittest.mock import patch from fastapi.testclient import TestClient +from litellm.types.utils import Choices, ModelResponse from temporalio.testing import WorkflowEnvironment from agents_api.worker.codec import pydantic_data_converter @@ -64,3 +65,21 @@ def make_request(method, url, **kwargs): return client.request(method, url, headers=headers, **kwargs) yield make_request, client + + +@contextmanager +def patch_embed_acompletion(output={"role": "assistant", "content": "Hello, world!"}): + mock_model_response = ModelResponse( + id="fake_id", + choices=[Choices(message=output)], + created=0, + object="text_completion", + ) + + with patch("agents_api.clients.embed.embed") as embed, patch( + "agents_api.clients.litellm.acompletion" + ) as acompletion: + embed.return_value = [[1.0] * EMBEDDING_SIZE] + acompletion.return_value = mock_model_response + + yield embed, acompletion From 47358c1458833532934a182b92883c7910c93fda Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 23 Aug 2024 18:54:42 -0400 Subject: [PATCH 104/110] fix(agents-api): Fix agent/create-or-update route and YAML handling Signed-off-by: Diwank Tomer --- agents-api/agents_api/middleware.py | 37 ----------------- .../agents_api/routers/agents/router.py | 2 +- agents-api/agents_api/routers/docs/router.py | 2 +- agents-api/agents_api/routers/jobs/routers.py | 2 +- .../agents_api/routers/sessions/router.py | 2 +- .../agents_api/routers/tasks/__init__.py | 1 + .../routers/tasks/create_or_update_task.py | 38 ++++++++++++++++++ agents-api/agents_api/routers/tasks/router.py | 40 +++++++++++++++++-- agents-api/agents_api/routers/users/router.py | 2 +- agents-api/agents_api/web.py | 4 -- agents-api/poetry.lock | 8 ++-- .../tests/sample_tasks/test_find_selector.py | 16 ++++---- agents-api/tests/test_workflow_routes.py | 28 ++++++------- sdks/python/poetry.lock | 8 ++-- sdks/ts/src/api/services/DefaultService.ts | 4 +- typespec/tasks/endpoints.tsp | 2 +- 16 files changed, 114 insertions(+), 82 deletions(-) delete mode 100644 agents-api/agents_api/middleware.py create mode 100644 agents-api/agents_api/routers/tasks/create_or_update_task.py diff --git a/agents-api/agents_api/middleware.py b/agents-api/agents_api/middleware.py deleted file mode 100644 index d8ebc38ef..000000000 --- a/agents-api/agents_api/middleware.py +++ /dev/null @@ -1,37 +0,0 @@ -import re - -import yaml -from fastapi import Request - - -class YamlMiddleware: - def __init__(self, path_regex: str = r".*"): - self.path_regex = re.compile(path_regex) - - async def __call__(self, request: Request, call_next): - content_type = request.headers.get("content-type", "").strip().lower() - - # Filter out requests that are not for YAML and not for the specified path - if not self.path_regex.match(request.url.path) or content_type not in [ - "application/x-yaml", - "application/yaml", - "text/yaml", - "text/x-yaml", - ]: - return await call_next(request) - - # TODO: This should be wrapped in a try/except block to catch any parsing errors - # - # Parse the YAML body into a Python object - body = yaml.load(await request.body(), yaml.CSafeLoader) - request._json = body - - # Switch headers to JSON - headers = request.headers.mutablecopy() - headers["content-type"] = "application/json" - - request._headers = headers - - # Continue processing the request - response = await call_next(request) - return response diff --git a/agents-api/agents_api/routers/agents/router.py b/agents-api/agents_api/routers/agents/router.py index 6582baa6f..b7cfed8f5 100644 --- a/agents-api/agents_api/routers/agents/router.py +++ b/agents-api/agents_api/routers/agents/router.py @@ -2,4 +2,4 @@ from fastapi import APIRouter -router: Any = APIRouter() +router: APIRouter = APIRouter() diff --git a/agents-api/agents_api/routers/docs/router.py b/agents-api/agents_api/routers/docs/router.py index 6582baa6f..b7cfed8f5 100644 --- a/agents-api/agents_api/routers/docs/router.py +++ b/agents-api/agents_api/routers/docs/router.py @@ -2,4 +2,4 @@ from fastapi import APIRouter -router: Any = APIRouter() +router: APIRouter = APIRouter() diff --git a/agents-api/agents_api/routers/jobs/routers.py b/agents-api/agents_api/routers/jobs/routers.py index 5f7d3ef67..c3a4d5ed4 100644 --- a/agents-api/agents_api/routers/jobs/routers.py +++ b/agents-api/agents_api/routers/jobs/routers.py @@ -7,7 +7,7 @@ from agents_api.autogen.openapi_model import JobStatus from agents_api.clients.temporal import get_client -router: Any = APIRouter() +router: APIRouter = APIRouter() def map_job_status( diff --git a/agents-api/agents_api/routers/sessions/router.py b/agents-api/agents_api/routers/sessions/router.py index 6582baa6f..b7cfed8f5 100644 --- a/agents-api/agents_api/routers/sessions/router.py +++ b/agents-api/agents_api/routers/sessions/router.py @@ -2,4 +2,4 @@ from fastapi import APIRouter -router: Any = APIRouter() +router: APIRouter = APIRouter() diff --git a/agents-api/agents_api/routers/tasks/__init__.py b/agents-api/agents_api/routers/tasks/__init__.py index 94cd1d8cd..7321d9ab9 100644 --- a/agents-api/agents_api/routers/tasks/__init__.py +++ b/agents-api/agents_api/routers/tasks/__init__.py @@ -1,4 +1,5 @@ # ruff: noqa: F401, F403, F405 +from .create_or_update_task import create_or_update_task from .create_task import create_task from .create_task_execution import create_task_execution from .get_execution_details import get_execution_details diff --git a/agents-api/agents_api/routers/tasks/create_or_update_task.py b/agents-api/agents_api/routers/tasks/create_or_update_task.py new file mode 100644 index 000000000..2efa41e2e --- /dev/null +++ b/agents-api/agents_api/routers/tasks/create_or_update_task.py @@ -0,0 +1,38 @@ +from typing import Annotated + +from fastapi import Depends +from pydantic import UUID4 +from starlette.status import HTTP_201_CREATED + +from agents_api.autogen.openapi_model import ( + CreateOrUpdateTaskRequest, + ResourceUpdatedResponse, + Task, +) +from agents_api.dependencies.developer_id import get_developer_id +from agents_api.models.task.create_or_update_task import ( + create_or_update_task as create_or_update_task_query, +) + +from .router import router + + +@router.post( + "/agents/{agent_id}/tasks/{task_id}", status_code=HTTP_201_CREATED, tags=["tasks"] +) +async def create_or_update_task( + data: CreateOrUpdateTaskRequest, + agent_id: UUID4, + task_id: UUID4, + x_developer_id: Annotated[UUID4, Depends(get_developer_id)], +) -> ResourceUpdatedResponse: + # TODO: Do thorough validation of the task spec + + task: Task = create_or_update_task_query( + developer_id=x_developer_id, + agent_id=agent_id, + task_id=task_id, + data=data, + ) + + return ResourceUpdatedResponse(id=task.id, updated_at=task.updated_at, jobs=[]) diff --git a/agents-api/agents_api/routers/tasks/router.py b/agents-api/agents_api/routers/tasks/router.py index 6582baa6f..46baf1a29 100644 --- a/agents-api/agents_api/routers/tasks/router.py +++ b/agents-api/agents_api/routers/tasks/router.py @@ -1,5 +1,39 @@ -from typing import Any +from typing import Any, Callable -from fastapi import APIRouter +import yaml +from fastapi import APIRouter, Request, Response +from fastapi.routing import APIRoute -router: Any = APIRouter() + +class YamlRequest(Request): + async def body(self) -> bytes: + if not hasattr(self, "_body"): + body = await super().body() + + if self.headers.get("content-type") in [ + "application/x-yaml", + "application/yaml", + "text/yaml", + ]: + body = yaml.load(body, yaml.CSafeLoader) + + self._body = body + + return self._body + + +class YamlRoute(APIRoute): + def get_route_handler(self) -> Callable: + original_route_handler = super().get_route_handler() + + async def custom_route_handler(request: Request) -> Response: + request = YamlRequest(request.scope, request.receive) + + return await original_route_handler(request) + + return custom_route_handler + + +router: APIRouter = APIRouter( + route_class=YamlRoute, +) diff --git a/agents-api/agents_api/routers/users/router.py b/agents-api/agents_api/routers/users/router.py index 6582baa6f..b7cfed8f5 100644 --- a/agents-api/agents_api/routers/users/router.py +++ b/agents-api/agents_api/routers/users/router.py @@ -2,4 +2,4 @@ from fastapi import APIRouter -router: Any = APIRouter() +router: APIRouter = APIRouter() diff --git a/agents-api/agents_api/web.py b/agents-api/agents_api/web.py index e633fc3ce..364497671 100644 --- a/agents-api/agents_api/web.py +++ b/agents-api/agents_api/web.py @@ -21,7 +21,6 @@ from .dependencies.auth import get_api_key from .env import sentry_dsn from .exceptions import PromptTooBigError -from .middleware import YamlMiddleware from .routers import ( agents, docs, @@ -99,9 +98,6 @@ def register_exceptions(app: FastAPI) -> None: app.add_middleware(GZipMiddleware, minimum_size=1000, compresslevel=3) -# Add yaml middleware -app.middleware("http")(YamlMiddleware(path_regex=r"/agents/.+/tasks.*")) - register_exceptions(app) app.include_router(agents.router) diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index de023db46..6099cb98d 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -1208,13 +1208,13 @@ typing = ["types-PyYAML", "types-requests", "types-simplejson", "types-toml", "t [[package]] name = "idna" -version = "3.7" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] diff --git a/agents-api/tests/sample_tasks/test_find_selector.py b/agents-api/tests/sample_tasks/test_find_selector.py index 405a92f13..09f79e340 100644 --- a/agents-api/tests/sample_tasks/test_find_selector.py +++ b/agents-api/tests/sample_tasks/test_find_selector.py @@ -32,16 +32,16 @@ async def _( client, ): make_request( - method="POST", + method="PUT", url=f"/agents/{agent_id}/tasks/{task_id}", headers={"Content-Type": "application/x-yaml"}, data=task_def, - ) + ).raise_for_status() - # execution_data = dict(input={"test": "input"}) + execution_data = dict(input={"test": "input"}) - # make_request( - # method="POST", - # url=f"/tasks/{task_id}/executions", - # json=execution_data, - # ) + make_request( + method="POST", + url=f"/tasks/{task_id}/executions", + json=execution_data, + ).raise_for_status() diff --git a/agents-api/tests/test_workflow_routes.py b/agents-api/tests/test_workflow_routes.py index b1fb5abf0..34aa0101c 100644 --- a/agents-api/tests/test_workflow_routes.py +++ b/agents-api/tests/test_workflow_routes.py @@ -34,7 +34,7 @@ async def _( method="POST", url=f"/agents/{agent_id}/tasks/{task_id}", json=task_data, - ) + ).raise_for_status() execution_data = dict(input={"test": "input"}) @@ -42,7 +42,7 @@ async def _( method="POST", url=f"/tasks/{task_id}/executions", json=execution_data, - ) + ).raise_for_status() @test("workflow route: evaluate step single with yaml") @@ -61,23 +61,23 @@ async def _( client, ): task_data = """ - name: test task - description: test task about - input_schema: - type: object - additionalProperties: true +name: test task +description: test task about +input_schema: + type: object + additionalProperties: true - main: - - evaluate: - hello: '"world"' - """ +main: + - evaluate: + hello: '"world"' +""" make_request( method="POST", url=f"/agents/{agent_id}/tasks/{task_id}", - content=task_data, + content=task_data.encode("utf-8"), headers={"Content-Type": "text/yaml"}, - ) + ).raise_for_status() execution_data = dict(input={"test": "input"}) @@ -85,4 +85,4 @@ async def _( method="POST", url=f"/tasks/{task_id}/executions", json=execution_data, - ) + ).raise_for_status() diff --git a/sdks/python/poetry.lock b/sdks/python/poetry.lock index 9a1decf35..c9b836639 100644 --- a/sdks/python/poetry.lock +++ b/sdks/python/poetry.lock @@ -804,13 +804,13 @@ socks = ["socksio (==1.*)"] [[package]] name = "idna" -version = "3.7" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] diff --git a/sdks/ts/src/api/services/DefaultService.ts b/sdks/ts/src/api/services/DefaultService.ts index 1b4a4680a..9e9540ed9 100644 --- a/sdks/ts/src/api/services/DefaultService.ts +++ b/sdks/ts/src/api/services/DefaultService.ts @@ -704,7 +704,7 @@ export class DefaultService { } /** * Create or update a task - * @returns Common_ResourceCreatedResponse The request has succeeded and a new resource has been created as a result. + * @returns Common_ResourceUpdatedResponse The request has succeeded and a new resource has been created as a result. * @throws ApiError */ public tasksCreateOrUpdateRouteCreateOrUpdate({ @@ -718,7 +718,7 @@ export class DefaultService { parentId: Common_uuid; id: Common_uuid; requestBody: Tasks_CreateTaskRequest; - }): CancelablePromise { + }): CancelablePromise { return this.httpRequest.request({ method: "POST", url: "/agents/{parent_id}/tasks/{id}", diff --git a/typespec/tasks/endpoints.tsp b/typespec/tasks/endpoints.tsp index 21cab7ed6..2d9ec422b 100644 --- a/typespec/tasks/endpoints.tsp +++ b/typespec/tasks/endpoints.tsp @@ -31,7 +31,7 @@ interface CreateOrUpdateEndpoints { @body @doc("Details of the task updated along with ID") - body: ResourceCreatedResponse; + body: ResourceUpdatedResponse; }; } From 80dd3f17e60e7252cde0d5cbaba87910a07bf837 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 23 Aug 2024 19:56:21 -0400 Subject: [PATCH 105/110] fix(agents-api): Fix tests and routes for sample Signed-off-by: Diwank Tomer --- .../activities/task_steps/prompt_step.py | 8 ++- agents-api/agents_api/autogen/Chat.py | 37 +++++++++++- agents-api/agents_api/autogen/Entries.py | 56 +++++++++++-------- agents-api/agents_api/autogen/Tasks.py | 37 +++++++++++- .../agents_api/autogen/openapi_model.py | 8 ++- .../agents_api/routers/agents/router.py | 2 - agents-api/agents_api/routers/docs/router.py | 2 - agents-api/agents_api/routers/jobs/routers.py | 2 +- .../agents_api/routers/sessions/router.py | 2 - .../routers/tasks/create_or_update_task.py | 18 +++++- .../agents_api/routers/tasks/create_task.py | 18 +++++- .../routers/tasks/create_task_execution.py | 3 +- agents-api/agents_api/routers/tasks/router.py | 2 +- agents-api/agents_api/routers/users/router.py | 2 - .../tests/sample_tasks/find_selector.yaml | 23 +++++--- .../tests/sample_tasks/test_find_selector.py | 44 +++++++++++++-- agents-api/tests/utils.py | 5 +- sdks/ts/src/api/index.ts | 4 -- .../models/Entries_ChatMLImageContentPart.ts | 15 ----- sdks/ts/src/api/models/Entries_ImageURL.ts | 15 ----- sdks/ts/src/api/models/Tasks_PromptStep.ts | 2 +- .../$Entries_ChatMLImageContentPart.ts | 22 -------- sdks/ts/src/api/schemas/$Entries_ImageURL.ts | 24 -------- sdks/ts/src/api/schemas/$Tasks_PromptStep.ts | 1 - typespec/entries/models.tsp | 11 ++-- typespec/tasks/steps.tsp | 2 +- 26 files changed, 206 insertions(+), 159 deletions(-) delete mode 100644 sdks/ts/src/api/models/Entries_ChatMLImageContentPart.ts delete mode 100644 sdks/ts/src/api/models/Entries_ImageURL.ts delete mode 100644 sdks/ts/src/api/schemas/$Entries_ChatMLImageContentPart.ts delete mode 100644 sdks/ts/src/api/schemas/$Entries_ImageURL.ts diff --git a/agents-api/agents_api/activities/task_steps/prompt_step.py b/agents-api/agents_api/activities/task_steps/prompt_step.py index cb0088550..003809909 100644 --- a/agents-api/agents_api/activities/task_steps/prompt_step.py +++ b/agents-api/agents_api/activities/task_steps/prompt_step.py @@ -3,7 +3,7 @@ from beartype import beartype from temporalio import activity -from ...autogen.openapi_model import InputChatMLMessage +from ...autogen.openapi_model import ChatSettings, InputChatMLMessage from ...clients import ( litellm, # We dont directly import `acompletion` so we can mock it ) @@ -41,11 +41,13 @@ async def prompt_step(context: StepContext) -> StepOutcome: for m in messages ] - settings: dict = context.current_step.settings.model_dump() # Get settings and run llm + settings: ChatSettings = context.current_step.settings or ChatSettings() + settings_data: dict = settings.model_dump() + response = await litellm.acompletion( messages=messages, - **settings, + **settings_data, ) return StepOutcome( diff --git a/agents-api/agents_api/autogen/Chat.py b/agents-api/agents_api/autogen/Chat.py index 5aa4d6d29..da214ec77 100644 --- a/agents-api/agents_api/autogen/Chat.py +++ b/agents-api/agents_api/autogen/Chat.py @@ -10,7 +10,6 @@ from .Common import LogitBias from .Docs import DocReference -from .Entries import ChatMLImageContentPart from .Tools import FunctionTool, NamedToolChoice @@ -155,6 +154,20 @@ class Content(BaseModel): """ +class ContentModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + image_url: ImageUrl + """ + The image URL + """ + type: Literal["image_url"] = "image_url" + """ + The type (fixed to 'image_url') + """ + + class Delta(BaseModel): """ The message generated by the model @@ -175,7 +188,7 @@ class Delta(BaseModel): """ The role of the message """ - content: str | list[str] | list[Content | ChatMLImageContentPart] + content: str | list[str] | list[Content | ContentModel] """ The content parts of the message """ @@ -189,6 +202,24 @@ class Delta(BaseModel): """ +class ImageUrl(BaseModel): + """ + The image URL + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + url: str + """ + Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) + """ + detail: Literal["low", "high", "auto"] = "auto" + """ + The detail level of the image + """ + + class LogProbResponse(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -215,7 +246,7 @@ class Message(BaseModel): """ The role of the message """ - content: str | list[str] | list[Content | ChatMLImageContentPart] + content: str | list[str] | list[Content | ContentModel] """ The content parts of the message """ diff --git a/agents-api/agents_api/autogen/Entries.py b/agents-api/agents_api/autogen/Entries.py index 753436702..1f4286eb9 100644 --- a/agents-api/agents_api/autogen/Entries.py +++ b/agents-api/agents_api/autogen/Entries.py @@ -6,7 +6,7 @@ from typing import Annotated, Literal from uuid import UUID -from pydantic import AnyUrl, AwareDatetime, BaseModel, ConfigDict, Field, RootModel +from pydantic import AwareDatetime, BaseModel, ConfigDict, Field, RootModel from .Tools import ChosenToolCall, Tool, ToolResponse @@ -29,17 +29,13 @@ class BaseEntry(BaseModel): """ name: str | None = None content: ( - list[Content | ChatMLImageContentPart] + list[Content | ContentModel] | Tool | ChosenToolCall | str | ToolResponse | list[ - list[Content | ChatMLImageContentPart] - | Tool - | ChosenToolCall - | str - | ToolResponse + list[Content | ContentModel] | Tool | ChosenToolCall | str | ToolResponse ] ) source: Literal[ @@ -53,20 +49,6 @@ class BaseEntry(BaseModel): """ -class ChatMLImageContentPart(BaseModel): - model_config = ConfigDict( - populate_by_name=True, - ) - image_url: ImageURL - """ - The image URL - """ - type: Literal["image_url"] = "image_url" - """ - The type (fixed to 'image_url') - """ - - class ChatMLRole( RootModel[ Literal[ @@ -108,6 +90,20 @@ class Content(BaseModel): """ +class ContentModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + image_url: ImageUrl + """ + The image URL + """ + type: Literal["image_url"] = "image_url" + """ + The type (fixed to 'image_url') + """ + + class Entry(BaseEntry): model_config = ConfigDict( populate_by_name=True, @@ -132,11 +128,25 @@ class History(BaseModel): """ -class ImageURL(BaseModel): +class ImageDetail(RootModel[Literal["low", "high", "auto"]]): + model_config = ConfigDict( + populate_by_name=True, + ) + root: Literal["low", "high", "auto"] + """ + Image detail level + """ + + +class ImageUrl(BaseModel): + """ + The image URL + """ + model_config = ConfigDict( populate_by_name=True, ) - url: AnyUrl + url: str """ Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) """ diff --git a/agents-api/agents_api/autogen/Tasks.py b/agents-api/agents_api/autogen/Tasks.py index a7641d043..ed935426b 100644 --- a/agents-api/agents_api/autogen/Tasks.py +++ b/agents-api/agents_api/autogen/Tasks.py @@ -15,7 +15,6 @@ TextOnlyDocSearchRequest, VectorDocSearchRequest, ) -from .Entries import ChatMLImageContentPart from .Tools import CreateToolRequest @@ -61,6 +60,20 @@ class Content(BaseModel): """ +class ContentModel(BaseModel): + model_config = ConfigDict( + populate_by_name=True, + ) + image_url: ImageUrl + """ + The image URL + """ + type: Literal["image_url"] = "image_url" + """ + The type (fixed to 'image_url') + """ + + class CreateTaskRequest(BaseModel): """ Payload for creating a task @@ -264,6 +277,24 @@ class IfElseWorkflowStep(BaseModel): """ +class ImageUrl(BaseModel): + """ + The image URL + """ + + model_config = ConfigDict( + populate_by_name=True, + ) + url: str + """ + Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) + """ + detail: Literal["low", "high", "auto"] = "auto" + """ + The detail level of the image + """ + + class LogStep(BaseModel): model_config = ConfigDict( populate_by_name=True, @@ -442,7 +473,7 @@ class PromptItem(BaseModel): """ The role of the message """ - content: list[str] | list[Content | ChatMLImageContentPart] | str + content: list[str] | list[Content | ContentModel] | str """ The content parts of the message """ @@ -470,7 +501,7 @@ class PromptStep(BaseModel): """ The prompt to run """ - settings: ChatSettings + settings: ChatSettings | None = None """ Settings for the prompt """ diff --git a/agents-api/agents_api/autogen/openapi_model.py b/agents-api/agents_api/autogen/openapi_model.py index bdf96cc10..272b5dae0 100644 --- a/agents-api/agents_api/autogen/openapi_model.py +++ b/agents-api/agents_api/autogen/openapi_model.py @@ -36,13 +36,13 @@ class ListResponse(BaseModel, Generic[DataT]): CreateOrUpdateAgentRequest = UpdateAgentRequest CreateOrUpdateUserRequest = UpdateUserRequest CreateOrUpdateSessionRequest = CreateSessionRequest -CreateOrUpdateTaskRequest = CreateTaskRequest ChatResponse = ChunkChatResponse | MessageChatResponse -ChatMLTextContentPart = Content -InputChatMLMessage = Message # TODO: Figure out wtf... 🤷‍♂️ MapReduceStep = Main +ChatMLTextContentPart = Content +ChatMLImageContentPart = ContentModel +InputChatMLMessage = Message # Custom types (not generated correctly) @@ -224,6 +224,8 @@ class CreateTaskRequest(_CreateTaskRequest): ) +CreateOrUpdateTaskRequest = CreateTaskRequest + _PatchTaskRequest = PatchTaskRequest diff --git a/agents-api/agents_api/routers/agents/router.py b/agents-api/agents_api/routers/agents/router.py index b7cfed8f5..5c3ec9311 100644 --- a/agents-api/agents_api/routers/agents/router.py +++ b/agents-api/agents_api/routers/agents/router.py @@ -1,5 +1,3 @@ -from typing import Any - from fastapi import APIRouter router: APIRouter = APIRouter() diff --git a/agents-api/agents_api/routers/docs/router.py b/agents-api/agents_api/routers/docs/router.py index b7cfed8f5..5c3ec9311 100644 --- a/agents-api/agents_api/routers/docs/router.py +++ b/agents-api/agents_api/routers/docs/router.py @@ -1,5 +1,3 @@ -from typing import Any - from fastapi import APIRouter router: APIRouter = APIRouter() diff --git a/agents-api/agents_api/routers/jobs/routers.py b/agents-api/agents_api/routers/jobs/routers.py index c3a4d5ed4..c9783053c 100644 --- a/agents-api/agents_api/routers/jobs/routers.py +++ b/agents-api/agents_api/routers/jobs/routers.py @@ -1,4 +1,4 @@ -from typing import Any, Literal +from typing import Literal from fastapi import APIRouter from pydantic import UUID4 diff --git a/agents-api/agents_api/routers/sessions/router.py b/agents-api/agents_api/routers/sessions/router.py index b7cfed8f5..5c3ec9311 100644 --- a/agents-api/agents_api/routers/sessions/router.py +++ b/agents-api/agents_api/routers/sessions/router.py @@ -1,5 +1,3 @@ -from typing import Any - from fastapi import APIRouter router: APIRouter = APIRouter() diff --git a/agents-api/agents_api/routers/tasks/create_or_update_task.py b/agents-api/agents_api/routers/tasks/create_or_update_task.py index 2efa41e2e..621d11187 100644 --- a/agents-api/agents_api/routers/tasks/create_or_update_task.py +++ b/agents-api/agents_api/routers/tasks/create_or_update_task.py @@ -1,13 +1,14 @@ from typing import Annotated -from fastapi import Depends +from fastapi import Depends, HTTPException +from jsonschema import validate +from jsonschema.exceptions import SchemaError, ValidationError from pydantic import UUID4 from starlette.status import HTTP_201_CREATED from agents_api.autogen.openapi_model import ( CreateOrUpdateTaskRequest, ResourceUpdatedResponse, - Task, ) from agents_api.dependencies.developer_id import get_developer_id from agents_api.models.task.create_or_update_task import ( @@ -28,7 +29,18 @@ async def create_or_update_task( ) -> ResourceUpdatedResponse: # TODO: Do thorough validation of the task spec - task: Task = create_or_update_task_query( + # Validate the input schema + try: + if data.input_schema is not None: + validate(None, data.input_schema) + + except SchemaError: + raise HTTPException(detail="Invalid input schema", status_code=400) + + except ValidationError: + pass + + task = create_or_update_task_query( developer_id=x_developer_id, agent_id=agent_id, task_id=task_id, diff --git a/agents-api/agents_api/routers/tasks/create_task.py b/agents-api/agents_api/routers/tasks/create_task.py index 0e63210d2..519cfd414 100644 --- a/agents-api/agents_api/routers/tasks/create_task.py +++ b/agents-api/agents_api/routers/tasks/create_task.py @@ -1,13 +1,14 @@ from typing import Annotated -from fastapi import Depends +from fastapi import Depends, HTTPException +from jsonschema import validate +from jsonschema.exceptions import SchemaError, ValidationError from pydantic import UUID4 from starlette.status import HTTP_201_CREATED from agents_api.autogen.openapi_model import ( CreateTaskRequest, ResourceCreatedResponse, - Task, ) from agents_api.dependencies.developer_id import get_developer_id from agents_api.models.task.create_task import create_task as create_task_query @@ -23,7 +24,18 @@ async def create_task( ) -> ResourceCreatedResponse: # TODO: Do thorough validation of the task spec - task: Task = create_task_query( + # Validate the input schema + try: + if data.input_schema is not None: + validate(None, data.input_schema) + + except SchemaError: + raise HTTPException(detail="Invalid input schema", status_code=400) + + except ValidationError: + pass + + task = create_task_query( developer_id=x_developer_id, agent_id=agent_id, data=data, diff --git a/agents-api/agents_api/routers/tasks/create_task_execution.py b/agents-api/agents_api/routers/tasks/create_task_execution.py index 6202baa93..d7f7b104e 100644 --- a/agents-api/agents_api/routers/tasks/create_task_execution.py +++ b/agents-api/agents_api/routers/tasks/create_task_execution.py @@ -97,9 +97,8 @@ async def create_task_execution( ) -> ResourceCreatedResponse: try: task = get_task_query(task_id=task_id, developer_id=x_developer_id) - task_data = task.model_dump() + validate(data.input, task.input_schema) - validate(data.input, task_data["input_schema"]) except ValidationError: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, diff --git a/agents-api/agents_api/routers/tasks/router.py b/agents-api/agents_api/routers/tasks/router.py index 46baf1a29..9a702c15a 100644 --- a/agents-api/agents_api/routers/tasks/router.py +++ b/agents-api/agents_api/routers/tasks/router.py @@ -1,4 +1,4 @@ -from typing import Any, Callable +from typing import Callable import yaml from fastapi import APIRouter, Request, Response diff --git a/agents-api/agents_api/routers/users/router.py b/agents-api/agents_api/routers/users/router.py index b7cfed8f5..5c3ec9311 100644 --- a/agents-api/agents_api/routers/users/router.py +++ b/agents-api/agents_api/routers/users/router.py @@ -1,5 +1,3 @@ -from typing import Any - from fastapi import APIRouter router: APIRouter = APIRouter() diff --git a/agents-api/tests/sample_tasks/find_selector.yaml b/agents-api/tests/sample_tasks/find_selector.yaml index f73e3069e..57000d17a 100644 --- a/agents-api/tests/sample_tasks/find_selector.yaml +++ b/agents-api/tests/sample_tasks/find_selector.yaml @@ -39,6 +39,10 @@ input_schema: items: type: string + # Shortcut to require all props + additionalProperties: false + minProperties: 3 + main: - map: prompt: @@ -66,16 +70,17 @@ main: yaml.safe_load(_["choices"][0]["message"]["content"].trim()) ] - - evaluate: >- - [ - {"value": result["value"], "network_request": request} - for request in execution.input.network_requests - if result["value"] in nr.response.body - for result in _ - if result["found"] - ] + - evaluate: + result: >- + [ + {"value": result["value"], "network_request": request} + for request in execution.input.network_requests + if result["value"] in nr.response.body + for result in _ + if result["found"] + ] - - if: len(_) > 0 + - if: len(_["result"]) > 0 then: workflow: find_selectors arguments: diff --git a/agents-api/tests/sample_tasks/test_find_selector.py b/agents-api/tests/sample_tasks/test_find_selector.py index 09f79e340..837fecb65 100644 --- a/agents-api/tests/sample_tasks/test_find_selector.py +++ b/agents-api/tests/sample_tasks/test_find_selector.py @@ -11,7 +11,7 @@ this_dir = os.path.dirname(__file__) -@test("workflow sample: find-selector") +@test("workflow sample: find-selector create task") async def _( cozo_client=cozo_client, developer_id=test_developer_id, @@ -29,19 +29,51 @@ async def _( cozo_client=cozo_client, developer_id=developer_id ) as ( make_request, - client, + _, ): make_request( - method="PUT", + method="POST", url=f"/agents/{agent_id}/tasks/{task_id}", headers={"Content-Type": "application/x-yaml"}, data=task_def, ).raise_for_status() - execution_data = dict(input={"test": "input"}) +@test("workflow sample: find-selector start") +async def _( + cozo_client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + agent_id = str(agent.id) + task_id = str(uuid4()) + + with patch_embed_acompletion(), open( + f"{this_dir}/find_selector.yaml", "r" + ) as sample_file: + task_def = sample_file.read() + + async with patch_http_client_with_temporal( + cozo_client=cozo_client, developer_id=developer_id + ) as ( + make_request, + temporal_client, + ): make_request( method="POST", - url=f"/tasks/{task_id}/executions", - json=execution_data, + url=f"/agents/{agent_id}/tasks/{task_id}", + headers={"Content-Type": "application/x-yaml"}, + data=task_def, ).raise_for_status() + + execution_data = dict(input={"test": "input"}) + + execution = ( + make_request( + method="POST", + url=f"/tasks/{task_id}/executions", + json=execution_data, + ) + .raise_for_status() + .json() + ) diff --git a/agents-api/tests/utils.py b/agents-api/tests/utils.py index d6c3e7ca8..0eb37819e 100644 --- a/agents-api/tests/utils.py +++ b/agents-api/tests/utils.py @@ -47,7 +47,7 @@ async def patch_testing_temporal(): @asynccontextmanager async def patch_http_client_with_temporal(*, cozo_client, developer_id): - async with patch_testing_temporal(): + async with patch_testing_temporal() as (worker, mock_get_client): from agents_api.env import api_key, api_key_header_name from agents_api.web import app @@ -64,7 +64,8 @@ def make_request(method, url, **kwargs): return client.request(method, url, headers=headers, **kwargs) - yield make_request, client + temporal_client = await mock_get_client() + yield make_request, temporal_client @contextmanager diff --git a/sdks/ts/src/api/index.ts b/sdks/ts/src/api/index.ts index 68ee1dd75..7b2e94710 100644 --- a/sdks/ts/src/api/index.ts +++ b/sdks/ts/src/api/index.ts @@ -64,12 +64,10 @@ export type { Docs_Snippet } from "./models/Docs_Snippet"; export type { Docs_TextOnlyDocSearchRequest } from "./models/Docs_TextOnlyDocSearchRequest"; export type { Docs_VectorDocSearchRequest } from "./models/Docs_VectorDocSearchRequest"; export type { Entries_BaseEntry } from "./models/Entries_BaseEntry"; -export type { Entries_ChatMLImageContentPart } from "./models/Entries_ChatMLImageContentPart"; export type { Entries_ChatMLRole } from "./models/Entries_ChatMLRole"; export type { Entries_Entry } from "./models/Entries_Entry"; export type { Entries_History } from "./models/Entries_History"; export type { Entries_ImageDetail } from "./models/Entries_ImageDetail"; -export type { Entries_ImageURL } from "./models/Entries_ImageURL"; export type { Entries_Relation } from "./models/Entries_Relation"; export type { Executions_CreateExecutionRequest } from "./models/Executions_CreateExecutionRequest"; export type { Executions_Execution } from "./models/Executions_Execution"; @@ -196,12 +194,10 @@ export { $Docs_Snippet } from "./schemas/$Docs_Snippet"; export { $Docs_TextOnlyDocSearchRequest } from "./schemas/$Docs_TextOnlyDocSearchRequest"; export { $Docs_VectorDocSearchRequest } from "./schemas/$Docs_VectorDocSearchRequest"; export { $Entries_BaseEntry } from "./schemas/$Entries_BaseEntry"; -export { $Entries_ChatMLImageContentPart } from "./schemas/$Entries_ChatMLImageContentPart"; export { $Entries_ChatMLRole } from "./schemas/$Entries_ChatMLRole"; export { $Entries_Entry } from "./schemas/$Entries_Entry"; export { $Entries_History } from "./schemas/$Entries_History"; export { $Entries_ImageDetail } from "./schemas/$Entries_ImageDetail"; -export { $Entries_ImageURL } from "./schemas/$Entries_ImageURL"; export { $Entries_Relation } from "./schemas/$Entries_Relation"; export { $Executions_CreateExecutionRequest } from "./schemas/$Executions_CreateExecutionRequest"; export { $Executions_Execution } from "./schemas/$Executions_Execution"; diff --git a/sdks/ts/src/api/models/Entries_ChatMLImageContentPart.ts b/sdks/ts/src/api/models/Entries_ChatMLImageContentPart.ts deleted file mode 100644 index ab07c9df2..000000000 --- a/sdks/ts/src/api/models/Entries_ChatMLImageContentPart.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Entries_ImageURL } from "./Entries_ImageURL"; -export type Entries_ChatMLImageContentPart = { - /** - * The image URL - */ - image_url: Entries_ImageURL; - /** - * The type (fixed to 'image_url') - */ - type: "image_url"; -}; diff --git a/sdks/ts/src/api/models/Entries_ImageURL.ts b/sdks/ts/src/api/models/Entries_ImageURL.ts deleted file mode 100644 index 0fe36804d..000000000 --- a/sdks/ts/src/api/models/Entries_ImageURL.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -import type { Entries_ImageDetail } from "./Entries_ImageDetail"; -export type Entries_ImageURL = { - /** - * Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) - */ - url: string; - /** - * The detail level of the image - */ - detail: Entries_ImageDetail; -}; diff --git a/sdks/ts/src/api/models/Tasks_PromptStep.ts b/sdks/ts/src/api/models/Tasks_PromptStep.ts index 7cb04ccea..a4e200f02 100644 --- a/sdks/ts/src/api/models/Tasks_PromptStep.ts +++ b/sdks/ts/src/api/models/Tasks_PromptStep.ts @@ -18,5 +18,5 @@ export type Tasks_PromptStep = { /** * Settings for the prompt */ - settings: Chat_ChatSettings; + settings?: Chat_ChatSettings; }; diff --git a/sdks/ts/src/api/schemas/$Entries_ChatMLImageContentPart.ts b/sdks/ts/src/api/schemas/$Entries_ChatMLImageContentPart.ts deleted file mode 100644 index 28026a78b..000000000 --- a/sdks/ts/src/api/schemas/$Entries_ChatMLImageContentPart.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Entries_ChatMLImageContentPart = { - properties: { - image_url: { - type: "all-of", - description: `The image URL`, - contains: [ - { - type: "Entries_ImageURL", - }, - ], - isRequired: true, - }, - type: { - type: "Enum", - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Entries_ImageURL.ts b/sdks/ts/src/api/schemas/$Entries_ImageURL.ts deleted file mode 100644 index 07dc23052..000000000 --- a/sdks/ts/src/api/schemas/$Entries_ImageURL.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* generated using openapi-typescript-codegen -- do no edit */ -/* istanbul ignore file */ -/* tslint:disable */ -/* eslint-disable */ -export const $Entries_ImageURL = { - properties: { - url: { - type: "string", - description: `Image URL or base64 data url (e.g. \`data:image/jpeg;base64,\`)`, - isRequired: true, - format: "uri", - }, - detail: { - type: "all-of", - description: `The detail level of the image`, - contains: [ - { - type: "Entries_ImageDetail", - }, - ], - isRequired: true, - }, - }, -} as const; diff --git a/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts b/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts index a0aeff91b..2e133936f 100644 --- a/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts +++ b/sdks/ts/src/api/schemas/$Tasks_PromptStep.ts @@ -39,7 +39,6 @@ export const $Tasks_PromptStep = { type: "Chat_ChatSettings", }, ], - isRequired: true, }, }, }, diff --git a/typespec/entries/models.tsp b/typespec/entries/models.tsp index 70a84233f..b254fa1d1 100644 --- a/typespec/entries/models.tsp +++ b/typespec/entries/models.tsp @@ -30,10 +30,9 @@ enum ChatMLRole { auto, } -model ImageURL { +model ImageURL { /** Image URL or base64 data url (e.g. `data:image/jpeg;base64,`) */ - @format("uri") - url: string; + url: T; /** The detail level of the image */ detail: ImageDetail = ImageDetail.auto; @@ -46,7 +45,7 @@ model ChatMLTextContentPart { type: "text" = "text"; } -model ChatMLImageContentPart { +model ChatMLImageContentPart { /** The image URL */ image_url: ImageURL; @@ -54,7 +53,7 @@ model ChatMLImageContentPart { type: "image_url" = "image_url"; } -alias ChatMLContentPart = ChatMLTextContentPart | ChatMLImageContentPart; +alias ChatMLContentPart = ChatMLTextContentPart | ChatMLImageContentPart; model ChatMLMessage { /** The role of the message */ @@ -117,4 +116,4 @@ model History { session_id: Session.id; ...HasCreatedAt; -} \ No newline at end of file +} diff --git a/typespec/tasks/steps.tsp b/typespec/tasks/steps.tsp index 3b2236ac5..ed630fa08 100644 --- a/typespec/tasks/steps.tsp +++ b/typespec/tasks/steps.tsp @@ -94,7 +94,7 @@ model PromptStepDef { prompt: JinjaTemplate | InputChatMLMessage[]; /** Settings for the prompt */ - settings: ChatSettings; + settings?: ChatSettings; } model EvaluateStep extends BaseWorkflowStep<"evaluate"> { From 3183267324cbdd16662132974cc0c9de61ecf487 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Fri, 23 Aug 2024 22:28:30 -0400 Subject: [PATCH 106/110] feat(agents-api): Make the sample work (more or less) Signed-off-by: Diwank Tomer --- agents-api/agents_api/activities/utils.py | 13 +++- .../routers/tasks/create_task_execution.py | 6 +- .../agents_api/workflows/task_execution.py | 3 + agents-api/poetry.lock | 62 ++++++++++++++++++- agents-api/pyproject.toml | 1 + .../tests/sample_tasks/find_selector.yaml | 6 +- .../tests/sample_tasks/test_find_selector.py | 62 +++++++++++++++++-- 7 files changed, 140 insertions(+), 13 deletions(-) diff --git a/agents-api/agents_api/activities/utils.py b/agents-api/agents_api/activities/utils.py index f491c7188..3a7c74f1c 100644 --- a/agents-api/agents_api/activities/utils.py +++ b/agents-api/agents_api/activities/utils.py @@ -1,12 +1,23 @@ +import json from typing import Any +import re2 +import yaml from beartype import beartype from simpleeval import EvalWithCompoundTypes, SimpleEval +from yaml import CSafeLoader +ALLOWED_FUNCTIONS = { + "len": len, + "load_yaml": lambda string: yaml.load(string, Loader=CSafeLoader), + "match_regex": lambda pattern, string: bool(re2.fullmatch(pattern, string)), + "search_regex": lambda pattern, string: re2.search(pattern, string), + "load_json": json.loads, +} @beartype def get_evaluator(names: dict[str, Any]) -> SimpleEval: - evaluator = EvalWithCompoundTypes(names=names) + evaluator = EvalWithCompoundTypes(names=names, functions=ALLOWED_FUNCTIONS) return evaluator diff --git a/agents-api/agents_api/routers/tasks/create_task_execution.py b/agents-api/agents_api/routers/tasks/create_task_execution.py index d7f7b104e..ec17146af 100644 --- a/agents-api/agents_api/routers/tasks/create_task_execution.py +++ b/agents-api/agents_api/routers/tasks/create_task_execution.py @@ -59,10 +59,12 @@ async def start_execution( client=client, ) + job_id=uuid4() + try: handle = await run_task_execution_workflow( execution_input=execution_input, - job_id=uuid4(), + job_id=job_id, ) except Exception as e: @@ -130,5 +132,5 @@ async def create_task_execution( return ResourceCreatedResponse( id=execution.id, created_at=execution.created_at, - jobs=[], + jobs=[handle.id], ) diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index 7e64e3437..b331a52c0 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -300,6 +300,9 @@ async def transition(**kwargs) -> None: args=map_reduce_args, ) + if hasattr(output, "model_dump"): + output = output.model_dump() + initial = await execute_activity( task_steps.base_evaluate, args=[ diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index 6099cb98d..e9eba34b2 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -1116,6 +1116,66 @@ files = [ {file = "genson-1.3.0.tar.gz", hash = "sha256:e02db9ac2e3fd29e65b5286f7135762e2cd8a986537c075b06fc5f1517308e37"}, ] +[[package]] +name = "google-re2" +version = "1.1.20240702" +description = "RE2 Python bindings" +optional = false +python-versions = "~=3.8" +files = [ + {file = "google_re2-1.1.20240702-1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:46e7ed614ffaafccae017542d68e9bbf664c8c1e5ca37046adee640bbee4846e"}, + {file = "google_re2-1.1.20240702-1-cp310-cp310-macosx_12_0_x86_64.whl", hash = "sha256:3c8d2c0a03e9fd24f78b624cf7e40ac32aaf4837fda7339e2c22ca42e3dca512"}, + {file = "google_re2-1.1.20240702-1-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:7fee39772aa2e1fe91b7694acc48888ac6fa0ca51f8805464272a2089b362c96"}, + {file = "google_re2-1.1.20240702-1-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:b2bcf1a43853cee5a088f40c75fe48a6e3ec7addae1d3f3d47ce679e2bb8936b"}, + {file = "google_re2-1.1.20240702-1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:69f9b156de6f93ea00844f6cd4f2ed5124f9f01692da7ae0fe9a9516df6c63c2"}, + {file = "google_re2-1.1.20240702-1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:f70db559ad768ad68a4d9897cb19fd13f7761e60208f475eb8a69b8aa4b6df20"}, + {file = "google_re2-1.1.20240702-1-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7bc4fa65ecec3d63ea6048ecaf8784560bbfb31191c02ffaa87771e4a2f813e1"}, + {file = "google_re2-1.1.20240702-1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7480309b133c39f2afb19ff28bc30d27b364cbc56b5d46918d1b4f1fb2e13183"}, + {file = "google_re2-1.1.20240702-1-cp310-cp310-win32.whl", hash = "sha256:1950f499b277789267afee1755394cd959898d0b192b7052bb3186000aff27de"}, + {file = "google_re2-1.1.20240702-1-cp310-cp310-win_amd64.whl", hash = "sha256:2269ff8c2e1de0ee77736bd9f65b5c9f7cd43544eff825dc7b4ab2bf1f1901e4"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:9802a5a5ec585048300d5a8ec522b15057b8f758fe9f8b0ec65ac2927a36a1aa"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-macosx_12_0_x86_64.whl", hash = "sha256:757cbefbe9f998c274c94afd8bf2a4789b983287f33d4f975389c1027ed686c6"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:7e1d36bd20ce04c1198fe482b6f3ce7dd699e1276946a9a2cf31b2e53026a370"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:bb6b2e456cd0002700ad58c3474fc9e342853ff2ef9f95a1f6606c819ffaf3d9"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:71a71d7f9e616d3067e913a1432111593ee41aab2e0ed21ecbcf039451b9d899"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:16bd5389baeb98936fb05926e6a38826c473206c13f1f789f7643a29dcccccc3"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b920c1b0356f0359b35a0e6d0b5ff12fba9067d3c455a9811952fbc9a213268"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:42c2c39b7378e928d197e4fdf4a23c9338f29cad6d4c5c5c06a2ad7c8c2a3ebc"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-win32.whl", hash = "sha256:d7fd6b6be4f86d4b6503689b492970920f4b50a8ac02427bc975c73bcedda374"}, + {file = "google_re2-1.1.20240702-1-cp311-cp311-win_amd64.whl", hash = "sha256:22217d7c8f57bb6c5e74e171a510b12cdde4eddc2528f89aa0f50e3fc10fe17e"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:00dcb09b15f92b490ae52f328cca229de2a157c8748f10df94dfea7637d32617"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-macosx_12_0_x86_64.whl", hash = "sha256:2ffc6fbe70ccf9fb66d0ab16ccad0f661031ceb0eec3d73d170cd782a93d62d5"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:652e517b6db6cbf7403bab370940718208b15e811fefe7635d4e78a8037f096b"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:449ae8286d644d24af8a6eb81eeba6995388581739920b80d9e4b063eefe0322"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:40568559b0a10240c10377fb5cdd46c9115da8a627c567db68c4dc29103a2ce9"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:8139df33d61aac335726b6f32108e763ba2932569c63d2b3ebf6e36a40449223"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e4a2b88516b4140891a014c6e5b774f57db90c8bd0ccf0554e9f9b99ee1e942"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d95b1e06298299b28e23288a6bfd3c6f13e0f7a01c1f2e86e74073928676cf88"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-win32.whl", hash = "sha256:fb025d4bcd1a3032546da048a6dcb39359967f4df6b3514e76e983256235f694"}, + {file = "google_re2-1.1.20240702-1-cp312-cp312-win_amd64.whl", hash = "sha256:a7e3129d31e12d51397d603adf45bd696135a5d9d61bc33643bc5d2e4366070b"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-macosx_12_0_arm64.whl", hash = "sha256:bc2f853ace690fb475f68b82b61e3b0ffe2a8603f052853eb21587ac7dcca537"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-macosx_12_0_x86_64.whl", hash = "sha256:974ac711ade3171004e9552d9e069cbe1a8de02c5e45a56101f8396f69a3e3c2"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-macosx_13_0_arm64.whl", hash = "sha256:ad3dc0084cad59a298ffa52c9def2f1b5332d396d76f3828237ac7141b6e7e7d"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-macosx_13_0_x86_64.whl", hash = "sha256:4e13241b8df4096d840f98993f39c62cff0cdab9d06c86b156d2944cfb3f0814"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-macosx_14_0_arm64.whl", hash = "sha256:6887fe9c291ad42003ad84e11c0a6fac0169adbda9cbc898b8657610512e4ce5"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:82824fa71f51a269cd9bad653d53e6ba5bee9095da059455ee1c6cc7e4ba014b"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cde1453681c2ab1648b9e7aed3861ccedce52c85b24873edd1ec1e92b4b3d7d4"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7388c5aadcc5489291d2804ecc384c2e3bb64832e1b46afd44d7bca6c948b615"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-win32.whl", hash = "sha256:cb20853af1104b5180eb2daea66a481723553aa66bf5a5c4c58420c7369364cb"}, + {file = "google_re2-1.1.20240702-1-cp38-cp38-win_amd64.whl", hash = "sha256:a7f0d950ba9508ac1b2d89837f4a4c74092ae3af015a9797b80570ee87b7d7d5"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:56c2a97d3d38345939fb3ff02d154f5c6ec929e0765723cfd390720f581d2581"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-macosx_12_0_x86_64.whl", hash = "sha256:dfe657979ab96da72f55b03ecdede5467a7193266ce7a0b85013819f052d231f"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:c6e218b831dfc89f5004c1bb7ae9182ec5ddc4d46e6035f636ba96344d5b7478"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:304ed3f740541742e7ef5c162b36619efdac7345f1429ab6d70aefaae9a5658d"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:05f5683e1bcfac8adcc0dbfe3ecb0866cec6eea2c7d419271dfd72930b368ce4"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:39c642041428efaa48f35adf4475a014ce272f87a453c6dff68f2b05793d516f"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d3d1e58f374510101273cda1b6c2b45c178eb94f4c1bd17f7f750cea8d1c85a"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:386d2a8c4b10daaeda03adc7f65c457f67ee8cb18b4f9b4178a44ed62ab291df"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-win32.whl", hash = "sha256:f853c3c68bed0d127e6ef8b29ee85461d9d0a4fa407e3f97e41ecd6803d24d88"}, + {file = "google_re2-1.1.20240702-1-cp39-cp39-win_amd64.whl", hash = "sha256:5e35c8db1bf58ddf1ac28782d6dca5894a0331fc0d33b2a2ce6eb59234d74312"}, + {file = "google_re2-1.1.20240702.tar.gz", hash = "sha256:8788db69f6c93cb229df62c74b2d9aa8e64bf754e9495700f85812afa32efd2b"}, +] + [[package]] name = "h11" version = "0.14.0" @@ -4484,4 +4544,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "f0057b059c1db08b485252ae5e4139f89126a1f82873ee56604c94a9b9d2142a" +content-hash = "f2e6680de8e96b10ec7a9e5edf15223fd4842342db950778ff1d4bb1940e7ae0" diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index 01a7f0e18..d74d9dba9 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -33,6 +33,7 @@ simpleeval = "^0.9.13" lz4 = "^4.3.3" pyyaml = "^6.0.2" +google-re2 = "^1.1.20240702" [tool.poetry.group.dev.dependencies] ipython = "^8.26.0" ruff = "^0.5.5" diff --git a/agents-api/tests/sample_tasks/find_selector.yaml b/agents-api/tests/sample_tasks/find_selector.yaml index 57000d17a..2c94df23f 100644 --- a/agents-api/tests/sample_tasks/find_selector.yaml +++ b/agents-api/tests/sample_tasks/find_selector.yaml @@ -61,7 +61,7 @@ main: content: - type: image_url image_url: - url: "inputs[0].screenshot_base64" + url: "{{inputs[0].screenshot_base64}}" over: _.parameters reduce: >- @@ -74,8 +74,8 @@ main: result: >- [ {"value": result["value"], "network_request": request} - for request in execution.input.network_requests - if result["value"] in nr.response.body + for request in inputs[0]["network_requests"] + if result["value"] in nr["response"]["body"] for result in _ if result["found"] ] diff --git a/agents-api/tests/sample_tasks/test_find_selector.py b/agents-api/tests/sample_tasks/test_find_selector.py index 837fecb65..40b649ca3 100644 --- a/agents-api/tests/sample_tasks/test_find_selector.py +++ b/agents-api/tests/sample_tasks/test_find_selector.py @@ -3,7 +3,7 @@ import os from uuid import uuid4 -from ward import test +from ward import raises, test from ..fixtures import cozo_client, test_agent, test_developer_id from ..utils import patch_embed_acompletion, patch_http_client_with_temporal @@ -39,7 +39,7 @@ async def _( ).raise_for_status() -@test("workflow sample: find-selector start") +@test("workflow sample: find-selector start with bad input should fail") async def _( cozo_client=cozo_client, developer_id=test_developer_id, @@ -68,12 +68,62 @@ async def _( execution_data = dict(input={"test": "input"}) - execution = ( + with raises(BaseException): make_request( method="POST", url=f"/tasks/{task_id}/executions", json=execution_data, - ) - .raise_for_status() - .json() + ).raise_for_status() + + +@test("workflow sample: find-selector start with correct input") +async def _( + cozo_client=cozo_client, + developer_id=test_developer_id, + agent=test_agent, +): + agent_id = str(agent.id) + task_id = str(uuid4()) + + + with patch_embed_acompletion(), open( + f"{this_dir}/find_selector.yaml", "r" + ) as sample_file: + task_def = sample_file.read() + + async with patch_http_client_with_temporal( + cozo_client=cozo_client, developer_id=developer_id + ) as ( + make_request, + temporal_client, + ): + make_request( + method="POST", + url=f"/agents/{agent_id}/tasks/{task_id}", + headers={"Content-Type": "application/x-yaml"}, + data=task_def, + ).raise_for_status() + + input = dict( + screenshot_base64="iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA", + network_requests=[{ + "request": {}, + "response": { + "body": "Lady Gaga" + } + }], + parameters=["name"], + ) + execution_data = dict(input=input) + + execution_created = make_request( + method="POST", + url=f"/tasks/{task_id}/executions", + json=execution_data, + ).json() + + handle = temporal_client.get_workflow_handle( + execution_created["jobs"][0] ) + + await handle.result() From 8ec70aec5fa46b3f0edc338364092b8220b8b91c Mon Sep 17 00:00:00 2001 From: Diwank Singh Tomer Date: Sat, 24 Aug 2024 11:32:59 -0400 Subject: [PATCH 107/110] Add files via upload --- docs/julep-concepts.md | 382 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 docs/julep-concepts.md diff --git a/docs/julep-concepts.md b/docs/julep-concepts.md new file mode 100644 index 000000000..0ceebbcad --- /dev/null +++ b/docs/julep-concepts.md @@ -0,0 +1,382 @@ +# Julep Concepts + +{{TOC}} + ++++ + +## Agent + +An Agent in Julep is the main orchestrator (or protagonist) of your application. These are backed by foundation models like GPT4 or Claude which use the agent's interaction history to figure out what to do/say next. Using agents in Julep, you can: + +- Interact with an agent in long-lived [sessions][Session]. +- Add system, integration or user-defined [tools][Tool] that the agent can use. +- Add agent-level [documents][Doc] that are auto-retrieved using semantic search inside [sessions][Session]. +- Define multi-step work flows that combine complex integrations using [tasks][Task]. Tasks are [executed][Execution] in the background, can recover from failures and manage many sub-tasks in parallel. + +> **(Upcoming Feature)** Access the [memories][Memory] that the agent makes about [users][User] in the background as the user interacts with it inside sessions. These memories are going to be scoped per user in order to maintain clear distinctions. + +At a high level, this is what defines an `Agent` (some properties omitted): + +| **Field** | **Description** | +| :------------- | :-------------------------------------------------------------- | +| `name` | The "name" of the Agent. | +| `about` | About the Agent: What it does, any guardrails, personality etc. | +| `model` | Which model to use for this Agent. | +| `instructions` | Instructions that this agent must follow across all sessions. | + +Important to keep in mind: These fields are optional. They are available inside sessions and task prompts as `jinja` templates. `Session`s, `Task`s etc. come with minimal default templates. You can override them with your own prompt templates throughout julep! + + + +## User + +You can associate sessions with `User`s. julep uses them to scope `memories` formed by agents. They are optional but, in addition to memories, can be useful to attach meta data that can be referenced by other sessions or task executions. + +A `User` consists of: + +| **Field** | **Description** | +| :-------- | :--------------------------- | +| `name` | The name of the user. | +| `about` | Information about this user. | + + + +## Session + +`Session` is the main workhorse for julep apps: +- You interact with agents inside sessions. You can create multiple sessions per agent. +- Each session maintains its own context for sending to the agent's model. +- A session can have *one or more* agents and *zero or more* users associated with it. +- You can control what happens when the history exceeds the context window limit using `context_overflow` setting. + +A `Session` consists of: + +| **Field** | **Description** | +| :----------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `agent(s)` | Agents associated with this session. At least one is required. | +| `user(s)` | The users associated with this session. Optional. | +| `situation` | The system prompt used for the session. Default prompt is shown below. | +| `token_budget` | The number of tokens to keep the context window under. Defaults to null which is equivalent to the model's context window limit. | +| `context_overflow` | Controls behavior for when context size exceeds the `token_budget`. Can be one of `null`, `"truncate"`, or `"adaptive"`. Defaults to `null` which raises an exception. | + + + +### `metadata` precedence order + +In julep, the following objects can have `metadata` added to them: +- `Agent` +- `User` +- `Session` +- `Doc` +- `Task` +- `Execution` + +Whenever multiple objects with the same `metadata` field are present in a scope, the value takes the following precedence (from highest to lowest): +- In a session: `session > user > agent` +- During a task execution: `execution > task > agent` + +### Context overflow + +Whenever the context size grows beyond the `token_budget` or the model's input limit, the backend figures out what to do next based on the `context_overflow` setting: +- `null`: Raise an exception. The client is responsible for creating a new session or clearing the history for the current one. +- `"truncate"`: Truncate the context from the top except the for system prompt until the size falls below the budget. Raises an error if system prompt and last message combined exceed the budget. +- `"adaptive"`: Whenever the context size reaches `75%` of the `token_budget`, a background task is created to compress the information by summarizing, merging and clipping messages in the context. This is done on a best effort basis. Requests might fail if the context wasn't compressed enough or on time. + +### Default system template + +```jinja +{%- if agent.name -%} +You are {{agent.name}}.{{" "}} +{%- endif -%} + +{%- if agent.about -%} +About you: {{agent.name}}.{{" "}} +{%- endif -%} + +{%- if user -%} +You are talking to a user + {%- if user.name -%}{{" "}} and their name is {{user.name}} + {%- if user.about -%}. About the user: {{user.about}}.{%- else -%}.{%- endif -%} + {%- endif -%} +{%- endif -%} + +{{"\n\n"}} + +{%- if agent.instructions -%} +Instructions:{{"\n"}} + {%- if agent.instructions is string -%} + {{agent.instructions}}{{"\n"}} + {%- else -%} + {%- for instruction in agent.instructions -%} + - {{instruction}}{{"\n"}} + {%- endfor -%} + {%- endif -%} + {{"\n"}} +{%- endif -%} + +{%- if tools -%} +Tools:{{"\n"}} + {%- for tool in tools -%} + {%- if tool.type == "function" -%} + - {{tool.function.name}} + {%- if tool.function.description -%}: {{tool.function.description}}{%- endif -%}{{"\n"}} + {%- else -%} + - {{ 0/0 }} {# Error: Other tool types aren't supported yet. #} + {%- endif -%} + {%- endfor -%} +{{"\n\n"}} +{%- endif -%} + +{%- if docs -%} +Relevant documents:{{"\n"}} + {%- for doc in docs -%} + {{doc.title}}{{"\n"}} + {%- if doc.content is string -%} + {{doc.content}}{{"\n"}} + {%- else -%} + {%- for snippet in doc.content -%} + {{snippet}}{{"\n"}} + {%- endfor -%} + {%- endif -%} + {{"---"}} + {%- endfor -%} +{%- endif -%} +``` + +### Multiple users and agents in a session + +A session can have more than one agents or users. The session's behavior changes depending on this. + +**No user**: No user data is retrieved. _(Upcoming)_ Memories are not mined from the session. + +**One or more users**: Docs, metadata, memories etc. are retrieved for all the users in the session. You can add messages for each user by referencing them by their name in the `ChatML` messages. _(Upcoming)_ Memories mined in the background are added to the corresponding user's scope. + +**One agent**: Works as expected. + +**Multiple agents**: When a message is received by the session, each agent is called one after another in the order they were defined in the session. You can also specify which `agent` to use in a request, in which case, just that agent will be used. + +### Chat endpoint + + + + +## Tool + +Agents can be given access to a number of "tools" -- any programmatic interface that a foundation model can "call" with a set of inputs to achieve a goal. For example, it might use a `web_search(query)` tool to search the Internet for some information. + +Unlike agent frameworks, julep is a _backend_ that manages agent execution. Clients can interact with agents using our SDKs. julep takes care of executing tasks and running integrations. + +Tools in julep can be one of: +1. User-defined `function`s + These are function signatures that you can give the model to choose from, similar to how [openai]'s function-calling works. An example: + ```yaml + name: send_text_message + description: Send a text message to a recipient. + parameters: + type: object + properties: + to: + type: string + description: Phone number of recipient. + text: + type: string + description: Content of the message. + ``` + +2. `system` tools (upcoming) + Built-in tools that can be used to call the julep APIs themselves, like triggering a task execution, appending to a metadata field, etc. + + `system` tools are built into the backend. They get executed automatically when needed. They do _not_ require any action from the client-side. + +3. Built-in `integration`s (upcoming) + julep backend ships with integrated third party tools from the following providers: + - [composio](https://composio.dev) \*\* + - [anon](https://anon.com) \*\* + - [langchain toolkits](https://python.langchain.com/v0.2/docs/integrations/toolkits/). Support for _Github, Gitlab, Gmail, Jira, MultiOn, Slack_ toolkits is planned. + + \*\* Since _composio_ and _anon_ are third-party providers, their tools require setting up account linking. + + `integration` tools are directly executed on the julep backend. Any additional parameters needed by them at runtime can be set in the agent/session/user's `metadata` fields. + +4. Webhooks & `api_call`s (upcoming) + julep can build natural-language tools from openapi specs. Under the hood, we use [langchain's NLA toolkit](https://python.langchain.com/v0.2/docs/integrations/toolkits/openapi_nla/) for this. Same as `integration`s, additional runtime parameters are loaded from `metadata` fields. + +### Partial application of arguments to tools + +Often, it's necessary to _partial_ some arguments of a particular tool. You can do that by setting the `x-tool-parameters` field on the `metadata` of the required scope. For instance, say you have the following user-defined function tool: +```yaml +name: check_account_status +description: Get the account status for a customer +parameters: + type: object + properties: + customer_id: + type: string + required: true +``` + +When chatting with a particular user, the `customer_id` field is expected to be fixed. In this case, you can set it on the `User` using: +```json +{ + "metadata": { + ... + "x-tool-parameters": { + "function:check_account_status": { + "customer_id": 42 + } + } + } +} +``` + +The convention for naming the fields for that object is `":"`. The values are partial-applied to the tool _before_ being sent to the model. + +### Resolving parameters with the same name + +This follows the precedence order of `metadata` fields. For example, say you are interacting with the following session: +```yaml +user: + id: 11 + metadata: + x-tool-parameters: + favorite: Emma Roberts +agent: + id: 22 + metadata: + x-tool-parameters: + favorite: Emma Watson + tools: + - type: function + name: send_fan_mail + parameters: + # ... favorite: ... +session: + id: 123 + metadata: + x-tool-parameters: + favorite: Emma Stone +``` + +Then, the `send_fan_mail` will be called with the value of `favorite` set to the session's `metadata` (as dictated by the precedence order) to `"Emma Stone"`. + + + +## Doc + +`Doc`s are collection of text snippets (image support planned) that are indexed into a built-in vector database: +- They can be scoped to an agent or a user. +- Snippets are recalled inside sessions on the fly. +- The retrieval pipeline is optimized for general-purpose use cases. +- We use vector embedding models that strike a balance between accuracy and performance. +- Any snippets retrieved during a session are returned as part of the response for attribution. +- The embeddings are kept up to date as new models and techniques emerge. +- For advanced use cases, it might be necessary to roll your own. The pros of using julep are speed and automatic updates. + +You can use the `Doc`s by: +- Searching using a query or embedding directly, or +- When they are recalled within `Session`s based on the context. + +We use the latest state-of-the-art open-source embedding model for producing the vector embeddings. As new models and techniques emerge, we migrate the existing `Doc`s in the system to use them. + +_julep cloud users:_ It is not possible to change the embedding model being used. + + + +## Task + +`Task`s, in julep, are _Github Actions_ style workflows that define long-running, multi-step actions. You can use them to conduct complex actions by defining them step-by-step. They have access to all julep integrations. + +A `Task`s is a workflow owned by an `Agent`. It consists of: + +| **Field** | **Description** | +| :-------------- | :--------------------------------------------------------------- | +| `inherit_tools` | Inherit the parent `Agent`s tools? Defaults to `true`. | +| `tools` | Additional tools for this task. | +| `input_schema` | JSON schema to validate input when executing the task. Optional. | +| `main` +others | List of steps that this task has to complete. | + +### Example task definition + +There can be multiple named workflows in a task. `main` is the entry point workflow for the task execution. Let's see an example of a task: + +```yaml +# An example Task definition +name: Daily Motivation +input_schema: + about_user: + type: string + topics: + type: array + items: + type: string + +tools: +- function: + name: send_email + # ... + +main: +# Pick a random topic. +# `evaluate` step takes a key-value object where the values are valid python *expressions*. +- evaluate: + chosen_topic: _["topics"][randint(len(_["topics"]))] + +# Think about what support the user might need. +# Note: `inputs` and `outputs` are globals. +- prompt: You are a motivational coach and you are coaching someone who is {{inputs[0]["about_user"]}}. Think of the challenges they might be facing on the {{_["chosen_topic"]}} topic and what to do about them. Write down your answer as a bulleted list. + +# Write a poem about it. +# Note: `_` stands for `outputs[-1]` i.e. the last step's output +- prompt: Write a short motivational poem about {{_["choices"][0].content}} + +# Manually call the send_email function. +# `arguments` is an object where values are python expressions. +- tool: + name: send_email + arguments: + subject: '"Daily Motivation"' + content: _["choices"][0].content + +# Sleep for a day +- sleep: 24*3600 + +# Start all over again +- workflow: main + arguments: inputs[0] +``` + +### Types of workflow steps + +| **Step Type** | **Description** | +| :------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | +| Prompt step | Runs a prompt using a model. You can override settings and interpolate variables using [jinja](https://jinja.palletsprojects.com/) templates. | +| Yield step | Used to switch to another named workflow. Can add custom inputs (Default: output of previous steps) | +| Evaluate | Accepts an object with values that are valid python expressions. The step runs the expressions and the result becomes the output of this step. | +| If-else | Conditional step where the `if` field expression is evaluated. If the output is truthy then the `then` branch is executed, otherwise `else` branch. | +| Error | Throws an error with the message provided and exits. | +| Sleep | Sleeps for the number of seconds evaluated. | +| Tool Call | Call the specified tool with some arguments. | +| Foreach | Run a step for every value from a list in serial order. | +| Map-reduce | Run a step for every value of the input list in parallel. Requires a reduce expression to collect the results. | +| Doc search | Search the doc store of the agent and user against a query. | +| Wait for input | Suspend the execution and wait for the caller to resume execution with an input. | + +## Execution + +An `Execution` is an instance of a `Task` that has been started with some `input`. + +At any given moment, it can be in one of these states: + +| **Status** | **Description** | +| :--------------- | :------------------------------------------------------- | +| "queued" | The execution is queued and waiting to start. | +| "starting" | The execution is starting. | +| "running" | The execution is running. | +| "awaiting_input" | The execution is suspended and awaiting input to resume. | +| "succeeded" | The execution has succeeded. | +| "failed" | The execution failed for some reason. | +| "cancelled" | The execution has been cancelled by the user. | + +Every time an execution enters a new state, a `Transition` object is created on the backend with details of the state change. You can retrieve the transition history of the execution. + +## Memory (Upcoming) \ No newline at end of file From af121a2929cad3a7dbb0b408e3fce488a3a5f63f Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Sat, 24 Aug 2024 23:37:30 +0300 Subject: [PATCH 108/110] fix: Fix templates rendering for different kind of messages --- .../activities/task_steps/prompt_step.py | 71 ++++++++++++++++--- agents-api/agents_api/activities/utils.py | 1 + .../agents_api/common/utils/template.py | 6 +- .../routers/tasks/create_task_execution.py | 2 +- .../tests/sample_tasks/test_find_selector.py | 12 +--- agents-api/tests/test_execution_workflow.py | 2 +- 6 files changed, 70 insertions(+), 24 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/prompt_step.py b/agents-api/agents_api/activities/task_steps/prompt_step.py index 003809909..84a569ee1 100644 --- a/agents-api/agents_api/activities/task_steps/prompt_step.py +++ b/agents-api/agents_api/activities/task_steps/prompt_step.py @@ -3,7 +3,12 @@ from beartype import beartype from temporalio import activity -from ...autogen.openapi_model import ChatSettings, InputChatMLMessage +from ...autogen.openapi_model import ( + ChatSettings, + Content, + ContentModel, + InputChatMLMessage, +) from ...clients import ( litellm, # We dont directly import `acompletion` so we can mock it ) @@ -11,6 +16,32 @@ from ...common.utils.template import render_template +def _content_to_dict( + content: str | list[str] | list[Content | ContentModel], role: str +) -> str | list[dict]: + if isinstance(content, str): + return content + + result = [] + for s in content: + if isinstance(s, str): + result.append({"content": {"type": "text", "text": s, "role": role}}) + elif isinstance(s, Content): + result.append({"content": {"type": s.type, "text": s.text, "role": role}}) + elif isinstance(s, ContentModel): + result.append( + { + "content": { + "type": s.type, + "image_url": {"url": s.image_url.url}, + "role": role, + } + } + ) + + return result + + @activity.defn @beartype async def prompt_step(context: StepContext) -> StepOutcome: @@ -27,26 +58,44 @@ async def prompt_step(context: StepContext) -> StepOutcome: template_messages: list[InputChatMLMessage] = prompt messages = await asyncio.gather( *[ - render_template(msg.content, context_data, skip_vars=["developer_id"]) + render_template( + _content_to_dict(msg.content, msg.role), + context_data, + skip_vars=["developer_id"], + ) for msg in template_messages ] ) - messages = [ - ( - InputChatMLMessage(role="user", content=m) - if isinstance(m, str) - else InputChatMLMessage(**m) - ) - for m in messages - ] + result_messages = [] + for m in messages: + if isinstance(m, str): + msg = InputChatMLMessage(role="user", content=m) + else: + msg = [] + for d in m: + role = d["content"].get("role") + d["content"] = [d["content"]] + d["role"] = role + msg.append(InputChatMLMessage(**d)) + + result_messages.append(msg) + + # messages = [ + # ( + # InputChatMLMessage(role="user", content=m) + # if isinstance(m, str) + # else [InputChatMLMessage(**d) for d in m] + # ) + # for m in messages + # ] # Get settings and run llm settings: ChatSettings = context.current_step.settings or ChatSettings() settings_data: dict = settings.model_dump() response = await litellm.acompletion( - messages=messages, + messages=result_messages, **settings_data, ) diff --git a/agents-api/agents_api/activities/utils.py b/agents-api/agents_api/activities/utils.py index 3a7c74f1c..231dee595 100644 --- a/agents-api/agents_api/activities/utils.py +++ b/agents-api/agents_api/activities/utils.py @@ -15,6 +15,7 @@ "load_json": json.loads, } + @beartype def get_evaluator(names: dict[str, Any]) -> SimpleEval: evaluator = EvalWithCompoundTypes(names=names, functions=ALLOWED_FUNCTIONS) diff --git a/agents-api/agents_api/common/utils/template.py b/agents-api/agents_api/common/utils/template.py index 1fcc143b3..35ae2c350 100644 --- a/agents-api/agents_api/common/utils/template.py +++ b/agents-api/agents_api/common/utils/template.py @@ -70,7 +70,11 @@ async def render_template_parts( # Parse template # FIXME: should template_strings contain a list of ChatMLTextContentPart? Should we handle it somehow? templates = [ - (jinja_env.from_string(msg["text"]) if msg["type"] == "text" else None) + ( + jinja_env.from_string(msg["content"]["text"]) + if msg["content"]["type"] == "text" + else None + ) for msg in template_strings ] diff --git a/agents-api/agents_api/routers/tasks/create_task_execution.py b/agents-api/agents_api/routers/tasks/create_task_execution.py index ec17146af..0497777bf 100644 --- a/agents-api/agents_api/routers/tasks/create_task_execution.py +++ b/agents-api/agents_api/routers/tasks/create_task_execution.py @@ -59,7 +59,7 @@ async def start_execution( client=client, ) - job_id=uuid4() + job_id = uuid4() try: handle = await run_task_execution_workflow( diff --git a/agents-api/tests/sample_tasks/test_find_selector.py b/agents-api/tests/sample_tasks/test_find_selector.py index 40b649ca3..caebd7547 100644 --- a/agents-api/tests/sample_tasks/test_find_selector.py +++ b/agents-api/tests/sample_tasks/test_find_selector.py @@ -85,7 +85,6 @@ async def _( agent_id = str(agent.id) task_id = str(uuid4()) - with patch_embed_acompletion(), open( f"{this_dir}/find_selector.yaml", "r" ) as sample_file: @@ -106,12 +105,7 @@ async def _( input = dict( screenshot_base64="iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA", - network_requests=[{ - "request": {}, - "response": { - "body": "Lady Gaga" - } - }], + network_requests=[{"request": {}, "response": {"body": "Lady Gaga"}}], parameters=["name"], ) execution_data = dict(input=input) @@ -122,8 +116,6 @@ async def _( json=execution_data, ).json() - handle = temporal_client.get_workflow_handle( - execution_created["jobs"][0] - ) + handle = temporal_client.get_workflow_handle(execution_created["jobs"][0]) await handle.result() diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index d55b62362..395f77de9 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -729,7 +729,7 @@ async def _( ) as task_file: task_definition = yaml.safe_load(task_file) acompletion.return_value = mock_model_response - data = CreateExecutionRequest(input={"test": "input"}) + data = CreateExecutionRequest(input={"parameters": ["param1", "param2"]}) task = create_task( developer_id=developer_id, From abb36ba1e9e113e15c37b3c0ba2c068c5893f502 Mon Sep 17 00:00:00 2001 From: Diwank Tomer Date: Sun, 25 Aug 2024 22:08:32 -0400 Subject: [PATCH 109/110] fix(agents-api): Make the sample work Signed-off-by: Diwank Tomer --- .../activities/task_steps/base_evaluate.py | 11 ++++++ agents-api/agents_api/activities/utils.py | 1 + .../agents_api/workflows/task_execution.py | 6 +-- agents-api/poetry.lock | 37 ++++++++++++++++++- agents-api/pyproject.toml | 1 + .../tests/sample_tasks/find_selector.yaml | 14 +++---- .../tests/sample_tasks/test_find_selector.py | 6 +-- 7 files changed, 59 insertions(+), 17 deletions(-) diff --git a/agents-api/agents_api/activities/task_steps/base_evaluate.py b/agents-api/agents_api/activities/task_steps/base_evaluate.py index 4b98cd13a..e65f1fe66 100644 --- a/agents-api/agents_api/activities/task_steps/base_evaluate.py +++ b/agents-api/agents_api/activities/task_steps/base_evaluate.py @@ -1,6 +1,8 @@ from typing import Any from beartype import beartype +from box import Box +from openai import BaseModel from temporalio import activity from ...env import testing @@ -15,6 +17,15 @@ async def base_evaluate( input_len = 1 if isinstance(exprs, str) else len(exprs) assert input_len > 0, "exprs must be a non-empty string, list or dict" + # Turn the nested dict values from pydantic to dicts where possible + values = { + k: v.model_dump() if isinstance(v, BaseModel) else v for k, v in values.items() + } + + # TODO: We should make this frozen_box=True, but we need to make sure that + # we don't break anything + values = Box(values, frozen_box=False, conversion_box=False) + evaluator = get_evaluator(names=values) try: diff --git a/agents-api/agents_api/activities/utils.py b/agents-api/agents_api/activities/utils.py index 231dee595..21c6b3675 100644 --- a/agents-api/agents_api/activities/utils.py +++ b/agents-api/agents_api/activities/utils.py @@ -8,6 +8,7 @@ from yaml import CSafeLoader ALLOWED_FUNCTIONS = { + "zip": zip, "len": len, "load_yaml": lambda string: yaml.load(string, Loader=CSafeLoader), "match_regex": lambda pattern, string: bool(re2.fullmatch(pattern, string)), diff --git a/agents-api/agents_api/workflows/task_execution.py b/agents-api/agents_api/workflows/task_execution.py index b331a52c0..72c7393ed 100644 --- a/agents-api/agents_api/workflows/task_execution.py +++ b/agents-api/agents_api/workflows/task_execution.py @@ -294,15 +294,13 @@ async def transition(**kwargs) -> None: previous_inputs + [item], ] + # TODO: We should parallelize this # Execute the chosen branch and come back here output = await workflow.execute_child_workflow( TaskExecutionWorkflow.run, args=map_reduce_args, ) - if hasattr(output, "model_dump"): - output = output.model_dump() - initial = await execute_activity( task_steps.base_evaluate, args=[ @@ -365,7 +363,7 @@ async def transition(**kwargs) -> None: ) case PromptStep(), StepOutcome(output=response): - state.output = response.get("choices", [{}])[0].get("message") + state.output = response case _: raise ApplicationError("Not implemented") diff --git a/agents-api/poetry.lock b/agents-api/poetry.lock index e9eba34b2..b703a0814 100644 --- a/agents-api/poetry.lock +++ b/agents-api/poetry.lock @@ -3076,6 +3076,41 @@ files = [ [package.extras] diagrams = ["jinja2", "railroad-diagrams"] +[[package]] +name = "python-box" +version = "7.2.0" +description = "Advanced Python dictionaries with dot notation access" +optional = false +python-versions = ">=3.8" +files = [ + {file = "python_box-7.2.0-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:6bdeec791e25258351388b3029a3ec5da302bb9ed3be175493c43cdc6c47f5e3"}, + {file = "python_box-7.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c449f7b3756a71479fa9c61a86e344ac00ed782a66d7662590f0afa294249d18"}, + {file = "python_box-7.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:6b0d61f182d394106d963232854e495b51edc178faa5316a797be1178212d7e0"}, + {file = "python_box-7.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:e2d752de8c1204255bf7b0c814c59ef48293c187a7e9fdcd2fefa28024b72032"}, + {file = "python_box-7.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a6c35ea356a386077935958a5debcd5b229b9a1b3b26287a52dfe1a7e65d99"}, + {file = "python_box-7.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:32ed58ec4d9e5475efe69f9c7d773dfea90a6a01979e776da93fd2b0a5d04429"}, + {file = "python_box-7.2.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:2a2d664c6a27f7515469b6f1e461935a2038ee130b7d194b4b4db4e85d363618"}, + {file = "python_box-7.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e8a5a7365db1aaf600d3e8a2747fcf6833beb5d45439a54318548f02e302e3ec"}, + {file = "python_box-7.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:739f827056ea148cbea3122d4617c994e829b420b1331183d968b175304e3a4f"}, + {file = "python_box-7.2.0-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:2617ef3c3d199f55f63c908f540a4dc14ced9b18533a879e6171c94a6a436f23"}, + {file = "python_box-7.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ffd866bed03087b1d8340014da8c3aaae19135767580641df1b4ae6fff6ac0aa"}, + {file = "python_box-7.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:9681f059e7e92bdf20782cd9ea6e533d4711fc7b8c57a462922a025d46add4d0"}, + {file = "python_box-7.2.0-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:6b59b1e2741c9ceecdf5a5bd9b90502c24650e609cd824d434fed3b6f302b7bb"}, + {file = "python_box-7.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e23fae825d809ae7520fdeac88bb52be55a3b63992120a00e381783669edf589"}, + {file = "python_box-7.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:573b1abdcb7bd745fa404444f060ee62fc35a74f067181e55dcb43cfe92f2827"}, + {file = "python_box-7.2.0-py3-none-any.whl", hash = "sha256:a3c90832dd772cb0197fdb5bc06123b6e1b846899a1b53d9c39450d27a584829"}, + {file = "python_box-7.2.0.tar.gz", hash = "sha256:551af20bdab3a60a2a21e3435120453c4ca32f7393787c3a5036e1d9fc6a0ede"}, +] + +[package.extras] +all = ["msgpack", "ruamel.yaml (>=0.17)", "toml"] +msgpack = ["msgpack"] +pyyaml = ["PyYAML"] +ruamel-yaml = ["ruamel.yaml (>=0.17)"] +toml = ["toml"] +tomli = ["tomli", "tomli-w"] +yaml = ["ruamel.yaml (>=0.17)"] + [[package]] name = "python-dateutil" version = "2.9.0.post0" @@ -4544,4 +4579,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "f2e6680de8e96b10ec7a9e5edf15223fd4842342db950778ff1d4bb1940e7ae0" +content-hash = "ce6fb9e63ff83a508ad9660217393b84e7174493f6b30f21f6a23fe9b4262fc8" diff --git a/agents-api/pyproject.toml b/agents-api/pyproject.toml index d74d9dba9..eceeba341 100644 --- a/agents-api/pyproject.toml +++ b/agents-api/pyproject.toml @@ -34,6 +34,7 @@ lz4 = "^4.3.3" pyyaml = "^6.0.2" google-re2 = "^1.1.20240702" +python-box = "^7.2.0" [tool.poetry.group.dev.dependencies] ipython = "^8.26.0" ruff = "^0.5.5" diff --git a/agents-api/tests/sample_tasks/find_selector.yaml b/agents-api/tests/sample_tasks/find_selector.yaml index 2c94df23f..465a2b578 100644 --- a/agents-api/tests/sample_tasks/find_selector.yaml +++ b/agents-api/tests/sample_tasks/find_selector.yaml @@ -63,11 +63,11 @@ main: image_url: url: "{{inputs[0].screenshot_base64}}" - over: _.parameters + over: _["parameters"] reduce: >- results + [ - yaml.safe_load(_["choices"][0]["message"]["content"].trim()) + load_yaml(_["choices"][0]["message"].content.strip()) ] - evaluate: @@ -75,16 +75,12 @@ main: [ {"value": result["value"], "network_request": request} for request in inputs[0]["network_requests"] - if result["value"] in nr["response"]["body"] for result in _ - if result["found"] + if result["found"] and result["value"] in request["response"]["body"] ] - if: len(_["result"]) > 0 then: - workflow: find_selectors - arguments: - results: list(zip(_, execution.input.network_requests)) - parameters: execution.input.parameters + log: list(zip(_, inputs[0]["network_requests"])) else: - error: "Could not find the selector in any of the network requests" + error: "Could not find the selector in any of the network requests" \ No newline at end of file diff --git a/agents-api/tests/sample_tasks/test_find_selector.py b/agents-api/tests/sample_tasks/test_find_selector.py index caebd7547..67ad88607 100644 --- a/agents-api/tests/sample_tasks/test_find_selector.py +++ b/agents-api/tests/sample_tasks/test_find_selector.py @@ -85,9 +85,9 @@ async def _( agent_id = str(agent.id) task_id = str(uuid4()) - with patch_embed_acompletion(), open( - f"{this_dir}/find_selector.yaml", "r" - ) as sample_file: + with patch_embed_acompletion( + output={"role": "assistant", "content": "found: true\nvalue: 'Gaga'"} + ), open(f"{this_dir}/find_selector.yaml", "r") as sample_file: task_def = sample_file.read() async with patch_http_client_with_temporal( From 9feb981ca69b9b0387a4cdfb25e5f868b79e270a Mon Sep 17 00:00:00 2001 From: Dmitry Paramonov Date: Mon, 26 Aug 2024 18:48:45 +0300 Subject: [PATCH 110/110] fix: Fix execute yaml task test --- agents-api/tests/test_execution_workflow.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/agents-api/tests/test_execution_workflow.py b/agents-api/tests/test_execution_workflow.py index 395f77de9..2dd79e017 100644 --- a/agents-api/tests/test_execution_workflow.py +++ b/agents-api/tests/test_execution_workflow.py @@ -719,7 +719,7 @@ async def _( ): mock_model_response = ModelResponse( id="fake_id", - choices=[Choices(message={"role": "assistant", "content": "Hello, world!"})], + choices=[Choices(message={"role": "assistant", "content": "found: true\nvalue: 'Gaga'"})], created=0, object="text_completion", ) @@ -727,9 +727,14 @@ async def _( with patch("agents_api.clients.litellm.acompletion") as acompletion, open( "./tests/sample_tasks/find_selector.yaml", "r" ) as task_file: + input = dict( + screenshot_base64="iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA", + network_requests=[{"request": {}, "response": {"body": "Lady Gaga"}}], + parameters=["name"], + ) task_definition = yaml.safe_load(task_file) acompletion.return_value = mock_model_response - data = CreateExecutionRequest(input={"parameters": ["param1", "param2"]}) + data = CreateExecutionRequest(input=input) task = create_task( developer_id=developer_id, @@ -752,6 +757,4 @@ async def _( mock_run_task_execution_workflow.assert_called_once() - result = await handle.result() - assert result["content"] == "Hello, world!" - assert result["role"] == "assistant" + await handle.result()

    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�?d1)3_f}kwz6Xvhmz2ZnrvD?^rro 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