Skip to content

Commit

Permalink
community: adds support for getting github releases for the configure…
Browse files Browse the repository at this point in the history
…d repository (#29318)

**Description:** adds support for github tool to query github releases
on the configure respository
**Issue:** N/A
**Dependencies:** N/A
**Twitter handle:** @MacsDickinson

---------

Co-authored-by: Chester Curme <chester.curme@gmail.com>
  • Loading branch information
MacsDickinson and ccurme authored Jan 22, 2025
1 parent ef1610e commit 7378c95
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 2 deletions.
33 changes: 32 additions & 1 deletion docs/docs/integrations/tools/github.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,37 @@
"8. **Delete File**- deletes a file from the repository."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Include release tools\n",
"\n",
"By default, the toolkit does not include release-related tools. You can include them by setting `include_release_tools=True` when initializing the toolkit:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"toolkit = GitHubToolkit.from_github_api_wrapper(github, include_release_tools=True)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Settings `include_release_tools=True` will include the following tools:\n",
"\n",
"* **Get Latest Release**- fetches the latest release from the repository.\n",
"\n",
"* **Get Releases**- fetches the latest 5 releases from the repository.\n",
"\n",
"* **Get Release**- fetches a specific release from the repository by tag name, e.g. `v1.0.0`.\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
Expand Down Expand Up @@ -321,7 +352,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.4"
"version": "3.13.1"
}
},
"nbformat": 4,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@
GET_FILES_FROM_DIRECTORY_PROMPT,
GET_ISSUE_PROMPT,
GET_ISSUES_PROMPT,
GET_LATEST_RELEASE_PROMPT,
GET_PR_PROMPT,
GET_RELEASE_PROMPT,
GET_RELEASES_PROMPT,
LIST_BRANCHES_IN_REPO_PROMPT,
LIST_PRS_PROMPT,
LIST_PULL_REQUEST_FILES,
Expand Down Expand Up @@ -152,6 +155,15 @@ class SearchIssuesAndPRs(BaseModel):
)


class TagName(BaseModel):
"""Schema for operations that require a tag name as input."""

tag_name: str = Field(
...,
description="The tag name of the release, e.g. `v1.0.0`.",
)


class GitHubToolkit(BaseToolkit):
"""GitHub Toolkit.
Expand Down Expand Up @@ -218,6 +230,25 @@ class GitHubToolkit(BaseToolkit):
Search code
Create review request
Include release tools:
By default, the toolkit does not include release-related tools.
You can include them by setting ``include_release_tools=True`` when
initializing the toolkit:
.. code-block:: python
toolkit = GitHubToolkit.from_github_api_wrapper(
github, include_release_tools=True
)
Setting ``include_release_tools=True`` will include the following tools:
.. code-block:: none
Get latest release
Get releases
Get release
Use within an agent:
.. code-block:: python
Expand Down Expand Up @@ -268,12 +299,14 @@ class GitHubToolkit(BaseToolkit):

@classmethod
def from_github_api_wrapper(
cls, github_api_wrapper: GitHubAPIWrapper
cls, github_api_wrapper: GitHubAPIWrapper, include_release_tools: bool = False
) -> "GitHubToolkit":
"""Create a GitHubToolkit from a GitHubAPIWrapper.
Args:
github_api_wrapper: GitHubAPIWrapper. The GitHub API wrapper.
include_release_tools: bool. Whether to include release-related tools.
Defaults to False.
Returns:
GitHubToolkit. The GitHub toolkit.
Expand Down Expand Up @@ -406,6 +439,29 @@ def from_github_api_wrapper(
"args_schema": CreateReviewRequest,
},
]

release_operations: List[Dict] = [
{
"mode": "get_latest_release",
"name": "Get latest release",
"description": GET_LATEST_RELEASE_PROMPT,
"args_schema": NoInput,
},
{
"mode": "get_releases",
"name": "Get releases",
"description": GET_RELEASES_PROMPT,
"args_schema": NoInput,
},
{
"mode": "get_release",
"name": "Get release",
"description": GET_RELEASE_PROMPT,
"args_schema": TagName,
},
]

