Skip to content

Commit

Permalink
CodelldBot
Browse files Browse the repository at this point in the history
  • Loading branch information
vadimcn committed Jan 11, 2025
1 parent c06be2e commit c9020a4
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 0 deletions.
150 changes: 150 additions & 0 deletions .github/workflows/analyze_issue.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import os
import json
import io
import time
from openai import OpenAI
from openai.types.beta.assistant_stream_event import ThreadRunRequiresAction, ThreadMessageCompleted
from octokit import Octokit

class IssueAnalyzer:
def __init__(self):
self.octokit = Octokit()
self.openai = OpenAI()
self.repo_full_name = os.getenv('GITHUB_REPOSITORY')

def handle_event(self):

with open(os.getenv('GITHUB_EVENT_PATH'), 'rb') as f:
event = json.load(f)

match os.getenv('GITHUB_EVENT_NAME'):
case 'issues':
issue = event['issue']
case 'workflow_dispatch':
issue_number = int(event['inputs']['issue'])
owner, repo = self.repo_full_name.split('/')
response = self.octokit.issues.get(owner=owner, repo=repo, issue_number=issue_number)
issue = response.json

assistant = self.openai.beta.assistants.retrieve(os.getenv('ASSISTANT_ID'))

issue_file = self.openai.files.create(
file=('BUG_REPORT.md', self.make_issue_content(issue, show_labels=False)),
purpose='assistants'
)

thread = self.openai.beta.threads.create(
metadata={
'issue': f'{issue["number"]}: {issue["title"]}',
'run_id': os.getenv('GITHUB_RUN_ID'),
'model': f'{assistant.model} t={assistant.temperature} top_p={assistant.top_p}',
},
messages=[{
'role': 'user',
'content': 'We have a new issue report (attached as BUG_REPORT.md)',
'attachments': [{
'file_id': issue_file.id,
'tools': [{'type': 'file_search'}]
}]
}
]
)
print('Thread:', thread.id)

thread_vstore_id = thread.tool_resources.file_search.vector_store_ids[0]
self.wait_vector_store(thread_vstore_id)

stream = self.openai.beta.threads.runs.create(
assistant_id=assistant.id,
thread_id=thread.id,
stream=True
)

streams = [stream]
while streams:
stream = streams.pop(0)
for event in stream:
match event:
case ThreadMessageCompleted():
for c in event.data.content:
print('<< Message >>', c.text.value)
case ThreadRunRequiresAction():
tool_outputs = []
for tool in event.data.required_action.submit_tool_outputs.tool_calls:
args = json.loads(tool.function.arguments)
print(f'<< Tool call >>', tool.function.name, args)
match tool.function.name:
case 'search_github':
query = f'repo:{self.repo_full_name} {args["query"]}'
output = self.search_github(query, thread_vstore_id, exclude=[issue['number']])
tool_outputs.append({'tool_call_id': tool.id, 'output': output})
case 'add_issue_labels':
tool_outputs.append({'tool_call_id': tool.id, 'output': 'Ok'})
case 'set_issue_title':
tool_outputs.append({'tool_call_id': tool.id, 'output': 'Ok'})
case 'add_issue_comment':
tool_outputs.append({'tool_call_id': tool.id, 'output': 'Ok'})

new_stream = self.openai.beta.threads.runs.submit_tool_outputs(
thread_id=thread.id,
run_id=event.data.id,
tool_outputs=tool_outputs,
stream=True)
streams.append(new_stream)

def search_github(self, query: str, vstore_id: str, exclude:list=[], max_results=5) -> str:
response = self.octokit.search.issues(q=query)
if response.json.get('status'):
return f'Search failed: {response.json["message"]}'

result_lines = []
for issue in response.json['items']:
issue_number = issue['number']
if issue_number in exclude:
continue
issue_file = self.openai.files.create(
file=(f'ISSUE_{issue_number}.md', self.make_issue_content(issue, fetch_comments=True)),
purpose='assistants'
)
self.openai.beta.vector_stores.files.create(
vector_store_id=vstore_id,
file_id=issue_file.id,
)
result_lines.append(f'Issue number: {issue_number}, file name: {issue_file.filename}')
if len(result_lines) >= max_results:
break

self.wait_vector_store(vstore_id)

result_lines.insert(0, f'Found {len(result_lines)} issues and attached as files to this thread:')
return '\n'.join(result_lines)

def make_issue_content(self, issue, fetch_comments=False, show_labels=True) -> bytes:
f = io.StringIO()
f.write(f'### Title: {issue["title"]}\n')
f.write(f'### Author: {issue["user"]["login"]}\n')
f.write(f'### State: {issue["state"]}\n')
if show_labels:
f.write(f'### Labels: {",".join(label["name"] for label in issue["labels"])}\n')
f.write(f'\n{issue["body"]}\n')

if fetch_comments:
owner, repo = self.repo_full_name.split('/')
comments = self.octokit.issues.list_issue_comments(
owner=owner, repo=repo, issue_number=issue['number'])
for comment in comments.json:
f.write(f'### Comment by {comment["user"]["login"]}\n')
f.write(f'\n{comment["body"]}\n')

return f.getvalue().encode('utf-8')

def wait_vector_store(self, vstore_id):
vstore = self.openai.beta.vector_stores.retrieve(vstore_id)
while vstore.status == 'in_progress':
print('Waiting for vector store update.')
time.sleep(1)
vstore = self.openai.beta.vector_stores.retrieve(vstore_id)


if __name__ == '__main__':
IssueAnalyzer().handle_event()
35 changes: 35 additions & 0 deletions .github/workflows/analyze_issue.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: analyze_issue

on:
issues:
types: [opened]
workflow_dispatch:
inputs:
issue: null

jobs:
analyze_issue:
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.11"
cache: 'pip'
cache-dependency-path: .github/workflows/analyze_issue.yml

- name: Install dependencies
run: pip install openai octokitpy

- name: Analysis
env:
GITHUB_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
ASSISTANT_ID: ${{ vars.ASSISTANT_ID }}
PYTHONUNBUFFERED: 1
run: |
python .github/workflows/analyze_issue.py

0 comments on commit c9020a4

Please sign in to comment.