Skip to content
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
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ jobs:
id: summary
uses: ./
with:
openAiKey: ${{ secrets.OPENAI_KEY }}
openAiOrg: ${{ secrets.OPENAI_ORG }}
#deepseekKey: ${{ secrets.DEEPSEEK_KEY }}
geminiKey: ${{ secrets.GEMINI_KEY }}
#openAiKey: ${{ secrets.OPENAI_KEY }}
#openAiOrg: ${{ secrets.OPENAI_ORG }}
#anthropicKey: ${{ secrets.ANTHROPIC_KEY }}
#notionKey: ${{ secrets.NOTION_KEY }}
#notionDbId: ${{ secrets.NOTION_DB_ID }}
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,6 @@ typings/
# TernJS port file
.tern-port

.env*

TODO.md
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ In the future we will allow more customization of the task/issue management tool
- **`openAiModel`** (optional): OpenAI model to use (e.g. gpt-4o, gpt-3.5-turbo). Default: gpt-4o
- **`anthropicKey`** (optional): Anthropic API key. Ignored if empty.
- **`anthropicModel`** (optional): Anthropic model to use (e.g. claude-3-opus-20240229, claude-3-5-sonnet-20240620). Default: claude-3-5-sonnet-20240620.
- **`deepseekKey`** (optional): Deepseek API key. If empty it will be ignored.
- **`deepseekModel`** (optional): Deepseek model to use (e.g. deepseek-chat, deepseek-reasoner). Default: deepseek-chat.
- **`geminiKey`** (optional): Gemini API key. If empty it will be ignored.
- **`geminiModel`** (optional): Gemini model to use (e.g. gemini-2.0-flash, gemini-1.5-pro). Default: gemini-2.0-flash.
- **`linearKey`** (optional): Linear API key. Ignored if empty.
- **`linearViewId`** (optional): Linear view ID. This allows you to specify a custom view for fetching issues that can be modified to your needs.
- **`notionKey`** (optional): Notion API key.
Expand Down Expand Up @@ -117,5 +121,6 @@ jobs:
- [ ] Add support for other task/issue management tools
- [ ] Add support for other LLM providers
- [x] different model selection
- [ ] DeepSeek
- [x] DeepSeek
- [x] Gemini
- [ ] Qween
20 changes: 19 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ inputs:
description: "Anthropic model to use (e.g. claude-3-opus-20240229, claude-3-5-sonnet-20240620). Default: claude-3-5-sonnet-20240620"
required: false
default: "claude-3-5-sonnet-20240620"
deepseekKey:
description: "Deepseek API key. If empty it will be ignored."
required: false
deepseekModel:
description: "Deepseek model to use (e.g. deepseek-chat, deepseek-reasoner). Default: deepseek-chat"
required: false
default: "deepseek-chat"
geminiKey:
description: "Gemini API key. If empty it will be ignored."
required: false
geminiModel:
description: "Gemini model to use (e.g. gemini-2.0-flash, gemini-1.5-pro). Default: gemini-2.0-flash"
required: false
default: "gemini-2.0-flash"
linearKey:
description: "Linear API key. If empty it will be ignored."
required: false
Expand Down Expand Up @@ -87,7 +101,7 @@ runs:
pip install -r ${{ github.action_path }}/requirements.txt
else
echo "requirements.txt not found, installing dependencies directly"
pip install 'requests==2.32.3' 'anthropic==0.40.0' 'openai==1.55.3' 'nltk==3.9.1' 'markdown-it-py==3.0.0'
pip install 'requests==2.32.3' 'anthropic==0.40.0' 'openai==1.55.3' 'nltk==3.9.1' 'markdown-it-py==3.0.0' 'google-genai==1.2.0'
fi

