Compare commits

...

20 Commits

Author SHA1 Message Date
Heiner Lohaus 04a5e33ebe Fix error in copilot 2024-01-21 20:04:43 +01:00
Heiner Lohaus d9386946cf Fix error in copilot 2024-01-21 20:01:53 +01:00
Heiner Lohaus 7e63b412b6 Join Phind context 2024-01-21 19:58:39 +01:00
H Lohaus 64052f253b
Merge pull request #1495 from RasyiidWho/main
Fix Phind context and chat history not keeping up with the prompt
2024-01-21 19:53:10 +01:00
H Lohaus d01f3f6b61
Merge pull request #1496 from hlohaus/neww
Check for existing comments
2024-01-21 19:44:17 +01:00
Heiner Lohaus 270bc8a44a Check for existing comments 2024-01-21 19:35:10 +01:00
RasyiidWho bab62e4345 Fix Phind context and chat history not keeping up with the prompt 2024-01-22 01:33:41 +07:00
H Lohaus c809346b43
Merge pull request #1494 from hlohaus/neww
No PR number found
2024-01-21 17:42:03 +01:00
Heiner Lohaus 04ec265ba0 Cast pr number 2024-01-21 17:41:40 +01:00
Heiner Lohaus f5ab6f8531 No PR number found 2024-01-21 17:38:26 +01:00
H Lohaus 4f18d864be
Merge pull request #1492 from hlohaus/neww
First checkout repo
2024-01-21 17:33:54 +01:00
Heiner Lohaus 46a8019e1f First checkout repo 2024-01-21 17:31:57 +01:00
H Lohaus 556373b724
Merge pull request #1491 from hlohaus/neww
Run tests
2024-01-21 17:27:14 +01:00
Heiner Lohaus f720105953 Save pr number 2024-01-21 17:26:26 +01:00
Heiner Lohaus 283bddc152 Run tests 2024-01-21 17:01:26 +01:00
H Lohaus ca6da4c858
Merge pull request #1490 from hlohaus/neww
Improve doctypes in copilot
2024-01-21 16:59:52 +01:00
Heiner Lohaus 1e8a343ba3 Print env 2024-01-21 16:59:34 +01:00
Heiner Lohaus 0a3070e605 On workflow_run copilot 2024-01-21 16:57:26 +01:00
Heiner Lohaus eb1668518f On workflow_run copilot 2024-01-21 16:42:55 +01:00
Heiner Lohaus fd0507a422 Improve doctypes in copilot 2024-01-21 15:07:29 +01:00
5 changed files with 114 additions and 47 deletions

View File

