Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PR Summary step #55

Merged
merged 47 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f8ae7fd
Test
glenn-jocher Jan 18, 2024
68dd7ac
Auto-format by Ultralytics actions
UltralyticsAssistant Jan 18, 2024
f34a55f
Test
glenn-jocher Jan 18, 2024
2015cd1
Test
glenn-jocher Jan 18, 2024
9ddc57b
Test
glenn-jocher Jan 18, 2024
9091137
Merge branch 'main' into test-local-python-script
glenn-jocher Jan 18, 2024
11c903b
Update action.yml
glenn-jocher Jan 18, 2024
d8fe686
Update action.yml
glenn-jocher Jan 18, 2024
67add8c
Test
glenn-jocher Jan 18, 2024
ed32b59
Auto-format by https://ultralytics.com/actions
UltralyticsAssistant Jan 18, 2024
d6e0749
Test
glenn-jocher Jan 18, 2024
23ec690
Merge remote-tracking branch 'origin/test-local-python-script' into t…
glenn-jocher Jan 18, 2024
a116fb5
Auto-format by https://ultralytics.com/actions
UltralyticsAssistant Jan 18, 2024
3e6f886
Test
glenn-jocher Jan 18, 2024
85172ee
Test
glenn-jocher Jan 18, 2024
8f3e5db
Test
glenn-jocher Jan 18, 2024
d7b9651
Test
glenn-jocher Jan 18, 2024
27165ce
Test
glenn-jocher Jan 18, 2024
28a139a
Test
glenn-jocher Jan 18, 2024
435b6cb
Test
glenn-jocher Jan 18, 2024
2ab17f0
Test
glenn-jocher Jan 18, 2024
6a41bad
Test
glenn-jocher Jan 18, 2024
6f1172f
Test
glenn-jocher Jan 18, 2024
fe99368
Test
glenn-jocher Jan 18, 2024
237a33f
Auto-format by https://ultralytics.com/actions
UltralyticsAssistant Jan 18, 2024
e8d60b3
Test
glenn-jocher Jan 18, 2024
1691315
Merge remote-tracking branch 'origin/test-local-python-script' into t…
glenn-jocher Jan 18, 2024
e6d0ae9
Test
glenn-jocher Jan 18, 2024
3964143
Test
glenn-jocher Jan 18, 2024
5030f0a
Test
glenn-jocher Jan 18, 2024
de990e9
Test
glenn-jocher Jan 18, 2024
0dce74a
Auto-format by https://ultralytics.com/actions
UltralyticsAssistant Jan 18, 2024
11bc73b
Test
glenn-jocher Jan 18, 2024
19d2835
Test
glenn-jocher Jan 18, 2024
0faabe3
Auto-format by https://ultralytics.com/actions
UltralyticsAssistant Jan 18, 2024
067320e
Test
glenn-jocher Jan 18, 2024
c5ce7f6
Auto-format by https://ultralytics.com/actions
UltralyticsAssistant Jan 18, 2024
214389b
Test
glenn-jocher Jan 18, 2024
ea28a5c
Merge remote-tracking branch 'origin/test-local-python-script' into t…
glenn-jocher Jan 18, 2024
419fb41
Test
glenn-jocher Jan 18, 2024
8f5a3cf
Auto-format by https://ultralytics.com/actions
UltralyticsAssistant Jan 18, 2024
7875a7c
Test
glenn-jocher Jan 18, 2024
b488089
Merge remote-tracking branch 'origin/test-local-python-script' into t…
glenn-jocher Jan 18, 2024
ac0e768
Test
glenn-jocher Jan 18, 2024
18f8e1d
Auto-format by https://ultralytics.com/actions
UltralyticsAssistant Jan 18, 2024
9e326a3
Test
glenn-jocher Jan 18, 2024
de3539c
Auto-format by https://ultralytics.com/actions
UltralyticsAssistant Jan 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions .github/workflows/format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ on:
branches: [main, master]
pull_request:
branches: [main, master]
types: [opened, closed, synchronize]

