diff --git a/ChatManager.py b/ChatManager.py
index 31f8823..892e85d 100644
--- a/ChatManager.py
+++ b/ChatManager.py
@@ -19,8 +19,7 @@ def get_client() -> OpenAI:
client = get_client()
def send_message(message: str) -> str:
- # hier gehts darum, die message vorzubereiten
- # -> also nachricht aus den Chatfenster mit systemprompts und file_conntext verknüpfen
+ # Nachricht aus dem Chatfenster wird für qwen vorbereitet und mit Kontext über SystemPrompter ergänzt
prompt = generate_prompt(message)
response = send_prompt(prompt)
return response
diff --git a/DebugLogger.py b/DebugLogger.py
index 01d5887..409f8be 100644
--- a/DebugLogger.py
+++ b/DebugLogger.py
@@ -2,7 +2,7 @@ import streamlit as st
import subprocess
def log_error(error_message):
- #speichern wieder über session_state
+ #speichern des strukturierten outputs von qwen über session_state
st.session_state["bugs"] = error_message
@@ -20,7 +20,6 @@ def format_debug_output(output: dict) -> str:
return "\n".join(lines)
-#qwen
def format_output(text: bytes) -> str:
return text.decode('utf-8', errors='replace').strip()
diff --git a/ExecutionEngine.py b/ExecutionEngine.py
index 70adbdf..a19cffc 100644
--- a/ExecutionEngine.py
+++ b/ExecutionEngine.py
@@ -6,6 +6,8 @@ from DebugLogger import log_error, format_output, format_error
def run_code(code: Path) -> None | str:
+ #führt Code aus angeklickter Datei im Editor aus. Sicherheitscheck über verbotene Imports und Funktionen geregelt
+
if "code_output" in st.session_state:
del st.session_state['code_output']
if "code_error" in st.session_state:
@@ -29,20 +31,11 @@ def run_code(code: Path) -> None | str:
st.session_state['code_error'] = format_error(exc)
log_error(st.session_state['code_error'])
-
-#brauchts diese Funktion noch? macht eigentlich schon run_code
-def capture_output():
- errors = st.session_state['code_error']
- #if errors == 0:
- # errors = "There are no errors"
- return st.session_state['code_output'], errors
-
-
#aus der uebungsloesung
def check_code_safety(code: str) -> str | None:
- #Analysiert den Code statisch nach verbotenen imports und builtins.
+ #analysiert den Code statisch nach verbotenen imports und builtins.
try:
tree = ast.parse(code)
except SyntaxError as e:
diff --git a/FileManager.py b/FileManager.py
index 805a03f..13fbf4e 100644
--- a/FileManager.py
+++ b/FileManager.py
@@ -15,7 +15,7 @@ def read_file(file_path: Path) -> str:
return file.read()
def save_file(file_path: Path, content: str):
- #wenn eine Datei im Code Editor abgeändert wird, soll die Funktion die Änderungen abspeichern
+ #wenn eine Datei im Code Editor abgeändert wird und der Save Button gedrückt wurde, speichert Funktion die Änderungen ab
with file_path.open('w') as file:
file.write(content)
diff --git a/SearchManager.py b/SearchManager.py
index a257faf..844bbe7 100644
--- a/SearchManager.py
+++ b/SearchManager.py
@@ -1,7 +1,7 @@
from ddgs import DDGS
-import re
def perform_search(query: str) -> dict:
+ #sucht Zusatzinfos im Internet
try:
results = DDGS().text(query, max_results=3)
return results
@@ -12,7 +12,8 @@ def perform_search(query: str) -> dict:
def parse_results(results: list[dict]) -> list[dict]:
- return [
+ #Limitiert Inhalt der Suchergebnisse auf 500 Zeichen, damit qwen nicht zu langen Prompt erhält
+ return [
{
"url": r.get("href", ""), #
"snippet": r.get("body", "")[:500]
diff --git a/SystemPrompter.py b/SystemPrompter.py
index eaf5a61..ed9995d 100644
--- a/SystemPrompter.py
+++ b/SystemPrompter.py
@@ -3,7 +3,7 @@ from SearchManager import perform_search, parse_results
def generate_prompt(user_message: str) -> str:
-
+ # der key "debug" im session_state als toggle: wenn an, dann strukturierter output mit feedbackschleife über "bugs" key
if "debug" in st.session_state and st.session_state["debug"]:
system_content = """\
You are a code-review assistant. You ALWAYS respond with valid JSON and
@@ -37,10 +37,11 @@ commentary. Your entire response must be parseable by json.loads().
context += (f" {st.session_state['bugs']} ")
prompt = system_content + task + context
- #st.session_state["last_prompt"] = (f"{prompt}")
+ st.session_state["last_prompt"] = (f"{prompt}")
return prompt
-
+
else:
+ # ohne "debug" key im session_state: unstrukturierter output
system_content = """\
You are an experienced Python coding assistant.
@@ -68,7 +69,7 @@ commentary. Your entire response must be parseable by json.loads().
prompt = system_content + task + context
- #st.session_state["last_prompt"] = (f"{prompt}")
+ st.session_state["last_prompt"] = (f"{prompt}")
return prompt
diff --git a/app.py b/app.py
index d6c6698..5757d1e 100644
--- a/app.py
+++ b/app.py
@@ -3,28 +3,22 @@ import json
import streamlit as st
from pathlib import Path
from ChatManager import send_message
-from FileManager import list_files, read_file, save_file, open_file
+from FileManager import list_files, save_file, open_file
from ExecutionEngine import run_code
from DebugLogger import format_debug_output, log_error
-#"Last sent prompt:",
-#if "last_prompt" in st.session_state:
-# st.session_state["last_prompt"]
-#if "file_content" in st.session_state:
-# st.session_state["file_content"]
- #type(st.session_state["file_content"])
- #print(type(st.session_state["file_content"]))
st.set_page_config(layout="wide")
+# Initialisierung wichtiger keys im session_state
if not "file_content" in st.session_state:
st.session_state["file_content"] = ""
if not "selected_file" in st.session_state:
st.session_state["selected_file"] = ""
with st.sidebar:
-
+# baut den Sidebar aus den vorhanden .py Dateien im Arbeitsverzeichnis
for file in list_files():
st.button(file.name, on_click=open_file, args=[file])
@@ -35,7 +29,7 @@ with col_editor_output:
if "file_content" in st.session_state:
text = st.text_area("Code Editor", height="stretch", key="file_content")
- # Preferred pattern for editor
+ # Oberfläche erhält zusätzliche Buttons, wenn eine Datei geöffnet wird
if st.session_state["selected_file"] != "":
st.button("Save", on_click=save_file, args=[st.session_state["selected_file"], st.session_state["file_content"]])
st.button("Execute Code", on_click=run_code, args=[st.session_state["selected_file"]])
@@ -55,13 +49,13 @@ with col_chat:
if "messages" not in st.session_state:
st.session_state.messages = []
- # Display History
+ # zeigt Chatverlauf als Abschnitt mit eigenem Scrollbalken
with st.container(border=True, height=500):
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.write(msg["content"])
- # Input & Response
+ # regelt Verhalten bei Chat mit qwen. Nach Eingabe wird einer der Promptopitionen gewählt und fertiger Prompt sowie qwen Antwort in die messages gespeichert
message = st.chat_input("How can qwen help you?", key="chat")
internet = st.toggle("enable internet search", key="internet")
if "code_output" in st.session_state or "code_error" in st.session_state:
@@ -72,7 +66,6 @@ with col_chat:
if "debug" in st.session_state and st.session_state["debug"]:
response = format_debug_output(json.loads(response))
log_error(response)
- # st.session_state["bugs"] = response
st.session_state.messages.append({"role": "assistant", "content": response})
st.rerun()
diff --git a/project_description.md b/project_description.md
new file mode 100644
index 0000000..d2b7fad
--- /dev/null
+++ b/project_description.md
@@ -0,0 +1,135 @@
+**Lightweight Code Editor with AI integration**
+
+##1. Introduction
+
+This code editor was made as a project for the course Artificial Intelligence in Software Engineering I at FH Graubuenden.
+Its purpose is to help with coding questions and debugging.
+
+
+##2. Requirements
+
+streamlit
+openai
+ddgs
+dotenv
+
+##3. Installation
+
+- install requirements through pip
+- aquire the environment variables thourgh FHGR
+- connect to the FHGR VPN to get access to the AI model.
+- run the app via terminal with "streamlit run app.py". A browser window will open.
+
+##4. Architecture
+
+The code is split up into frontend (app.py) and backend modules (FileManager, ChatManager, SystemPrompter, ExecutionEngine, DebugLogger and SearchManager).
+A lot of functionality is realized through the use of the session_state from streamlit.
+Since the streamlit app is rerun after each action the session_state is used to keep track of relevant information.
+
+# 4.1 app.py
+
+This module is the entrypoint for the streamlit functionality. It contains all elements that are visible when the program is run.
+
+
+ - sidebar:
+ The sidebar shows all python files within the working directory through fuctions found in the FileManager. Each file is represented by a clickable button.
+ There is no functionality to add or delete files from within the program. To change the available files the user has to manually add them to the directory.
+ Interacts with FileManger
+
+ - code editor:
+ The code editor shows the contents of the opened file. The contents can be changed, saved and also executed.
+ Interacts with FileManager, ExecutionEngine
+
+ - chat:
+ This element consists of an input area to send messages to the AI, two toggles to enable different modes as well as a scrollable history.
+ The first toggle is "enable internet search". This appends the results of an internet search to the prompt that is sent to the AI.
+ This means the results from the search are not primarily for the user, but for the AI to use recent information before responing to the user.
+ The second toggle "debug with qwen" appears only when a file was executed. This mode uses more structured output which is also sent to the next prompt for the AI.
+ With this feedback the AI can help with a more focused approach to find and fix bugs in the code. The user has to implement the fixes themself since the AI cannot change the contents of the file.
+ Interacts with ChatManager, Debuglogger
+
+# 4.2 FileManager.py
+
+This module manages the access to the working directory. It uses Path objects.
+The open_file function is only used as a helper function to save the file path and contents to the session_state.
+
+# 4.3 ChatManager.py
+
+The environment variables are located in a .env file which is excluded from the gitea upload.
+The qwen AI is accessed through the openai API client.
+While this module sends the messages to the AI it is heavily dependend on the SystemPrompter to supply the relevant context for the finished prompt.
+Interacts with SystemPrompter
+
+# 4.4 SystemPrompter.py
+
+This module handles all the context to add to the user message sent via the ChatManager. The instructions for the AI are structured with xml tags.
+There are two modes which are chosen with a toggle. Both modes differ in the system component and sent context.
+When the toggle is off the generated prompt includes system instructions which make the AI act as a coding assistant who gives short responses.
+With the toggle on the output is structured with json and focussed on finding bugs in the provided code. The AI's response is also loaded into the session_state.
+This means that subsequent messages to the AI keep its own responses therefore having the ability to iterate on them.
+Interacts with SearchManager
+
+# 4.5 ExecutionEngine.py
+
+With this module the program can execute the opened file after clicking the corresponding button in the streamlit app.
+There is a safety check before the subprocess is called to make sure no malicious actions can be made from within the program.
+Interacts with Debuglogger
+
+# 4.6 DebugLogger.py
+
+This module handles making all outputs from various other module more readable and useable.
+It saves the AI's response in the session_state to be able to send it with the next prompt when using the debug toggle.
+
+# 4.7 SearchManager.py
+
+This modules uses the duckduckgosearch API to make searching the internet possible.
+The user does not directly see the search, but the result is sent with the prompt to the AI.
+
+
+##5. Usage
+
+The following examples illustrate how to use this program.
+
+*I want to ask the AI about something in my file*
+ **user actions**: - click on button with name of the file
+ - write message to AI
+ - wait for response
+ - implement suggestions from AI
+
+ **interaction flow**: - st.sidebar uses list_files() to generate Buttons
+ - on_click uses open_file() to load content and file Path into session_state
+ - send_message() uses generate_prompt() to add system_prompt and context = file_content to the user_message
+ - response is displayed by st.session_state.messages
+
+
+*I want to ask the AI about somehting in my file, but the AI has outdated info about the topic*
+ **user actions**: - click on button with name of the file
+ - activate "enable internet search" toggle
+ - write message to AI
+ - wait for response
+ - implement suggestions from AI
+
+ **interaction flow**: - st.sidebar uses list_files() to generate Buttons
+ - on_click uses open_file() to load content and file Path into session_state
+ - send_message() uses generate_prompt() to add system_prompt and context = file_content and results from perform_search() to the user_message
+ - response is displayed by st.session_state.messages
+
+
+*I want to debug my code*
+ **user actions**: - click on button with name of the file
+ - click the "execute code" button
+ - write message to AI
+ - wait for response
+ - implement suggestions from AI
+ - write message to AI
+ - implement suggestions from AI
+
+ **interaction flow**: - st.sidebar uses list_files() to generate Buttons
+ - on_click uses open_file() to load content and file Path into session_state
+ - on_click uses run_code() to execute the file, results are formatted by format_output() or format_error()
+ - code_output or code_error are loaded into seassion_state
+ - send_message() uses generate_prompt() to add system_prompt which detects the "debug" toggle and adds context = file_content and code_output / code_error to the user_message
+ - response is displayed by st.session_state.messages
+ - send_message() uses generate_prompt() to add system_prompt which detects the "debug" toggle and adds context = file_content and code_output / code_error and debug response to the user_message
+ - response is displayed by st.session_state.messages
+
diff --git a/~$praesentation_ai_se.pptx b/~$praesentation_ai_se.pptx
new file mode 100644
index 0000000..77e5f90
Binary files /dev/null and b/~$praesentation_ai_se.pptx differ