@ -1,29 +1,53 @@
name: AI Code Reviewer
on:
pull_request:
workflow_run:
workflows: ["Unittest"]
types:
- opened
- synchronize
- completed
jobs:
review:
runs-on: ubuntu-latest
permissions: write-all
permissions:
contents: read
pull-requests: write
steps:
- name: Checkout Repo
uses: actions/checkout@v3
- name: 'Download artifact'
uses: actions/github-script@v6
with:
script: |
let allArtifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.payload.workflow_run.id,
});
let matchArtifact = allArtifacts.data.artifacts.filter((artifact) => {
return artifact.name == "pr_number"
})[0];
let download = await github.rest.actions.downloadArtifact({
owner: context.repo.owner,
repo: context.repo.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});
let fs = require('fs');
fs.writeFileSync(`${process.env.GITHUB_WORKSPACE}/pr_number.zip`, Buffer.from(download.data));
- name: 'Unzip artifact'
run: unzip pr_number.zip
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: "3.x"
cache: 'pip'
- name: Install Requirements
run: pip install -r requirements.txt
- name: Install PyGithub
run: pip install PyGithub
run: |
pip install -r requirements.txt
pip install PyGithub
- name: AI Code Review
env:
GITHUB_TOKEN: ${{ secrets.COPILOT_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_REPOSITORY: ${{ github.repository }}
run: python -m etc.tool.copilot

View File

@ -23,4 +23,14 @@ jobs:
- name: Install requirements
run: pip install -r requirements.txt
- name: Run tests
run: python -m etc.unittest
run: python -m etc.unittest
- name: Save PR number
env:
PR_NUMBER: ${{ github.event.number }}
run: |
mkdir -p ./pr
echo $PR_NUMBER > ./pr/pr_number
- uses: actions/upload-artifact@v4
with:
name: pr_number
path: pr/

View File

@ -16,39 +16,54 @@ g4f.debug.logging = True
g4f.debug.version_check = False
GITHUB_TOKEN = os.getenv('GITHUB_TOKEN')
GITHUB_REPOSITORY = os.getenv('GITHUB_REPOSITORY')
G4F_PROVIDER = os.getenv('G4F_PROVIDER')
G4F_MODEL = os.getenv('G4F_MODEL') or g4f.models.gpt_4
def get_pr_details(github: Github) -> PullRequest:
"""
Rteurns the details of the pull request from GitHub.
Retrieves the details of the pull request from GitHub.
Args:
github (Github): The Github object to interact with the GitHub API.
Returns:
PullRequest: A PullRequest instance.
PullRequest: An object representing the pull request.
"""
with open(os.getenv('GITHUB_EVENT_PATH', ''), 'r') as file:
data = json.load(file)
with open('./pr_number', 'r') as file:
pr_number = file.read().strip()
if not pr_number:
return
repo = github.get_repo(f"{data['repository']['owner']['login']}/{data['repository']['name']}")
pull = repo.get_pull(data['number'])
repo = github.get_repo(GITHUB_REPOSITORY)
pull = repo.get_pull(int(pr_number))
return pull
def get_diff(diff_url: str) -> str:
"""
Fetches the diff of the pull request.
Fetches the diff of the pull request from a given URL.
Args:
pull (PullRequest): Pull request.
diff_url (str): URL to the pull request diff.
Returns:
str or None: The diff of the pull request or None if not available.
str: The diff of the pull request.
"""
response = requests.get(diff_url)
response.raise_for_status()
return response.text
def read_json(text: str) -> dict:
"""
Parses JSON code block from a string.
Args:
text (str): A string containing a JSON code block.
Returns:
dict: A dictionary parsed from the JSON code block.
"""
match = re.search(r"```(json|)\n(?P<code>[\S\s]+?)\n```", text)
if match:
text = match.group("code")
@ -56,23 +71,33 @@ def read_json(text: str) -> dict:
return json.loads(text.strip())
except json.JSONDecodeError:
print("No valid json:", text)
return {}
return {}
def read_text(text: str) -> dict:
def read_text(text: str) -> str:
"""
Extracts text from a markdown code block.
Args:
text (str): A string containing a markdown code block.
Returns:
str: The extracted text.
"""
match = re.search(r"```(markdown|)\n(?P<text>[\S\s]+?)\n```", text)
if match:
return match.group("text")
return text
def get_ai_response(prompt, as_json: bool = True) -> Union[dict, str]:
def get_ai_response(prompt: str, as_json: bool = True) -> Union[dict, str]:
"""
Gets a response from g4f API based on the prompt.
Args:
prompt (str): The prompt to send to g4f.
as_json (bool): Whether to parse the response as JSON.
Returns:
dict: The parsed response from g4f.
Union[dict, str]: The parsed response from g4f, either as a dictionary or a string.
"""
response = g4f.ChatCompletion.create(
G4F_MODEL,
@ -80,20 +105,18 @@ def get_ai_response(prompt, as_json: bool = True) -> Union[dict, str]:
G4F_PROVIDER,
ignore_stream_and_auth=True
)
if as_json:
return read_json(response)
return read_text(response)
return read_json(response) if as_json else read_text(response)
def analyze_code(pull: PullRequest, diff: str)-> list:
def analyze_code(pull: PullRequest, diff: str)-> list[dict]:
"""
Analyzes the code changes in the pull request.
Args:
pull (PullRequest): The pull request object.
diff (str): The diff of the pull request.
pr_details (dict): Details of the pull request.
Returns:
list: List of comments generated by the analysis.
list[dict]: A list of comments generated by the analysis.
"""
comments = []
changed_lines = []
@ -108,8 +131,8 @@ def analyze_code(pull: PullRequest, diff: str)-> list:
if match:
offset_line = int(match.group(1))
elif current_file_path:
if line.startswith('\\') or line.startswith('diff') and changed_lines:
prompt = create_prompt(changed_lines, pull, current_file_path)
if (line.startswith('\\') or line.startswith('diff')) and changed_lines:
prompt = create_analyze_prompt(changed_lines, pull, current_file_path)
response = get_ai_response(prompt)
for review in response.get('reviews', []):
review['path'] = current_file_path
@ -122,13 +145,14 @@ def analyze_code(pull: PullRequest, diff: str)-> list:
return comments
def create_prompt(changed_lines: list, pull: PullRequest, file_path: str):
def create_analyze_prompt(changed_lines: list[str], pull: PullRequest, file_path: str):
"""
Creates a prompt for the g4f model.
Args:
diff (str): The line of code to analyze.
pr_details (dict): Details of the pull request.
changed_lines (list[str]): The lines of code that have changed.
pull (PullRequest): The pull request object.
file_path (str): The path to the file being reviewed.
Returns:
str: The generated prompt.
@ -159,19 +183,19 @@ Each line is prefixed by its number. Code to review:
def create_review_prompt(pull: PullRequest, diff: str):
"""
Creates a prompt to create a review.
Creates a prompt to create a review comment.
Args:
diff (str): The line of code to analyze.
pull (PullRequest): The pull request object.
diff (str): The diff of the pull request.
Returns:
str: The generated prompt.
str: The generated prompt for review.
"""
return f"""Your task is to review a pull request. Instructions:
- Your name / you are copilot.
- Write in name of g4f copilot. Don't use placeholder.
- Write the review in GitHub Markdown format.
- Thank the author for contributing to the project.
- Point out that you might leave a few comments on the files.
Pull request author: {pull.user.name}
Pull request title: {pull.title}
@ -190,9 +214,15 @@ def main():
try:
github = Github(GITHUB_TOKEN)
pull = get_pr_details(github)
if not pull:
print(f"No PR number found")
exit()
if pull.get_reviews().totalCount > 0 or pull.get_comments().totalCount > 0:
print(f"Has already a review")
exit()
diff = get_diff(pull.diff_url)
except Exception as e:
print(f"Error get details: {e}")
print(f"Error get details: {e.__class__.__name__}: {e}")
exit(1)
try:
review = get_ai_response(create_review_prompt(pull, diff), False)
@ -206,7 +236,10 @@ def main():
exit(1)
print("Comments:", comments)
try:
pull.create_review(body=review, comments=comments)
if comments:
pull.create_review(body=review, comments=comments)
else:
pull.create_comment(body=review)
except Exception as e:
print(f"Error posting review: {e}")
exit(1)

View File

@ -6,14 +6,14 @@ from g4f import ChatCompletion, get_last_provider
from g4f.Provider import RetryProvider
from .mocks import ProviderMock
class TestChatCompletion(unittest.TestCase):
class NoTestChatCompletion(unittest.TestCase):
def test_create_default(self):
def no_test_create_default(self):
result = ChatCompletion.create(g4f.models.default, DEFAULT_MESSAGES)
if "Good" not in result and "Hi" not in result:
self.assertIn("Hello", result)
def test_bing_provider(self):
def no_test_bing_provider(self):
provider = g4f.Provider.Bing
result = ChatCompletion.create(g4f.models.default, DEFAULT_MESSAGES, provider)
self.assertIn("Bing", result)

View File

@ -39,10 +39,10 @@ class Phind(AsyncGeneratorProvider):
prompt = messages[-1]["content"]
data = {
"question": prompt,
"questionHistory": [
"question_history": [
message["content"] for message in messages[:-1] if message["role"] == "user"
],
"answerHistory": [
"answer_history": [
message["content"] for message in messages if message["role"] == "assistant"
],
"webResults": [],
@ -55,7 +55,7 @@ class Phind(AsyncGeneratorProvider):
"creativeMode": creative_mode,
"customLinks": []
},
"context": "",
"context": "\n".join([message["content"] for message in messages if message["role"] == "system"]),
"rewrittenQuestion": prompt,
"challenge": 0.21132115912208504
}