Skip to content

Commit

Permalink
Merge pull request #5 from typhonshambo/main
Browse files Browse the repository at this point in the history
Update formatting to prevent linting errors
  • Loading branch information
typhonshambo authored Jul 7, 2024
2 parents 6703ba9 + 4f2d3be commit b8b9685
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 89 deletions.
33 changes: 33 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "Python 3",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
"customizations": {
"codespaces": {
"openFiles": [
"README.md",
"streamlit.py"
]
},
"vscode": {
"settings": {},
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance"
]
}
},
"updateContentCommand": "[ -f packages.txt ] && sudo apt update && sudo apt upgrade -y && sudo xargs apt install -y <packages.txt; [ -f requirements.txt ] && pip3 install --user -r requirements.txt; pip3 install --user streamlit; echo '✅ Packages installed and Requirements met'",
"postAttachCommand": {
"server": "streamlit run streamlit.py --server.enableCORS false --server.enableXsrfProtection false"
},
"portsAttributes": {
"8501": {
"label": "Application",
"onAutoForward": "openPreview"
}
},
"forwardPorts": [
8501
]
}
8 changes: 8 additions & 0 deletions .flake8
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[flake8]
ignore = E302, W292
exclude =
.git,
__pycache__,
venv
examples
max-line-length = 120
39 changes: 39 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Lint and Streamlit Check

on:
push:
branches:
- '**'
pull_request:
branches:
- '**'

jobs:
run-linters:
name: Run Linters
runs-on: ubuntu-latest

steps:
- name: Check out Git repository
uses: actions/checkout@v3

- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install Python dependencies
run: |
pip install black flake8 streamlit
- name: Run Python linters
uses: wearerequired/lint-action@v2
with:
auto_fix: true
black: true
flake8: true

- name: Streamlit Check
run: |
# Remove the old 'streamlit run streamlit.py' line.
python streamlit_test.py && echo "Streamlit test passed!" || echo "Streamlit test failed."
5 changes: 2 additions & 3 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from .gemini_analyzer import CodeAnalyzer
__all__ = [
'CodeAnalyzer'
]

__all__ = ["CodeAnalyzer"]
93 changes: 50 additions & 43 deletions app/gemini_analyzer.py
Original file line number Diff line number Diff line change
@@ -1,39 +1,44 @@
import google.generativeai as genai
from typing import Optional, Union, TypedDict
from typing import Optional, Union

#for logging
# for logging
import structlog

structlog.stdlib.recreate_defaults()
log = structlog.get_logger(__name__).info(f"module loaded successfully.")
log = structlog.get_logger(__name__).info("module loaded successfully.")


class CodeAnalyzer:
def __init__(self) -> None:
from .gemini_config import AnalyzerConfigs, ResponseData #prevent circular import

self.language: str[Optional] = "python"
self.style_guide: str[Optional] = "google official"
self.gemini_model = genai.GenerativeModel(
AnalyzerConfigs.gemini_models[1],
generation_config={
"response_mime_type": "application/json",
"response_schema": list[ResponseData]
}
)
self._gen_ai_config = genai.configure(api_key=AnalyzerConfigs.genai_api_key)
self._input_code: str = None
self._prompt: str = None
self._response: Union[list, None] = None
self._logger = structlog.get_logger("CodeAnalyzer")

def analyze_code(self) -> Union[list, None]:
'''
This method analyzes the input code and generates a style guide using the Gemini model.
'''
try:
prompt = f"""Analyze the following {self.language} code according to {self.style_guide} style guidelines:
def __init__(self) -> None:
from .gemini_config import (
AnalyzerConfigs,
ResponseData,
) # prevent circular import

self.language: str[Optional] = "python"
self.style_guide: str[Optional] = "google official"
self.gemini_model = genai.GenerativeModel(
AnalyzerConfigs.gemini_models[1],
generation_config={
"response_mime_type": "application/json",
"response_schema": list[ResponseData],
},
)
self._gen_ai_config = genai.configure(api_key=AnalyzerConfigs.genai_api_key)
self._input_code: str = None
self._prompt: str = None
self._response: Union[list, None] = None
self._logger = structlog.get_logger("CodeAnalyzer")

def analyze_code(self) -> Union[list, None]:
"""
This method analyzes the input code and generates a style guide using the Gemini model.
"""
try:
prompt = f"""Analyze the following {self.language} code according to {self.style_guide} style guidelines:
```{self.language}
{self._input_code}
```
Expand All @@ -42,19 +47,21 @@ def analyze_code(self) -> Union[list, None]:
1. Specific violations message with line numbers.
2. Suggestions for how to fix each violation, ideally with code examples.
3. Prioritize Only the most critical issues for readability and maintainability.
"""

model = self.gemini_model
response = model.generate_content(prompt)

# Extract and format the style guide from the response
# You will need to implement parsing logic here based on Gemini's response format

style_guide = response.text # Adjust this based on the actual response structure
self._logger.info("Style guide generated successfully.")
#self._logger.info(style_guide)
return style_guide

except Exception as e:
self._logger.error(f"Error generating style guide: {e}")
return None
"""

model = self.gemini_model
response = model.generate_content(prompt)

# Extract and format the style guide from the response
# You will need to implement parsing logic here based on Gemini's response format