jobs:
format:
Expand All @@ -17,8 +18,12 @@ jobs:
- name: Run Ultralytics Formatting
uses: ultralytics/actions@main
with:
token: ${{ secrets.GITHUB_TOKEN }} # automatically generated
token: ${{ secrets.GITHUB_TOKEN }} # automatically generated, do not modify
python: true
prettier: true
markdown: true
spelling: false
links: true
links: false
summary: true # requires either 'openai_api_key' or 'openai_azure_api_key' and 'openai_azure_endpoint'
openai_azure_api_key: ${{ secrets.OPENAI_AZURE_API_KEY }}
openai_azure_endpoint: ${{ secrets.OPENAI_AZURE_ENDPOINT }}
#openai_api_key: ${{ secrets.OPENAI_API_KEY }}
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ To use this action in your Ultralytics repository:
with:
token: ${{ secrets.GITHUB_TOKEN }} # automatically generated
python: true
docstrings: true
markdown: true
spelling: true
links: true
Expand Down
40 changes: 38 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,32 @@ inputs:
description: "Run Broken Links checks"
required: false
default: "false"
summary:
description: "Run PR Summary"
required: false
default: "false"
openai_api_key:
description: "OpenAI API Key"
required: false
openai_azure_api_key:
description: "OpenAI Azure API Key"
required: false
openai_azure_endpoint:
description: "OpenAI Azure Endpoint"
required: false
openai_model:
description: "OpenAI Model"
required: false
default: "gpt-4-1106-preview"
runs:
using: "composite"
steps:
- name: Print Action Information
run: |
echo "github.event_name: ${{ github.event_name }}"
echo "github.event_name: ${{ github.event_name }}" # i.e. "pull_request"
echo "github.event.action: ${{ github.event.action }}" # i.e. "open" or "close"
echo "github.repository: ${{ github.repository }}"
echo "github.event.pull_request.number: ${{ github.event.pull_request.number }}"
echo "github.event.pull_request.head.repo.full_name: ${{ github.event.pull_request.head.repo.full_name }}"
echo "github.actor: ${{ github.actor }}"
echo "github.event.pull_request.head.ref: ${{ github.event.pull_request.head.ref }}"
Expand Down Expand Up @@ -88,7 +107,7 @@ runs:

# Prettier (JavaScript, JSX, Angular, Vue, Flow, TypeScript, CSS, HTML, JSON, GraphQL, Markdown, YAML) -------------
- name: Run Prettier
if: inputs.prettier == 'true' || inputs.markdown == 'true'
if: inputs.prettier == 'true'
run: |
npm install --global prettier
npx prettier \
Expand Down Expand Up @@ -123,6 +142,23 @@ runs:
shell: bash
continue-on-error: false

# PR Summary -------------------------------------------------------------------------------------------------------
- name: Summarize PR
if: inputs.summary == 'true' && (github.event_name == 'pull_request' || github.event_name == 'pull_request_target') && (inputs.event.action == 'opened' || inputs.event.action == 'closed')
env:
REPO_NAME: ${{ github.repository }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ inputs.token }}
OPENAI_API_KEY: ${{ inputs.openai_api_key }}
OPENAI_AZURE_API_KEY: ${{ inputs.openai_azure_api_key }}
OPENAI_AZURE_ENDPOINT: ${{ inputs.openai_azure_endpoint }}
OPENAI_MODEL: ${{ inputs.openai_model }}
run: |
pip install --no-cache -q openai
python utils/run_pr_summary.py
shell: bash
continue-on-error: true

# Broken links -----------------------------------------------------------------------------------------------------
- name: Broken Link Checker
if: inputs.links == 'true'
Expand Down
107 changes: 107 additions & 0 deletions utils/run_pr_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import os

import requests
from openai import OpenAI, AzureOpenAI

REPO_NAME = os.getenv("REPO_NAME")
PR_NUMBER = os.getenv("PR_NUMBER")
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
GITHUB_HEADERS = {"Authorization": f"token {GITHUB_TOKEN}"}
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
OPENAI_AZURE_API_KEY = os.getenv("OPENAI_AZURE_API_KEY")
OPENAI_AZURE_ENDPOINT = os.getenv("OPENAI_AZURE_ENDPOINT")
OPENAI_AZURE_BOTH = OPENAI_AZURE_API_KEY and OPENAI_AZURE_ENDPOINT
OPENAI_MODEL = os.getenv("OPENAI_MODEL")
OPENAI_MODEL_TOKENS = 128000 # update with model
SUMMARY_START = (
"## πŸ› οΈ PR Summary\n\n<sub>Made with ❀️ by [Ultralytics Actions](https://github.com/ultralytics/actions)<sub>\n\n"
)

