ultralytics-actions 0.0.26__tar.gz → 0.0.28__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/PKG-INFO +1 -1
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/__init__.py +1 -1
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/first_interaction.py +2 -2
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/summarize_pr.py +104 -11
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/utils/__init__.py +2 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/utils/github_utils.py +21 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/PKG-INFO +1 -1
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/LICENSE +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/README.md +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/summarize_release.py +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/update_markdown_code_blocks.py +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/utils/common_utils.py +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/utils/openai_utils.py +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/pyproject.toml +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/setup.cfg +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/SOURCES.txt +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/dependency_links.txt +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/entry_points.txt +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/requires.txt +0 -0
- {ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ultralytics-actions
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.28
|
4
4
|
Summary: Ultralytics Actions for GitHub automation and PR management.
|
5
5
|
Author-email: Glenn Jocher <glenn.jocher@ultralytics.com>
|
6
6
|
Maintainer-email: Ultralytics <hello@ultralytics.com>
|
@@ -335,7 +335,7 @@ INSTRUCTIONS:
|
|
335
335
|
- Adapt the example {issue_type} response below as appropriate, keeping all badges, links and references provided
|
336
336
|
- For bug reports, specifically request a minimum reproducible example (MRE) if not provided
|
337
337
|
- INCLUDE ALL LINKS AND INSTRUCTIONS IN THE EXAMPLE BELOW, customized as appropriate
|
338
|
-
-
|
338
|
+
- Mention to the user that this is an automated response and that an Ultralytics engineer will also assist soon
|
339
339
|
- Do not add a sign-off or valediction like "best regards" at the end of your response
|
340
340
|
- Do not add spaces between bullet points or numbered lists
|
341
341
|
- Only link to files or URLs in the example below, do not add external links
|
@@ -359,7 +359,7 @@ YOUR {issue_type.upper()} RESPONSE:
|
|
359
359
|
messages = [
|
360
360
|
{
|
361
361
|
"role": "system",
|
362
|
-
"content": f"You are a helpful assistant responding to GitHub {issue_type}s for
|
362
|
+
"content": f"You are a helpful assistant responding to GitHub {issue_type}s for {org_name}.",
|
363
363
|
},
|
364
364
|
{"role": "user", "content": prompt},
|
365
365
|
]
|
@@ -10,6 +10,7 @@ from .utils import (
|
|
10
10
|
GITHUB_REPOSITORY,
|
11
11
|
PR,
|
12
12
|
get_completion,
|
13
|
+
get_github_username,
|
13
14
|
get_pr_diff,
|
14
15
|
)
|
15
16
|
|
@@ -19,6 +20,62 @@ SUMMARY_START = (
|
|
19
20
|
)
|
20
21
|
|
21
22
|
|
23
|
+
def generate_merge_message(pr_author, contributors, pr_summary=None):
|
24
|
+
"""Generates an AI thank you message for merged PRs using OpenAI."""
|
25
|
+
contributors_str = ", ".join(f"@{c}" for c in contributors if c != pr_author)
|
26
|
+
mention_str = f"@{pr_author}"
|
27
|
+
if contributors_str:
|
28
|
+
mention_str += f" and {contributors_str}"
|
29
|
+
|
30
|
+
messages = [
|
31
|
+
{
|
32
|
+
"role": "system",
|
33
|
+
"content": "You are an Ultralytics AI assistant. Generate meaningful, inspiring messages to GitHub users.",
|
34
|
+
},
|
35
|
+
{
|
36
|
+
"role": "user",
|
37
|
+
"content": f"Write a friendly thank you for a merged PR by these GitHub contributors: {mention_str}. "
|
38
|
+
f"Context from PR:\n{pr_summary}\n\n"
|
39
|
+
f"Start with the exciting message that this PR is now merged, and weave in an inspiring quote "
|
40
|
+
f"from a famous figure in science, philosophy or stoicism. "
|
41
|
+
f"Make the message relevant to the specific contributions in this PR. "
|
42
|
+
f"We want them to feel their hard work is acknowledged and will make a difference in the world.",
|
43
|
+
},
|
44
|
+
]
|
45
|
+
return get_completion(messages)
|
46
|
+
|
47
|
+
|
48
|
+
def post_merge_message(pr_number, pr_author, contributors, summary):
|
49
|
+
"""Posts AI-generated thank you message on PR after merge."""
|
50
|
+
message = generate_merge_message(pr_author, contributors, summary)
|
51
|
+
comment_url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/issues/{pr_number}/comments"
|
52
|
+
response = requests.post(comment_url, json={"body": message}, headers=GITHUB_HEADERS)
|
53
|
+
return response.status_code == 201
|
54
|
+
|
55
|
+
|
56
|
+
def generate_issue_comment(pr_url, pr_summary):
|
57
|
+
"""Generates a personalized issue comment using AI based on the PR context."""
|
58
|
+
messages = [
|
59
|
+
{
|
60
|
+
"role": "system",
|
61
|
+
"content": "You are an Ultralytics AI assistant. Generate friendly GitHub issue comments. No @ mentions or direct addressing.",
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"role": "user",
|
65
|
+
"content": f"Write a comment for a GitHub issue where a potential fix has been merged in PR: {pr_url}\n\n"
|
66
|
+
f"Context from PR:\n{pr_summary}\n\n"
|
67
|
+
f"Include:\n"
|
68
|
+
f"1. An explanation of key changes from the PR that may resolve this issue\n"
|
69
|
+
f"2. Testing options:\n"
|
70
|
+
f" - pip install git+https://github.com/ultralytics/ultralytics.git@main # test latest changes\n"
|
71
|
+
f" - or await next official PyPI release\n"
|
72
|
+
f"3. Request feedback on whether these changes resolve the issue\n"
|
73
|
+
f"4. Thank 🙏 for reporting the issue and welcome any further feedback if the issue persists\n\n",
|
74
|
+
},
|
75
|
+
]
|
76
|
+
return get_completion(messages)
|
77
|
+
|
78
|
+
|
22
79
|
def generate_pr_summary(repo_name, diff_text):
|
23
80
|
"""Generates a concise, professional summary of a PR using OpenAI's API for Ultralytics repositories."""
|
24
81
|
if not diff_text:
|
@@ -71,9 +128,8 @@ def update_pr_description(repo_name, pr_number, new_summary, max_retries=2):
|
|
71
128
|
return update_response.status_code
|
72
129
|
|
73
130
|
|
74
|
-
def label_fixed_issues(pr_number):
|
75
|
-
"""Labels issues
|
76
|
-
# GraphQL query to get closing issues
|
131
|
+
def label_fixed_issues(pr_number, pr_summary):
|
132
|
+
"""Labels issues closed by this PR when merged, notifies users, and returns PR contributors."""
|
77
133
|
query = """
|
78
134
|
query($owner: String!, $repo: String!, $pr_number: Int!) {
|
79
135
|
repository(owner: $owner, name: $repo) {
|
@@ -83,6 +139,15 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
83
139
|
number
|
84
140
|
}
|
85
141
|
}
|
142
|
+
url
|
143
|
+
body
|
144
|
+
author { login }
|
145
|
+
reviews(first: 50) {
|
146
|
+
nodes { author { login } }
|
147
|
+
}
|
148
|
+
comments(first: 50) {
|
149
|
+
nodes { author { login } }
|
150
|
+
}
|
86
151
|
}
|
87
152
|
}
|
88
153
|
}
|
@@ -94,21 +159,44 @@ query($owner: String!, $repo: String!, $pr_number: Int!) {
|
|
94
159
|
response = requests.post(graphql_url, json={"query": query, "variables": variables}, headers=GITHUB_HEADERS)
|
95
160
|
if response.status_code != 200:
|
96
161
|
print(f"Failed to fetch linked issues. Status code: {response.status_code}")
|
97
|
-
return
|
162
|
+
return [], None
|
98
163
|
|
99
164
|
try:
|
100
|
-
|
165
|
+
data = response.json()["data"]["repository"]["pullRequest"]
|
166
|
+
issues = data["closingIssuesReferences"]["nodes"]
|
167
|
+
author = data["author"]["login"]
|
168
|
+
|
169
|
+
# Get unique contributors from reviews and comments
|
170
|
+
contributors = {review["author"]["login"] for review in data["reviews"]["nodes"]}
|
171
|
+
contributors.update(comment["author"]["login"] for comment in data["comments"]["nodes"])
|
172
|
+
contributors.discard(author) # Remove author from contributors list
|
173
|
+
|
174
|
+
# Generate personalized comment
|
175
|
+
comment = generate_issue_comment(pr_url=data["url"], pr_summary=pr_summary)
|
176
|
+
|
177
|
+
# Update linked issues
|
101
178
|
for issue in issues:
|
102
179
|
issue_number = issue["number"]
|
180
|
+
# Add fixed label
|
103
181
|
label_url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/issues/{issue_number}/labels"
|
104
182
|
label_response = requests.post(label_url, json={"labels": ["fixed"]}, headers=GITHUB_HEADERS)
|
105
|
-
|
106
|
-
|
183
|
+
|
184
|
+
# Add comment
|
185
|
+
comment_url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/issues/{issue_number}/comments"
|
186
|
+
comment_response = requests.post(comment_url, json={"body": comment}, headers=GITHUB_HEADERS)
|
187
|
+
|
188
|
+
if label_response.status_code == 200 and comment_response.status_code == 201:
|
189
|
+
print(f"Added 'fixed' label and comment to issue #{issue_number}")
|
107
190
|
else:
|
108
|
-
print(
|
191
|
+
print(
|
192
|
+
f"Failed to update issue #{issue_number}. Label status: {label_response.status_code}, "
|
193
|
+
f"Comment status: {comment_response.status_code}"
|
194
|
+
)
|
195
|
+
|
196
|
+
return contributors, author
|
109
197
|
except KeyError as e:
|
110
198
|
print(f"Error parsing GraphQL response: {e}")
|
111
|
-
return
|
199
|
+
return [], None
|
112
200
|
|
113
201
|
|
114
202
|
def remove_todos_on_merge(pr_number):
|
@@ -138,12 +226,17 @@ def main():
|
|
138
226
|
else:
|
139
227
|
print(f"Failed to update PR description. Status code: {status_code}")
|
140
228
|
|
141
|
-
# Update linked issues
|
229
|
+
# Update linked issues and post thank you message if merged
|
142
230
|
if PR.get("merged"):
|
143
231
|
print("PR is merged, labeling fixed issues...")
|
144
|
-
label_fixed_issues(pr_number)
|
232
|
+
contributors, author = label_fixed_issues(pr_number, summary)
|
145
233
|
print("Removing TODO label from PR...")
|
146
234
|
remove_todos_on_merge(pr_number)
|
235
|
+
username = get_github_username() # get GITHUB_TOKEN username
|
236
|
+
if author and author != username:
|
237
|
+
print("Posting PR author thank you message...")
|
238
|
+
contributors.discard(username)
|
239
|
+
post_merge_message(pr_number, author, contributors, summary)
|
147
240
|
|
148
241
|
|
149
242
|
if __name__ == "__main__":
|
@@ -14,6 +14,7 @@ from .github_utils import (
|
|
14
14
|
PR,
|
15
15
|
check_pypi_version,
|
16
16
|
get_github_data,
|
17
|
+
get_github_username,
|
17
18
|
get_pr_diff,
|
18
19
|
graphql_request,
|
19
20
|
ultralytics_actions_info,
|
@@ -38,6 +39,7 @@ __all__ = (
|
|
38
39
|
"OPENAI_API_KEY",
|
39
40
|
"OPENAI_MODEL",
|
40
41
|
"get_completion",
|
42
|
+
"get_github_username",
|
41
43
|
"check_pypi_version",
|
42
44
|
"ultralytics_actions_info",
|
43
45
|
)
|
@@ -24,6 +24,27 @@ PR = EVENT_DATA.get("pull_request", {})
|
|
24
24
|
DISCUSSION = EVENT_DATA.get("discussion", {})
|
25
25
|
|
26
26
|
|
27
|
+
def get_github_username():
|
28
|
+
"""Gets username associated with the GitHub token in GITHUB_HEADERS."""
|
29
|
+
query = """
|
30
|
+
query {
|
31
|
+
viewer {
|
32
|
+
login
|
33
|
+
}
|
34
|
+
}
|
35
|
+
"""
|
36
|
+
response = requests.post("https://api.github.com/graphql", json={"query": query}, headers=GITHUB_HEADERS)
|
37
|
+
if response.status_code != 200:
|
38
|
+
print(f"Failed to fetch authenticated user. Status code: {response.status_code}")
|
39
|
+
return None
|
40
|
+
|
41
|
+
try:
|
42
|
+
return response.json()["data"]["viewer"]["login"]
|
43
|
+
except KeyError as e:
|
44
|
+
print(f"Error parsing authenticated user response: {e}")
|
45
|
+
return None
|
46
|
+
|
47
|
+
|
27
48
|
def get_pr_diff(pr_number: int) -> str:
|
28
49
|
"""Retrieves the diff content for a specified pull request in a GitHub repository."""
|
29
50
|
url = f"{GITHUB_API_URL}/repos/{GITHUB_REPOSITORY}/pulls/{pr_number}"
|
{ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/PKG-INFO
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: ultralytics-actions
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.28
|
4
4
|
Summary: Ultralytics Actions for GitHub automation and PR management.
|
5
5
|
Author-email: Glenn Jocher <glenn.jocher@ultralytics.com>
|
6
6
|
Maintainer-email: Ultralytics <hello@ultralytics.com>
|
File without changes
|
File without changes
|
File without changes
|
{ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/actions/update_markdown_code_blocks.py
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/SOURCES.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|
{ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/requires.txt
RENAMED
File without changes
|
{ultralytics_actions-0.0.26 → ultralytics_actions-0.0.28}/ultralytics_actions.egg-info/top_level.txt
RENAMED
File without changes
|