diff --git a/pkgs/standards/swarmauri_standard/pyproject.toml b/pkgs/standards/swarmauri_standard/pyproject.toml index defc505f..29b8f58c 100644 --- a/pkgs/standards/swarmauri_standard/pyproject.toml +++ b/pkgs/standards/swarmauri_standard/pyproject.toml @@ -88,7 +88,7 @@ full = [ #"transformers", #"torch", #"keras", "tf-keras", - "matplotlib" + #"matplotlib" ] [tool.poetry.group.dev.dependencies] diff --git a/pkgs/standards/swarmauri_doc2vec_vectorstore/README.md b/pkgs/standards/swarmauri_tool_matplotlib/README.md similarity index 100% rename from pkgs/standards/swarmauri_doc2vec_vectorstore/README.md rename to pkgs/standards/swarmauri_tool_matplotlib/README.md diff --git a/pkgs/standards/swarmauri_tool_matplotlib/pyproject.toml b/pkgs/standards/swarmauri_tool_matplotlib/pyproject.toml new file mode 100644 index 00000000..07e15431 --- /dev/null +++ b/pkgs/standards/swarmauri_tool_matplotlib/pyproject.toml @@ -0,0 +1,55 @@ +[tool.poetry] +name = "swarmauri_tool_matplotlib" +version = "0.6.0.dev1" +description = "This repository includes an example of a First Class Swarmauri Example." +authors = ["Jacob Stewart "] +license = "Apache-2.0" +readme = "README.md" +repository = "http://github.com/swarmauri/swarmauri-sdk" +classifiers = [ + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12" +] + +[tool.poetry.dependencies] +python = ">=3.10,<3.13" + +# Swarmauri +swarmauri_core = { path = "../../core" } +swarmauri_base = { path = "../../base" } + +[tool.poetry.group.dev.dependencies] +flake8 = "^7.0" +pytest = "^8.0" +pytest-asyncio = ">=0.24.0" +pytest-xdist = "^3.6.1" +pytest-json-report = "^1.5.0" +python-dotenv = "*" +requests = "^2.32.3" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" + +[tool.pytest.ini_options] +norecursedirs = ["combined", "scripts"] + +markers = [ + "test: standard test", + "unit: Unit tests", + "integration: Integration tests", + "acceptance: Acceptance tests", + "experimental: Experimental tests" +] +log_cli = true +log_cli_level = "INFO" +log_cli_format = "%(asctime)s [%(levelname)s] %(message)s" +log_cli_date_format = "%Y-%m-%d %H:%M:%S" +asyncio_default_fixture_loop_scope = "function" + +[tool.poetry.plugins."swarmauri.tools"] +MatplotlibTool = "swarmauri_tool_matplotlib:MatplotlibTool" +MatplotlibCsvTool = "swarmauri_tool_matplotlib:MatplotlibCsvTool" + diff --git a/pkgs/standards/swarmauri_tool_matplotlib/swarmauri_tool_matplotlib/MatplotlibCsvTool.py b/pkgs/standards/swarmauri_tool_matplotlib/swarmauri_tool_matplotlib/MatplotlibCsvTool.py new file mode 100644 index 00000000..6c9c37ab --- /dev/null +++ b/pkgs/standards/swarmauri_tool_matplotlib/swarmauri_tool_matplotlib/MatplotlibCsvTool.py @@ -0,0 +1,78 @@ +import pandas as pd +import matplotlib.pyplot as plt +import base64 +from typing import List, Literal, Dict +from pydantic import Field +from swarmauri_standard.tools.Parameter import Parameter +from swarmauri_base.tools.ToolBase import ToolBase +from swarmauri_core.ComponentBase import ComponentBase + + +@ComponentBase.register_type(ToolBase, "MatplotlibCsvTool") +class MatplotlibCsvTool(ToolBase): + type: Literal["MatplotlibCsvTool"] = "MatplotlibCsvTool" + name: str = Field( + "MatplotlibCsvTool", + description="Tool to generate plots from CSV data using Matplotlib.", + ) + description: str = Field( + "This tool reads data from a CSV file and generates a plot using Matplotlib.", + description="Description of the MatplotlibCsvTool", + ) + + parameters: List[Parameter] = Field( + default_factory=lambda: [ + Parameter( + name="csv_file", + type="string", + description="The path to the CSV file containing the data.", + required=True, + ), + Parameter( + name="x_column", + type="string", + description="The name of the column to use for the x-axis.", + required=True, + ), + Parameter( + name="y_column", + type="string", + description="The name of the column to use for the y-axis.", + required=True, + ), + Parameter( + name="output_file", + type="string", + description="The filename where the plot will be saved.", + required=True, + ), + ] + ) + + def __call__( + self, csv_file: str, x_column: str, y_column: str, output_file: str + ) -> Dict[str, str]: + # Read data from CSV + data = pd.read_csv(csv_file) + + # Check if columns exist in the DataFrame + if x_column not in data.columns or y_column not in data.columns: + raise ValueError( + f"Columns {x_column} and/or {y_column} not found in the CSV file." + ) + + # Generate plot + plt.figure(figsize=(10, 6)) + plt.plot(data[x_column], data[y_column], marker="o") + plt.xlabel(x_column) + plt.ylabel(y_column) + plt.title(f"{y_column} vs {x_column}") + plt.grid(True) + plt.savefig(output_file) + plt.close() + print(f"Plot generated and saved to {output_file}") + # Encode the plot image as base64 + with open(output_file, "rb") as image_file: + encoded_image = base64.b64encode(image_file.read()).decode("utf-8") + + return {"img_path": output_file, "img_base64": encoded_image, "data": []} diff --git a/pkgs/standards/swarmauri_tool_matplotlib/swarmauri_tool_matplotlib/MatplotlibTool.py b/pkgs/standards/swarmauri_tool_matplotlib/swarmauri_tool_matplotlib/MatplotlibTool.py new file mode 100644 index 00000000..ff77caf2 --- /dev/null +++ b/pkgs/standards/swarmauri_tool_matplotlib/swarmauri_tool_matplotlib/MatplotlibTool.py @@ -0,0 +1,120 @@ +import base64 +import matplotlib.pyplot as plt +from typing import List, Literal +from pydantic import Field +from swarmauri_base.tools.ToolBase import ToolBase +from swarmauri_standard.tools.Parameter import Parameter +from swarmauri_core.ComponentBase import ComponentBase + + +@ComponentBase.register_type(ToolBase, "MatplotlibTool") +class MatplotlibTool(ToolBase): + version: str = "1.0.0" + name: str = "MatplotlibTool" + description: str = ( + "Generates a plot using Matplotlib library based on provided configuration." + ) + type: Literal["MatplotlibTool"] = "MatplotlibTool" + + parameters: List[Parameter] = Field( + default_factory=lambda: [ + Parameter( + name="plot_type", + type="string", + description="Type of plot to generate (e.g., 'line', 'bar', 'scatter').", + required=True, + enum=["line", "bar", "scatter"], + ), + Parameter( + name="x_data", + type="list", + description="X-axis data for the plot.", + required=True, + ), + Parameter( + name="y_data", + type="list", + description="Y-axis data for the plot.", + required=True, + ), + Parameter( + name="title", + type="string", + description="Title of the plot.", + required=False, + default="", + ), + Parameter( + name="x_label", + type="string", + description="Label for the X-axis.", + required=False, + default="", + ), + Parameter( + name="y_label", + type="string", + description="Label for the Y-axis.", + required=False, + default="", + ), + Parameter( + name="save_path", + type="string", + description="Path to save the generated plot image.", + required=False, + default="plot.png", + ), + ] + ) + + def __call__( + self, + plot_type: str, + x_data: List[float], + y_data: List[float], + title: str = "", + x_label: str = "", + y_label: str = "", + save_path: str = "plot.png", + ): + """ + Generates a plot using Matplotlib based on provided configuration. + + Parameters: + plot_type (str): The type of the plot ('line', 'bar', 'scatter'). + x_data (List[float]): X-axis data for the plot. + y_data (List[float]): Y-axis data for the plot. + title (str): Title of the plot. + x_label (str): Label for the X-axis. + y_label (str): Label for the Y-axis. + save_path (str): Path to save the generated plot image. + + Returns: + str: Path where the plot image is saved. + """ + plt.figure() + + if plot_type == "line": + plt.plot(x_data, y_data) + elif plot_type == "bar": + plt.bar(x_data, y_data) + elif plot_type == "scatter": + plt.scatter(x_data, y_data) + else: + raise ValueError(f"Unsupported plot type: {plot_type}") + + if title: + plt.title(title) + if x_label: + plt.xlabel(x_label) + if y_label: + plt.ylabel(y_label) + + plt.savefig(save_path) + plt.close() + + with open(save_path, "rb") as image_file: + encoded_image = base64.b64encode(image_file.read()).decode("utf-8") + + return {"img_path": save_path, "img_base64": encoded_image, "data": []} diff --git a/pkgs/standards/swarmauri_tool_matplotlib/swarmauri_tool_matplotlib/__init__.py b/pkgs/standards/swarmauri_tool_matplotlib/swarmauri_tool_matplotlib/__init__.py new file mode 100644 index 00000000..a76bf5b4 --- /dev/null +++ b/pkgs/standards/swarmauri_tool_matplotlib/swarmauri_tool_matplotlib/__init__.py @@ -0,0 +1,17 @@ +from .MatplotlibCsvTool import MatplotlibCsvTool +from .MatplotlibTool import MatplotlibTool +__version__ = "0.6.0.dev26" +__long_desc__ = """ + +# Swarmauri Matplotlib Based Components + +Components Included: +- MatplotlibTool +- MatplotlibCsvTool + +Visit us at: https://swarmauri.com +Follow us at: https://github.com/swarmauri +Star us at: https://github.com/swarmauri/swarmauri-sdk + +""" + \ No newline at end of file diff --git a/pkgs/standards/swarmauri_tool_matplotlib/tests/unit/MatplotlibCsvTool_unit_test.py b/pkgs/standards/swarmauri_tool_matplotlib/tests/unit/MatplotlibCsvTool_unit_test.py new file mode 100644 index 00000000..c162d181 --- /dev/null +++ b/pkgs/standards/swarmauri_tool_matplotlib/tests/unit/MatplotlibCsvTool_unit_test.py @@ -0,0 +1,84 @@ +import os +from tempfile import NamedTemporaryFile + +import pytest +from swarmauri_tool_matplotlib.MatplotlibCsvTool import MatplotlibCsvTool as Tool + + +@pytest.mark.unit +def test_ubc_resource(): + tool = Tool() + assert tool.resource == "Tool" + + +@pytest.mark.unit +def test_ubc_type(): + assert Tool().type == "MatplotlibCsvTool" + + +@pytest.mark.unit +def test_initialization(): + tool = Tool() + assert type(tool.id) == str + + +@pytest.mark.unit +def test_serialization(): + tool = Tool() + assert tool.id == Tool.model_validate_json(tool.model_dump_json()).id + + +@pytest.mark.parametrize( + "csv_content, x_column, y_column, expected_error", + [ + ( + "x,y\n1,2\n3,4\n5,6", # CSV content + "x", # x_column + "y", # y_column + None, # No error expected + ), + ( + "a,b\n1,2\n3,4\n5,6", # CSV content + "x", # x_column + "y", # y_column + ValueError, # Error expected due to missing columns + ), + ( + "x,z\n1,2\n3,4\n5,6", # CSV content + "x", # x_column + "y", # y_column + ValueError, # Error expected due to missing y_column + ), + ], +) +@pytest.mark.unit +def test_call(csv_content, x_column, y_column, expected_error): + with NamedTemporaryFile(delete=False, suffix=".csv") as csv_file: + csv_file.write(csv_content.encode()) + csv_file_path = csv_file.name + + with NamedTemporaryFile(delete=False, suffix=".png") as output_file: + output_file_path = output_file.name + + tool = Tool() + expected_keys = {"img_path", "img_base64", "data"} + + if expected_error: + with pytest.raises(expected_error): + tool(csv_file_path, x_column, y_column, output_file_path) + else: + result = tool(csv_file_path, x_column, y_column, output_file_path) + assert isinstance( + result, dict + ), f"Expected dict, but got {type(result).__name__}" + assert expected_keys.issubset( + result.keys() + ), f"Expected keys {expected_keys} but got {result.keys()}" + assert isinstance( + result.get("data"), list + ), f"Expected list, but got {type(result).__name__}" + assert os.path.exists(output_file_path) + + os.remove(csv_file_path) + if os.path.exists(output_file_path): + os.remove(output_file_path) diff --git a/pkgs/standards/swarmauri_tool_matplotlib/tests/unit/MatplotlibTool_unit_test.py b/pkgs/standards/swarmauri_tool_matplotlib/tests/unit/MatplotlibTool_unit_test.py new file mode 100644 index 00000000..2f8e21de --- /dev/null +++ b/pkgs/standards/swarmauri_tool_matplotlib/tests/unit/MatplotlibTool_unit_test.py @@ -0,0 +1,77 @@ +import os +import pytest +from swarmauri_tool_matplotlib.MatplotlibTool import MatplotlibTool as Tool + + +@pytest.mark.unit +def test_ubc_resource(): + tool = Tool() + assert tool.resource == "Tool" + + +@pytest.mark.unit +def test_ubc_type(): + assert Tool().type == "MatplotlibTool" + + +@pytest.mark.unit +def test_initialization(): + tool = Tool() + assert type(tool.id) == str + + +@pytest.mark.unit +def test_serialization(): + tool = Tool() + assert tool.id == Tool.model_validate_json(tool.model_dump_json()).id + + +@pytest.mark.parametrize( + "plot_type, x_data, y_data, title, x_label, y_label, save_path", + [ + ( + "line", + [1, 2, 3], + [4, 5, 6], + "Line Plot", + "X-axis", + "Y-axis", + "test_line_plot.png", + ), + ( + "bar", + [1, 2, 3], + [4, 5, 6], + "Bar Plot", + "X-axis", + "Y-axis", + "test_bar_plot.png", + ), + ( + "scatter", + [1, 2, 3], + [4, 5, 6], + "Scatter Plot", + "X-axis", + "Y-axis", + "test_scatter_plot.png", + ), + ], +) +@pytest.mark.unit +def test_call(plot_type, x_data, y_data, title, x_label, y_label, save_path): + tool = Tool() + expected_keys = {"img_path", "img_base64", "data"} + + result = tool(plot_type, x_data, y_data, title, x_label, y_label, save_path) + + assert isinstance(result, dict), f"Expected dict, but got {type(result).__name__}" + assert expected_keys.issubset( + result.keys() + ), f"Expected keys {expected_keys} but got {result.keys()}" + assert isinstance( + result.get("data"), list + ), f"Expected list, but got {type(result).__name__}" + assert os.path.exists(save_path) + + os.remove(save_path) diff --git a/pkgs/standards/swarmauri_vectorstore_doc2vec/README.md b/pkgs/standards/swarmauri_vectorstore_doc2vec/README.md new file mode 100644 index 00000000..24ded9c4 --- /dev/null +++ b/pkgs/standards/swarmauri_vectorstore_doc2vec/README.md @@ -0,0 +1 @@ +# Swarmauri Example Plugin \ No newline at end of file diff --git a/pkgs/standards/swarmauri_doc2vec_vectorstore/pyproject.toml b/pkgs/standards/swarmauri_vectorstore_doc2vec/pyproject.toml similarity index 89% rename from pkgs/standards/swarmauri_doc2vec_vectorstore/pyproject.toml rename to pkgs/standards/swarmauri_vectorstore_doc2vec/pyproject.toml index 3e6b7e4e..943d6c8e 100644 --- a/pkgs/standards/swarmauri_doc2vec_vectorstore/pyproject.toml +++ b/pkgs/standards/swarmauri_vectorstore_doc2vec/pyproject.toml @@ -1,5 +1,5 @@ [tool.poetry] -name = "swarmauri_doc2vec_vectorstore" +name = "swarmauri_vectorstore_doc2vec" version = "0.6.0.dev1" description = "A Doc2Vec based Vector Store and Doc2Vec Based Embedding Model." authors = ["Jacob Stewart "] @@ -52,7 +52,7 @@ log_cli_date_format = "%Y-%m-%d %H:%M:%S" asyncio_default_fixture_loop_scope = "function" [tool.poetry.plugins."swarmauri.vector_stores"] -Doc2VecVectorStore = "swarmauri_doc2vec_vectorstore:Doc2VecVectorStore" +Doc2VecVectorStore = "swarmauri_vectorstore_doc2vec:Doc2VecVectorStore" [tool.poetry.plugins."swarmauri.embeddings"] -Doc2VecEmbedding = "swarmauri_doc2vec_vectorstore:Doc2VecEmbedding" +Doc2VecEmbedding = "swarmauri_vectorstore_doc2vec:Doc2VecEmbedding" diff --git a/pkgs/standards/swarmauri_doc2vec_vectorstore/swarmauri_doc2vec_vectorstore/Doc2VecEmbedding.py b/pkgs/standards/swarmauri_vectorstore_doc2vec/swarmauri_vectorstore_doc2vec/Doc2VecEmbedding.py similarity index 100% rename from pkgs/standards/swarmauri_doc2vec_vectorstore/swarmauri_doc2vec_vectorstore/Doc2VecEmbedding.py rename to pkgs/standards/swarmauri_vectorstore_doc2vec/swarmauri_vectorstore_doc2vec/Doc2VecEmbedding.py diff --git a/pkgs/standards/swarmauri_doc2vec_vectorstore/swarmauri_doc2vec_vectorstore/Doc2VecVectorStore.py b/pkgs/standards/swarmauri_vectorstore_doc2vec/swarmauri_vectorstore_doc2vec/Doc2VecVectorStore.py similarity index 100% rename from pkgs/standards/swarmauri_doc2vec_vectorstore/swarmauri_doc2vec_vectorstore/Doc2VecVectorStore.py rename to pkgs/standards/swarmauri_vectorstore_doc2vec/swarmauri_vectorstore_doc2vec/Doc2VecVectorStore.py diff --git a/pkgs/standards/swarmauri_doc2vec_vectorstore/swarmauri_doc2vec_vectorstore/__init__.py b/pkgs/standards/swarmauri_vectorstore_doc2vec/swarmauri_vectorstore_doc2vec/__init__.py similarity index 100% rename from pkgs/standards/swarmauri_doc2vec_vectorstore/swarmauri_doc2vec_vectorstore/__init__.py rename to pkgs/standards/swarmauri_vectorstore_doc2vec/swarmauri_vectorstore_doc2vec/__init__.py diff --git a/pkgs/standards/swarmauri_doc2vec_vectorstore/tests/unit/Doc2VecEmbedding_unit_test.py b/pkgs/standards/swarmauri_vectorstore_doc2vec/tests/unit/Doc2VecEmbedding_unit_test.py similarity index 91% rename from pkgs/standards/swarmauri_doc2vec_vectorstore/tests/unit/Doc2VecEmbedding_unit_test.py rename to pkgs/standards/swarmauri_vectorstore_doc2vec/tests/unit/Doc2VecEmbedding_unit_test.py index bb5e37d5..1164c3fb 100644 --- a/pkgs/standards/swarmauri_doc2vec_vectorstore/tests/unit/Doc2VecEmbedding_unit_test.py +++ b/pkgs/standards/swarmauri_vectorstore_doc2vec/tests/unit/Doc2VecEmbedding_unit_test.py @@ -1,5 +1,5 @@ import pytest -from swarmauri_doc2vec_vectorstore.Doc2VecEmbedding import Doc2VecEmbedding +from swarmauri_vectorstore_doc2vec.Doc2VecEmbedding import Doc2VecEmbedding @pytest.mark.unit diff --git a/pkgs/standards/swarmauri_doc2vec_vectorstore/tests/unit/Doc2VecVectorStore_unit_test.py b/pkgs/standards/swarmauri_vectorstore_doc2vec/tests/unit/Doc2VecVectorStore_unit_test.py similarity index 96% rename from pkgs/standards/swarmauri_doc2vec_vectorstore/tests/unit/Doc2VecVectorStore_unit_test.py rename to pkgs/standards/swarmauri_vectorstore_doc2vec/tests/unit/Doc2VecVectorStore_unit_test.py index e846f5c7..8f0d9ca7 100644 --- a/pkgs/standards/swarmauri_doc2vec_vectorstore/tests/unit/Doc2VecVectorStore_unit_test.py +++ b/pkgs/standards/swarmauri_vectorstore_doc2vec/tests/unit/Doc2VecVectorStore_unit_test.py @@ -1,6 +1,6 @@ import pytest from swarmauri_standard.documents.Document import Document -from swarmauri_doc2vec_vectorstore.Doc2VecVectorStore import Doc2VecVectorStore +from swarmauri_vectorstore_doc2vec.Doc2VecVectorStore import Doc2VecVectorStore @pytest.mark.unit diff --git a/pkgs/swarmauri/pyproject.toml b/pkgs/swarmauri/pyproject.toml index 743bd96e..1590c09b 100644 --- a/pkgs/swarmauri/pyproject.toml +++ b/pkgs/swarmauri/pyproject.toml @@ -50,6 +50,7 @@ full = [ "matplotlib" ] doc2vecvectorstore = ["swarmauri_doc2vec_vectorstore"] +matplotlib_tool = ["swarmauri_tool_matplotlib"] [tool.setuptools] namespace_packages = ["swarmauri"] diff --git a/pkgs/swarmauri/swarmauri/plugin_citizenship_registry.py b/pkgs/swarmauri/swarmauri/plugin_citizenship_registry.py index a6cd514e..00f19d41 100644 --- a/pkgs/swarmauri/swarmauri/plugin_citizenship_registry.py +++ b/pkgs/swarmauri/swarmauri/plugin_citizenship_registry.py @@ -174,8 +174,8 @@ class PluginCitizenshipRegistry: "swarmauri.tools.GunningFogTool": "swarmauri_standard.tools.GunningFogTool", "swarmauri.tools.ImportMemoryModuleTool": "swarmauri_standard.tools.ImportMemoryModuleTool", "swarmauri.tools.JSONRequestsTool": "swarmauri_standard.tools.JSONRequestsTool", - "swarmauri.tools.MatplotlibCsvTool": "swarmauri_standard.tools.MatplotlibCsvTool", - "swarmauri.tools.MatplotlibTool": "swarmauri_standard.tools.MatplotlibTool", + # "swarmauri.tools.MatplotlibCsvTool": "swarmauri_standard.tools.MatplotlibCsvTool", + # "swarmauri.tools.MatplotlibTool": "swarmauri_standard.tools.MatplotlibTool", "swarmauri.tools.Parameter": "swarmauri_standard.tools.Parameter", "swarmauri.tools.RequestsTool": "swarmauri_standard.tools.RequestsTool", "swarmauri.tools.SentenceComplexityTool": "swarmauri_standard.tools.SentenceComplexityTool", @@ -222,7 +222,10 @@ class PluginCitizenshipRegistry: "swarmauri.vector_stores.TfidfVectorStore": "swarmauri_standard.vector_stores.TfidfVectorStore", "swarmauri.vectors.Vector": "swarmauri_standard.vectors.Vector", # extra - "swarmauri.vector_stores.Doc2vecVectorStore": "swarmauri_doc2vec_vectorstore.vector_stores.Doc2vecVectorStore", + "swarmauri.vector_stores.Doc2vecVectorStore": "swarmauri_vectorstore_doc2vec.Doc2vecVectorStore", + "swarmauri.vector_stores.Doc2VecEmbedding": "swarmauri_vectorstore_doc2vec.Doc2VecEmbedding", + "swarmauri.tools.MatplotlibCsvTool": "swarmauri_tool_matplotlib.MatplotlibCsvTool", + "swarmauri.tools.MatplotlibTool": "swarmauri_tool_matplotlib.MatplotlibTool", } SECOND_CLASS_REGISTRY: Dict[str, str] = {} THIRD_CLASS_REGISTRY: Dict[str, str] = {}