- name: 🤖 Run
Expand All @@ -109,6 +123,10 @@ runs:
OPENAI_MODEL: ${{ inputs.openAiModel }}
ANTHROPIC_KEY: ${{ inputs.anthropicKey }}
ANTHROPIC_MODEL: ${{ inputs.anthropicModel }}
DEEPSEEK_KEY: ${{ inputs.deepseekKey }}
DEEPSEEK_MODEL: ${{ inputs.deepseekModel }}
GEMINI_KEY: ${{ inputs.geminiKey }}
GEMINI_MODEL: ${{ inputs.geminiModel }}
NOTION_KEY: ${{ inputs.notionKey }}
LINEAR_KEY: ${{ inputs.linearKey }}
LINEAR_VIEW_ID: ${{ inputs.linearViewId }}
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ anthropic==0.40.0
openai==1.55.3
pytest==8.3.3
nltk==3.9.1
markdown-it-py==3.0.0
markdown-it-py==3.0.0
google-genai==1.2.0
36 changes: 36 additions & 0 deletions src/deepseek_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import requests

def deepseek_summary(issues, prompt, key, model="deepseek-chat"):
url = "https://api.deepseek.com/chat/completions"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {key}"
}

prompt = f"{prompt} {issues}"
data = {
"model": model,
"messages": [
{"role": "user", "content": prompt}
],
"stream": False
}

response = requests.post(url, headers=headers, json=data)

if response.status_code == 200:
result = response.json()
if not result.choices:
raise ValueError("No response choices available")
if not result.choices[0]:
raise ValueError("First choice is null")
if not result.choices[0].message:
raise ValueError("Message is null")

summary = result.choices[0].message.content
if not summary:
raise ValueError("Summary is null or empty.")

return summary
else:
raise ValueError("Request failed with status code: " + str(response.status_code))
Comment on lines +19 to +36
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix response parsing and add request error handling.

  1. The response parsing has incorrect attribute access.
  2. Missing try-catch for request errors.

Apply this diff to fix the issues:

-    response = requests.post(url, headers=headers, json=data)
+    try:
+        response = requests.post(url, headers=headers, json=data)
+    except requests.exceptions.RequestException as e:
+        raise ValueError(f"Request failed: {str(e)}")

     if response.status_code == 200:
         result = response.json()
-        if not result.choices:
+        if not result.get('choices'):
             raise ValueError("No response choices available")
-        if not result.choices[0]:
+        if not result['choices'][0]:
             raise ValueError("First choice is null")
-        if not result.choices[0].message:
+        if not result['choices'][0].get('message'):
             raise ValueError("Message is null")

-        summary = result.choices[0].message.content
+        summary = result['choices'][0]['message']['content']
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
response = requests.post(url, headers=headers, json=data)
if response.status_code == 200:
result = response.json()
if not result.choices:
raise ValueError("No response choices available")
if not result.choices[0]:
raise ValueError("First choice is null")
if not result.choices[0].message:
raise ValueError("Message is null")
summary = result.choices[0].message.content
if not summary:
raise ValueError("Summary is null or empty.")
return summary
else:
raise ValueError("Request failed with status code: " + str(response.status_code))
try:
response = requests.post(url, headers=headers, json=data)
except requests.exceptions.RequestException as e:
raise ValueError(f"Request failed: {str(e)}")
if response.status_code == 200:
result = response.json()
if not result.get('choices'):
raise ValueError("No response choices available")
if not result['choices'][0]:
raise ValueError("First choice is null")
if not result['choices'][0].get('message'):
raise ValueError("Message is null")
summary = result['choices'][0]['message']['content']
if not summary:
raise ValueError("Summary is null or empty.")
return summary
else:
raise ValueError("Request failed with status code: " + str(response.status_code))

14 changes: 14 additions & 0 deletions src/gemini_summary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from google import genai

def gemini_summary(issues, prompt, key, model="gemini-2.0-flash"):
client = genai.Client(api_key=key)

prompt = f"{prompt} {issues}"
response = client.models.generate_content(
model=model, contents=prompt
)

