From 5bf73517498aa6d9590297461153b89846eb9070 Mon Sep 17 00:00:00 2001 From: MichaelDecent Date: Thu, 9 Jan 2025 09:37:17 +0100 Subject: [PATCH 01/12] Add dependencies to various packages in pyproject.toml files --- pkgs/standards/swarmauri_distance_minkowski/pyproject.toml | 4 ++++ pkgs/standards/swarmauri_embedding_nmf/pyproject.toml | 4 ++++ .../swarmauri_parser_beautifulsoupelement/pyproject.toml | 3 +++ .../swarmauri_parser_keywordextractor/pyproject.toml | 3 +++ pkgs/standards/swarmauri_standard/pyproject.toml | 2 +- pkgs/standards/swarmauri_tool_matplotlib/pyproject.toml | 3 +++ pkgs/standards/swarmauri_vectorstore_doc2vec/pyproject.toml | 2 +- pkgs/standards/swarmauri_vectorstore_tfidf/pyproject.toml | 3 +++ 8 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pkgs/standards/swarmauri_distance_minkowski/pyproject.toml b/pkgs/standards/swarmauri_distance_minkowski/pyproject.toml index 1f629e91..387d9a7c 100644 --- a/pkgs/standards/swarmauri_distance_minkowski/pyproject.toml +++ b/pkgs/standards/swarmauri_distance_minkowski/pyproject.toml @@ -20,6 +20,10 @@ python = ">=3.10,<3.13" swarmauri_core = { path = "../../core" } swarmauri_base = { path = "../../base" } +# Dependencies +scipy = ">=1.7.0,<1.14.0" + + [tool.poetry.group.dev.dependencies] flake8 = "^7.0" pytest = "^8.0" diff --git a/pkgs/standards/swarmauri_embedding_nmf/pyproject.toml b/pkgs/standards/swarmauri_embedding_nmf/pyproject.toml index 75e64f6c..b21023c3 100644 --- a/pkgs/standards/swarmauri_embedding_nmf/pyproject.toml +++ b/pkgs/standards/swarmauri_embedding_nmf/pyproject.toml @@ -20,6 +20,10 @@ python = ">=3.10,<3.13" swarmauri_core = { path = "../../core" } swarmauri_base = { path = "../../base" } +# Dependencies +scikit-learn = "^1.4.2" + + [tool.poetry.group.dev.dependencies] flake8 = "^7.0" pytest = "^8.0" diff --git a/pkgs/standards/swarmauri_parser_beautifulsoupelement/pyproject.toml b/pkgs/standards/swarmauri_parser_beautifulsoupelement/pyproject.toml index 0fe832f2..5eeca833 100644 --- a/pkgs/standards/swarmauri_parser_beautifulsoupelement/pyproject.toml +++ b/pkgs/standards/swarmauri_parser_beautifulsoupelement/pyproject.toml @@ -20,6 +20,9 @@ python = ">=3.10,<3.13" swarmauri_core = { path = "../../core" } swarmauri_base = { path = "../../base" } +# Dependencies +beautifulsoup4 = "04.12.3" + [tool.poetry.group.dev.dependencies] flake8 = "^7.0" pytest = "^8.0" diff --git a/pkgs/standards/swarmauri_parser_keywordextractor/pyproject.toml b/pkgs/standards/swarmauri_parser_keywordextractor/pyproject.toml index 4baa1e0e..d262ad2f 100644 --- a/pkgs/standards/swarmauri_parser_keywordextractor/pyproject.toml +++ b/pkgs/standards/swarmauri_parser_keywordextractor/pyproject.toml @@ -20,6 +20,9 @@ python = ">=3.10,<3.13" swarmauri_core = { path = "../../core" } swarmauri_base = { path = "../../base" } +# Dependencies +yake = "==0.4.8" + [tool.poetry.group.dev.dependencies] flake8 = "^7.0" pytest = "^8.0" diff --git a/pkgs/standards/swarmauri_standard/pyproject.toml b/pkgs/standards/swarmauri_standard/pyproject.toml index 29f64b88..206afab4 100644 --- a/pkgs/standards/swarmauri_standard/pyproject.toml +++ b/pkgs/standards/swarmauri_standard/pyproject.toml @@ -46,7 +46,7 @@ aiofiles = { version = "24.1.0", optional = true } #nltk = { version = "^3.9.1", optional = true } #textblob = { version = "^0.18.0", optional = true } #yake = { version = "==0.4.8", optional = true } -beautifulsoup4 = { version = "04.12.3", optional = true } +#beautifulsoup4 = { version = "04.12.3", optional = true } #gensim = { version = "==4.3.3", optional = true } #scipy = { version = ">=1.7.0,<1.14.0", optional = true } #scikit-learn = { version = "^1.4.2", optional = true } diff --git a/pkgs/standards/swarmauri_tool_matplotlib/pyproject.toml b/pkgs/standards/swarmauri_tool_matplotlib/pyproject.toml index e70bc0dd..13c5f6db 100644 --- a/pkgs/standards/swarmauri_tool_matplotlib/pyproject.toml +++ b/pkgs/standards/swarmauri_tool_matplotlib/pyproject.toml @@ -20,6 +20,9 @@ python = ">=3.10,<3.13" swarmauri_core = { path = "../../core" } swarmauri_base = { path = "../../base" } +# Dependencies +matplotlib = ">=3.9.2" + [tool.poetry.group.dev.dependencies] flake8 = "^7.0" pytest = "^8.0" diff --git a/pkgs/standards/swarmauri_vectorstore_doc2vec/pyproject.toml b/pkgs/standards/swarmauri_vectorstore_doc2vec/pyproject.toml index 943d6c8e..d474118f 100644 --- a/pkgs/standards/swarmauri_vectorstore_doc2vec/pyproject.toml +++ b/pkgs/standards/swarmauri_vectorstore_doc2vec/pyproject.toml @@ -20,7 +20,7 @@ python = ">=3.10,<3.13" swarmauri_core = { path = "../../core" } swarmauri_base = { path = "../../base" } -gensim = { version = "^4.3.3"} +gensim = "^4.3.3" [tool.poetry.group.dev.dependencies] flake8 = "^7.0" diff --git a/pkgs/standards/swarmauri_vectorstore_tfidf/pyproject.toml b/pkgs/standards/swarmauri_vectorstore_tfidf/pyproject.toml index 08697987..567e85cc 100644 --- a/pkgs/standards/swarmauri_vectorstore_tfidf/pyproject.toml +++ b/pkgs/standards/swarmauri_vectorstore_tfidf/pyproject.toml @@ -20,6 +20,9 @@ python = ">=3.10,<3.13" swarmauri_core = { path = "../../core" } swarmauri_base = { path = "../../base" } +# Dependencies +scikit-learn = "^1.4.2" + [tool.poetry.group.dev.dependencies] flake8 = "^7.0" pytest = "^8.0" From 22d55c2db57679468f247026b036b339ee838318 Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Thu, 9 Jan 2025 03:09:59 -0600 Subject: [PATCH 02/12] base - reorg --- pkgs/base/swarmauri_base/distances/DistanceBase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/base/swarmauri_base/distances/DistanceBase.py b/pkgs/base/swarmauri_base/distances/DistanceBase.py index ab5a68f9..21627b6f 100644 --- a/pkgs/base/swarmauri_base/distances/DistanceBase.py +++ b/pkgs/base/swarmauri_base/distances/DistanceBase.py @@ -4,8 +4,8 @@ from typing import List, Optional, Literal from pydantic import Field -from swarmauri_core.distances.IDistanceSimilarity import IDistanceSimilarity from swarmauri_standard.vectors.Vector import Vector +from swarmauri_core.distances.IDistanceSimilarity import IDistanceSimilarity from swarmauri_core.ComponentBase import ComponentBase, ResourceTypes @ComponentBase.register_model() From 42b635ca12c5a625df273cdcbbb6116e0b8b6c21 Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Thu, 9 Jan 2025 03:10:07 -0600 Subject: [PATCH 03/12] base - fix typo --- pkgs/base/swarmauri_base/agents/AgentBase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/base/swarmauri_base/agents/AgentBase.py b/pkgs/base/swarmauri_base/agents/AgentBase.py index e99204f2..e6fa0c31 100644 --- a/pkgs/base/swarmauri_base/agents/AgentBase.py +++ b/pkgs/base/swarmauri_base/agents/AgentBase.py @@ -13,4 +13,4 @@ class AgentBase(IAgent, ComponentBase): type: Literal['AgentBase'] = 'AgentBase' def exec(self, input_str: Optional[Union[str, IMessage]] = "", llm_kwargs: Optional[Dict] = {}) -> Any: - raise NotImplementedError('The `exec` function has not been implemeneted on this class.') \ No newline at end of file + raise NotImplementedError('The `exec` function has not been implemented on this class.') \ No newline at end of file From 5ab73f7f6f22cd27e8f45f9450df1b695de3cfe1 Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Thu, 9 Jan 2025 03:41:36 -0600 Subject: [PATCH 04/12] standard - add default --- .../swarmauri_standard/swarmauri_standard/agents/RagAgent.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/standards/swarmauri_standard/swarmauri_standard/agents/RagAgent.py b/pkgs/standards/swarmauri_standard/swarmauri_standard/agents/RagAgent.py index 6defb403..43da8bbd 100644 --- a/pkgs/standards/swarmauri_standard/swarmauri_standard/agents/RagAgent.py +++ b/pkgs/standards/swarmauri_standard/swarmauri_standard/agents/RagAgent.py @@ -30,7 +30,7 @@ class RagAgent(AgentRetrieveMixin, llm: SubclassUnion[LLMBase] conversation: Union[MaxSystemContextConversation, SessionCacheConversation] = MaxSystemContextConversation(system_context="") vector_store: SubclassUnion[VectorStoreBase] - system_context: Union[SystemMessage, str] + system_context: Union[SystemMessage, str] = SystemMessage(content="") type: Literal['RagAgent'] = 'RagAgent' def _create_preamble_context(self): From ae1f7493099b900267fd956f825b218547749c01 Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Thu, 9 Jan 2025 03:42:03 -0600 Subject: [PATCH 05/12] standard - utilize SubclassUnion[ConversationBase] --- .../swarmauri_standard/swarmauri_standard/agents/QAAgent.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/standards/swarmauri_standard/swarmauri_standard/agents/QAAgent.py b/pkgs/standards/swarmauri_standard/swarmauri_standard/agents/QAAgent.py index 5074d7e6..1d4bb073 100644 --- a/pkgs/standards/swarmauri_standard/swarmauri_standard/agents/QAAgent.py +++ b/pkgs/standards/swarmauri_standard/swarmauri_standard/agents/QAAgent.py @@ -5,11 +5,12 @@ from swarmauri_base.llms.LLMBase import LLMBase from swarmauri_base.agents.AgentBase import AgentBase +from swarmauri_base.conversations.ConversationBase import ConversationBase from swarmauri_core.ComponentBase import SubclassUnion, ComponentBase @ComponentBase.register_type(AgentBase, 'QAAgent') class QAAgent(AgentBase): - conversation: MaxSystemContextConversation = MaxSystemContextConversation(max_size=2) + conversation: SubclassUnion[ConversationBase] = MaxSystemContextConversation(max_size=2) type: Literal['QAAgent'] = 'QAAgent' def exec(self, From 59364af9328a0db70263c6c8fceb8574779a3f09 Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Thu, 9 Jan 2025 04:05:17 -0600 Subject: [PATCH 06/12] core - add support for model_validate_yaml() --- pkgs/core/pyproject.toml | 1 + pkgs/core/swarmauri_core/ComponentBase.py | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/pkgs/core/pyproject.toml b/pkgs/core/pyproject.toml index d6f2e95e..63edba59 100644 --- a/pkgs/core/pyproject.toml +++ b/pkgs/core/pyproject.toml @@ -16,6 +16,7 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.10,<3.13" pydantic = "^2.0" +pyyaml = "^6.0.2" #numpy = "==1.26.4" #requests = "^2.0" diff --git a/pkgs/core/swarmauri_core/ComponentBase.py b/pkgs/core/swarmauri_core/ComponentBase.py index ce6ffaab..33dbaedd 100644 --- a/pkgs/core/swarmauri_core/ComponentBase.py +++ b/pkgs/core/swarmauri_core/ComponentBase.py @@ -3,6 +3,7 @@ import hashlib import inspect import json +import yaml import logging from enum import Enum from threading import Lock @@ -490,3 +491,16 @@ def recreate_models(cls): except Exception as e: logger.error(f"Error while rebuilding model '{model_class.__name__}': {e}") logger.info("All models have been successfully recreated.") + + @classmethod + def model_validate_yaml(cls, yaml_data: str): + try: + # Parse YAML into a Python dictionary + yaml_content = yaml.safe_load(yaml_data) + + # Convert the dictionary to JSON and validate using Pydantic + return cls.model_validate_json(json.dumps(yaml_content)) + except yaml.YAMLError as e: + raise ValueError(f"Invalid YAML data: {e}") + except ValidationError as e: + raise ValueError(f"Validation failed: {e}") \ No newline at end of file From db7fcbcf2ec1103d2471d9efba90171588ae28d7 Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Thu, 9 Jan 2025 04:11:13 -0600 Subject: [PATCH 07/12] core - implementing model_dump_yaml() --- pkgs/core/swarmauri_core/ComponentBase.py | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pkgs/core/swarmauri_core/ComponentBase.py b/pkgs/core/swarmauri_core/ComponentBase.py index 33dbaedd..91b467c2 100644 --- a/pkgs/core/swarmauri_core/ComponentBase.py +++ b/pkgs/core/swarmauri_core/ComponentBase.py @@ -503,4 +503,26 @@ def model_validate_yaml(cls, yaml_data: str): except yaml.YAMLError as e: raise ValueError(f"Invalid YAML data: {e}") except ValidationError as e: - raise ValueError(f"Validation failed: {e}") \ No newline at end of file + raise ValueError(f"Validation failed: {e}") + + def model_dump_yaml(self, fields_to_exclude=None): + if fields_to_exclude is None: + fields_to_exclude = [] + + # Load the JSON string into a Python dictionary + json_data = json.loads(self.json()) + + # Function to recursively remove specific keys + def remove_fields(data, fields_to_exclude): + if isinstance(data, dict): + return {key: remove_fields(value, fields_to_exclude) for key, value in data.items() if key not in fields_to_exclude} + elif isinstance(data, list): + return [remove_fields(item, fields_to_exclude) for item in data] + else: + return data + + # Filter the JSON data + filtered_data = remove_fields(json_data, fields_to_exclude) + + # Convert the filtered data into YAML + return yaml.dump(filtered_data, default_flow_style=False) \ No newline at end of file From f62fd91efa9f3681cafc2035f57a5f14b358d661 Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Thu, 9 Jan 2025 06:32:26 -0600 Subject: [PATCH 08/12] Update ComponentBase.py --- pkgs/core/swarmauri_core/ComponentBase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/core/swarmauri_core/ComponentBase.py b/pkgs/core/swarmauri_core/ComponentBase.py index 91b467c2..af7f7ed8 100644 --- a/pkgs/core/swarmauri_core/ComponentBase.py +++ b/pkgs/core/swarmauri_core/ComponentBase.py @@ -510,7 +510,7 @@ def model_dump_yaml(self, fields_to_exclude=None): fields_to_exclude = [] # Load the JSON string into a Python dictionary - json_data = json.loads(self.json()) + json_data = json.loads(self.model_dump_json()) # Function to recursively remove specific keys def remove_fields(data, fields_to_exclude): From cd834045474a042d44eab9665761dbd237b99ece Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Thu, 9 Jan 2025 11:08:45 -0600 Subject: [PATCH 09/12] core - update model_dump_yaml --- pkgs/core/swarmauri_core/ComponentBase.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/pkgs/core/swarmauri_core/ComponentBase.py b/pkgs/core/swarmauri_core/ComponentBase.py index af7f7ed8..b1d4da75 100644 --- a/pkgs/core/swarmauri_core/ComponentBase.py +++ b/pkgs/core/swarmauri_core/ComponentBase.py @@ -505,24 +505,27 @@ def model_validate_yaml(cls, yaml_data: str): except ValidationError as e: raise ValueError(f"Validation failed: {e}") - def model_dump_yaml(self, fields_to_exclude=None): + def model_dump_yaml(self, fields_to_exclude=None, api_key_placeholder=None): if fields_to_exclude is None: fields_to_exclude = [] # Load the JSON string into a Python dictionary - json_data = json.loads(self.model_dump_json()) + json_data = json.loads(self.json()) - # Function to recursively remove specific keys - def remove_fields(data, fields_to_exclude): + # Function to recursively remove specific keys and handle api_key placeholders + def process_fields(data, fields_to_exclude): if isinstance(data, dict): - return {key: remove_fields(value, fields_to_exclude) for key, value in data.items() if key not in fields_to_exclude} + return { + key: (api_key_placeholder if key == "api_key" and api_key_placeholder is not None else process_fields(value, fields_to_exclude)) + for key, value in data.items() if key not in fields_to_exclude + } elif isinstance(data, list): - return [remove_fields(item, fields_to_exclude) for item in data] + return [process_fields(item, fields_to_exclude) for item in data] else: return data # Filter the JSON data - filtered_data = remove_fields(json_data, fields_to_exclude) + filtered_data = process_fields(json_data, fields_to_exclude) # Convert the filtered data into YAML return yaml.dump(filtered_data, default_flow_style=False) \ No newline at end of file From f35aa8012cd2f7f0e3b35e2f4abe904bf6b492da Mon Sep 17 00:00:00 2001 From: MichaelDecent Date: Thu, 9 Jan 2025 18:52:36 +0100 Subject: [PATCH 10/12] feat: add Swarmauri GitHub Tool package with README, configuration, and unit tests --- .../swarmauri_community/pyproject.toml | 2 +- .../swarmauri_tool_communitygithub/README.md | 1 + .../pyproject.toml | 63 ++ .../GithubBranchTool.py | 106 ++++ .../GithubCommitTool.py | 141 +++++ .../GithubIssueTool.py | 117 ++++ .../GithubPRTool.py | 131 ++++ .../GithubRepoTool.py | 104 +++ .../GithubTool.py | 594 ++++++++++++++++++ .../__init__.py | 20 + .../tests/unit/GithubBranchTool_test.py | 107 ++++ .../tests/unit/GithubCommitTool_test.py | 106 ++++ .../tests/unit/GithubIssueTool_test.py | 100 +++ .../tests/unit/GithubPRTool_test.py | 93 +++ .../tests/unit/GithubRepoTool_test.py | 93 +++ .../tests/unit/GithubTool_test.py | 205 ++++++ 16 files changed, 1982 insertions(+), 1 deletion(-) create mode 100644 pkgs/community/swarmauri_tool_communitygithub/README.md create mode 100644 pkgs/community/swarmauri_tool_communitygithub/pyproject.toml create mode 100644 pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubBranchTool.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubCommitTool.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubIssueTool.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubPRTool.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubRepoTool.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubTool.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/__init__.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubBranchTool_test.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubCommitTool_test.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubIssueTool_test.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubPRTool_test.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubRepoTool_test.py create mode 100644 pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubTool_test.py diff --git a/pkgs/community/swarmauri_community/pyproject.toml b/pkgs/community/swarmauri_community/pyproject.toml index dfccb914..5fc8ac27 100644 --- a/pkgs/community/swarmauri_community/pyproject.toml +++ b/pkgs/community/swarmauri_community/pyproject.toml @@ -31,7 +31,7 @@ captcha = "^0.6.0" #gensim = { version = "^4.3.3", optional = true } #gradio = { version = "^5.4.0", optional = true } leptonai = { version = "^0.22.0", optional = true } -neo4j = { version = "^5.25.0", optional = true } +#neo4j = { version = "^5.25.0", optional = true } nltk = { version = "^3.9.1", optional = true } pandas = "^2.2.3" psutil = { version = "^6.1.0", optional = true } diff --git a/pkgs/community/swarmauri_tool_communitygithub/README.md b/pkgs/community/swarmauri_tool_communitygithub/README.md new file mode 100644 index 00000000..cd26902a --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/README.md @@ -0,0 +1 @@ +# Swarmauri Example Community Package \ No newline at end of file diff --git a/pkgs/community/swarmauri_tool_communitygithub/pyproject.toml b/pkgs/community/swarmauri_tool_communitygithub/pyproject.toml new file mode 100644 index 00000000..b68091bc --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/pyproject.toml @@ -0,0 +1,63 @@ +[tool.poetry] +name = "swarmauri_tool_communitygithub" +version = "0.6.0.dev1" +description = "Github Tool" +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" } + +# Dependencies +pygithub = "^2.4.0" + + + +[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"] +GithubBranchTool = "swarmauri_tool_communitygithub.GithubBranchTool" +GithubCommitTool = "swarmauri_tool_communitygithub.GithubCommitTool" +GithubIssueTool = "swarmauri_tool_communitygithub.GithubIssueTool" +GithubPRTool = "swarmauri_tool_communitygithub.GithubPRTool" +GithubTool = "swarmauri_tool_communitygithub.GithubTool" +GithubRepoTool = "swarmauri_tool_communitygithub.GithubRepoTool" diff --git a/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubBranchTool.py b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubBranchTool.py new file mode 100644 index 00000000..3ab36aff --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubBranchTool.py @@ -0,0 +1,106 @@ +# swarmauri/community/tools/concrete/GithubCommunityTool.py + +from github import Github, GithubException +from typing import List, Dict, Literal, Any +from pydantic import Field, ConfigDict +from swarmauri_base.tools.ToolBase import ToolBase +from swarmauri_standard.tool.Parameter import Parameter + + +class GithubBranchTool(ToolBase): + version: str = "1.1.0" + parameters: List[Parameter] = Field( + default_factory=lambda: [ + Parameter( + name="action", + type="string", + description="The action to perform on the GitHub API, e.g., 'create_repo', 'delete_repo', 'create_issue', etc.", + required=True, + ), + Parameter( + name="repo_name", + type="string", + description="The full name of the repository to interact with, e.g. 'owner/repository'.", + required=True, + ), + Parameter( + name="branch_name", + type="string", + description="The name of the branch to interact with.", + required=False, + ), + Parameter( + name="source_branch", + type="string", + description="The name of the source branch to create a branch from.", + required=False, + ), + ] + ) + name: str = "GithubBranchTool" + description: str = "Interacts with GitHub branches using PyGithub." + type: Literal["GithubBranchTool"] = "GithubBranchTool" + token: str + model_config = ConfigDict(arbitrary_types_allowed=True) + + def __call__(self, action: str, **kwargs) -> Dict[str, Any]: + """ + Central method to call various GitHub API actions. + + Args: + action (str): The action to perform. + **kwargs: Additional keyword arguments related to the action. + + Returns: + Dict[str, Any]: The result of the action. + """ + action_map = { + "create_branch": self.create_branch, + "delete_branch": self.delete_branch, + "list_branches": self.list_branches, + "get_branch": self.get_branch, + } + + if action in action_map: + self._github = Github(self.token) + return {action: action_map[action](**kwargs)} + + raise ValueError(f"Action '{action}' is not supported.") + + # Branch Management Methods + def create_branch( + self, repo_name: str, branch_name: str, source: str = "main" + ) -> str: + try: + repo = self._github.get_repo(repo_name) + source_branch = repo.get_branch(source) + repo.create_git_ref( + ref=f"refs/heads/{branch_name}", sha=source_branch.commit.sha + ) + return f"Branch '{branch_name}' created successfully." + except GithubException as e: + return f"Error creating branch: {e}" + + def delete_branch(self, repo_name: str, branch_name: str) -> str: + try: + repo = self._github.get_repo(repo_name) + ref = repo.get_git_ref(f"heads/{branch_name}") + ref.delete() + return f"Branch '{branch_name}' deleted successfully." + except GithubException as e: + return f"Error deleting branch: {e}" + + def list_branches(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [branch.name for branch in repo.get_branches()] + except GithubException as e: + return f"Error listing branches: {e}" + + def get_branch(self, repo_name: str, branch_name: str) -> str: + try: + repo = self._github.get_repo(repo_name) + branch = repo.get_branch(branch_name) + return f"Branch {branch.name}: {branch.commit.sha}" + except GithubException as e: + return f"Error retrieving branch: {e}" diff --git a/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubCommitTool.py b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubCommitTool.py new file mode 100644 index 00000000..fd69b3be --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubCommitTool.py @@ -0,0 +1,141 @@ +# swarmauri/standard/tools/concrete/GithubTool.py + +from github import Github, GithubException +from typing import List, Dict, Literal, Any +from pydantic import Field, ConfigDict +from swarmauri_base.tools.ToolBase import ToolBase +from swarmauri_standard.tool.Parameter import Parameter + + +class GithubCommitTool(ToolBase): + version: str = "1.1.0" + parameters: List[Parameter] = Field( + default_factory=lambda: [ + Parameter( + name="action", + type="string", + description="The action to perform on the GitHub API, e.g., 'create_repo', 'delete_repo', 'create_issue', etc.", + required=True, + ), + Parameter( + name="repo_name", + type="string", + description="The full name of the repository to interact with, e.g. 'owner/repository'.", + required=True, + ), + Parameter( + name="file_path", + type="string", + description="The path to the file in the repository, e.g. 'path/to/file.txt'.", + required=False, + ), + Parameter( + name="message", + type="string", + description=".", + required=False, + ), + Parameter( + name="content", + type="string", + description="The name of the branch to interact with.", + required=False, + ), + Parameter( + name="branch_name", + type="string", + description="The name of the branch to interact with.", + required=False, + ), + Parameter( + name="sha", + type="string", + description="The sha of the commit to interact with.", + required=False, + ), + Parameter( + name="base", + type="string", + description="The base of the commit to interact with.", + required=False, + ), + Parameter( + name="head", + type="string", + description="The head of the commit to interact with.", + required=False, + ), + ] + ) + name: str = "GithubCommitTool" + description: str = ( + "Interacts with GitHub repositories using PyGithub to submit commits." + ) + type: Literal["GithubCommitTool"] = "GithubCommitTool" + token: str + model_config = ConfigDict(arbitrary_types_allowed=True) + + def __call__(self, action: str, **kwargs) -> Dict[str, Any]: + """ + Central method to call various GitHub API actions. + + Args: + action (str): The action to perform. + **kwargs: Additional keyword arguments related to the action. + + Returns: + Dict[str, Any]: The result of the action. + """ + action_map = { + "create_commit": self.create_commit, + "list_commits": self.list_commits, + "get_commit": self.get_commit, + "compare_commits": self.compare_commits, + } + + if action in action_map: + self._github = Github(self.token) + return {action: action_map[action](**kwargs)} + + raise ValueError(f"Action '{action}' is not supported.") + + # Commit Management Methods + def create_commit( + self, + repo_name: str, + file_path: str, + message: str, + content: str, + branch: str = "main", + ) -> str: + try: + repo = self._github.get_repo(repo_name) + repo.create_file( + path=file_path, message=message, content=content, branch=branch + ) + return f"Commit created successfully at {file_path}." + except GithubException as e: + return f"Error creating commit: {e}" + + def list_commits(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [commit.commit.message for commit in repo.get_commits()] + except GithubException as e: + return f"Error listing commits: {e}" + + def get_commit(self, repo_name: str, sha: str) -> str: + try: + repo = self._github.get_repo(repo_name) + commit = repo.get_commit(sha=sha) + return f"Commit {commit.sha}: {commit.commit.message}" + except GithubException as e: + return f"Error retrieving commit: {e}" + + def compare_commits(self, repo_name: str, base: str, head: str) -> str: + try: + repo = self._github.get_repo(repo_name) + comparison = repo.compare(base, head) + return f"Comparison from {base} to {head}:\n{comparison.diff_url}" + except GithubException as e: + return f"Error comparing commits: {e}" diff --git a/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubIssueTool.py b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubIssueTool.py new file mode 100644 index 00000000..47d330b9 --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubIssueTool.py @@ -0,0 +1,117 @@ +# swarmauri/standard/tools/concrete/GithubTool.py + +from github import Github, GithubException +from typing import List, Dict, Literal, Any +from pydantic import Field, ConfigDict +from swarmauri_base.tools.ToolBase import ToolBase +from swarmauri_standard.tool.Parameter import Parameter + + +class GithubIssueTool(ToolBase): + version: str = "1.1.0" + parameters: List[Parameter] = Field( + default_factory=lambda: [ + Parameter( + name="action", + type="string", + description="The action to perform on the GitHub API, e.g., 'create_issue', 'delete_issue', 'close_issue', 'list_issues', 'get_issue'", + required=True, + ), + Parameter( + name="repo_name", + type="string", + description="The full name of the repository to interact with, e.g. 'owner/repository'.", + required=False, + ), + Parameter( + name="title", + type="string", + description="Title of the issue to create", + required=False, + ), + Parameter( + name="body", + type="string", + description="Body of the issue to create", + required=False, + ), + Parameter( + name="issue_number", + type="integer", + description="The number of the issue to interact with.", + required=False, + ), + ] + ) + name: str = "GithubIssueTool" + description: str = "Interacts with GitHub repositories using PyGithub." + type: Literal["GithubIssueTool"] = "GithubIssueTool" + token: str + model_config = ConfigDict(arbitrary_types_allowed=True) + + def __call__(self, action: str, **kwargs) -> Dict[str, Any]: + """ + Central method to call various GitHub API actions. + + Args: + action (str): The action to perform. + **kwargs: Additional keyword arguments related to the action. + + Returns: + Dict[str, Any]: The result of the action. + """ + action_map = { + "create_issue": self.create_issue, + "close_issue": self.close_issue, + "update_issue": self.update_issue, + "list_issues": self.list_issues, + "get_issue": self.get_issue, + } + + if action in action_map: + self._github = Github(self.token) + return {action: action_map[action](**kwargs)} + + raise ValueError(f"Action '{action}' is not supported.") + + # Issue Management Methods + def create_issue(self, repo_name: str, title: str, body: str = None) -> str: + try: + repo = self._github.get_repo(repo_name) + issue = repo.create_issue(title=title, body=body) + return f"Issue '{title}' created successfully." + except GithubException as e: + return f"Error creating issue: {e}" + + def close_issue(self, repo_name: str, issue_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + issue = repo.get_issue(number=issue_number) + issue.edit(state="closed") + return f"Issue '{issue_number}' closed successfully." + except GithubException as e: + return f"Error closing issue: {e}" + + def update_issue(self, repo_name: str, issue_number: int, **kwargs) -> str: + try: + repo = self._github.get_repo(repo_name) + issue = repo.get_issue(number=issue_number) + issue.edit(**kwargs) + return f"Issue '{issue_number}' updated successfully." + except GithubException as e: + return f"Error updating issue: {e}" + + def list_issues(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [issue.title for issue in repo.get_issues(state="open")] + except GithubException as e: + return f"Error listing issues: {e}" + + def get_issue(self, repo_name: str, issue_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + issue = repo.get_issue(number=issue_number) + return f"Issue #{issue.number}: {issue.title}\n{issue.body}" + except GithubException as e: + return f"Error retrieving issue: {e}" diff --git a/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubPRTool.py b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubPRTool.py new file mode 100644 index 00000000..e67ff415 --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubPRTool.py @@ -0,0 +1,131 @@ +# swarmauri/community/tools/concrete/GithubPRTool.py + +from github import Github, GithubException +from typing import List, Dict, Literal, Any +from pydantic import Field, ConfigDict +from swarmauri_base.tools.base.ToolBase import ToolBase +from swarmauri_standard.tool.Parameter import Parameter + + +class GithubPRTool(ToolBase): + version: str = "1.1.0" + parameters: List[Parameter] = Field( + default_factory=lambda: [ + Parameter( + name="action", + type="string", + description="The action to perform on the GitHub API, e.g., 'create_pull', 'merge_pull', 'close_pull', 'get_pull', 'list_pulls' etc.", + required=True, + ), + Parameter( + name="repo_name", + type="string", + description="The full name of the repository to interact with, e.g. 'owner/repository'.", + required=False, + ), + Parameter( + name="pull_number", + type="integer", + description="The number of the pull request to interact with.", + required=False, + ), + Parameter( + name="title", + type="string", + description="The title of the pull request to create.", + required=False, + ), + Parameter( + name="head", + type="string", + description="The head branch with your changes", + required=False, + ), + Parameter( + name="base", + type="string", + description="The base branch you're merging into, typically 'main' or 'master'", + required=False, + ), + Parameter( + name="body", + type="string", + description="The description of the pull request to create.", + required=False, + ), + ] + ) + name: str = "GithubPRTool" + description: str = "Interacts with GitHub repositories using PyGithub." + type: Literal["GithubPRTool"] = "GithubPRTool" + token: str + model_config = ConfigDict(arbitrary_types_allowed=True) + + def __call__(self, action: str, **kwargs) -> Dict[str, Any]: + """ + Central method to call various GitHub API actions. + + Args: + action (str): The action to perform. + **kwargs: Additional keyword arguments related to the action. + + Returns: + Dict[str, Any]: The result of the action. + """ + action_map = { + "create_pull": self.create_pull, + "merge_pull": self.merge_pull, + "close_pull": self.close_pull, + "list_pulls": self.list_pulls, + "get_pull": self.get_pull, + } + + if action in action_map: + self._github = Github(self.token) + return {action: action_map[action](**kwargs)} + + raise ValueError(f"Action '{action}' is not supported.") + + # Pull Request Management Methods + def create_pull( + self, repo_name: str, title: str, head: str, base: str, body: str = None + ) -> str: + try: + repo = self._github.get_repo(repo_name) + pull = repo.create_pull(title=title, body=body, head=head, base=base) + return f"Pull request '{title}' created successfully." + except GithubException as e: + return f"Error creating pull request: {e}" + + def merge_pull(self, repo_name: str, pull_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + pull = repo.get_pull(number=pull_number) + pull.merge() + return f"Pull request '{pull_number}' merged successfully." + except GithubException as e: + return f"Error merging pull request: {e}" + + def close_pull(self, repo_name: str, pull_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + pull = repo.get_pull(number=pull_number) + pull.edit(state="closed") + return f"Pull request '{pull_number}' closed successfully." + except GithubException as e: + return f"Error closing pull request: {e}" + + def list_pulls(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [pr.title for pr in repo.get_pulls(state="open")] + except GithubException as e: + return f"Error listing pull requests: {e}" + + def get_pull(self, repo_name: str, pull_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + pull = repo.get_pull(number=pull_number) + return f"Pull Request #{pull.number}: {pull.title}\n{pull.body}" + except GithubException as e: + return f"Error retrieving pull request: {e}" diff --git a/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubRepoTool.py b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubRepoTool.py new file mode 100644 index 00000000..520514fa --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubRepoTool.py @@ -0,0 +1,104 @@ +# swarmauri/standard/tools/concrete/GithubTool.py + +from github import Github, GithubException +from typing import List, Dict, Literal, Any +from pydantic import Field, ConfigDict +from swarmauri_base.tools.ToolBase import ToolBase +from swarmauri_standard.tools.Parameter import Parameter + + +class GithubRepoTool(ToolBase): + version: str = "1.1.0" + parameters: List[Parameter] = Field( + default_factory=lambda: [ + Parameter( + name="action", + type="string", + description="The action to perform on the GitHub API, e.g., 'create_repo', 'delete_repo', 'update_repo', and 'get_repo'", + required=True, + ), + Parameter( + name="repo_name", + type="string", + description="The full name of the repository to interact with, e.g. 'owner/repository'.", + required=False, + ), + Parameter( + name="file_path", + type="string", + description="The path to the file in the repository, e.g. 'path/to/file.txt'.", + required=False, + ), + ] + ) + name: str = "GithubRepoTool" + description: str = "Interacts with GitHub repositories using PyGithub." + type: Literal["GithubRepoTool"] = "GithubRepoTool" + token: str + model_config = ConfigDict(arbitrary_types_allowed=True) + + def __call__(self, action: str, **kwargs) -> Dict[str, Any]: + """ + Central method to call various GitHub API actions. + + Args: + action (str): The action to perform. + **kwargs: Additional keyword arguments related to the action. + + Returns: + Dict[str, Any]: The result of the action. + """ + action_map = { + "create_repo": self.create_repo, + "delete_repo": self.delete_repo, + "get_repo": self.get_repo, + "list_repos": self.list_repos, + "update_repo": self.update_repo, + } + + if action in action_map: + self._github = Github(self.token) + return {action: action_map[action](**kwargs)} + + raise ValueError(f"Action '{action}' is not supported.") + + # Repository Management Methods + def create_repo(self, repo_name: str, private: bool = False) -> str: + try: + user = self._github.get_user() + repo = user.create_repo(repo_name, private=private) + return f"Repository '{repo_name}' created successfully." + except GithubException as e: + return f"Error creating repository: {e}" + + def delete_repo(self, repo_name: str) -> str: + try: + user = self._github.get_user() + repo = user.get_repo(repo_name) + repo.delete() + return f"Repository '{repo_name}' deleted successfully." + except GithubException as e: + return f"Error deleting repository: {e}" + + def get_repo(self, repo_name: str) -> str: + try: + repo = self._github.get_repo(repo_name) + repo_info = f"Repository: {repo.full_name}\nDescription: {repo.description}\nClone URL: {repo.clone_url}" + return repo_info + except GithubException as e: + return f"Error retrieving repository info: {e}" + + def list_repos(self) -> List[str]: + try: + user = self._github.get_user() + return [repo.full_name for repo in user.get_repos()] + except GithubException as e: + return f"Error listing repositories: {e}" + + def update_repo(self, repo_name: str, **kwargs) -> str: + try: + repo = self._github.get_repo(repo_name) + repo.edit(**kwargs) + return f"Repository '{repo_name}' updated successfully." + except GithubException as e: + return f"Error updating repository: {e}" diff --git a/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubTool.py b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubTool.py new file mode 100644 index 00000000..65d0ba78 --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/GithubTool.py @@ -0,0 +1,594 @@ +# swarmauri/standard/tools/concrete/GithubTool.py + +from github import Github, GithubException +from typing import List, Dict, Literal, Optional, Any +from pydantic import Field, ConfigDict +from swarmauri_base.tools.ToolBase import ToolBase +from swarmauri_standard.tool.Parameter import Parameter + + +class GithubTool(ToolBase): + version: str = "1.1.0" + parameters: List[Parameter] = Field( + default_factory=lambda: [ + Parameter( + name="action", + type="string", + description="The action to perform on the GitHub API, e.g., 'create_repo', 'delete_repo', 'create_issue', etc.", + required=True, + ), + Parameter( + name="repo_name", + type="string", + description="The full name of the repository to interact with, e.g. 'owner/repository'.", + required=False, + ), + Parameter( + name="file_path", + type="string", + description="The path to the file in the repository, e.g. 'path/to/file.txt'.", + required=False, + ), + Parameter( + name="issue_number", + type="integer", + description="The number of the issue to interact with.", + required=False, + ), + Parameter( + name="pull_number", + type="integer", + description="The number of the pull request to interact with.", + required=False, + ), + Parameter( + name="branch_name", + type="string", + description="The name of the branch to interact with.", + required=False, + ), + Parameter( + name="username", + type="string", + description="The GitHub username for collaborator management.", + required=False, + ), + Parameter( + name="milestone_number", + type="integer", + description="The number of the milestone to interact with.", + required=False, + ), + Parameter( + name="label_name", + type="string", + description="The name of the label to interact with.", + required=False, + ), + Parameter( + name="webhook_id", + type="integer", + description="The ID of the webhook to interact with.", + required=False, + ), + Parameter( + name="gist_id", + type="string", + description="The ID of the gist to interact with.", + required=False, + ), + ] + ) + name: str = "GithubTool" + description: str = "Interacts with GitHub repositories using PyGithub." + type: Literal["GithubTool"] = "GithubTool" + token: str + model_config = ConfigDict(arbitrary_types_allowed=True) + + def __call__(self, action: str, **kwargs) -> Dict[str, Any]: + """ + Central method to call various GitHub API actions. + + Args: + action (str): The action to perform. + **kwargs: Additional keyword arguments related to the action. + + Returns: + Dict[str, Any]: The result of the action. + """ + action_map = { + "create_repo": self.create_repo, + "delete_repo": self.delete_repo, + "get_repo": self.get_repo, + "list_repos": self.list_repos, + "update_repo": self.update_repo, + "create_issue": self.create_issue, + "close_issue": self.close_issue, + "update_issue": self.update_issue, + "list_issues": self.list_issues, + "get_issue": self.get_issue, + "create_pull": self.create_pull, + "merge_pull": self.merge_pull, + "close_pull": self.close_pull, + "list_pulls": self.list_pulls, + "get_pull": self.get_pull, + "create_commit": self.create_commit, + "list_commits": self.list_commits, + "get_commit": self.get_commit, + "compare_commits": self.compare_commits, + "create_branch": self.create_branch, + "delete_branch": self.delete_branch, + "list_branches": self.list_branches, + "get_branch": self.get_branch, + "add_collaborator": self.add_collaborator, + "remove_collaborator": self.remove_collaborator, + "list_collaborators": self.list_collaborators, + "check_collaborator": self.check_collaborator, + "create_milestone": self.create_milestone, + "close_milestone": self.close_milestone, + "update_milestone": self.update_milestone, + "list_milestones": self.list_milestones, + "get_milestone": self.get_milestone, + "create_label": self.create_label, + "delete_label": self.delete_label, + "update_label": self.update_label, + "list_labels": self.list_labels, + "get_label": self.get_label, + "create_webhook": self.create_webhook, + "delete_webhook": self.delete_webhook, + "list_webhooks": self.list_webhooks, + "get_webhook": self.get_webhook, + "create_gist": self.create_gist, + "delete_gist": self.delete_gist, + "update_gist": self.update_gist, + "list_gists": self.list_gists, + "get_gist": self.get_gist, + } + + if action in action_map: + self._github = Github(self.token) + return {action: action_map[action](**kwargs)} + + raise ValueError(f"Action '{action}' is not supported.") + + # Repository Management Methods + def create_repo(self, repo_name: str, private: bool = False) -> str: + try: + user = self._github.get_user() + repo = user.create_repo(repo_name, private=private) + return f"Repository '{repo_name}' created successfully." + except GithubException as e: + return f"Error creating repository: {e}" + + def delete_repo(self, repo_name: str) -> str: + try: + user = self._github.get_user() + repo = user.get_repo(repo_name) + repo.delete() + return f"Repository '{repo_name}' deleted successfully." + except GithubException as e: + return f"Error deleting repository: {e}" + + def get_repo(self, repo_name: str) -> str: + try: + repo = self._github.get_repo(repo_name) + repo_info = f"Repository: {repo.full_name}\nDescription: {repo.description}\nClone URL: {repo.clone_url}" + return repo_info + except GithubException as e: + return f"Error retrieving repository info: {e}" + + def list_repos(self) -> List[str]: + try: + user = self._github.get_user() + return [repo.full_name for repo in user.get_repos()] + except GithubException as e: + return f"Error listing repositories: {e}" + + def update_repo(self, repo_name: str, **kwargs) -> str: + try: + repo = self._github.get_repo(repo_name) + repo.edit(**kwargs) + return f"Repository '{repo_name}' updated successfully." + except GithubException as e: + return f"Error updating repository: {e}" + + # Issue Management Methods + def create_issue(self, repo_name: str, title: str, body: str = None) -> str: + try: + repo = self._github.get_repo(repo_name) + issue = repo.create_issue(title=title, body=body) + return f"Issue '{title}' created successfully." + except GithubException as e: + return f"Error creating issue: {e}" + + def close_issue(self, repo_name: str, issue_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + issue = repo.get_issue(number=issue_number) + issue.edit(state="closed") + return f"Issue '{issue_number}' closed successfully." + except GithubException as e: + return f"Error closing issue: {e}" + + def update_issue(self, repo_name: str, issue_number: int, **kwargs) -> str: + try: + repo = self._github.get_repo(repo_name) + issue = repo.get_issue(number=issue_number) + issue.edit(**kwargs) + return f"Issue '{issue_number}' updated successfully." + except GithubException as e: + return f"Error updating issue: {e}" + + def list_issues(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [issue.title for issue in repo.get_issues(state="open")] + except GithubException as e: + return f"Error listing issues: {e}" + + def get_issue(self, repo_name: str, issue_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + issue = repo.get_issue(number=issue_number) + return f"Issue #{issue.number}: {issue.title}\n{issue.body}" + except GithubException as e: + return f"Error retrieving issue: {e}" + + # Pull Request Management Methods + def create_pull( + self, repo_name: str, title: str, head: str, base: str, body: str = None + ) -> str: + try: + repo = self._github.get_repo(repo_name) + pull = repo.create_pull(title=title, body=body, head=head, base=base) + return f"Pull request '{title}' created successfully." + except GithubException as e: + return f"Error creating pull request: {e}" + + def merge_pull(self, repo_name: str, pull_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + pull = repo.get_pull(number=pull_number) + pull.merge() + return f"Pull request '{pull_number}' merged successfully." + except GithubException as e: + return f"Error merging pull request: {e}" + + def close_pull(self, repo_name: str, pull_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + pull = repo.get_pull(number=pull_number) + pull.edit(state="closed") + return f"Pull request '{pull_number}' closed successfully." + except GithubException as e: + return f"Error closing pull request: {e}" + + def list_pulls(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [pr.title for pr in repo.get_pulls(state="open")] + except GithubException as e: + return f"Error listing pull requests: {e}" + + def get_pull(self, repo_name: str, pull_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + pull = repo.get_pull(number=pull_number) + return f"Pull Request #{pull.number}: {pull.title}\n{pull.body}" + except GithubException as e: + return f"Error retrieving pull request: {e}" + + # Commit Management Methods + def create_commit( + self, + repo_name: str, + path: str, + message: str, + content: str, + branch: str = "main", + ) -> str: + try: + repo = self._github.get_repo(repo_name) + repo.create_file(path=path, message=message, content=content, branch=branch) + return f"Commit created successfully at {path}." + except GithubException as e: + return f"Error creating commit: {e}" + + def list_commits(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [commit.commit.message for commit in repo.get_commits()] + except GithubException as e: + return f"Error listing commits: {e}" + + def get_commit(self, repo_name: str, sha: str) -> str: + try: + repo = self._github.get_repo(repo_name) + commit = repo.get_commit(sha=sha) + return f"Commit {commit.sha}: {commit.commit.message}" + except GithubException as e: + return f"Error retrieving commit: {e}" + + def compare_commits(self, repo_name: str, base: str, head: str) -> str: + try: + repo = self._github.get_repo(repo_name) + comparison = repo.compare(base, head) + return f"Comparison from {base} to {head}:\n{comparison.diff_url}" + except GithubException as e: + return f"Error comparing commits: {e}" + + # Branch Management Methods + def create_branch( + self, repo_name: str, branch_name: str, source: str = "main" + ) -> str: + try: + repo = self._github.get_repo(repo_name) + source_branch = repo.get_branch(source) + repo.create_git_ref( + ref=f"refs/heads/{branch_name}", sha=source_branch.commit.sha + ) + return f"Branch '{branch_name}' created successfully." + except GithubException as e: + return f"Error creating branch: {e}" + + def delete_branch(self, repo_name: str, branch_name: str) -> str: + try: + repo = self._github.get_repo(repo_name) + ref = repo.get_git_ref(f"heads/{branch_name}") + ref.delete() + return f"Branch '{branch_name}' deleted successfully." + except GithubException as e: + return f"Error deleting branch: {e}" + + def list_branches(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [branch.name for branch in repo.get_branches()] + except GithubException as e: + return f"Error listing branches: {e}" + + def get_branch(self, repo_name: str, branch_name: str) -> str: + try: + repo = self._github.get_repo(repo_name) + branch = repo.get_branch(branch_name) + return f"Branch {branch.name}: {branch.commit.sha}" + except GithubException as e: + return f"Error retrieving branch: {e}" + + # Collaborator Management Methods + def add_collaborator( + self, repo_name: str, username: str, permission: str = "push" + ) -> str: + try: + repo = self._github.get_repo(repo_name) + repo.add_to_collaborators(username, permission) + return f"Collaborator '{username}' added successfully with '{permission}' permission." + except GithubException as e: + return f"Error adding collaborator: {e}" + + def remove_collaborator(self, repo_name: str, username: str) -> str: + try: + repo = self._github.get_repo(repo_name) + repo.remove_from_collaborators(username) + return f"Collaborator '{username}' removed successfully." + except GithubException as e: + return f"Error removing collaborator: {e}" + + def list_collaborators(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [collaborator.login for collaborator in repo.get_collaborators()] + except GithubException as e: + return f"Error listing collaborators: {e}" + + def check_collaborator(self, repo_name: str, username: str) -> str: + try: + repo = self._github.get_repo(repo_name) + if repo.has_in_collaborators(username): + return f"User '{username}' is a collaborator." + else: + return f"User '{username}' is not a collaborator." + except GithubException as e: + return f"Error checking collaborator status: {e}" + + # Milestone Management Methods + def create_milestone( + self, repo_name: str, title: str, description: str = None, state: str = "open" + ) -> str: + try: + repo = self._github.get_repo(repo_name) + milestone = repo.create_milestone( + title=title, description=description, state=state + ) + return f"Milestone '{title}' created successfully." + except GithubException as e: + return f"Error creating milestone: {e}" + + def close_milestone(self, repo_name: str, milestone_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + milestone = repo.get_milestone(number=milestone_number) + milestone.edit(state="closed") + return f"Milestone '{milestone.title}' closed successfully." + except GithubException as e: + return f"Error closing milestone: {e}" + + def update_milestone(self, repo_name: str, milestone_number: int, **kwargs) -> str: + try: + repo = self._github.get_repo(repo_name) + milestone = repo.get_milestone(number=milestone_number) + milestone.edit(**kwargs) + return f"Milestone '{milestone.title}' updated successfully." + except GithubException as e: + return f"Error updating milestone: {e}" + + def list_milestones(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [milestone.title for milestone in repo.get_milestones()] + except GithubException as e: + return f"Error listing milestones: {e}" + + def get_milestone(self, repo_name: str, milestone_number: int) -> str: + try: + repo = self._github.get_repo(repo_name) + milestone = repo.get_milestone(number=milestone_number) + return f"Milestone {milestone.number}: {milestone.title}\n{milestone.description}" + except GithubException as e: + return f"Error retrieving milestone: {e}" + + # Label Management Methods + def create_label( + self, + repo_name: str, + label_name: str, + color: str, + description: Optional[str] = None, + ) -> str: + try: + repo = self._github.get_repo(repo_name) + label = repo.create_label( + name=label_name, color=color, description=description + ) + return f"Label '{label_name}' created successfully." + except GithubException as e: + return f"Error creating label: {e}" + + def delete_label(self, repo_name: str, label_name: str) -> str: + try: + repo = self._github.get_repo(repo_name) + label = repo.get_label(name=label_name) + label.delete() + return f"Label '{label_name}' deleted successfully." + except GithubException as e: + return f"Error deleting label: {e}" + + def update_label( + self, + repo_name: str, + label_name: str, + new_name: Optional[str] = None, + color: Optional[str] = None, + description: Optional[str] = None, + ) -> str: + try: + repo = self._github.get_repo(repo_name) + label = repo.get_label(name=label_name) + label.edit( + name=new_name or label_name, + color=color or label.color, + description=description or label.description, + ) + return f"Label '{label_name}' updated successfully." + except GithubException as e: + return f"Error updating label: {e}" + + def list_labels(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [label.name for label in repo.get_labels()] + except GithubException as e: + return f"Error listing labels: {e}" + + def get_label(self, repo_name: str, label_name: str) -> str: + try: + repo = self._github.get_repo(repo_name) + label = repo.get_label(name=label_name) + return f"Label {label.name}: {label.color}\n{label.description}" + except GithubException as e: + return f"Error retrieving label: {e}" + + # Webhook Management Methods + def create_webhook( + self, + repo_name: str, + config: Dict[str, str], + events: List[str], + active: bool = True, + ) -> str: + try: + repo = self._github.get_repo(repo_name) + webhook = repo.create_hook( + name="web", config=config, events=events, active=active + ) + return f"Webhook created successfully with ID {webhook.id}." + except GithubException as e: + return f"Error creating webhook: {e}" + + def delete_webhook(self, repo_name: str, hook_id: int) -> str: + try: + repo = self._github.get_repo(repo_name) + hook = repo.get_hook(hook_id) + hook.delete() + return f"Webhook with ID {hook_id} deleted successfully." + except GithubException as e: + return f"Error deleting webhook: {e}" + + def list_webhooks(self, repo_name: str) -> List[str]: + try: + repo = self._github.get_repo(repo_name) + return [ + f"Webhook {hook.id}: {hook.config['url']}" for hook in repo.get_hooks() + ] + except GithubException as e: + return f"Error listing webhooks: {e}" + + def get_webhook(self, repo_name: str, hook_id: int) -> str: + try: + repo = self._github.get_repo(repo_name) + hook = repo.get_hook(hook_id) + return f"Webhook {hook.id}: {hook.config['url']}\nEvents: {hook.events}\nActive: {hook.active}" + except GithubException as e: + return f"Error retrieving webhook: {e}" + + # Gist Management Methods + def create_gist( + self, files: Dict[str, str], description: str = "", public: bool = True + ) -> str: + try: + gist = self._github.get_user().create_gist( + public=public, files=files, description=description + ) + return f"Gist created successfully with ID {gist.id}." + except GithubException as e: + return f"Error creating gist: {e}" + + def delete_gist(self, gist_id: str) -> str: + try: + gist = self._github.get_gist(gist_id) + gist.delete() + return f"Gist with ID {gist_id} deleted successfully." + except GithubException as e: + return f"Error deleting gist: {e}" + + def update_gist( + self, + gist_id: str, + files: Optional[Dict[str, str]] = None, + description: Optional[str] = None, + ) -> str: + try: + gist = self._github.get_gist(gist_id) + gist.edit(files=files, description=description) + return f"Gist with ID {gist_id} updated successfully." + except GithubException as e: + return f"Error updating gist: {e}" + + def list_gists(self) -> List[str]: + try: + return [gist.id for gist in self._github.get_user().get_gists()] + except GithubException as e: + return f"Error listing gists: {e}" + + def get_gist(self, gist_id: str) -> str: + try: + gist = self._github.get_gist(gist_id) + files = "\n".join( + [ + f"{fname}: {fileinfo['raw_url']}" + for fname, fileinfo in gist.files.items() + ] + ) + return f"Gist {gist.id}: {gist.description}\nFiles: \n{files}" + except GithubException as e: + return f"Error retrieving gist: {e}" diff --git a/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/__init__.py b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/__init__.py new file mode 100644 index 00000000..046d23d1 --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/swarmauri_tool_communitygithub/__init__.py @@ -0,0 +1,20 @@ +from .GithubBranchTool import GithubBranchTool +from .GithubCommitTool import GithubCommitTool +from .GithubIssueTool import GithubIssueTool +from .GithubPRTool import GithubPRTool +from .GithubRepoTool import GithubRepoTool +from .GithubTool import GithubTool + + +__version__ = "0.6.0.dev26" +__long_desc__ = """ + +# Swarmauri github Plugin + +This repository includes github of a Swarmauri Plugin. + +Visit us at: https://swarmauri.com +Follow us at: https://github.com/swarmauri +Star us at: https://github.com/swarmauri/swarmauri-sdk + +""" diff --git a/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubBranchTool_test.py b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubBranchTool_test.py new file mode 100644 index 00000000..bd3a7e12 --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubBranchTool_test.py @@ -0,0 +1,107 @@ +import os +from unittest.mock import MagicMock, patch +from dotenv import load_dotenv + +import pytest +from swarmauri_tool_communitygithub.GithubBranchTool import ( + GithubBranchTool as Tool, +) + +# Load environment variables from the .env file +load_dotenv() + + +# Fixture for retrieving GitHub token and skipping tests if not available +@pytest.fixture(scope="module") +def github_token(): + token = os.getenv("GITHUBTOOL_TEST_TOKEN") + if not token: + pytest.skip("Skipping due to GITHUBTOOL_TEST_TOKEN not set") + return token + + +# Fixture for initializing the GithubBranchTool +@pytest.fixture(scope="module") +def github_branch_tool(github_token): + return Tool(token=github_token) + + +@pytest.mark.unit +def test_ubc_resource(github_branch_tool): + assert github_branch_tool.resource == "Tool" + + +@pytest.mark.unit +def test_ubc_type(github_branch_tool): + assert github_branch_tool.type == "GithubBranchTool" + + +@pytest.mark.unit +def test_initialization(github_branch_tool): + assert type(github_branch_tool.id) == str + + +@pytest.mark.unit +def test_serialization(github_branch_tool): + serialized_data = github_branch_tool.model_dump_json() + deserialized_tool = Tool.model_validate_json(serialized_data) + assert github_branch_tool.id == deserialized_tool.id + + +@pytest.mark.parametrize( + "action, kwargs, method_called", + [ + ( + "create_branch", + { + "repo_name": "test-repo", + "branch_name": "new-branch", + "source_branch": "main", + }, + "create_branch", + ), + ( + "delete_branch", + {"repo_name": "test-repo", "branch_name": "new-branch"}, + "delete_branch", + ), + ("list_branches", {"repo_name": "test-repo"}, "list_branches"), + ("get_branch", {"repo_name": "test-repo", "branch_name": "main"}, "get_branch"), + # Invalid action + ("invalid_action", {}, None), + ], +) +@pytest.mark.unit +@patch("swarmauri_community.tools.concrete.GithubTool.Github") +def test_call(mock_github, github_branch_tool, action, kwargs, method_called): + expected_keys = {action} + + # Mock the GitHub object + mock_github.return_value = MagicMock() + + if method_called is not None: + with patch.object( + Tool, + method_called, + return_value="performed a test action successfully", + ) as mock_method: + result = github_branch_tool(action=action, **kwargs) + + # Verify the method is called with the correct arguments + mock_method.assert_called_once_with(**kwargs) + + # Check the result + 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(action), str + ), f"Expected str, but got {type(result.get(action)).__name__}" + assert result == {f"{action}": "performed a test action successfully"} + else: + # If an invalid action is provided, it should raise a ValueError + with pytest.raises(ValueError, match=f"Action '{action}' is not supported."): + github_branch_tool(action=action, **kwargs) diff --git a/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubCommitTool_test.py b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubCommitTool_test.py new file mode 100644 index 00000000..dbfc2e5a --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubCommitTool_test.py @@ -0,0 +1,106 @@ +import os +from unittest.mock import MagicMock, patch +from dotenv import load_dotenv + +import pytest +from swarmauri_tool_communitygithub.GithubCommitTool import ( + GithubCommitTool as Tool, +) + +load_dotenv() + + +# Fixture for retrieving GitHub token and skipping tests if not available +@pytest.fixture(scope="module") +def github_token(): + token = os.getenv("GITHUBTOOL_TEST_TOKEN") + if not token: + pytest.skip("Skipping due to GITHUBTOOL_TEST_TOKEN not set") + return token + + +# Fixture for initializing the GithubCommitTool +@pytest.fixture(scope="module") +def github_commit_tool(github_token): + return Tool(token=github_token) + + +@pytest.mark.unit +def test_ubc_resource(github_commit_tool): + assert github_commit_tool.resource == "Tool" + + +@pytest.mark.unit +def test_ubc_type(github_commit_tool): + assert github_commit_tool.type == "GithubCommitTool" + + +@pytest.mark.unit +def test_initialization(github_commit_tool): + assert type(github_commit_tool.id) == str + + +@pytest.mark.unit +def test_serialization(github_commit_tool): + assert ( + github_commit_tool.id + == Tool.model_validate_json(github_commit_tool.model_dump_json()).id + ) + + +@pytest.mark.parametrize( + "action, kwargs, method_called", + [ + # Valid cases for repo management + ( + "create_commit", + { + "repo_name": "test-repo", + "file_path": "path/to/file.txt", + "message": "Test Commit", + "content": "File content", + "branch": "main", + }, + "create_commit", + ), + ("list_commits", {"repo_name": "test-repo"}, "list_commits"), + ("get_commit", {"repo_name": "test-repo", "sha": "abcdef"}, "get_commit"), + ( + "compare_commits", + {"repo_name": "test-repo", "base": "main", "head": "feature"}, + "compare_commits", + ), + # Invalid action + ("invalid_action", {}, None), + ], +) +@pytest.mark.unit +@patch("swarmauri_community.tools.concrete.GithubCommitTool.Github") +def test_call(mock_github, github_commit_tool, action, kwargs, method_called): + expected_keys = {action} + + mock_github.return_value = MagicMock() + + if method_called is not None: + with patch.object( + Tool, + method_called, + return_value="performed a test action successfully", + ) as mock_method: + result = github_commit_tool(action=action, **kwargs) + + mock_method.assert_called_once_with(**kwargs) + + 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(action), str + ), f"Expected int, but got {type(result.get(action)).__name__}" + assert result == {f"{action}": "performed a test action successfully"} + else: + with pytest.raises(ValueError, match=f"Action '{action}' is not supported."): + github_commit_tool(action=action, **kwargs) diff --git a/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubIssueTool_test.py b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubIssueTool_test.py new file mode 100644 index 00000000..e6191da5 --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubIssueTool_test.py @@ -0,0 +1,100 @@ +import os +from unittest.mock import MagicMock, patch +from dotenv import load_dotenv + +import pytest +from swarmauri_tool_communitygithub.GithubIssueTool import ( + GithubIssueTool as Tool, +) + +load_dotenv() + + +# Fixture for retrieving GitHub token and skipping tests if not available +@pytest.fixture(scope="module") +def github_token(): + token = os.getenv("GITHUBTOOL_TEST_TOKEN") + if not token: + pytest.skip("Skipping due to GITHUBTOOL_TEST_TOKEN not set") + return token + + +# Fixture for initializing the GithubIssueTool +@pytest.fixture(scope="module") +def github_issue_tool(github_token): + return Tool(token=github_token) + + +@pytest.mark.unit +def test_ubc_resource(github_issue_tool): + assert github_issue_tool.resource == "Tool" + + +@pytest.mark.unit +def test_ubc_type(github_issue_tool): + assert github_issue_tool.type == "GithubIssueTool" + + +@pytest.mark.unit +def test_initialization(github_issue_tool): + assert type(github_issue_tool.id) == str + + +@pytest.mark.unit +def test_serialization(github_issue_tool): + assert ( + github_issue_tool.id + == Tool.model_validate_json(github_issue_tool.model_dump_json()).id + ) + + +@pytest.mark.parametrize( + "action, kwargs, method_called", + [ + ( + "create_issue", + {"repo_name": "test-repo", "title": "Test Issue"}, + "create_issue", + ), + ("close_issue", {"repo_name": "test-repo", "issue_number": 1}, "close_issue"), + ( + "update_issue", + {"repo_name": "test-repo", "issue_number": 1, "title": "Updated Issue"}, + "update_issue", + ), + ("list_issues", {"repo_name": "test-repo"}, "list_issues"), + ("get_issue", {"repo_name": "test-repo", "issue_number": 1}, "get_issue"), + # Invalid action + ("invalid_action", {}, None), + ], +) +@pytest.mark.unit +@patch("swarmauri_community.tools.concrete.GithubIssueTool.Github") +def test_call(mock_github, github_issue_tool, action, kwargs, method_called): + expected_keys = {action} + + mock_github.return_value = MagicMock() + + if method_called is not None: + with patch.object( + Tool, + method_called, + return_value="performed a test action successfully", + ) as mock_method: + result = github_issue_tool(action=action, **kwargs) + + mock_method.assert_called_once_with(**kwargs) + + 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(action), str + ), f"Expected int, but got {type(result.get(action)).__name__}" + assert result == {f"{action}": "performed a test action successfully"} + else: + with pytest.raises(ValueError, match=f"Action '{action}' is not supported."): + github_issue_tool(action=action, **kwargs) diff --git a/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubPRTool_test.py b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubPRTool_test.py new file mode 100644 index 00000000..508b6900 --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubPRTool_test.py @@ -0,0 +1,93 @@ +import os +from unittest.mock import MagicMock, patch +from dotenv import load_dotenv + +import pytest +from swarmauri_tool_communitygithub.GithubPRTool import ( + GithubPRTool as Tool, +) + +load_dotenv() + + +# Fixture for retrieving GitHub token and skipping tests if not available +@pytest.fixture(scope="module") +def github_token(): + token = os.getenv("GITHUBTOOL_TEST_TOKEN") + if not token: + pytest.skip("Skipping due to GITHUBTOOL_TEST_TOKEN not set") + return token + + +# Fixture for initializing the GithubPRTool +@pytest.fixture(scope="module") +def github_pr_tool(github_token): + return Tool(token=github_token) + + +@pytest.mark.unit +def test_ubc_resource(github_pr_tool): + assert github_pr_tool.resource == "Tool" + + +@pytest.mark.unit +def test_ubc_type(github_pr_tool): + assert github_pr_tool.type == "GithubPRTool" + + +@pytest.mark.unit +def test_initialization(github_pr_tool): + assert type(github_pr_tool.id) == str + + +@pytest.mark.unit +def test_serialization(github_pr_tool): + assert ( + github_pr_tool.id + == Tool.model_validate_json(github_pr_tool.model_dump_json()).id + ) + + +@pytest.mark.parametrize( + "action, kwargs, method_called", + [ + # Valid cases for repo management + ("create_pull", {"repo_name": "test-repo", "title": "Test PR"}, "create_pull"), + ("merge_pull", {"repo_name": "test-repo", "pull_number": 1}, "merge_pull"), + ("close_pull", {"repo_name": "test-repo", "pull_number": 1}, "close_pull"), + ("list_pulls", {"repo_name": "test-repo"}, "list_pulls"), + ("get_pull", {"repo_name": "test-repo", "pull_number": 1}, "get_pull"), + # Invalid action + ("invalid_action", {}, None), + ], +) +@pytest.mark.unit +@patch("swarmauri_community.tools.concrete.GithubPRTool.Github") +def test_call(mock_github, github_pr_tool, action, kwargs, method_called): + expected_keys = {action} + + mock_github.return_value = MagicMock() + + if method_called is not None: + with patch.object( + Tool, + method_called, + return_value="performed a test action successfully", + ) as mock_method: + result = github_pr_tool(action=action, **kwargs) + + mock_method.assert_called_once_with(**kwargs) + + 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(action), str + ), f"Expected int, but got {type(result.get(action)).__name__}" + assert result == {f"{action}": "performed a test action successfully"} + else: + with pytest.raises(ValueError, match=f"Action '{action}' is not supported."): + github_pr_tool(action=action, **kwargs) diff --git a/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubRepoTool_test.py b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubRepoTool_test.py new file mode 100644 index 00000000..0d3bdc50 --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubRepoTool_test.py @@ -0,0 +1,93 @@ +import os +from unittest.mock import MagicMock, patch +from dotenv import load_dotenv + +import pytest +from swarmauri_tool_communitygithub.GithubRepoTool import ( + GithubRepoTool as Tool, +) + +load_dotenv() + + +# Fixture for retrieving GitHub token and skipping tests if not available +@pytest.fixture(scope="module") +def github_token(): + token = os.getenv("GITHUBTOOL_TEST_TOKEN") + if not token: + pytest.skip("Skipping due to GITHUBTOOL_TEST_TOKEN not set") + return token + + +# Fixture for initializing the GithubRepoTool +@pytest.fixture(scope="module") +def github_repo_tool(github_token): + return Tool(token=github_token) + + +@pytest.mark.unit +def test_ubc_resource(github_repo_tool): + assert github_repo_tool.resource == "Tool" + + +@pytest.mark.unit +def test_ubc_type(github_repo_tool): + assert github_repo_tool.type == "GithubRepoTool" + + +@pytest.mark.unit +def test_initialization(github_repo_tool): + assert type(github_repo_tool.id) == str + + +@pytest.mark.unit +def test_serialization(github_repo_tool): + assert ( + github_repo_tool.id + == Tool.model_validate_json(github_repo_tool.model_dump_json()).id + ) + + +@pytest.mark.parametrize( + "action, kwargs, method_called", + [ + # Valid cases for repo management + ("create_repo", {"repo_name": "test-repo"}, "create_repo"), + ("delete_repo", {"repo_name": "test-repo"}, "delete_repo"), + ("get_repo", {"repo_name": "test-repo"}, "get_repo"), + ("list_repos", {}, "list_repos"), + ("update_repo", {"repo_name": "test-repo"}, "update_repo"), + # Invalid action + ("invalid_action", {}, None), + ], +) +@pytest.mark.unit +@patch("swarmauri_community.tools.concrete.GithubRepoTool.Github") +def test_call(mock_github, github_repo_tool, action, kwargs, method_called): + expected_keys = {action} + + mock_github.return_value = MagicMock() + + if method_called is not None: + with patch.object( + Tool, + method_called, + return_value="performed a test action successfully", + ) as mock_method: + result = github_repo_tool(action=action, **kwargs) + + mock_method.assert_called_once_with(**kwargs) + + 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(action), str + ), f"Expected int, but got {type(result.get(action)).__name__}" + assert result == {f"{action}": "performed a test action successfully"} + else: + with pytest.raises(ValueError, match=f"Action '{action}' is not supported."): + github_repo_tool(action=action, **kwargs) diff --git a/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubTool_test.py b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubTool_test.py new file mode 100644 index 00000000..2dcb18aa --- /dev/null +++ b/pkgs/community/swarmauri_tool_communitygithub/tests/unit/GithubTool_test.py @@ -0,0 +1,205 @@ +import os +from unittest.mock import MagicMock, patch +from dotenv import load_dotenv +import pytest +from swarmauri_tool_communitygithub.GithubTool import GithubTool as Tool + +load_dotenv() + + +# Fixture to handle Github Tool token retrieval and skipping if not set +@pytest.fixture(scope="module") +def github_tool(): + token = os.getenv("GITHUBTOOL_TEST_TOKEN") + if not token: + pytest.skip("Skipping tests due to missing GITHUBTOOL_TEST_TOKEN") + return Tool(token=token) + + +@pytest.mark.unit +def test_ubc_resource(github_tool): + assert github_tool.resource == "Tool" + + +@pytest.mark.unit +def test_ubc_type(github_tool): + assert github_tool.type == "GithubTool" + + +@pytest.mark.unit +def test_initialization(github_tool): + assert type(github_tool.id) is str + + +@pytest.mark.unit +def test_serialization(github_tool): + assert github_tool.id == Tool.model_validate_json(github_tool.model_dump_json()).id + + +@pytest.mark.parametrize( + "action, kwargs, method_called", + [ + # Valid cases for repo management + ("create_repo", {"repo_name": "test-repo"}, "create_repo"), + ("delete_repo", {"repo_name": "test-repo"}, "delete_repo"), + ("get_repo", {"repo_name": "test-repo"}, "get_repo"), + ("list_repos", {}, "list_repos"), + ("update_repo", {"repo_name": "test-repo"}, "update_repo"), + ( + "create_issue", + {"repo_name": "test-repo", "title": "Test Issue"}, + "create_issue", + ), + ("close_issue", {"repo_name": "test-repo", "issue_number": 1}, "close_issue"), + ( + "update_issue", + {"repo_name": "test-repo", "issue_number": 1, "title": "Updated Issue"}, + "update_issue", + ), + ("list_issues", {"repo_name": "test-repo"}, "list_issues"), + ("get_issue", {"repo_name": "test-repo", "issue_number": 1}, "get_issue"), + ("create_pull", {"repo_name": "test-repo", "title": "Test PR"}, "create_pull"), + ("merge_pull", {"repo_name": "test-repo", "pull_number": 1}, "merge_pull"), + ("close_pull", {"repo_name": "test-repo", "pull_number": 1}, "close_pull"), + ("list_pulls", {"repo_name": "test-repo"}, "list_pulls"), + ("get_pull", {"repo_name": "test-repo", "pull_number": 1}, "get_pull"), + ( + "create_commit", + {"repo_name": "test-repo", "message": "Test Commit"}, + "create_commit", + ), + ("list_commits", {"repo_name": "test-repo"}, "list_commits"), + ( + "get_commit", + {"repo_name": "test-repo", "commit_sha": "abcdef"}, + "get_commit", + ), + ( + "compare_commits", + {"repo_name": "test-repo", "base": "main", "head": "feature"}, + "compare_commits", + ), + ( + "create_branch", + {"repo_name": "test-repo", "branch_name": "new-branch"}, + "create_branch", + ), + ( + "delete_branch", + {"repo_name": "test-repo", "branch_name": "new-branch"}, + "delete_branch", + ), + ("list_branches", {"repo_name": "test-repo"}, "list_branches"), + ("get_branch", {"repo_name": "test-repo", "branch_name": "main"}, "get_branch"), + ( + "add_collaborator", + {"repo_name": "test-repo", "username": "collaborator"}, + "add_collaborator", + ), + ( + "remove_collaborator", + {"repo_name": "test-repo", "username": "collaborator"}, + "remove_collaborator", + ), + ("list_collaborators", {"repo_name": "test-repo"}, "list_collaborators"), + ( + "check_collaborator", + {"repo_name": "test-repo", "username": "collaborator"}, + "check_collaborator", + ), + ( + "create_milestone", + {"repo_name": "test-repo", "title": "Milestone 1"}, + "create_milestone", + ), + ( + "close_milestone", + {"repo_name": "test-repo", "milestone_number": 1}, + "close_milestone", + ), + ( + "update_milestone", + { + "repo_name": "test-repo", + "milestone_number": 1, + "title": "Updated Milestone", + }, + "update_milestone", + ), + ("list_milestones", {"repo_name": "test-repo"}, "list_milestones"), + ( + "get_milestone", + {"repo_name": "test-repo", "milestone_number": 1}, + "get_milestone", + ), + ( + "create_label", + {"repo_name": "test-repo", "name": "bug", "color": "f29513"}, + "create_label", + ), + ("delete_label", {"repo_name": "test-repo", "name": "bug"}, "delete_label"), + ( + "update_label", + {"repo_name": "test-repo", "name": "bug", "color": "f29513"}, + "update_label", + ), + ("list_labels", {"repo_name": "test-repo"}, "list_labels"), + ("get_label", {"repo_name": "test-repo", "name": "bug"}, "get_label"), + ( + "create_webhook", + {"repo_name": "test-repo", "config": {"url": "http://example.com"}}, + "create_webhook", + ), + ("delete_webhook", {"repo_name": "test-repo", "hook_id": 1}, "delete_webhook"), + ("list_webhooks", {"repo_name": "test-repo"}, "list_webhooks"), + ("get_webhook", {"repo_name": "test-repo", "hook_id": 1}, "get_webhook"), + ( + "create_gist", + { + "description": "Test Gist", + "files": {"file1.txt": {"content": "Hello World"}}, + }, + "create_gist", + ), + ("delete_gist", {"gist_id": "12345"}, "delete_gist"), + ( + "update_gist", + {"gist_id": "12345", "description": "Updated Gist"}, + "update_gist", + ), + ("list_gists", {"username": "test-user"}, "list_gists"), + ("get_gist", {"gist_id": "12345"}, "get_gist"), + # Invalid action + ("invalid_action", {}, None), + ], +) +@pytest.mark.unit +@patch("swarmauri_community.tools.concrete.GithubTool.Github") +def test_call(mock_github, github_tool, action, kwargs, method_called): + expected_keys = {action} + + mock_github.return_value = MagicMock() + + if method_called is not None: + with patch.object( + Tool, + method_called, + return_value="performed a test action successfully", + ) as mock_method: + result = github_tool(action=action, **kwargs) + + mock_method.assert_called_once_with(**kwargs) + + 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(action), str + ), f"Expected str, but got {type(result.get(action)).__name__}" + assert result == {f"{action}": "performed a test action successfully"} + else: + with pytest.raises(ValueError, match=f"Action '{action}' is not supported."): + github_tool(action=action, **kwargs) From d2c3f7b3c3f1e6d8dc413098ad5c7e0cebed2ad3 Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Fri, 10 Jan 2025 10:01:15 -0600 Subject: [PATCH 11/12] standard - fix AttributeError --- .../swarmauri_standard/distances/CosineDistance.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkgs/standards/swarmauri_standard/swarmauri_standard/distances/CosineDistance.py b/pkgs/standards/swarmauri_standard/swarmauri_standard/distances/CosineDistance.py index a2d80e43..04be1fdc 100644 --- a/pkgs/standards/swarmauri_standard/swarmauri_standard/distances/CosineDistance.py +++ b/pkgs/standards/swarmauri_standard/swarmauri_standard/distances/CosineDistance.py @@ -1,3 +1,4 @@ +import numpy as np from numpy.linalg import norm from typing import List, Literal from swarmauri_standard.vectors.Vector import Vector @@ -13,7 +14,11 @@ class CosineDistance(DistanceBase): of these vectors. """ type: Literal['CosineDistance'] = 'CosineDistance' - + + def dot_product(self, vector_a: Vector, vector_b: Vector) -> float: + """Compute the dot product of two vectors using their underlying values.""" + return np.dot(vector_a.value, vector_b.value) + def distance(self, vector_a: Vector, vector_b: Vector) -> float: """ Computes the cosine distance between two vectors: 1 - cosine similarity. From 20c52010e8be3f9e1926d879779a6b05b5b2b80b Mon Sep 17 00:00:00 2001 From: cobycloud <25079070+cobycloud@users.noreply.github.com> Date: Fri, 10 Jan 2025 10:01:25 -0600 Subject: [PATCH 12/12] base - add fill() --- pkgs/base/swarmauri_base/prompts/PromptTemplateBase.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkgs/base/swarmauri_base/prompts/PromptTemplateBase.py b/pkgs/base/swarmauri_base/prompts/PromptTemplateBase.py index 338c431e..1fb54664 100644 --- a/pkgs/base/swarmauri_base/prompts/PromptTemplateBase.py +++ b/pkgs/base/swarmauri_base/prompts/PromptTemplateBase.py @@ -1,6 +1,9 @@ from typing import Dict, List, Union, Optional, Literal from pydantic import Field +import warnings + + from swarmauri_core.prompts.IPrompt import IPrompt from swarmauri_core.prompts.ITemplate import ITemplate from swarmauri_core.ComponentBase import ComponentBase, ResourceTypes @@ -29,9 +32,14 @@ def set_variables(self, variables: Dict[str, str]) -> None: self.variables = variables def generate_prompt(self, variables: Dict[str, str] = None) -> str: + warnings.warn("Deprecating 'generate_prompt()', use `fill()`.") + return self.fill(variables) + + def fill(self, variables: Dict[str, str] = None) -> str: variables = variables or self.variables return self.template.format(**variables) + def __call__(self, variables: Optional[Dict[str, str]] = None) -> str: """ Generates a prompt using the current template and provided keyword arguments for substitution.