# Checks
assert OPENAI_MODEL, "No model found, please define OPENAI_MODEL"
assert (
OPENAI_API_KEY or OPENAI_AZURE_BOTH
), "No OpenAI Keys found, please pass either OPENAI_API_KEY or both (OPENAI_AZURE_API_KEY and OPENAI_AZURE_ENDPOINT)"
if OPENAI_AZURE_API_KEY or OPENAI_AZURE_ENDPOINT:
assert OPENAI_AZURE_BOTH, "For Azure usage both both OPENAI_AZURE_API_KEY and OPENAI_AZURE_ENDPOINT must be passed."


def openai_client(azure=OPENAI_AZURE_BOTH):
"""Returns OpenAI client instance."""
return (
AzureOpenAI(
api_key=OPENAI_AZURE_API_KEY, api_version="2023-09-01-preview", azure_endpoint=OPENAI_AZURE_ENDPOINT
)
if azure
else OpenAI(api_key=OPENAI_API_KEY)
)


def get_pr_diff(repo_name, pr_number):
"""Fetches the diff of a specific PR from a GitHub repository."""
url = f"https://github.com/gitapi/repos/{repo_name}/pulls/{pr_number}"
headers = {"Authorization": f"token {GITHUB_TOKEN}", "Accept": "application/vnd.github.v3.diff"}
response = requests.get(url, headers=headers)
return response.text if response.status_code == 200 else ""


def generate_pr_summary(repo_name, diff_text):
"""Generates a professionally written yet accessible summary of a PR using OpenAI's API."""
if not diff_text:
diff_text = "**ERROR: DIFF IS EMPTY, THERE ARE ZERO CODE CHANGES IN THIS PR."
ratio = 3.3 # about 3.3 characters per token
limit = round(OPENAI_MODEL_TOKENS * ratio * 0.7) # use up to 70% of the context window
messages = [
{
"role": "system",
"content": "You are an Ultralytics AI assistant skilled in software development and technical communication. Your task is to summarize GitHub PRs from Ultralytics in a way that is accurate, concise, and understandable to both expert developers and non-expert users. Focus on highlighting the key changes and their impact in simple, concise terms.",
},
{
"role": "user",
"content": f"Summarize this '{repo_name}' PR, focusing on major changes, their purpose, and potential impact. Keep the summary clear and concise, suitable for a broad audience. Add emojis to enliven the summary. Reply directly with a summary along these example guidelines, though feel free to adjust as appropriate:\n\n"
f"### 🌟 Summary (single-line synopsis)\n"
f"### πŸ“Š Key Changes (bullet points highlighting any major changes)\n"
f"### 🎯 Purpose & Impact (bullet points explaining any benefits and potential impact to users)\n"
f"\n\nHere's the PR diff:\n\n{diff_text[:limit]}",
},
]
response = openai_client().chat.completions.create(model=OPENAI_MODEL, messages=messages).choices[0]
reply = response.message.content.strip()
if len(diff_text) > limit:
return SUMMARY_START + "**WARNING ⚠️** this PR is very large, summary may not cover all changes.\n\n" + reply
else:
return SUMMARY_START + reply


def update_pr_description(repo_name, pr_number, new_summary):
"""Updates the original PR description with a new summary, replacing an existing summary if found."""
# Fetch the current PR description
pr_url = f"https://github.com/gitapi/repos/{repo_name}/pulls/{pr_number}"
pr_response = requests.get(pr_url, headers=GITHUB_HEADERS)
pr_data = pr_response.json()
current_description = pr_data.get("body") or "" # warning, can be None

# Check if existing summary is present and update accordingly
if SUMMARY_START in current_description:
updated_description = current_description.split(SUMMARY_START)[0] + new_summary
else:
updated_description = current_description + "\n\n" + new_summary

# Update the PR description
update_response = requests.patch(pr_url, json={"body": updated_description}, headers=GITHUB_HEADERS)
return update_response.status_code


if __name__ == "__main__":
# Fetch PR details
diff = get_pr_diff(REPO_NAME, PR_NUMBER)

# Generate PR summary
summary = generate_pr_summary(REPO_NAME, diff)

# Update PR description
status_code = update_pr_description(REPO_NAME, PR_NUMBER, summary)
if status_code == 200:
print("PR description updated successfully.")
else:
print(f"Failed to update PR description. Status code: {status_code}")
Loading