Skip to content

Commit

Permalink
seems to be working...
Browse files Browse the repository at this point in the history
  • Loading branch information
rsivilli committed Oct 4, 2024
1 parent da8778f commit c011a24
Show file tree
Hide file tree
Showing 10 changed files with 627 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
__pycache__

.env.local
.venv
36 changes: 36 additions & 0 deletions lazy_dev_ai/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import click
import os
from lazy_dev_ai.llm import getClient,apply_code_template
from dotenv import load_dotenv
from pathlib import Path
from lazy_dev_ai.prompts.defaults import load_default_prompt
# Load .env and .env.local files
load_dotenv('.env')
load_dotenv('.env.local', override=True)

@click.group()
@click.option('--api-key', envvar='OPENAI_API_KEY', help='API Key for authentication')
@click.option('--project', envvar='OPENAI_PROJECT',default=None, help='Project identifier (optional)')
@click.option('--organization',envvar='OPENAI_ORGANIZATION', default=None, help='Organization identifier (optional)')
def cli( api_key, project=None,organization=None):
"""Main CLI group with API Key"""
if not api_key:
click.echo("API key not provided. Use --api-key option or set API_KEY in environment variables or .env file.")
getClient(api_key=api_key,project=project,organization=organization)


@cli.command()
@click.argument('paths', nargs=-1, type=click.Path(exists=True, path_type=Path))
def improve_comments(paths):
"""Improve the comments of the provided file"""
client = getClient()
prompt = load_default_prompt('comment')

for f in paths:
if f.is_file():
click.echo(f"Applying template to {f.as_posix()}")
# apply_code_template(code_file=path,prompt=prompt)


if __name__ == '__main__':
cli()
101 changes: 101 additions & 0 deletions lazy_dev_ai/llm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
from openai import OpenAI
from pydantic import BaseModel, Field, ConfigDict
from pathlib import Path
from string import Template
from enum import Enum
import importlib.resources

default_template=None
client = None

with importlib.resources.open_text("lazy_dev_ai.templates", "default_base.txt") as file:
default_template = Template(file.read())

class OpenAIRole(str, Enum):
SYSTEM = "system"
USER = "user"
ASSISTANT = "assistant"

class SEVERITY(str,Enum):
LOW = "LOW"
MEDIUM="MEDIUM"
HIGH = "HIGH"
CRITICAL = "CRITICAL"
class ChatGPTMessage(BaseModel):
role: OpenAIRole
content: str


class CodeChangeResponse(BaseModel):
#To provide flexibility in response model, we are allowing extra fields
model_config = ConfigDict(extra="allow")
change_required:bool
content:str|None = Field(None)
change_explanation:str|None = Field(None)
severity:SEVERITY|None = Field(None)


def getClient(api_key:str=None, organization:str=None, project:str=None)->OpenAI:
global client
if client is None:
client = OpenAI(
api_key=api_key,
organization=organization,
project = project,

)
return client


def load_template(file: str | Path|None =None) -> Template:
if file is None:
return default_template
# Ensure the file path is a Path object
file_path = Path(file)

# Check if the file exists
if not file_path.exists():
raise FileNotFoundError(f"The file {file_path} does not exist.")

# Read the contents of the file
with file_path.open("r", encoding="utf-8") as f:
template_content = f.read()

# Return the Template object
return Template(template_content)

def load_file(file:str | Path) -> str:
# Ensure the file path is a Path object
file_path = Path(file)

# Check if the file exists
if not file_path.exists():
raise FileNotFoundError(f"The file {file_path} does not exist.")

# Read the contents of the file
with file_path.open("r", encoding="utf-8") as f:
content = f.read()

# Return the Template object
return content
def write_file(file:str|Path, contents:str):
file_path = Path(file)
with file_path.open("w", encoding="utf-8") as f:
f.write(contents)

def apply_code_template(code_file:str|Path,prompt_file:str|Path=None,prompt:str=None,model: str = "gpt-4-turbo",template_file:str|Path=None)->CodeChangeResponse:
if prompt is None and prompt_file is None:
raise ValueError("Must provide either a prompt or a prompt file")
prompt = prompt or load_file(prompt_file)
template = load_template(template_file)
code = load_file(code_file)
messages = [
ChatGPTMessage(role=OpenAIRole.SYSTEM,content=template.substitute({"file_contents":code,"prompt":prompt})),

]

response = getClient().chat.completions.create(model=model, messages=messages)
code_changes = CodeChangeResponse.model_validate_json(response.choices[0].message)
if code_changes.change_required() and code_changes.content is not None:
write_file(file=code_file,contents=code_changes.content)

1 change: 1 addition & 0 deletions lazy_dev_ai/prompts/default_cleanup.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Review the following code and suggest improvements to the codebase. Ensure that you provide comments explaining the changes.
1 change: 1 addition & 0 deletions lazy_dev_ai/prompts/default_comment.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Review the following code for good and useful comments. Please suggested changes to existing comments and feel free to add new ones that make sense.
13 changes: 13 additions & 0 deletions lazy_dev_ai/prompts/defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from typing import Literal
import importlib

default_prompts = Literal["comment","cleanup"]

def load_default_prompt(prompt_name:default_prompts)->str:
file = None
if prompt_name == "cleanup":
file = "default_cleanup.txt"
elif prompt_name == "comment":
file= "default_comment.txt"
with importlib.resources.open_text("lazy_dev_ai.prompts", file) as file:
return file.read()
15 changes: 15 additions & 0 deletions lazy_dev_ai/templates/default_base.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
$prompt

Based on your analysis, respond with a JSON object containing two fields:

change_required: A boolean field indicating whether any changes are required.
content: An optional field. If changes are required, provide the updated file here. If no changes are necessary, this field can be omitted or left empty.
change_explanation: An optional field. If explanations to the changes are required, provide the explanation here. This field can be omitted or left empty.
severity: A indication as to the severity of the suggested changes. Possible values are LOW, MEDIUM, HIGH, CRITICAL

File content:

$file_contents

Respond with a JSON object only. Do not include explanations or commentary outside the JSON object.

Loading

0 comments on commit c011a24

Please sign in to comment.