Files
dify-plugin-sdks/python/examples/github/tools/github_repository_commits.py
Maries c6f83a63e1 feat[0.4.2]: Tool OAuth (#179)
* chore: fix ruff issue

* feat(oauth): implement OAuth

* feat(invoke-message): refactor message handling and introduce InvokeMessage class

* feat(plugin-oauth): add credential_id and credential_type to tool parameters

* feat(plugin-oauth): add credential_id and credential_type to tool parameters

* chore: update dify_plugin version to 0.5.0b4 and clean up github.yaml

* chore: update plugin version to 0.1.2 in manifest.yaml

* feat(session): session context and tool backwards invocation credential support

* feat(oauth): session context and tool backwards invocation credential support

* feat: update README and requirements for OAuth support in version 0.4.2

* feat: add .gitignore to exclude IDE files and secret keys

* chore: apply ruff

* feat: bump version to 0.4.2b1

* feat: update GitHub plugin configuration for OAuth support and improve credential handling

* feat: update .gitignore to exclude dify plugin files and public keys

* feat: fix credential validation for GitHub API and bump version to 0.2.1

* feat: update GitHub plugin to support multiple access tokens and bump version to 0.2.5

* chore: apply ruff

* feat: add ToolProviderOAuthError for improved OAuth error handling in GitHub plugin

* chore: apply ruff

* chore: bump version to 0.4.2

* chore: update examples sdk version to 0.4.2

* fix: thread deadlock in PluginRunner when running tests without gevent monkey patching

* feat: add support for refreshing OAuth credentials in Plugin and GitHub provider

* feat: refactor OAuth credential handling to return structured OAuthCredentials object

* apply ruff

* feat: refactor OAuth credential handling to use ToolOAuthCredentials for improved structure

* feat: reorganize imports in __init__.py for improved clarity and structure

* feat: add Microsoft To Do plugin for refresh token example

* chore: apply ruff

* fix: update author in GitHub configuration and clean up Microsoft To Do schema

* chore: bump version to 0.4.2b2 in pyproject.toml

* feat: update Microsoft To Do plugin to handle OAuth token encoding and version bump

* feat:remove inelegant example

* chore: update dify_plugin version to 0.4.2

* chore: bump version to 0.4.2 in pyproject.toml

---------

Co-authored-by: Yeuoly <admin@srmxy.cn>
2025-07-23 13:49:01 +08:00

131 lines
5.6 KiB
Python

import json
from collections.abc import Generator
from datetime import datetime
from typing import Any
import requests
from dify_plugin import Tool
from dify_plugin.entities.provider_config import CredentialType
from dify_plugin.entities.tool import ToolInvokeMessage
from dify_plugin.errors.model import InvokeError
class GithubRepositoryCommitsTool(Tool):
def _invoke(self, tool_parameters: dict[str, Any]) -> Generator[ToolInvokeMessage, None, None]:
"""
invoke tools
"""
owner = tool_parameters.get("owner", "")
repo = tool_parameters.get("repo", "")
per_page = tool_parameters.get("per_page", 10)
sha = tool_parameters.get("sha", "")
path = tool_parameters.get("path", "")
credential_type = self.runtime.credential_type
if not owner:
yield self.create_text_message("Please input owner")
return
if not repo:
yield self.create_text_message("Please input repo")
return
if credential_type == CredentialType.API_KEY and "access_tokens" not in self.runtime.credentials:
yield self.create_text_message("GitHub API Access Tokens is required.")
return
if credential_type == CredentialType.OAUTH and "access_tokens" not in self.runtime.credentials:
yield self.create_text_message("GitHub OAuth Access Tokens is required.")
return
access_token = self.runtime.credentials.get("access_tokens")
try:
headers = {
"Content-Type": "application/vnd.github+json",
"Authorization": f"Bearer {access_token}",
"X-GitHub-Api-Version": "2022-11-28",
}
s = requests.session()
api_domain = "https://api.github.com"
url = f"{api_domain}/repos/{owner}/{repo}/commits"
params = {"per_page": per_page}
if sha:
params["sha"] = sha
if path:
params["path"] = path
response = s.request(
method="GET",
headers=headers,
url=url,
params=params,
)
if response.status_code == 200:
response_data = response.json()
commits = []
for commit in response_data:
commit_info = {
"sha": commit.get("sha", "")[:7],
"full_sha": commit.get("sha", ""),
"message": commit.get("commit", {}).get("message", ""),
"author": {
"name": commit.get("commit", {}).get("author", {}).get("name", ""),
"email": commit.get("commit", {}).get("author", {}).get("email", ""),
"date": datetime.strptime(
commit.get("commit", {}).get("author", {}).get("date", ""), "%Y-%m-%dT%H:%M:%SZ"
).strftime("%Y-%m-%d %H:%M:%S")
if commit.get("commit", {}).get("author", {}).get("date")
else "",
},
"committer": {
"name": commit.get("commit", {}).get("committer", {}).get("name", ""),
"email": commit.get("commit", {}).get("committer", {}).get("email", ""),
"date": datetime.strptime(
commit.get("commit", {}).get("committer", {}).get("date", ""), "%Y-%m-%dT%H:%M:%SZ"
).strftime("%Y-%m-%d %H:%M:%S")
if commit.get("commit", {}).get("committer", {}).get("date")
else "",
},
"url": commit.get("html_url", ""),
"comment_count": commit.get("commit", {}).get("comment_count", 0),
"verification": {
"verified": commit.get("commit", {}).get("verification", {}).get("verified", False),
"reason": commit.get("commit", {}).get("verification", {}).get("reason", ""),
},
"stats": {
"additions": commit.get("stats", {}).get("additions", 0),
"deletions": commit.get("stats", {}).get("deletions", 0),
"total": commit.get("stats", {}).get("total", 0),
}
if commit.get("stats")
else {},
"files_changed": len(commit.get("files", [])) if commit.get("files") else 0,
}
commits.append(commit_info)
s.close()
if not commits:
yield self.create_text_message(f"No commits found in {owner}/{repo}")
else:
yield self.create_text_message(
self.session.model.summary.invoke(
text=json.dumps(commits, ensure_ascii=False),
instruction="Summarize the GitHub commits in a structured format",
)
)
else:
response_data = response.json()
raise InvokeError(
f"Request failed: {response.status_code} {response_data.get('message', 'Unknown error')}"
)
except InvokeError as e:
raise e
except Exception as e:
raise InvokeError(f"GitHub API request failed: {e}") from e