style_guide = (
response.text
) # Adjust this based on the actual response structure
self._logger.info("Style guide generated successfully.")
# self._logger.info(style_guide)
return style_guide

except Exception as e:
self._logger.error(f"Error generating style guide: {e}")
return None
39 changes: 26 additions & 13 deletions app/gemini_config.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,38 @@
import os
import streamlit as st
from typing import TypedDict
from typing_extensions import TypedDict


class AnalyzerConfigs:
'''
"""
This class contains configuration settings for the CodeAnalyzer class.
'''
"""

gemini_models: list = [
"gemini-1.5-flash",
"gemini-1.5-pro", #INFO : https://ai.google.dev/gemini-api/docs/json-mode?lang=python
"gemini-1.5-flash",
"gemini-1.5-pro", # INFO : https://ai.google.dev/gemini-api/docs/json-mode?lang=python
]
famous_languages: list = [
"Python",
"Java",
"JavaScript",
"C++",
"C#",
"Ruby",
"Go",
"Swift",
"Rust",
]
famous_languages: list = ["Python", "Java", "JavaScript", "C++", "C#", "Ruby", "Go", "Swift", "Rust"]
genai_api_key: str = st.secrets["GENAI_API_KEY"]
style_guides: dict = {
'google style': 'https://google.github.io/styleguide/pyguide.html',
'pep8': 'https://pep8.org/'
"google style": "https://google.github.io/styleguide/pyguide.html",
"pep8": "https://pep8.org/",
}



class ResponseData(TypedDict):
'''
"""
For schema based responses like `JSON`
'''
"""

line: int
message: str

8 changes: 5 additions & 3 deletions app/gemini_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@
import json
from gemini_analyzer import CodeAnalyzer


def main():
code = sys.argv[1]
analyzer = CodeAnalyzer()
analyzer.setCode(code)
style_guide = analyzer.analyze_code()
if style_guide:
print(json.dumps({'style_guide': style_guide}))
print(json.dumps({"style_guide": style_guide}))
else:
print(json.dumps({'error': 'Analysis failed'}))
print(json.dumps({"error": "Analysis failed"}))


if __name__ == '__main__':
if __name__ == "__main__":
main()
3 changes: 2 additions & 1 deletion examples/class.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ class Dog:
def __init__(self, name, breed):
self.name = name
self.breed = breed

def bark(self):
print("Woof!")


my_dog = Dog("Buddy", "Golden Retriever")
my_dog.bark()
5 changes: 3 additions & 2 deletions examples/greet.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
def greet(name):
"""Greets the person with the given name."""
"""Greets the person with the given name."""

print("Hello,", name)

print("Hello,", name)

greet("Alice")
8 changes: 5 additions & 3 deletions examples/sum.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
def calculate_sum( a , b): # Intentional spacing issues
def calculate_sum(a, b): # Intentional spacing issues
"""This function calculates the sum of two numbers."""
result=a+b
return result # Incorrect indentation
result = a + b


return result # Incorrect indentation

print(calculate_sum(10, 5))
4 changes: 3 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ python-dotenv==1.0.1
structlog==24.2.0
rich==13.7.1
watchdog==4.0.1
flask==3.0.3
flask==3.0.3
flake8==7.1.0
black==24.4.2
15 changes: 8 additions & 7 deletions server.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,22 @@

app = Flask(__name__)

@app.route('/analyze', methods=['POST'])

@app.route("/analyze", methods=["POST"])
def analyze():
data = request.get_json()
code = data['code']
code = data["code"]

analyzer = CodeAnalyzer()
analyzer._input_code = code
style_guide = analyzer.analyze_code()
processed_stule_guide = json.loads(style_guide)
issues = {'issues':processed_stule_guide}
issues = {"issues": processed_stule_guide}
if style_guide:
#data = json.loads(style_guide) # Convert string to dictionary
return jsonify({'style_guide' : issues}), 200
return jsonify({"style_guide": issues}), 200
else:
return jsonify({'error': 'Analysis failed'}), 500
return jsonify({"error": "Analysis failed"}), 500


if __name__ == '__main__':
if __name__ == "__main__":
app.run(port=5000)
19 changes: 6 additions & 13 deletions streamlit.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,24 @@

code_handler = CodeAnalyzer()


st.title("AI-Powered Code Style Guide with Gemini")
code_input = st.text_area("Paste your code here:", height=250)

if st.button("Analyze"):
if code_input:
with st.spinner('Analyzing...'):
with st.spinner("Analyzing..."):
code_handler._input_code = code_input
response = code_handler.analyze_code()
data = json.loads(response)
st.subheader("Style Guide and Suggestions:")
# Expanders
try:
for items in data["issues"]:
with st.expander(f"Line `{items['line']}`"):
st.write(items['message'])
except:
pass

for items in data["issues"]:
with st.expander(f"Line `{items['line']}`"):
st.write(items["message"])
# JSON output
st.subheader("Raw API response : ")
with st.expander(f"Expand"):
with st.expander("Expand"):
st.json(response) # Display the Gemini-generated style guide

time.sleep(5)

else:
st.warning("Please paste your code to analyze.")
st.warning("Please paste your code to analyze.")

0 comments on commit b8b9685

Please sign in to comment.