if not response or not response.text:
raise ValueError("Summary is null or empty.")

return response.text
Comment on lines +3 to +14
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling and input validation.

The function needs additional error handling and input validation:

  1. Add validation for empty/null input parameters.
  2. Add try-catch block for API errors.
  3. Consider reusing the client instance for better performance.

Apply this diff to improve error handling and validation:

 def gemini_summary(issues, prompt, key, model="gemini-2.0-flash"):
+    if not issues or not prompt or not key:
+        raise ValueError("Required parameters (issues, prompt, key) cannot be empty.")
+
     client = genai.Client(api_key=key)
     
     prompt = f"{prompt} {issues}"
-    response = client.models.generate_content(
-        model=model, contents=prompt
-    )
+    try:
+        response = client.models.generate_content(
+            model=model, contents=prompt
+        )
+    except Exception as e:
+        raise ValueError(f"Failed to generate content: {str(e)}")
  
     if not response or not response.text:
         raise ValueError("Summary is null or empty.")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def gemini_summary(issues, prompt, key, model="gemini-2.0-flash"):
client = genai.Client(api_key=key)
prompt = f"{prompt} {issues}"
response = client.models.generate_content(
model=model, contents=prompt
)
if not response or not response.text:
raise ValueError("Summary is null or empty.")
return response.text
def gemini_summary(issues, prompt, key, model="gemini-2.0-flash"):
if not issues or not prompt or not key:
raise ValueError("Required parameters (issues, prompt, key) cannot be empty.")
client = genai.Client(api_key=key)
prompt = f"{prompt} {issues}"
try:
response = client.models.generate_content(
model=model, contents=prompt
)
except Exception as e:
raise ValueError(f"Failed to generate content: {str(e)}")
if not response or not response.text:
raise ValueError("Summary is null or empty.")
return response.text

10 changes: 10 additions & 0 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
from claude_summary import claude_summary
from linear import linear
from openai_summary import openai_summary
from deepseek_summary import deepseek_summary
from gemini_summary import gemini_summary
from notion import notion
from helpers import is_empty

Expand All @@ -15,6 +17,10 @@ def main():
OPENAI_MODEL = os.environ.get("OPENAI_MODEL")
ANTHROPIC_KEY = os.environ.get("ANTHROPIC_KEY")
ANTHROPIC_MODEL = os.environ.get("ANTHROPIC_MODEL")
DEEPSEEK_KEY = os.environ.get("DEEPSEEK_KEY")
DEEPSEEK_MODEL = os.environ.get("DEEPSEEK_MODEL")
GEMINI_KEY = os.environ.get("GEMINI_KEY")
GEMINI_MODEL = os.environ.get("GEMINI_MODEL")
NOTION_KEY = os.environ.get("NOTION_KEY")
NOTION_DB_ID = os.environ.get("NOTION_DB_ID")
LINEAR_KEY = os.environ.get("LINEAR_KEY")
Expand All @@ -40,6 +46,10 @@ def main():
release_notes = claude_summary(issues, PROMPT, ANTHROPIC_KEY, ANTHROPIC_MODEL)
elif(not is_empty(OPENAI_KEY) and not is_empty(OPENAI_ORG)):
release_notes = openai_summary(issues, PROMPT, OPENAI_KEY, OPENAI_ORG, OPENAI_MODEL)
elif(not is_empty(DEEPSEEK_KEY)):
release_notes = deepseek_summary(issues, PROMPT, DEEPSEEK_KEY, DEEPSEEK_MODEL)
elif(not is_empty(GEMINI_KEY)):
release_notes = gemini_summary(issues, PROMPT, GEMINI_KEY, GEMINI_MODEL)

if(not is_empty(NOTION_KEY) and not is_empty(release_notes)):
notion(release_notes, COMMITS, NOTION_KEY, NOTION_DB_ID, VERSION, CHANGELOG, PR_LINK)
Expand Down