Skip to content

feat(changelog): add a gen ai summary of the diff #16

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

Merged
merged 1 commit into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
88 changes: 88 additions & 0 deletions changelog_generator/ai_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import json
import logging
import os
from google import genai
from google.genai import types
from google.oauth2 import service_account

PROMPT = (
"Here's a git diff between two versions (represented as git tags). "
"Write a summary of this diff by following this structure "
"A TL;DR section with very few lines expressing the salient points of your analysis "
"A Functional Changes section that should be understandable by readers that have no technical knowledge "
"A section about possible regressions that could appear : this section should aim to be brief as it might be used during a production incident, but it musn't be too generic either"
"A section about key kong/log metrics that should be observed during the production release process, these must be very precise"
"The summary must be written with github and slack compatible markdown. It should be pleasing and give priority to salient points, must use colors and must use tasteful fonts, and follow best practices. You can use UML schemas and diagrams."
"Do not print a description of each commit, the goal is to be as concise as possible. Do not take more than 200 words."
)

SCOPES = [
"https://www.googleapis.com/auth/generative-language",
"https://www.googleapis.com/auth/cloud-platform",
]


def generate_ai_summary(git_diff: str | None) -> str | None:
if not git_diff:
return "No changes were provided in the diff"

project = os.getenv("VERTEX_PROJECT")
location = os.getenv("VERTEX_LOCATION")
model = os.getenv("VERTEX_MODEL")
service_account_key = os.getenv("VERTEX_CREDENTIALS")
prompt = os.getenv("VERTEX_PROMPT", PROMPT)

if not project:
logging.error("Missing VERTEX_PROJECT environment variable")

if not location:
logging.error("Missing VERTEX_LOCATION environment variable")

if not model:
logging.error("Missing VERTEX_MODEL environment variable")

if not service_account_key:
logging.error("Missing VERTEX_CREDENTIALS environment variable")

credentials = service_account.Credentials.from_service_account_info(
json.loads(service_account_key), scopes=SCOPES,
)
client = genai.Client(
vertexai=True,
project=project,
location=location,
credentials=credentials,
)

contents = [
types.Content(
role="user",
parts=[types.Part.from_text(f"{prompt} {git_diff}")],
)
]
generate_content_config = types.GenerateContentConfig(
temperature=1,
top_p=0.95,
max_output_tokens=8192,
response_modalities=["TEXT"],
safety_settings=[
types.SafetySetting(category="HARM_CATEGORY_HATE_SPEECH", threshold="OFF"),
types.SafetySetting(
category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="OFF"
),
types.SafetySetting(
category="HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold="OFF"
),
types.SafetySetting(category="HARM_CATEGORY_HARASSMENT", threshold="OFF"),
],
)

res = []
for chunk in client.models.generate_content_stream(
model=model,
contents=contents,
config=generate_content_config,
):
res.append(chunk.text.replace("`", "'"))

return "".join(res)
5 changes: 5 additions & 0 deletions changelog_generator/changelog_template.jinja
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# [{{current_tag}}](https://github.com/{{organization}}/{{repository}}/compare/{{previous_tag}}...{{current_tag}})

{% if ai_summary %}
## AI generated summary
{{ ai_summary }}
{% endif -%}

{% for type_node in commit_trees %}
## {{type_node.commit_type}}

Expand Down
6 changes: 6 additions & 0 deletions changelog_generator/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .commit import Commit
from .repository_manager import RepositoryManager
from .ai_generator import generate_ai_summary


class CommitTree(NamedTuple):
Expand All @@ -18,6 +19,7 @@ def render_changelog(
previous_tag: str,
current_tag: str,
commit_trees: Sequence[CommitTree],
ai_summary: str | None,
) -> str:
template_loader = FileSystemLoader(searchpath=os.path.dirname(__file__))
template_environment = Environment(loader=template_loader)
Expand All @@ -29,6 +31,7 @@ def render_changelog(
previous_tag=previous_tag,
current_tag=current_tag,
commit_trees=commit_trees,
ai_summary=ai_summary,
)


Expand Down Expand Up @@ -67,15 +70,18 @@ def generate(
if target:
commits = repository.from_target(target)
previous_tag, current_tag = target.split("..")
diff = None
else:
commits = repository.commits_since_last_tag
previous_tag, current_tag = repository.previous_tag, repository.current_tag
diff = repository.get_diff_since_last_tag

changelog = render_changelog(
organization=repository.organization,
repository=repository.name,
previous_tag=previous_tag,
current_tag=current_tag,
commit_trees=get_commit_trees(commits),
ai_summary=generate_ai_summary(diff),
)
return changelog
8 changes: 8 additions & 0 deletions changelog_generator/repository_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,14 @@ def commits_since_last_tag(self) -> Sequence[Commit]:
revision = self.current_tag
return self._get_commits(revision)

@property
@lru_cache()
def get_diff_since_last_tag(self) -> str | None:
if not self.previous_tag:
return None

return self.repository.git.diff(f"{self.previous_tag}..{self.current_tag}")

def _get_commits(self, revision: str) -> Sequence[Commit]:
options = {"no_merges": True}
if self.filter_paths:
Expand Down
1 change: 1 addition & 0 deletions requirements.in
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
gitpython
jinja2
google-genai
Loading