diff --git a/.gitignore b/.gitignore index 3c02882..e6c05c6 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,6 @@ dmypy.json # misc .DS_Store + +# Tests +.*_cache/ \ No newline at end of file diff --git a/README.md b/README.md index 88c0af8..503a6ac 100644 --- a/README.md +++ b/README.md @@ -15,15 +15,15 @@ Communication between agents is not possible if there are discrepancies between Ensuring that agents are semantically compatible, i.e., the output of the one agent contains the information needed by later agents, is an problem of composition or planning in the application. This project, the IO Mapper Agent, -addresses level 2 and 3 compatibility. It is a component, implemented as an agent, that can make use of an LLM +addresses level 2 and 3 compatibility. It is a component, implemented as an agent, that can make use of an LLM to transform the output of one agent to become compatible to the input of another agent. Note that this may mean many different things, for example: -* JSON structure transcoding: A JSON dictionary needs to be remapped into another JSON dictionary -* Text summarisation: A text needs to be summarised or some information needs to be removed -* Text translation: A text needs to be translated from one language to another -* Text manipulation: Part of the information of one text needs to be reformulated into another text -* Any combination of the above +- JSON structure transcoding: A JSON dictionary needs to be remapped into another JSON dictionary +- Text summarisation: A text needs to be summarised or some information needs to be removed +- Text translation: A text needs to be translated from one language to another +- Text manipulation: Part of the information of one text needs to be reformulated into another text +- Any combination of the above The IO mapper Agent can be fed the schema definitions of inputs and outputs as defined by the [Agent Connect Protocol](https://github.com/agntcy/acp-spec). @@ -43,6 +43,9 @@ To get a local copy up and running follow these simple steps. ## Usage +Learn how to use our different Mappers +[USAGE.md](_usage.md) + ## Contributing Contributions are what make the open source community such an amazing place to diff --git a/agntcy_iomapper/__init__.py b/agntcy_iomapper/__init__.py index af363c7..92b9c86 100644 --- a/agntcy_iomapper/__init__.py +++ b/agntcy_iomapper/__init__.py @@ -7,6 +7,7 @@ IOMapperOutput, IOModelSettings, ) +from .imperative import ImperativeIOMapper from .iomapper import ( AgentIOMapper, IOMapperConfig, diff --git a/agntcy_iomapper/base.py b/agntcy_iomapper/base.py index eb9379c..1d25e03 100644 --- a/agntcy_iomapper/base.py +++ b/agntcy_iomapper/base.py @@ -1,11 +1,11 @@ # SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates. # SPDX-License-Identifier: Apache-2.0 -from abc import abstractmethod, ABC -from pydantic import BaseModel, model_validator, Field -from typing import Any +from abc import ABC, abstractmethod +from typing import Any, TypedDict + from openapi_pydantic import Schema +from pydantic import BaseModel, Field, model_validator from typing_extensions import Self -from typing import TypedDict class ArgumentsDescription(BaseModel): diff --git a/agntcy_iomapper/imperative.py b/agntcy_iomapper/imperative.py new file mode 100644 index 0000000..a56477a --- /dev/null +++ b/agntcy_iomapper/imperative.py @@ -0,0 +1,136 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates. +# SPDX-License-Identifier: Apache-2.0" +""" +The deterministic I/O mapper is a component +designed to translate specific inputs into +corresponding outputs in a predictable and consistent manner. +When configured with a JSONPath definition, +this mapper utilizes the JSONPath query language +to extract data from JSON-formatted input, +transforming it into a structured output based on predefined rules. +The deterministic nature of the mapper ensures that given the same input and +JSONPath configuration, the output will always be the same, +providing reliability and repeatability. +This is particularly useful in scenarios where +consistent data transformation is required. +""" + +import json +import logging +from typing import Any, Callable, Union + +import jsonschema +from jsonpath_ng.ext import parse + +from agntcy_iomapper.base import BaseIOMapper, IOMapperInput, IOMapperOutput + +logger = logging.getLogger(__name__) + + +class ImperativeIOMapper(BaseIOMapper): + field_mapping: dict[str, Union[str, Callable]] + """A dictionary for where the keys are fields of the output object + and values are JSONPath (strings) representing how the mapping + """ + + def __init__(self, field_mapping: dict[str, Union[str, Callable]] | None) -> None: + super().__init__() + self.field_mapping = field_mapping + + def invoke(self, input: IOMapperInput) -> IOMapperOutput | None: + if input.data is None: + return None + if self.field_mapping is None: + return IOMapperOutput(data=input.data) + + data = self._imperative_map(input) + return IOMapperOutput(data=data) + + def ainvoke(self, input: IOMapperInput) -> IOMapperOutput | None: + return self.invoke(input) + + def _imperative_map(self, input_definition: IOMapperInput) -> Any: + """ + Converts input data to a desired output type. + + This function attempts to convert the provided data into the specified + target type. It performs validation using a JSON schema and raises a + ValidationError if the data does not conform to the expected schema for + the target type. + + Parameters: + ---------- + data : Any + The input data to be converted. This can be of any type. + Returns: + ------- + Any + The converted data in the desired output type. + Raises: + ------ + ValidationError + If the input data does not conform to the expected schema for the + target type. + Notes: + ----- + The function assumes that the caller provides a valid `input_schema`. + Unsupported target types should be handled as needed within the function. + """ + data = input_definition.data + input_schema = input_definition.input.json_schema + + jsonschema.validate( + instance=data, + schema=input_schema.model_dump(exclude_none=True, mode="json"), + ) + + mapped_output = {} + + for output_field, json_path_or_func in self.field_mapping.items(): + if isinstance(json_path_or_func, str): + jsonpath_expr = parse(json_path_or_func) + match = jsonpath_expr.find(data) + expect_value = match[0].value if match else None + elif callable(json_path_or_func): + expect_value = json_path_or_func(data) + else: + raise TypeError( + "Mapping values must be strings (JSONPath) or callables (functions)." + ) + + self._set_jsonpath(mapped_output, output_field, expect_value) + jsonschema.validate( + instance=mapped_output, + schema=input_definition.output.json_schema.model_dump( + exclude_none=True, mode="json" + ), + ) + # return a serialized version of the object + return json.dumps(mapped_output) + + def _set_jsonpath( + self, data: dict[str, Any], path: str, value: Any + ) -> dict[str, Any]: + """set value for field based on its json path + Args: + data: Data so far + path: the json path + value: the value to set the json path to + Returns: + ----- + dict[str,Any] + The mapped filed with the value + """ + copy_data: dict[str, Any] = data + # Split the path into parts and remove the leading root + parts = path.strip("$.").split(".") + # Add value to corresponding path + for part in parts[:-1]: + if part not in copy_data: + copy_data[part] = {} + + copy_data = copy_data[part] + + copy_data[parts[-1]] = value + + return copy_data diff --git a/agntcy_iomapper/iomapper.py b/agntcy_iomapper/iomapper.py index 3966c39..88a73f4 100644 --- a/agntcy_iomapper/iomapper.py +++ b/agntcy_iomapper/iomapper.py @@ -2,21 +2,21 @@ # SPDX-License-Identifier: Apache-2.0 import argparse import asyncio -import aiofiles -import logging -import jsonschema import json +import logging import re +from typing import ClassVar, TypedDict -from dotenv import load_dotenv, find_dotenv -from pydantic import Field, model_validator, BaseModel -from typing import TypedDict, ClassVar -from typing_extensions import Self +import aiofiles +import jsonschema +from dotenv import find_dotenv, load_dotenv from jinja2 import Environment from jinja2.sandbox import SandboxedEnvironment +from pydantic import BaseModel, Field, model_validator from pydantic_ai import Agent +from typing_extensions import Self -from .base import BaseIOMapper, IOModelSettings, IOMapperOutput, IOMapperInput +from .base import BaseIOMapper, IOMapperInput, IOMapperOutput, IOModelSettings from .supported_agents import get_supported_agent logger = logging.getLogger(__name__) @@ -76,7 +76,9 @@ def _validate_obj(self) -> Self: class AgentIOMapper(BaseIOMapper): - _json_search_pattern: ClassVar[re.Pattern] = re.compile(r"```json\n(.*?)\n```", re.DOTALL) + _json_search_pattern: ClassVar[re.Pattern] = re.compile( + r"```json\n(.*?)\n```", re.DOTALL + ) def __init__( self, diff --git a/agntcy_iomapper/supported_agents.py b/agntcy_iomapper/supported_agents.py index 5794256..a18f9d7 100644 --- a/agntcy_iomapper/supported_agents.py +++ b/agntcy_iomapper/supported_agents.py @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates. # SPDX-License-Identifier: Apache-2.0 -from typing import Literal, Any +from typing import Any, Literal + from openai import AsyncAzureOpenAI from pydantic_ai import Agent from pydantic_ai.models import KnownModelName @@ -27,7 +28,7 @@ def get_supported_agent( Args: model_name (SupportedModelName): The name of the model to be used. If the name starts with "azure:", an `AsyncAzureOpenAI` client is used. - model_args (dict[str, Any], optional): Additional arguments for model + model_args (dict[str, Any], optional): Additional arguments for model initialization. Defaults to an empty dictionary. **kwargs: Additional keyword arguments passed to the `Agent` constructor. @@ -35,8 +36,8 @@ def get_supported_agent( Agent: An instance of the `Agent` class configured with the specified model. Notes: - - The `pydantic-ai` package does not currently pass `model_args` to the - inferred model in the constructor, but this behavior might change in + - The `pydantic-ai` package does not currently pass `model_args` to the + inferred model in the constructor, but this behavior might change in the future. """ if model_name.startswith("azure:"): diff --git a/docs/README.md b/docs/README.md index 932fb3d..536fc74 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,15 +12,15 @@ Communication between agents is not possible if there are discrepancies between Ensuring that agents are semantically compatible, i.e., the output of the one agent contains the information needed by later agents, is an problem of composition or planning in the application. This project, the IO Mapper Agent, -addresses level 2 and 3 compatibility. It is a component, implemented as an agent, that can make use of an LLM +addresses level 2 and 3 compatibility. It is a component, implemented as an agent, that can make use of an LLM to transform the output of one agent to become compatible to the input of another agent. Note that this may mean many different things, for example: -* JSON structure transcoding: A JSON dictionary needs to be remapped into another JSON dictionary -* Text summarisation: A text needs to be summarised or some information needs to be removed -* Text translation: A text needs to be translated from one language to another -* Text manipulation: Part of the information of one text needs to be reformulated into another text -* Any combination of the above +- JSON structure transcoding: A JSON dictionary needs to be remapped into another JSON dictionary +- Text summarisation: A text needs to be summarised or some information needs to be removed +- Text translation: A text needs to be translated from one language to another +- Text manipulation: Part of the information of one text needs to be reformulated into another text +- Any combination of the above The IO mapper Agent can be fed the schema definitions of inputs and outputs as defined by the [Agent Connect Protocol](https://github.com/agntcy/acp-spec). @@ -40,6 +40,9 @@ To get a local copy up and running follow these simple steps. ## Usage +Learn how to use our different Mappers +[usage.md](usage) + ## Contributing Contributions are what make the open source community such an amazing place to @@ -61,7 +64,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..923fb1c --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,63 @@ +# Usage + +### Use Agent IO Mapper + +:TODO + +### Use Determenistic + +The code snippet below illustrates a fully functional deterministic mapping that transforms the output of one agent into input for a second agent. The code for the agents is omitted. + +```python + #define schema for the origin agent + input_schema = {"question": {"type": "string"}} + + #define schema to witch the input should be converted to + output_schema = { + "quiz": { + "type": "object", + "properties": { + "prof_question": {"type": "string"}, + "due_date": {"type": "string"}, + }, + } + } + + #the mapping object using jsonpath, note: the value of the mapping can be either a jsonpath or a function + mapping_object = { + "prof_question": "$.question", + "due_date": lambda _: datetime.now().strftime("%x"), + } + + input = IOMapperInput( + input=ArgumentsDescription( + json_schema=Schema.model_validate(input_schema) + ), + output=ArgumentsDescription( + json_schema=Schema.model_validate(output_schema) + ), + data={"question": output_prof}, + ) + #instantiate the mapper + imerative_mapp = ImperativeIOMapper( + field_mapping=mapping_object, + ) + #get the mapping result and send to the other agent + mapping_result = imerative_mapp.invoke(input=input) + + + +``` + +### Use Examples + +1. To run the examples we strongly recommend that a [virtual environment is created](https://packaging.python.org/en/latest/guides/installing-using-pip-and-virtual-environments/) +2. Install the requirements file +3. from within examples folder run: + +```sh +make run_imperative_example +``` + +[GitHub](https://github.com/agntcy/iomapper-agnt/) +[Get Started](#getting-started) diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..e4b62bb --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,5 @@ + +EXAMPLES ?= . + +run_imperative_example: + python $(EXAMPLES)/imperative.py diff --git a/examples/imperative.py b/examples/imperative.py new file mode 100644 index 0000000..70d2850 --- /dev/null +++ b/examples/imperative.py @@ -0,0 +1,120 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates. +# SPDX-License-Identifier: Apache-2.0 +import json +import logging +from datetime import datetime + +from langchain_core.language_models import FakeListChatModel +from langchain_core.messages import HumanMessage +from openapi_pydantic import Schema +from pydantic import BaseModel + +from agntcy_iomapper import ImperativeIOMapper +from agntcy_iomapper.base import ArgumentsDescription, IOMapperInput + +logger = logging.getLogger(__name__) + + +class ProfessorAgent: + """ + This agent mission is to test it's students knowledges + """ + + predefined_questions = [ + "What is the capital of France?", + "Who wrote 'To Kill a Mockingbird'?", + "What is the largest planet in our solar system?", + "What is the chemical symbol for gold?", + "Who painted the Mona Lisa?", + ] + + def __init__(self) -> None: + self.model = FakeListChatModel(responses=self.predefined_questions) + + def ask_question(self) -> str: + response = self.model.invoke([HumanMessage(content="Generate a question")]) + return str(response.content) + + +class InputQuiz(BaseModel): + prof_question: str + due_date: str + + +class StudentAgent: + """ + This agent mission is to answer questions + """ + + predefined_answers = [ + "The capital of France is Paris.", + "Harper Lee wrote 'To Kill a Mockingbird'.", + "The largest planet in our solar system is Jupiter.", + "The chemical symbol for gold is Au.", + "Leonardo da Vinci painted the Mona Lisa.", + ] + + def __init__(self) -> None: + self.model = FakeListChatModel(responses=self.predefined_answers) + + def answer(self, quiz) -> str: + response = self.model.invoke([HumanMessage(content=quiz.prof_question)]) + return str(response.content) + + +class MultiAgentApp: + @staticmethod + def run_app(): + agent_prof = ProfessorAgent() + agent_student = StudentAgent() + + output_prof = agent_prof.ask_question() + + prof_agent_output_schema = {"question": {"type": "string"}} + student_agent_schema = { + "quiz": { + "type": "object", + "properties": { + "prof_question": {"type": "string"}, + "due_date": {"type": "string"}, + }, + } + } + + mapping_object = { + "prof_question": "$.question", + "due_date": lambda _: datetime.now().strftime("%x"), + } + + input = IOMapperInput( + input=ArgumentsDescription( + json_schema=Schema.model_validate(prof_agent_output_schema) + ), + output=ArgumentsDescription( + json_schema=Schema.model_validate(student_agent_schema) + ), + data={"question": output_prof}, + ) + + imerative_mapp = ImperativeIOMapper( + field_mapping=mapping_object, + ) + + print(f"professors question was {output_prof}") + + mapping_result = imerative_mapp.invoke(input=input) + + print(f"the mapping_result was {mapping_result}") + + response = agent_student.answer(InputQuiz(**(json.loads(mapping_result.data)))) + + print(f"student response was {response}") + # map data between agents + + +def run(): + MultiAgentApp.run_app() + + +if __name__ == "__main__": + run() diff --git a/examples/requirements.txt b/examples/requirements.txt new file mode 100644 index 0000000..f78758c --- /dev/null +++ b/examples/requirements.txt @@ -0,0 +1,4 @@ +langchain-core==0.3.35 +langgraph==0.2.72 +pydantic==2.10.6 +-e ../ diff --git a/poetry.lock b/poetry.lock index ad62fd6..65b75eb 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.5 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "aiofiles" @@ -6,6 +6,7 @@ version = "24.1.0" description = "File support for asyncio." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "aiofiles-24.1.0-py3-none-any.whl", hash = "sha256:b4ec55f4195e3eb5d7abd1bf7e061763e864dd4954231fb8539a0ef8bb8260e5"}, {file = "aiofiles-24.1.0.tar.gz", hash = "sha256:22a075c9e5a3810f0c2e48f3008c94d68c65d763b9b03857924c99e57355166c"}, @@ -17,6 +18,7 @@ version = "0.7.0" description = "Reusable constraint types to use with typing.Annotated" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53"}, {file = "annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"}, @@ -28,6 +30,7 @@ version = "0.45.2" description = "The official Python library for the anthropic API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "anthropic-0.45.2-py3-none-any.whl", hash = "sha256:ecd746f7274451dfcb7e1180571ead624c7e1195d1d46cb7c70143d2aedb4d35"}, {file = "anthropic-0.45.2.tar.gz", hash = "sha256:32a18b9ecd12c91b2be4cae6ca2ab46a06937b5aa01b21308d97a6d29794fb5e"}, @@ -52,12 +55,14 @@ version = "4.8.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"}, {file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"}, ] [package.dependencies] +exceptiongroup = {version = ">=1.0.2", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""} @@ -73,6 +78,7 @@ version = "1.3.0" description = "Better dates & times for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80"}, {file = "arrow-1.3.0.tar.gz", hash = "sha256:d4540617648cb5f895730f1ad8c82a65f2dad0166f57b75f3ca54759c4d67a85"}, @@ -92,6 +98,7 @@ version = "25.1.0" description = "Classes Without Boilerplate" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "attrs-25.1.0-py3-none-any.whl", hash = "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a"}, {file = "attrs-25.1.0.tar.gz", hash = "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e"}, @@ -111,6 +118,7 @@ version = "5.5.1" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "cachetools-5.5.1-py3-none-any.whl", hash = "sha256:b76651fdc3b24ead3c648bbdeeb940c1b04d365b38b4af66788f9ec4a81d42bb"}, {file = "cachetools-5.5.1.tar.gz", hash = "sha256:70f238fbba50383ef62e55c6aff6d9673175fe59f7c6782c7a0b9e38f4a9df95"}, @@ -122,6 +130,7 @@ version = "2025.1.31" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, @@ -133,6 +142,7 @@ version = "3.4.1" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, @@ -234,6 +244,7 @@ version = "5.13.12" description = "" optional = false python-versions = "<4.0,>=3.9" +groups = ["main"] files = [ {file = "cohere-5.13.12-py3-none-any.whl", hash = "sha256:2a043591a3e5280b47716a6b311e4c7f58e799364113a9cb81b50cd4f6c95f7e"}, {file = "cohere-5.13.12.tar.gz", hash = "sha256:97bb9ac107e580780b941acbabd3aa5e71960e6835398292c46aaa8a0a4cab88"}, @@ -256,10 +267,12 @@ 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" +groups = ["main", "test"] files = [ {file = "colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6"}, {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] +markers = {test = "sys_platform == \"win32\""} [[package]] name = "deepdiff" @@ -267,6 +280,7 @@ version = "8.2.0" description = "Deep Difference and Search of any Python object/data. Recreate objects by adding adding deltas to each other." optional = false python-versions = ">=3.8" +groups = ["test"] files = [ {file = "deepdiff-8.2.0-py3-none-any.whl", hash = "sha256:5091f2cdfd372b1b9f6bfd8065ba323ae31118dc4e42594371b38c8bea3fd0a4"}, {file = "deepdiff-8.2.0.tar.gz", hash = "sha256:6ec78f65031485735545ffbe7a61e716c3c2d12ca6416886d5e9291fc76c46c3"}, @@ -285,6 +299,7 @@ version = "1.9.0" description = "Distro - an OS platform information API" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "distro-1.9.0-py3-none-any.whl", hash = "sha256:7bffd925d65168f85027d8da9af6bddab658135b840670a223589bc0c8ef02b2"}, {file = "distro-1.9.0.tar.gz", hash = "sha256:2fa77c6fd8940f116ee1d6b94a2f90b13b5ea8d019b98bc8bafdcabcdd9bdbed"}, @@ -296,6 +311,7 @@ version = "0.2.2" description = "Like `typing._eval_type`, but lets older Python versions use newer typing features." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "eval_type_backport-0.2.2-py3-none-any.whl", hash = "sha256:cb6ad7c393517f476f96d456d0412ea80f0a8cf96f6892834cd9340149111b0a"}, {file = "eval_type_backport-0.2.2.tar.gz", hash = "sha256:f0576b4cf01ebb5bd358d02314d31846af5e07678387486e2c798af0e7d849c1"}, @@ -304,12 +320,29 @@ files = [ [package.extras] tests = ["pytest"] +[[package]] +name = "exceptiongroup" +version = "1.2.2" +description = "Backport of PEP 654 (exception groups)" +optional = false +python-versions = ">=3.7" +groups = ["main", "test"] +markers = "python_version < \"3.11\"" +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 = "fastavro" version = "1.10.0" description = "Fast read/write of AVRO files" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "fastavro-1.10.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1a9fe0672d2caf0fe54e3be659b13de3cad25a267f2073d6f4b9f8862acc31eb"}, {file = "fastavro-1.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:86dd0410770e0c99363788f0584523709d85e57bb457372ec5c285a482c17fe6"}, @@ -356,6 +389,7 @@ version = "3.17.0" description = "A platform independent file lock." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "filelock-3.17.0-py3-none-any.whl", hash = "sha256:533dc2f7ba78dc2f0f531fc6c4940addf7b70a481e269a5a3b93be94ffbe8338"}, {file = "filelock-3.17.0.tar.gz", hash = "sha256:ee4e77401ef576ebb38cd7f13b9b28893194acc20a8e68e18730ba9c0e54660e"}, @@ -372,6 +406,7 @@ 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" +groups = ["main"] files = [ {file = "fqdn-1.5.1-py3-none-any.whl", hash = "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014"}, {file = "fqdn-1.5.1.tar.gz", hash = "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f"}, @@ -383,6 +418,7 @@ version = "2025.2.0" description = "File-system specification" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "fsspec-2025.2.0-py3-none-any.whl", hash = "sha256:9de2ad9ce1f85e1931858535bc882543171d197001a0a5eb2ddc04f1781ab95b"}, {file = "fsspec-2025.2.0.tar.gz", hash = "sha256:1c24b16eaa0a1798afa0337aa0db9b256718ab2a89c425371f5628d22c3b6afd"}, @@ -422,6 +458,7 @@ version = "2.38.0" description = "Google Authentication Library" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "google_auth-2.38.0-py2.py3-none-any.whl", hash = "sha256:e7dae6694313f434a2727bf2906f27ad259bae090d7aa896590d86feec3d9d4a"}, {file = "google_auth-2.38.0.tar.gz", hash = "sha256:8285113607d3b80a3f1543b75962447ba8a09fe85783432a784fdeef6ac094c4"}, @@ -446,6 +483,7 @@ version = "1.5.7" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "griffe-1.5.7-py3-none-any.whl", hash = "sha256:4af8ec834b64de954d447c7b6672426bb145e71605c74a4e22d510cc79fe7d8b"}, {file = "griffe-1.5.7.tar.gz", hash = "sha256:465238c86deaf1137761f700fb343edd8ffc846d72f6de43c3c345ccdfbebe92"}, @@ -460,6 +498,7 @@ version = "0.18.0" description = "The official Python library for the groq API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "groq-0.18.0-py3-none-any.whl", hash = "sha256:81d5ac00057a45d8ce559d23ab5d3b3893011d1f12c35187ab35a9182d826ea6"}, {file = "groq-0.18.0.tar.gz", hash = "sha256:8e2ccfea406d68b3525af4b7c0e321fcb3d2a73fc60bb70b4156e6cd88c72f03"}, @@ -479,6 +518,7 @@ version = "0.14.0" description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -490,6 +530,7 @@ version = "1.0.7" description = "A minimal low-level HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd"}, {file = "httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c"}, @@ -511,6 +552,7 @@ version = "0.28.1" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad"}, {file = "httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc"}, @@ -535,6 +577,7 @@ version = "0.4.0" description = "Consume Server-Sent Event (SSE) messages with HTTPX." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721"}, {file = "httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f"}, @@ -546,6 +589,7 @@ version = "0.28.1" description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub" optional = false python-versions = ">=3.8.0" +groups = ["main"] files = [ {file = "huggingface_hub-0.28.1-py3-none-any.whl", hash = "sha256:aa6b9a3ffdae939b72c464dbb0d7f99f56e649b55c3d52406f49e0a5a620c0a7"}, {file = "huggingface_hub-0.28.1.tar.gz", hash = "sha256:893471090c98e3b6efbdfdacafe4052b20b84d59866fb6f54c33d9af18c303ae"}, @@ -580,6 +624,7 @@ version = "3.10" description = "Internationalized Domain Names in Applications (IDNA)" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3"}, {file = "idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9"}, @@ -594,6 +639,7 @@ version = "2.0.0" description = "brain-dead simple config-ini parsing" optional = false python-versions = ">=3.7" +groups = ["test"] files = [ {file = "iniconfig-2.0.0-py3-none-any.whl", hash = "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374"}, {file = "iniconfig-2.0.0.tar.gz", hash = "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3"}, @@ -605,6 +651,7 @@ version = "20.11.0" description = "Operations with ISO 8601 durations" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "isoduration-20.11.0-py3-none-any.whl", hash = "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042"}, {file = "isoduration-20.11.0.tar.gz", hash = "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9"}, @@ -619,6 +666,7 @@ version = "3.1.5" description = "A very fast and expressive template engine." optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb"}, {file = "jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb"}, @@ -636,6 +684,7 @@ version = "0.8.2" description = "Fast iterable JSON parser." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "jiter-0.8.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:ca8577f6a413abe29b079bc30f907894d7eb07a865c4df69475e868d73e71c7b"}, {file = "jiter-0.8.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b25bd626bde7fb51534190c7e3cb97cee89ee76b76d7585580e22f34f5e3f393"}, @@ -715,12 +764,29 @@ files = [ {file = "jiter-0.8.2.tar.gz", hash = "sha256:cd73d3e740666d0e639f678adb176fad25c1bcbdae88d8d7b857e1783bb4212d"}, ] +[[package]] +name = "jsonpath-ng" +version = "1.7.0" +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 = "*" +groups = ["main"] +files = [ + {file = "jsonpath-ng-1.7.0.tar.gz", hash = "sha256:f6f5f7fd4e5ff79c785f1573b394043b39849fb2bb47bcead935d12b00beab3c"}, + {file = "jsonpath_ng-1.7.0-py2-none-any.whl", hash = "sha256:898c93fc173f0c336784a3fa63d7434297544b7198124a68f9a3ef9597b0ae6e"}, + {file = "jsonpath_ng-1.7.0-py3-none-any.whl", hash = "sha256:f3d7f9e848cba1b6da28c55b1c26ff915dc9e0b1ba7e752a53d6da8d5cbd00b6"}, +] + +[package.dependencies] +ply = "*" + [[package]] name = "jsonpath-python" version = "1.0.6" description = "A more powerful JSONPath implementation in modern python" optional = false python-versions = ">=3.6" +groups = ["main"] files = [ {file = "jsonpath-python-1.0.6.tar.gz", hash = "sha256:dd5be4a72d8a2995c3f583cf82bf3cd1a9544cfdabf2d22595b67aff07349666"}, {file = "jsonpath_python-1.0.6-py3-none-any.whl", hash = "sha256:1e3b78df579f5efc23565293612decee04214609208a2335884b3ee3f786b575"}, @@ -732,6 +798,7 @@ version = "3.0.0" description = "Identify specific nodes in a JSON document (RFC 6901)" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "jsonpointer-3.0.0-py2.py3-none-any.whl", hash = "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942"}, {file = "jsonpointer-3.0.0.tar.gz", hash = "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef"}, @@ -743,6 +810,7 @@ version = "4.23.0" description = "An implementation of JSON Schema validation for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "jsonschema-4.23.0-py3-none-any.whl", hash = "sha256:fbadb6f8b144a8f8cf9f0b89ba94501d143e50411a1278633f56a7acf7fd5566"}, {file = "jsonschema-4.23.0.tar.gz", hash = "sha256:d71497fef26351a33265337fa77ffeb82423f3ea21283cd9467bb03999266bc4"}, @@ -772,6 +840,7 @@ version = "2024.10.1" description = "The JSON Schema meta-schemas and vocabularies, exposed as a Registry" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "jsonschema_specifications-2024.10.1-py3-none-any.whl", hash = "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf"}, {file = "jsonschema_specifications-2024.10.1.tar.gz", hash = "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272"}, @@ -786,6 +855,7 @@ version = "3.5.3" description = "Shim for the Logfire SDK which does nothing unless Logfire is installed" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "logfire_api-3.5.3-py3-none-any.whl", hash = "sha256:abecb0d2e43f900c58582c333f261c8f9b187e72933cf3f5aec93685666f86ae"}, {file = "logfire_api-3.5.3.tar.gz", hash = "sha256:2c15a0ad6f12f39003f62550a35f9247c29c87b466f7839836f346867442a55c"}, @@ -797,6 +867,7 @@ version = "3.0.2" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8"}, {file = "MarkupSafe-3.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158"}, @@ -867,6 +938,7 @@ version = "1.5.0" description = "Python Client SDK for the Mistral AI API." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "mistralai-1.5.0-py3-none-any.whl", hash = "sha256:9372537719f87bd6f9feef4747d0bf1f4fbe971f8c02945ca4b4bf3c94571c97"}, {file = "mistralai-1.5.0.tar.gz", hash = "sha256:fd94bc93bc25aad9c6dd8005b1a0bc4ba1250c6b3fbf855a49936989cc6e5c0d"}, @@ -889,6 +961,7 @@ version = "1.15.0" description = "Optional static typing for Python" optional = false python-versions = ">=3.9" +groups = ["sanity"] files = [ {file = "mypy-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:979e4e1a006511dacf628e36fadfecbcc0160a8af6ca7dad2f5025529e082c13"}, {file = "mypy-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c4bb0e1bd29f7d34efcccd71cf733580191e9a264a2202b0239da95984c5b559"}, @@ -926,6 +999,7 @@ files = [ [package.dependencies] mypy_extensions = ">=1.0.0" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} typing_extensions = ">=4.6.0" [package.extras] @@ -941,6 +1015,7 @@ version = "1.0.0" description = "Type system extensions for programs checked with the mypy type checker." optional = false python-versions = ">=3.5" +groups = ["main", "sanity"] 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"}, @@ -948,13 +1023,14 @@ files = [ [[package]] name = "openai" -version = "1.62.0" +version = "1.63.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ - {file = "openai-1.62.0-py3-none-any.whl", hash = "sha256:dcb7f9fb4fbc3f27e3ffd2d7bf045be9211510d7fafefcef7ad2302cb27484e0"}, - {file = "openai-1.62.0.tar.gz", hash = "sha256:ef3f6864ae2f75fa6296bc9811acf684b95557fcb611fe95734215a8b9150b43"}, + {file = "openai-1.63.0-py3-none-any.whl", hash = "sha256:a664dfc78f0a05ca46c3e21f344f840cf6bf7174f13cfa9de214ed28bfca1dda"}, + {file = "openai-1.63.0.tar.gz", hash = "sha256:597d7a1b35b113e5a09fcb953bdb1eef44f404a39985f3d7573b3ab09221fd66"}, ] [package.dependencies] @@ -977,6 +1053,7 @@ version = "0.5.1" description = "Pydantic OpenAPI schema implementation" optional = false python-versions = "<4.0,>=3.8" +groups = ["main"] files = [ {file = "openapi_pydantic-0.5.1-py3-none-any.whl", hash = "sha256:a3a09ef4586f5bd760a8df7f43028b60cafb6d9f61de2acba9574766255ab146"}, {file = "openapi_pydantic-0.5.1.tar.gz", hash = "sha256:ff6835af6bde7a459fb93eb93bb92b8749b754fc6e51b2f1590a19dc3005ee0d"}, @@ -991,6 +1068,7 @@ version = "5.3.0" description = "Orderly set" optional = false python-versions = ">=3.8" +groups = ["test"] files = [ {file = "orderly_set-5.3.0-py3-none-any.whl", hash = "sha256:c2c0bfe604f5d3d9b24e8262a06feb612594f37aa3845650548befd7772945d1"}, {file = "orderly_set-5.3.0.tar.gz", hash = "sha256:80b3d8fdd3d39004d9aad389eaa0eab02c71f0a0511ba3a6d54a935a6c6a0acc"}, @@ -1002,6 +1080,7 @@ version = "24.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" +groups = ["main", "test"] files = [ {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, @@ -1013,6 +1092,7 @@ version = "1.5.0" description = "plugin and hook calling mechanisms for python" optional = false python-versions = ">=3.8" +groups = ["test"] files = [ {file = "pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"}, {file = "pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1"}, @@ -1022,12 +1102,25 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "ply" +version = "3.11" +description = "Python Lex & Yacc" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "ply-3.11-py2.py3-none-any.whl", hash = "sha256:096f9b8350b65ebd2fd1346b12452efe5b9607f7482813ffca50c22722a807ce"}, + {file = "ply-3.11.tar.gz", hash = "sha256:00c7c1aaa88358b9c765b6d3000c6eec0ba42abca5351b095321aef446081da3"}, +] + [[package]] name = "pyasn1" version = "0.6.1" description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pyasn1-0.6.1-py3-none-any.whl", hash = "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629"}, {file = "pyasn1-0.6.1.tar.gz", hash = "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034"}, @@ -1039,6 +1132,7 @@ version = "0.4.1" description = "A collection of ASN.1-based protocols modules" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pyasn1_modules-0.4.1-py3-none-any.whl", hash = "sha256:49bfa96b45a292b711e986f222502c1c9a5e1f4e568fc30e2574a6c7d07838fd"}, {file = "pyasn1_modules-0.4.1.tar.gz", hash = "sha256:c28e2dbf9c06ad61c71a075c7e0f9fd0f1b0bb2d2ad4377f240d33ac2ab60a7c"}, @@ -1053,6 +1147,7 @@ version = "2.10.6" description = "Data validation using Python type hints" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic-2.10.6-py3-none-any.whl", hash = "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584"}, {file = "pydantic-2.10.6.tar.gz", hash = "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236"}, @@ -1073,6 +1168,7 @@ version = "0.0.23" description = "Agent Framework / shim to use Pydantic with LLMs" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pydantic_ai-0.0.23-py3-none-any.whl", hash = "sha256:e4a00883c9b2324839dd1d2cbf5ff3aaffe8339de61de991dd2da1756de87d9d"}, {file = "pydantic_ai-0.0.23.tar.gz", hash = "sha256:61d79e163205288576e7f157c89b65a7bd642b24b266a2434b8cbcb375d987b9"}, @@ -1091,6 +1187,7 @@ version = "0.0.23" description = "Agent Framework / shim to use Pydantic with LLMs, slim package" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pydantic_ai_slim-0.0.23-py3-none-any.whl", hash = "sha256:fb9714667a7c9b5ced769f2bcab7be9619470dde3aaf44aabdcef5787dce79f3"}, {file = "pydantic_ai_slim-0.0.23.tar.gz", hash = "sha256:383f4efdb9b1bb522b42ac236690ea3590e8736ac62d7c9f98f5b42a7139454d"}, @@ -1126,6 +1223,7 @@ version = "2.27.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "pydantic_core-2.27.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa"}, {file = "pydantic_core-2.27.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c"}, @@ -1238,6 +1336,7 @@ version = "0.0.23" description = "Graph and state machine library" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "pydantic_graph-0.0.23-py3-none-any.whl", hash = "sha256:b1e4f17dbb72d9828a3699f328c8588825295a5451cdd5bb8aca9c99a99062f0"}, {file = "pydantic_graph-0.0.23.tar.gz", hash = "sha256:9ddd1fe7d24cc048a01bc10333c23635b9a8a3e37c2ece34a6967a8314d62cb6"}, @@ -1254,6 +1353,7 @@ version = "8.3.4" description = "pytest: simple powerful testing with Python" optional = false python-versions = ">=3.8" +groups = ["test"] files = [ {file = "pytest-8.3.4-py3-none-any.whl", hash = "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6"}, {file = "pytest-8.3.4.tar.gz", hash = "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761"}, @@ -1261,9 +1361,11 @@ files = [ [package.dependencies] colorama = {version = "*", markers = "sys_platform == \"win32\""} +exceptiongroup = {version = ">=1.0.0rc8", markers = "python_version < \"3.11\""} iniconfig = "*" packaging = "*" pluggy = ">=1.5,<2" +tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] @@ -1274,6 +1376,7 @@ version = "0.23.8" description = "Pytest support for asyncio" optional = false python-versions = ">=3.8" +groups = ["test"] files = [ {file = "pytest_asyncio-0.23.8-py3-none-any.whl", hash = "sha256:50265d892689a5faefb84df80819d1ecef566eb3549cf915dfb33569359d1ce2"}, {file = "pytest_asyncio-0.23.8.tar.gz", hash = "sha256:759b10b33a6dc61cce40a8bd5205e302978bbbcc00e279a8b61d9a6a3c82e4d3"}, @@ -1292,6 +1395,7 @@ 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" +groups = ["main"] 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"}, @@ -1306,6 +1410,7 @@ 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" +groups = ["main"] 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"}, @@ -1320,6 +1425,7 @@ version = "6.0.2" description = "YAML parser and emitter for Python" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {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"}, @@ -1382,6 +1488,7 @@ version = "0.36.2" description = "JSON Referencing + Python" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "referencing-0.36.2-py3-none-any.whl", hash = "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0"}, {file = "referencing-0.36.2.tar.gz", hash = "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa"}, @@ -1398,6 +1505,7 @@ version = "2.32.3" description = "Python HTTP for Humans." optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "requests-2.32.3-py3-none-any.whl", hash = "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6"}, {file = "requests-2.32.3.tar.gz", hash = "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760"}, @@ -1419,6 +1527,7 @@ 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.*" +groups = ["main"] 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"}, @@ -1433,6 +1542,7 @@ version = "1.3.8" description = "Parsing and validation of URIs (RFC 3986) and IRIs (RFC 3987)" optional = false python-versions = "*" +groups = ["main"] files = [ {file = "rfc3987-1.3.8-py2.py3-none-any.whl", hash = "sha256:10702b1e51e5658843460b189b185c0366d2cf4cff716f13111b0ea9fd2dce53"}, {file = "rfc3987-1.3.8.tar.gz", hash = "sha256:d3c4d257a560d544e9826b38bc81db676890c79ab9d7ac92b39c7a253d5ca733"}, @@ -1444,6 +1554,7 @@ version = "0.22.3" description = "Python bindings to Rust's persistent data structures (rpds)" optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "rpds_py-0.22.3-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967"}, {file = "rpds_py-0.22.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37"}, @@ -1556,6 +1667,7 @@ version = "4.9" description = "Pure-Python RSA implementation" optional = false python-versions = ">=3.6,<4" +groups = ["main"] files = [ {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, @@ -1570,6 +1682,7 @@ version = "0.4.10" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" +groups = ["sanity"] files = [ {file = "ruff-0.4.10-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5c2c4d0859305ac5a16310eec40e4e9a9dec5dcdfbe92697acd99624e8638dac"}, {file = "ruff-0.4.10-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a79489607d1495685cdd911a323a35871abfb7a95d4f98fc6f85e799227ac46e"}, @@ -1596,6 +1709,7 @@ version = "1.17.0" description = "Python 2 and 3 compatibility utilities" optional = false python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" +groups = ["main"] files = [ {file = "six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274"}, {file = "six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81"}, @@ -1607,6 +1721,7 @@ version = "1.3.1" description = "Sniff out which async library your code is running under" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2"}, {file = "sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc"}, @@ -1618,6 +1733,7 @@ version = "0.21.0" description = "" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tokenizers-0.21.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3c4c93eae637e7d2aaae3d376f06085164e1660f89304c0ab2b1d08a406636b2"}, {file = "tokenizers-0.21.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:f53ea537c925422a2e0e92a24cce96f6bc5046bbef24a1652a5edc8ba975f62e"}, @@ -1644,12 +1760,56 @@ dev = ["tokenizers[testing]"] docs = ["setuptools-rust", "sphinx", "sphinx-rtd-theme"] testing = ["black (==22.3)", "datasets", "numpy", "pytest", "requests", "ruff"] +[[package]] +name = "tomli" +version = "2.2.1" +description = "A lil' TOML parser" +optional = false +python-versions = ">=3.8" +groups = ["sanity", "test"] +markers = "python_version < \"3.11\"" +files = [ + {file = "tomli-2.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:678e4fa69e4575eb77d103de3df8a895e1591b48e740211bd1067378c69e8249"}, + {file = "tomli-2.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:023aa114dd824ade0100497eb2318602af309e5a55595f76b626d6d9f3b7b0a6"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ece47d672db52ac607a3d9599a9d48dcb2f2f735c6c2d1f34130085bb12b112a"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6972ca9c9cc9f0acaa56a8ca1ff51e7af152a9f87fb64623e31d5c83700080ee"}, + {file = "tomli-2.2.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c954d2250168d28797dd4e3ac5cf812a406cd5a92674ee4c8f123c889786aa8e"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:8dd28b3e155b80f4d54beb40a441d366adcfe740969820caf156c019fb5c7ec4"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:e59e304978767a54663af13c07b3d1af22ddee3bb2fb0618ca1593e4f593a106"}, + {file = "tomli-2.2.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:33580bccab0338d00994d7f16f4c4ec25b776af3ffaac1ed74e0b3fc95e885a8"}, + {file = "tomli-2.2.1-cp311-cp311-win32.whl", hash = "sha256:465af0e0875402f1d226519c9904f37254b3045fc5084697cefb9bdde1ff99ff"}, + {file = "tomli-2.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2d0f2fdd22b02c6d81637a3c95f8cd77f995846af7414c5c4b8d0545afa1bc4b"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4a8f6e44de52d5e6c657c9fe83b562f5f4256d8ebbfe4ff922c495620a7f6cea"}, + {file = "tomli-2.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8d57ca8095a641b8237d5b079147646153d22552f1c637fd3ba7f4b0b29167a8"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e340144ad7ae1533cb897d406382b4b6fede8890a03738ff1683af800d54192"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db2b95f9de79181805df90bedc5a5ab4c165e6ec3fe99f970d0e302f384ad222"}, + {file = "tomli-2.2.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:40741994320b232529c802f8bc86da4e1aa9f413db394617b9a256ae0f9a7f77"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:400e720fe168c0f8521520190686ef8ef033fb19fc493da09779e592861b78c6"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:02abe224de6ae62c19f090f68da4e27b10af2b93213d36cf44e6e1c5abd19fdd"}, + {file = "tomli-2.2.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:b82ebccc8c8a36f2094e969560a1b836758481f3dc360ce9a3277c65f374285e"}, + {file = "tomli-2.2.1-cp312-cp312-win32.whl", hash = "sha256:889f80ef92701b9dbb224e49ec87c645ce5df3fa2cc548664eb8a25e03127a98"}, + {file = "tomli-2.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:7fc04e92e1d624a4a63c76474610238576942d6b8950a2d7f908a340494e67e4"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f4039b9cbc3048b2416cc57ab3bda989a6fcf9b36cf8937f01a6e731b64f80d7"}, + {file = "tomli-2.2.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:286f0ca2ffeeb5b9bd4fcc8d6c330534323ec51b2f52da063b11c502da16f30c"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a92ef1a44547e894e2a17d24e7557a5e85a9e1d0048b0b5e7541f76c5032cb13"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9316dc65bed1684c9a98ee68759ceaed29d229e985297003e494aa825ebb0281"}, + {file = "tomli-2.2.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e85e99945e688e32d5a35c1ff38ed0b3f41f43fad8df0bdf79f72b2ba7bc5272"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ac065718db92ca818f8d6141b5f66369833d4a80a9d74435a268c52bdfa73140"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:d920f33822747519673ee656a4b6ac33e382eca9d331c87770faa3eef562aeb2"}, + {file = "tomli-2.2.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a198f10c4d1b1375d7687bc25294306e551bf1abfa4eace6650070a5c1ae2744"}, + {file = "tomli-2.2.1-cp313-cp313-win32.whl", hash = "sha256:d3f5614314d758649ab2ab3a62d4f2004c825922f9e370b29416484086b264ec"}, + {file = "tomli-2.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:a38aa0308e754b0e3c67e344754dff64999ff9b513e691d0e786265c93583c69"}, + {file = "tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc"}, + {file = "tomli-2.2.1.tar.gz", hash = "sha256:cd45e1dc79c835ce60f7404ec8119f2eb06d38b1deba146f07ced3bbc44505ff"}, +] + [[package]] name = "tqdm" version = "4.67.1" description = "Fast, Extensible Progress Meter" optional = false python-versions = ">=3.7" +groups = ["main"] files = [ {file = "tqdm-4.67.1-py3-none-any.whl", hash = "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2"}, {file = "tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2"}, @@ -1671,6 +1831,7 @@ version = "2.9.0.20241206" description = "Typing stubs for python-dateutil" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "types_python_dateutil-2.9.0.20241206-py3-none-any.whl", hash = "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53"}, {file = "types_python_dateutil-2.9.0.20241206.tar.gz", hash = "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb"}, @@ -1682,6 +1843,7 @@ version = "2.32.0.20241016" description = "Typing stubs for requests" optional = false python-versions = ">=3.8" +groups = ["main"] files = [ {file = "types-requests-2.32.0.20241016.tar.gz", hash = "sha256:0d9cad2f27515d0e3e3da7134a1b6f28fb97129d86b867f24d9c726452634d95"}, {file = "types_requests-2.32.0.20241016-py3-none-any.whl", hash = "sha256:4195d62d6d3e043a4eaaf08ff8a62184584d2e8684e9d2aa178c7915a7da3747"}, @@ -1696,6 +1858,7 @@ version = "4.12.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" +groups = ["main", "sanity"] files = [ {file = "typing_extensions-4.12.2-py3-none-any.whl", hash = "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d"}, {file = "typing_extensions-4.12.2.tar.gz", hash = "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"}, @@ -1707,6 +1870,7 @@ version = "0.9.0" description = "Runtime inspection utilities for typing module." optional = false python-versions = "*" +groups = ["main"] 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"}, @@ -1722,6 +1886,7 @@ version = "1.3.0" description = "RFC 6570 URI Template Processor" optional = false python-versions = ">=3.7" +groups = ["main"] 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"}, @@ -1736,6 +1901,7 @@ version = "2.3.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, @@ -1753,12 +1919,13 @@ version = "24.11.1" description = "A library for working with the color formats defined by HTML and CSS." optional = false python-versions = ">=3.9" +groups = ["main"] files = [ {file = "webcolors-24.11.1-py3-none-any.whl", hash = "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9"}, {file = "webcolors-24.11.1.tar.gz", hash = "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6"}, ] [metadata] -lock-version = "2.0" -python-versions = "^3.12" -content-hash = "7df871d1dfc872e5527232536b9b45c9173ca2c76d145402a75fb65dcce7a6e6" +lock-version = "2.1" +python-versions = ">=3.9.0,<4.0" +content-hash = "82a760aaaf5ca496cda5a81e4fe66c4f7754afd5ddb7666d434243563cb89a76" diff --git a/pyproject.toml b/pyproject.toml index 62f79c2..a03999e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,29 +5,32 @@ license = "Apache-2.0" description = "A tool to transform output from one agent to the input of another." readme = "README.md" authors = [ - { name = "Jeff Napper", email = "jenapper@cisco.com" }, - { name = "Reginaldo Costa", email = "regcosta@cisco.com" } + { name = "Jeff Napper", email = "jenapper@cisco.com" }, + { name = "Reginaldo Costa", email = "regcosta@cisco.com" }, ] maintainers = [ - { name = "Jeff Napper", email = "jenapper@cisco.com" }, - { name = "Reginaldo Costa", email = "regcosta@cisco.com" } + { name = "Jeff Napper", email = "jenapper@cisco.com" }, + { name = "Reginaldo Costa", email = "regcosta@cisco.com" }, ] [tool.poetry.dependencies] -python = "^3.12" -pydantic = "^2.9.1" +python = ">=3.9.0,<4.0" python-dotenv = "^1.0.1" aiofiles = "^24.1.0" -jsonschema = { extras=["format"], version="^4.23.0" } jinja2 = "^3.1.5" openapi-pydantic = "^0.5.1" pydantic-ai = "^0.0.23" +jsonschema = { extras = ["format"], version = "^4.23.0" } +jsonpath-ng = "^1.7.0" +pydantic = "^2.10.6" + [tool.poetry.group.test.dependencies] pytest = "*" pytest-asyncio = "^0.23.5" deepdiff = "^8.1.1" + [tool.poetry.group.sanity.dependencies] mypy = "^1.10.0" ruff = "^0.4.6" @@ -35,9 +38,32 @@ ruff = "^0.4.6" [tool.pytest.ini_options] pythonpath = ["."] asyncio_mode = "auto" -markers = [ - "llm: Requires access to an LLM" -] +markers = ["llm: Requires access to an LLM"] + +[tool.ruff] +lint.select = ["E", "F", "I", "TID251"] +lint.ignore = ["E501"] +line-length = 88 +indent-width = 4 +extend-include = ["*.ipynb"] + +[tool.ruff.format] +quote-style = "double" +indent-style = "space" +skip-magic-trailing-comma = false +line-ending = "auto" +docstring-code-format = false +docstring-code-line-length = "dynamic" + +[tool.mypy] +disallow_untyped_defs = true +explicit_package_bases = true +warn_no_return = false +warn_unused_ignores = true +warn_redundant_casts = true +allow_redefinition = true +# https://mypy.readthedocs.io/en/stable/config_file.html +disable_error_code = "typeddict-item, return-value, override, has-type" [build-system] requires = ["poetry-core"] diff --git a/tests/test_determenistic_iomapper.py b/tests/test_determenistic_iomapper.py new file mode 100644 index 0000000..0e3e2e8 --- /dev/null +++ b/tests/test_determenistic_iomapper.py @@ -0,0 +1,197 @@ +# SPDX-FileCopyrightText: Copyright (c) 2025 Cisco and/or its affiliates. +# SPDX-License-Identifier: Apache-2.0 +import json + +import pytest +from openapi_pydantic import Schema + +from agntcy_iomapper import ImperativeIOMapper +from agntcy_iomapper.base import ArgumentsDescription, IOMapperInput + + +@pytest.mark.parametrize( + "input_schema, output_schema,field_mapping,input_value, expected", + [ + ( + { + "type": "object", + "properties": {"fullName": {"type": "string"}}, + "required": ["fullName"], + }, + { + "type": "object", + "properties": { + "firstName": {"type": "string"}, + "lastName": {"type": "string"}, + }, + "required": ["firstName", "lastName"], + }, + { + "$.firstName": "$.fullName.`split(' ', 0, 1)`", + "$.lastName": "$.fullName.`split(' ', -1, -1)`", + }, + {"fullName": "John Doe"}, + {"firstName": "John", "lastName": "Doe"}, + ), + ( + { + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "integer"}, + "address": { + "type": "object", + "properties": { + "street": {"type": "string"}, + "city": {"type": "string"}, + }, + }, + }, + }, + { + "type": "object", + "properties": { + "fullName": {"type": "string"}, + "location": { + "type": "object", + "properties": { + "streetAddress": {"type": "string"}, + "cityName": {"type": "string"}, + }, + }, + }, + }, + { + "fullName": "$.name", + "location.streetAddress": "$.address.street", + "location.cityName": "$.address.city", + }, + { + "name": "John Doe", + "age": 30, + "address": {"street": "123 Elm St", "city": "Springfield"}, + }, + { + "fullName": "John Doe", + "location": {"streetAddress": "123 Elm St", "cityName": "Springfield"}, + }, + ), + ( + { + "type": "object", + "properties": { + "employeeId": {"type": "number"}, + "firstName": {"type": "string"}, + "lastName": {"type": "string"}, + "email": {"type": "string"}, + "position": {"type": "string"}, + "department": {"type": "string"}, + }, + "required": ["firstName", "email", "position", "department"], + }, + { + "type": "object", + "properties": { + "recipient": {"type": "string"}, + "subject": {"type": "string"}, + "body": {"type": "string"}, + }, + "required": ["recipient", "subject", "body"], + }, + {"recipient": "$.email", "subject": "$.position", "body": "$.firstName"}, + { + "employeeId": 123, + "firstName": "Jane", + "lastName": "Doe", + "email": "jane.doe@example.com", + "position": "Marketing Manager", + "department": "Marketing", + }, + { + "recipient": "jane.doe@example.com", + "subject": "Marketing Manager", + "body": "Jane", + }, + ), + ( + { + "type": "object", + "properties": { + "name": {"type": "string"}, + "details": { + "type": "object", + "properties": { + "brand": {"type": "string"}, + "specifications": { + "type": "object", + "properties": { + "memory": {"type": "string"}, + "storage": {"type": "string"}, + }, + }, + }, + }, + }, + }, + { + "product_name": {"type": "string"}, + "product_category": {"type": "string"}, + "brand": {"type": "string"}, + "storage": {"type": "string"}, + "full_product_info": {"type": "string"}, + }, + { + "product_name": "$.product.name", + "product_category": "$.category", + "brand": "$.product.details.brand", + "storage": "$.product.details.specifications.storage", + "full_product_info": lambda d: d.get("product", {}), + }, + { + "product": { + "name": "Laptop", + "details": { + "brand": "Dell", + "specifications": {"memory": "16GB", "storage": "1TB"}, + }, + }, + "category": "Electronics", + }, + { + "product_name": "Laptop", + "product_category": "Electronics", + "brand": "Dell", + "storage": "1TB", + "full_product_info": { + "details": { + "brand": "Dell", + "specifications": {"memory": "16GB", "storage": "1TB"}, + }, + "name": "Laptop", + }, + }, + ), + ], +) +def test_imperative_iomapp( + input_schema, output_schema, field_mapping, input_value, expected +) -> None: + """Test imperative io mapping""" + + io_mapp = ImperativeIOMapper( + field_mapping=field_mapping, + ) + + input = IOMapperInput( + input=ArgumentsDescription(json_schema=Schema.model_validate(input_schema)), + output=ArgumentsDescription(json_schema=Schema.model_validate(output_schema)), + data=input_value, + ) + actual = io_mapp.invoke(input=input) + + # When test returns none fail the test + if actual is None: + assert True is False + return + + assert expected == json.loads(actual.data)