operations = operations + (release_operations if include_release_tools else [])
tools = [
GitHubAction(
name=action["name"],
Expand Down
9 changes: 9 additions & 0 deletions libs/community/langchain_community/tools/github/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,12 @@

GET_FILES_FROM_DIRECTORY_PROMPT = """
This tool will fetch a list of all files in a specified directory. **VERY IMPORTANT**: You must specify the path of the directory as a string input parameter."""

GET_LATEST_RELEASE_PROMPT = """
This tool will fetch the latest release of the repository. No input parameters are required."""

GET_RELEASES_PROMPT = """
This tool will fetch the latest 5 releases of the repository. No input parameters are required."""

GET_RELEASE_PROMPT = """
This tool will fetch a specific release of the repository. **VERY IMPORTANT**: You must specify the tag name of the release as a string input parameter."""
56 changes: 56 additions & 0 deletions libs/community/langchain_community/utilities/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,56 @@ def create_review_request(self, reviewer_username: str) -> str:
except Exception as e:
return f"Failed to create a review request with error {e}"

def get_latest_release(self) -> str:
"""
Fetches the latest release of the repository.
Returns:
str: The latest release
"""
release = self.github_repo_instance.get_latest_release()
return (
f"Latest title: {release.title} "
f"tag: {release.tag_name} "
f"body: {release.body}"
)

def get_releases(self) -> str:
"""
Fetches all releases of the repository.
Returns:
str: The releases
"""
releases = self.github_repo_instance.get_releases()
max_results = min(5, releases.totalCount)
results = [f"Top {max_results} results:"]
for release in releases[:max_results]:
results.append(
f"Title: {release.title}, "
f"Tag: {release.tag_name}, "
f"Body: {release.body}"
)

return "\n".join(results)

def get_release(self, tag_name: str) -> str:
"""
Fetches a specific release of the repository.
Parameters:
tag_name(str): The tag name of the release
Returns:
str: The release
"""
release = self.github_repo_instance.get_release(tag_name)
return (
f"Release: {release.title} "
f"tag: {release.tag_name} "
f"body: {release.body}"
)

def run(self, mode: str, query: str) -> str:
if mode == "get_issue":
return json.dumps(self.get_issue(int(query)))
Expand Down Expand Up @@ -854,5 +904,11 @@ def run(self, mode: str, query: str) -> str:
return self.search_code(query)
elif mode == "create_review_request":
return self.create_review_request(query)
elif mode == "get_latest_release":
return self.get_latest_release()
elif mode == "get_releases":
return self.get_releases()
elif mode == "get_release":
return self.get_release(query)
else:
raise ValueError("Invalid mode" + mode)
12 changes: 12 additions & 0 deletions libs/community/tests/integration_tests/utilities/test_github.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ def test_get_open_issues(api_client: GitHubAPIWrapper) -> None:
assert len(issues) != 0


def test_get_latest_release(api_client: GitHubAPIWrapper) -> None:
"""Basic test to fetch latest release"""
release = api_client.get_latest_release()
assert release is not None


def test_get_releases(api_client: GitHubAPIWrapper) -> None:
"""Basic test to fetch releases"""
releases = api_client.get_releases()
assert releases is not None


def test_search_issues_and_prs(api_client: GitHubAPIWrapper) -> None:
"""Basic test to search issues and PRs"""
results = api_client.search_issues_and_prs("is:pr is:merged")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from unittest.mock import MagicMock

from langchain_community.agent_toolkits.github.toolkit import GitHubToolkit
from langchain_community.utilities.github import GitHubAPIWrapper


def test_github_toolkit() -> None:
# Create a mock GitHub wrapper with required attributes
mock_github = MagicMock(spec=GitHubAPIWrapper)
mock_github.github_repository = "fake/repo"
mock_github.github_app_id = "fake_id"
mock_github.github_app_private_key = "fake_key"
mock_github.active_branch = "main"
mock_github.github_base_branch = "main"

# Test without release tools
toolkit = GitHubToolkit.from_github_api_wrapper(mock_github)
tools = toolkit.get_tools()
assert len(tools) == 21 # Base number of tools

# Test with release tools
toolkit_with_releases = GitHubToolkit.from_github_api_wrapper(
mock_github, include_release_tools=True
)
tools_with_releases = toolkit_with_releases.get_tools()
assert len(tools_with_releases) == 24 # Base tools + 3 release tools

0 comments on commit 7378c95

Please sign in to comment.