├── Procfile ├── .env ├── requirements.txt ├── utils ├── github_handler.py └── notebook_handler.py ├── config └── settings.py ├── README.md ├── templates ├── chat.html └── base.html ├── formatters └── output_formatter.py ├── static ├── js │ └── main.js └── css │ └── style.css ├── app.py ├── database └── chat_history.py └── analyzers └── code_analyzer.py /Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn app:app 2 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY="api-key-here" 2 | GEMINI_API_KEY="api-key-here" 3 | DEFAULT_AI_SERVICE=auto -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gunicorn 2 | openai==1.35.13 3 | flask==3.0.3 4 | google-generativeai==0.8.3 5 | requests==2.31.0 6 | nbformat==5.10.4 7 | python-dotenv==1.0.1 -------------------------------------------------------------------------------- /utils/github_handler.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import re 3 | 4 | class GitHubHandler: 5 | @staticmethod 6 | def get_raw_github_url(github_url): 7 | """Normal GitHub URL'sini raw içerik URL'sine dönüştürür""" 8 | pattern = r'https://github\.com/([^/]+)/([^/]+)/blob/([^/]+)/(.+)' 9 | match = re.match(pattern, github_url) 10 | 11 | if not match: 12 | raise ValueError("Geçersiz GitHub URL'si") 13 | 14 | user, repo, branch, path = match.groups() 15 | raw_url = f'https://raw.githubusercontent.com/{user}/{repo}/{branch}/{path}' 16 | return raw_url 17 | 18 | @staticmethod 19 | def get_file_content(url): 20 | """GitHub URL'inden dosya içeriğini alır""" 21 | try: 22 | raw_url = GitHubHandler.get_raw_github_url(url) 23 | response = requests.get(raw_url) 24 | response.raise_for_status() 25 | return response.text 26 | 27 | except requests.exceptions.RequestException as e: 28 | raise Exception(f"Dosya alınırken hata oluştu: {str(e)}") -------------------------------------------------------------------------------- /config/settings.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | import codecs 4 | 5 | sys.stdout = codecs.getwriter('utf-8')(sys.stdout.buffer) 6 | 7 | DEFAULT_AI_SERVICE = os.getenv('DEFAULT_AI_SERVICE', 'auto') 8 | 9 | OPENAI_MODEL = "gpt-4o-mini" 10 | TEMPERATURE = 0.7 11 | 12 | ANALYSIS_TEMPLATE = { 13 | "proje_amaci": "Analiz sırasında bir hata oluştu", 14 | "proje_ozeti": "JSON formatı elde edilemedi", 15 | "kullanilan_teknolojiler": ["Belirlenemedi"], 16 | "genel_degerlendirme": "GPT yanıtı JSON formatında değildi", 17 | "guclu_yonler": ["Belirlenemedi"], 18 | "iyilestirme_alanlari": ["Belirlenemedi"], 19 | "kod_ornekleri": ["Belirlenemedi"], 20 | "guvenlik_onerileri": ["Belirlenemedi"], 21 | "performans_onerileri": ["Belirlenemedi"] 22 | } 23 | 24 | SYSTEM_PROMPT = """Sen senior bir Python geliştiricisisin. Verilen kodu ve Markdown dokümantasyonunu aşağıdaki kriterlere göre analiz et ve SADECE JSON formatında yanıt ver. Açıklamalar JSON içinde olmalı, dışında hiçbir metin olmamalı. 25 | 26 | Özellikle kod örnekleri kısmında, her bir öneri için önce açıklama, sonra çalışabilir kod örneği ver. Her bir kod örneği şu formatı takip etmeli: 27 | 28 | "kod_ornekleri": [ 29 | { 30 | "aciklama": "Bu iyileştirme, mevcut fonksiyonun performansını artırır ve bellek kullanımını optimize eder. List comprehension kullanarak döngü yerine daha pythonic bir yaklaşım sunar.", 31 | "kod": "def optimize_function(data):\\n # Optimize edilmiş versiyon\\n result = [x * 2 for x in data]\\n return result" 32 | }, 33 | { 34 | "aciklama": "Cache mekanizması ekleyerek tekrarlanan işlemleri önler ve performansı artırır. Bu özellikle ağır işlemler için önemli bir optimizasyondur.", 35 | "kod": "class BetterImplementation:\\n def __init__(self):\\n self.cache = {}\\n\\n def process(self, data):\\n return self.cache.get(data, self._process(data))" 36 | } 37 | ] 38 | 39 | Yanıt formatı tam olarak şöyle olmalı: 40 | { 41 | "proje_amaci": "Projenin ana amacının kısa açıklaması", 42 | "proje_ozeti": "Projenin nasıl çalıştığına dair kısa özet", 43 | "kullanilan_teknolojiler": ["Teknoloji 1", "Teknoloji 2"], 44 | "genel_degerlendirme": "Kodun genel kalitesi hakkında kısa özet", 45 | "guclu_yonler": ["Güçlü yön 1", "Güçlü yön 2"], 46 | "iyilestirme_alanlari": ["İyileştirme 1", "İyileştirme 2"], 47 | "kod_ornekleri": [ 48 | { 49 | "aciklama": "İyileştirmenin açıklaması", 50 | "kod": "Örnek kod" 51 | } 52 | ], 53 | "guvenlik_onerileri": ["Güvenlik önerisi 1", "Güvenlik önerisi 2"], 54 | "performans_onerileri": ["Performans önerisi 1", "Performans önerisi 2"] 55 | }""" 56 | 57 | """ 58 | Lütfen şunları analiz et: 59 | 1. Proje Analizi: 60 | - Projenin amacı ve kapsamı 61 | - Kullanılan temel teknolojiler ve kütüphaneler 62 | - Projenin kısa özeti 63 | 64 | 2. Kod Kalitesi: 65 | - Okunabilirlik 66 | - Maintainability (Sürdürülebilirlik) 67 | - Best practices uyumu 68 | - PEP 8 standartlarına uyum 69 | 70 | 3. Potansiyel İyileştirmeler: 71 | - Performans optimizasyonları 72 | - Kod organizasyonu 73 | - Hata yönetimi 74 | - Güvenlik konuları 75 | 76 | 4. Öneriler: 77 | - Spesifik kod örnekleriyle birlikte iyileştirme önerileri 78 | - Modern Python özelliklerinin kullanımı 79 | - Alternatif yaklaşımlar 80 | """ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Code Feedback Agent 2 | 3 |      4 | 5 | 6 | Code Feedback Agent is an interactive web application that analyzes your Python projects on GitHub and provides AI-powered feedback. You can receive detailed analysis of your project and chat with the bot to improve your code. 7 | 8 | ### 🌐 Demo 9 | 10 | #### [https://feedback-agent.onrender.com/](https://feedback-agent.onrender.com/) 11 | 12 | ## 🚀 Features 13 | 14 | - 📊 Comprehensive code analysis 15 | - 💬 Interactive chat interface 16 | - 📝 Markdown formatted outputs 17 | - 🔄 Chat history tracking 18 | - 🐍 Python and Jupyter Notebook support 19 | - 🔒 Secure and scalable architecture 20 | 21 | ## 🛠️ Technologies 22 | 23 | - **Backend**: Flask 24 | - **AI**: OpenAI GPT-4 & Gemini Pro 25 | - **Database**: SQLite 26 | - **Frontend**: HTML, CSS (Tailwind), JavaScript 27 | 28 | ## 📋 Prerequisites 29 | 30 | - Python 3.10+ 31 | - OpenAI & Gemini API key 32 | - Git & pip 33 | 34 | ## 🔧 Installation 35 | 36 | 1. Clone the repository: 37 | ```bash 38 | git clone https://github.com/enesmanan/feedback-agent.git 39 | cd feedback-agent 40 | ``` 41 | 42 | 2. Create a virtual environment: 43 | ```bash 44 | python -m venv venv 45 | source venv/bin/activate # Linux/Mac 46 | venv\Scripts\activate # Windows 47 | ``` 48 | 49 | 3. Install requirements: 50 | ```bash 51 | pip install -r requirements.txt 52 | ``` 53 | 54 | 4. Set `.env` file: 55 | ```env 56 | OPENAI_API_KEY="api-key-here" 57 | GEMINI_API_KEY="api-key-here" 58 | DEFAULT_AI_SERVICE=auto 59 | ``` 60 | 61 | 5. Run the application: 62 | ```bash 63 | python3 app.py # Linux 64 | python app.py # Windows 65 | ``` 66 | 67 | ## 💡 Usage 68 | 69 | 1. Go to `http://localhost:5000` in your browser 70 | 2. Enter your GitHub project URL 71 | 3. Review the analysis results 72 | 4. Chat with the bot to improve your project 73 | 74 | ## 📁 Project Structure 75 | 76 | ``` 77 | feedback_agent/ 78 | ├── templates/ # HTML templates 79 | │ ├── base.html 80 | │ └── chat.html 81 | ├── static/ # Static files 82 | │ ├── css/ 83 | │ │ └── style.css 84 | │ └── js/ 85 | │ └── main.js 86 | ├── database/ # Database operations 87 | │ └── chat_history.py 88 | ├── utils/ # Utility functions 89 | │ ├── github_handler.py 90 | │ └── notebook_handler.py 91 | ├── analyzers/ # Code analysis 92 | │ └── code_analyzer.py 93 | ├── formatters/ # Output formatting 94 | │ └── output_formatter.py 95 | ├── config/ # Configuration 96 | │ └── settings.py 97 | ├── app.py # Main application 98 | ├── requirements.txt # Dependencies 99 | └── README.md # Documentation 100 | ``` 101 | 102 | 103 | ## 🤝 Contributing 104 | 105 | 1. Fork the Project 106 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 107 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 108 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 109 | 5. Open a Pull Request 110 | 111 | 112 | ## 📝 License 113 | 114 | This project is licensed under the MIT License - see the [LICENSE](https://mit-license.org/) file for details. 115 | 116 | ## 👥 Contact 117 | 118 | Enes Fehmi Manan - [@enesfehmimanan](https://www.linkedin.com/in/enesfehmimanan/) 119 | 120 | 121 | -------------------------------------------------------------------------------- /templates/chat.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 |
' +
7 | Prism.highlight(str, Prism.languages[lang], lang) +
8 | '';
9 | } catch (__) {}
10 | }
11 | return '' + md.utils.escapeHtml(str) + '';
12 | }
13 | });
14 |
15 | const messagesDiv = document.getElementById('messages');
16 | // URL'den conversation_id'yi al
17 | const urlParams = new URLSearchParams(window.location.pathname);
18 | let conversationId = document.getElementById('chatForm').dataset.conversationId || null;
19 |
20 | // Markdown içeriğini render et ve Prism.js'i başlat
21 | function renderMarkdownAndInitPrism(element) {
22 | element.innerHTML = md.render(element.textContent);
23 | Prism.highlightAllUnder(element);
24 | }
25 |
26 | // Mevcut markdown içeriğini işle
27 | document.querySelectorAll('.markdown-content').forEach(renderMarkdownAndInitPrism);
28 |
29 | // Mesajları en alta kaydır
30 | if (messagesDiv) {
31 | messagesDiv.scrollTop = messagesDiv.scrollHeight;
32 | }
33 |
34 | function addMessage(sender, content) {
35 | const messageDiv = document.createElement('div');
36 | messageDiv.className = `message ${sender} mb-4`;
37 |
38 | const contentDiv = document.createElement('div');
39 | contentDiv.className = sender === 'user' ?
40 | 'bg-blue-100 p-3 rounded-lg mx-auto max-w-3xl' :
41 | 'bg-white shadow-lg p-4 rounded-lg mx-auto max-w-3xl markdown-content';
42 |
43 | if (sender === 'assistant') {
44 | contentDiv.textContent = content;
45 | renderMarkdownAndInitPrism(contentDiv);
46 | } else {
47 | contentDiv.textContent = content;
48 | }
49 |
50 | messageDiv.appendChild(contentDiv);
51 | messagesDiv.appendChild(messageDiv);
52 | messagesDiv.scrollTop = messagesDiv.scrollHeight;
53 | }
54 |
55 | // GitHub URL form submit
56 | const urlForm = document.getElementById('urlForm');
57 | if (urlForm) {
58 | urlForm.addEventListener('submit', async function(e) {
59 | e.preventDefault();
60 | const url = document.getElementById('githubUrl').value;
61 |
62 | try {
63 | const response = await fetch('/analyze', {
64 | method: 'POST',
65 | headers: {
66 | 'Content-Type': 'application/json'
67 | },
68 | body: JSON.stringify({ url: url })
69 | });
70 |
71 | const data = await response.json();
72 | conversationId = data.conversation_id;
73 |
74 | // URL input'u gizle
75 | document.getElementById('urlInput').style.display = 'none';
76 |
77 | // Chat input'u aktif et
78 | document.getElementById('userMessage').disabled = false;
79 | document.getElementById('chatForm').querySelector('button').disabled = false;
80 |
81 | // Conversation ID'yi forma ekle
82 | document.getElementById('chatForm').dataset.conversationId = conversationId;
83 |
84 | // Analiz sonucunu göster
85 | addMessage('assistant', data.response);
86 |
87 | // URL'i güncelle (sayfa yenilenmeden)
88 | window.history.pushState({}, '', `/history/${conversationId}`);
89 |
90 | } catch (error) {
91 | console.error('Error:', error);
92 | alert('Bir hata oluştu!');
93 | }
94 | });
95 | }
96 |
97 | // Chat form submit
98 | const chatForm = document.getElementById('chatForm');
99 | if (chatForm) {
100 | chatForm.addEventListener('submit', async function(e) {
101 | e.preventDefault();
102 | const messageInput = document.getElementById('userMessage');
103 | const message = messageInput.value;
104 |
105 | if (!message.trim()) return;
106 |
107 | const currentConversationId = this.dataset.conversationId || conversationId;
108 |
109 | if (!currentConversationId) {
110 | alert('Geçerli bir konuşma bulunamadı!');
111 | return;
112 | }
113 |
114 | addMessage('user', message);
115 | messageInput.value = '';
116 |
117 | try {
118 | const response = await fetch('/chat', {
119 | method: 'POST',
120 | headers: {
121 | 'Content-Type': 'application/json'
122 | },
123 | body: JSON.stringify({
124 | message: message,
125 | conversation_id: currentConversationId
126 | })
127 | });
128 |
129 | const data = await response.json();
130 | if (data.error) {
131 | throw new Error(data.error);
132 | }
133 | addMessage('assistant', data.response);
134 |
135 | } catch (error) {
136 | console.error('Error:', error);
137 | alert('Bir hata oluştu!');
138 | }
139 | });
140 | }
141 | });
--------------------------------------------------------------------------------
/static/css/style.css:
--------------------------------------------------------------------------------
1 | .markdown-content {
2 | line-height: 1.6;
3 | }
4 |
5 | .markdown-content h1 {
6 | font-size: 1.5em;
7 | font-weight: bold;
8 | margin: 1em 0;
9 | }
10 |
11 | .markdown-content h2 {
12 | font-size: 1.3em;
13 | font-weight: bold;
14 | margin: 0.8em 0;
15 | }
16 |
17 | .markdown-content pre {
18 | margin: 1em 0;
19 | border-radius: 6px;
20 | box-shadow: 0 2px 4px rgba(211, 205, 205, 0.1);
21 | }
22 |
23 | .markdown-content code {
24 | font-family: 'Fira Code', monospace;
25 | border-radius: 3px;
26 | }
27 |
28 | .markdown-content p code {
29 | background-color: rgba(211, 205, 205, 0.1);
30 | padding: 0.2em 0.4em;
31 | }
32 |
33 | .markdown-content ul {
34 | list-style-type: disc;
35 | margin-left: 1.5em;
36 | margin-bottom: 1em;
37 | }
38 |
39 | .markdown-content ol {
40 | list-style-type: decimal;
41 | margin-left: 1.5em;
42 | margin-bottom: 1em;
43 | }
44 |
45 | /* Mevcut stiller korunacak, aşağıdaki stiller eklenecek */
46 |
47 | /* Font ailesi */
48 | .font-space-grotesk {
49 | font-family: 'Space Grotesk', sans-serif;
50 | }
51 |
52 | /* Sidebar stilleri */
53 | #sidebar {
54 | box-shadow: 4px 0 6px -1px rgba(0, 0, 0, 0.1);
55 | }
56 |
57 | #sidebar .history-item {
58 | transition: all 0.2s ease;
59 | }
60 |
61 | #sidebar .history-item:hover {
62 | transform: translateX(4px);
63 | }
64 |
65 | /* Gradient başlık */
66 | .gradient-title {
67 | background: linear-gradient(135deg, #4F46E5, #2563EB);
68 | -webkit-background-clip: text;
69 | -webkit-text-fill-color: transparent;
70 | }
71 |
72 | /* Message boxes */
73 | .message .markdown-content {
74 | transition: transform 0.2s ease;
75 | }
76 |
77 | .message .markdown-content:hover {
78 | transform: translateY(-2px);
79 | box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
80 | }
81 |
82 | /* Scrollbar */
83 | .overflow-y-auto::-webkit-scrollbar {
84 | width: 6px;
85 | }
86 |
87 | .overflow-y-auto::-webkit-scrollbar-track {
88 | background: #f1f1f1;
89 | }
90 |
91 | .overflow-y-auto::-webkit-scrollbar-thumb {
92 | background: #cbd5e0;
93 | border-radius: 3px;
94 | }
95 |
96 | .overflow-y-auto::-webkit-scrollbar-thumb:hover {
97 | background: #a0aec0;
98 | }
99 |
100 |
101 | /* Prism.js özelleştirmeleri */
102 | pre[class*="language-"] {
103 | background: #f5f5f5; /* Açık gri arka plan */
104 | margin: 1em 0;
105 | padding: 1em;
106 | overflow: auto;
107 | border-radius: 6px;
108 | border: 1px solid #e0e0e0;
109 | }
110 |
111 | :not(pre) > code[class*="language-"],
112 | pre[class*="language-"] {
113 | background: #f5f5f5; /* Açık gri arka plan */
114 | }
115 |
116 | code[class*="language-"] {
117 | color: #333; /* Koyu gri ana metin rengi */
118 | text-shadow: none; /* Text shadow'u kaldır */
119 | }
120 |
121 | /* Sözdizimi renklendirme */
122 | .token.comment,
123 | .token.prolog,
124 | .token.doctype,
125 | .token.cdata {
126 | color: #666; /* Yorumlar için daha koyu gri */
127 | font-style: italic;
128 | }
129 |
130 | .token.function {
131 | color: #0050b3; /* Fonksiyonlar için koyu mavi */
132 | }
133 |
134 | .token.keyword {
135 | color: #8250df; /* Anahtar kelimeler için mor */
136 | font-weight: 500;
137 | }
138 |
139 | .token.string {
140 | color: #0a7343; /* String'ler için koyu yeşil */
141 | }
142 |
143 | .token.number {
144 | color: #cd5c5c; /* Sayılar için kırmızımsı */
145 | }
146 |
147 | .token.operator {
148 | color: #0550ae; /* Operatörler için mavi */
149 | }
150 |
151 | .token.class-name {
152 | color: #886ce4; /* Sınıf isimleri için mor */
153 | }
154 |
155 | .token.variable {
156 | color: #953800; /* Değişkenler için turuncu-kahve */
157 | }
158 |
159 | .token.property {
160 | color: #0550ae; /* Property'ler için mavi */
161 | }
162 |
163 | .token.punctuation {
164 | color: #24292f; /* Noktalama işaretleri için koyu gri */
165 | }
166 |
167 | /* Kod bloğu container stil güncellemesi */
168 | .code-toolbar {
169 | position: relative;
170 | margin: 1em 0;
171 | }
172 |
173 | pre[class*="language-"] {
174 | box-shadow: 0 2px 8px rgba(0,0,0,0.05);
175 | }
176 |
177 | /* Copy buton stilleri */
178 | .toolbar-item button {
179 | position: absolute;
180 | right: 0.5em;
181 | top: 0.5em;
182 | font-size: 0.8em;
183 | padding: 0.4em 0.8em;
184 | background: rgba(0,0,0,0.1);
185 | border: 1px solid rgba(0,0,0,0.1);
186 | border-radius: 4px;
187 | color: #333;
188 | cursor: pointer;
189 | transition: all 0.2s ease;
190 | }
191 |
192 | .toolbar-item button:hover {
193 | background: rgba(0,0,0,0.15);
194 | border-color: rgba(0,0,0,0.15);
195 | }
196 |
197 | /* Copy success message */
198 | div.code-toolbar > .toolbar .toolbar-item > button:focus {
199 | outline: none;
200 | }
201 |
202 | div.code-toolbar > .toolbar .toolbar-item > button[data-copy-state="copy"] {
203 | background: rgba(0,0,0,0.1);
204 | }
205 |
206 | div.code-toolbar > .toolbar .toolbar-item > button[data-copy-state="copied"] {
207 | background: #28a745;
208 | color: white;
209 | }
210 |
211 | /* Seçim rengi */
212 | pre[class*="language-"]::-moz-selection,
213 | pre[class*="language-"] ::-moz-selection,
214 | code[class*="language-"]::-moz-selection,
215 | code[class*="language-"] ::-moz-selection {
216 | background: #b3d4fc;
217 | }
218 |
219 | pre[class*="language-"]::selection,
220 | pre[class*="language-"] ::selection,
221 | code[class*="language-"]::selection,
222 | code[class*="language-"] ::selection {
223 | background: #b3d4fc;
224 | }
225 |
226 | /* Kod bloğu içindeki satır yüksekliği */
227 | pre[class*="language-"] > code {
228 | line-height: 1.5;
229 | font-size: 0.9em;
230 | font-family: 'Fira Code', 'Consolas', 'Monaco', 'Andale Mono', 'Ubuntu Mono', monospace;
231 | }
232 |
233 | /* Kod bloğu hover efekti */
234 | pre[class*="language-"]:hover {
235 | box-shadow: 0 3px 10px rgba(0,0,0,0.08);
236 | }
237 |
238 | /* Özel scrollbar */
239 | pre[class*="language-"]::-webkit-scrollbar {
240 | width: 8px;
241 | height: 8px;
242 | }
243 |
244 | pre[class*="language-"]::-webkit-scrollbar-track {
245 | background: #f1f1f1;
246 | border-radius: 4px;
247 | }
248 |
249 | pre[class*="language-"]::-webkit-scrollbar-thumb {
250 | background: #c1c1c1;
251 | border-radius: 4px;
252 | }
253 |
254 | pre[class*="language-"]::-webkit-scrollbar-thumb:hover {
255 | background: #a8a8a8;
256 | }
--------------------------------------------------------------------------------
/app.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, render_template, request, jsonify
2 | from database.chat_history import ChatHistory
3 | from utils.github_handler import GitHubHandler
4 | from utils.notebook_handler import NotebookHandler
5 | from analyzers.code_analyzer import CodeAnalyzer
6 | from formatters.output_formatter import OutputFormatter
7 | import os
8 | from dotenv import load_dotenv
9 |
10 | load_dotenv()
11 |
12 | class CodeFeedbackSystem:
13 | def __init__(self, api_keys):
14 | self.analyzer = CodeAnalyzer(api_keys)
15 | self.github_handler = GitHubHandler()
16 | self.notebook_handler = NotebookHandler()
17 | self.formatter = OutputFormatter()
18 |
19 | def analyze_code(self, github_url):
20 | """Analyze code from GitHub URL"""
21 | try:
22 | # Get content from GitHub
23 | content = self.github_handler.get_file_content(github_url)
24 |
25 | # Handle Jupyter notebooks
26 | if github_url.endswith('.ipynb'):
27 | notebook_data = self.notebook_handler.extract_notebook_code(content)
28 | analysis = self.analyzer.analyze_code(
29 | code=notebook_data['code'],
30 | notebook_data=notebook_data
31 | )
32 | elif github_url.endswith('.py'):
33 | analysis = self.analyzer.analyze_code(code=content)
34 | else:
35 | raise ValueError("Unsupported file format. Only .py and .ipynb files are supported.")
36 |
37 | return self.formatter.format_analysis(analysis)
38 |
39 | except Exception as e:
40 | return f"Error occurred: {str(e)}"
41 |
42 | def chat_about_code(self, message, code_context):
43 | """Chat about code using AI"""
44 | try:
45 | return self.analyzer.chat_about_code(message, code_context)
46 | except Exception as e:
47 | return f"Chat error: {str(e)}"
48 |
49 | def create_app():
50 | """Create and configure the Flask application"""
51 | app = Flask(__name__)
52 |
53 | # Configuration
54 | app.config.from_mapping(
55 | OPENAI_API_KEY=os.getenv('OPENAI_API_KEY'),
56 | GEMINI_API_KEY=os.getenv('GEMINI_API_KEY'),
57 | DEFAULT_AI_SERVICE=os.getenv('DEFAULT_AI_SERVICE', 'auto'),
58 | DATABASE=os.path.join(app.instance_path, 'chat_history.sqlite'),
59 | )
60 |
61 | # Ensure the instance folder exists
62 | try:
63 | os.makedirs(app.instance_path)
64 | except OSError:
65 | pass
66 |
67 | # Initialize services
68 | chat_history = ChatHistory(app.config['DATABASE'])
69 | api_keys = {
70 | "OPENAI_API_KEY": app.config['OPENAI_API_KEY'],
71 | "GEMINI_API_KEY": app.config['GEMINI_API_KEY']
72 | }
73 | feedback_system = CodeFeedbackSystem(api_keys)
74 |
75 | @app.route('/')
76 | def home():
77 | """Home page route with conversation history"""
78 | histories = chat_history.get_all_conversations()
79 | return render_template('chat.html', histories=histories)
80 |
81 | @app.route('/analyze', methods=['POST'])
82 | def analyze():
83 | """Analyze code from GitHub URL"""
84 | try:
85 | data = request.json
86 | github_url = data['url']
87 |
88 | # Start new conversation
89 | conversation_id = chat_history.start_conversation(github_url)
90 |
91 | # Analyze code
92 | response = feedback_system.analyze_code(github_url)
93 |
94 | # Save initial analysis
95 | chat_history.add_message(conversation_id, "Analyze code", response)
96 |
97 | return jsonify({
98 | 'conversation_id': conversation_id,
99 | 'response': response
100 | })
101 |
102 | except Exception as e:
103 | return jsonify({
104 | 'error': str(e)
105 | }), 400
106 |
107 | @app.route('/chat', methods=['POST'])
108 | def chat():
109 | """Handle chat messages"""
110 | try:
111 | data = request.json
112 | message = data['message']
113 | conversation_id = data['conversation_id']
114 |
115 | # Get conversation history
116 | history = chat_history.get_conversation_history(conversation_id)
117 | github_url = history['github_url']
118 |
119 | # Get code context
120 | code_context = feedback_system.github_handler.get_file_content(github_url)
121 |
122 | if github_url.endswith('.ipynb'):
123 | code_context = feedback_system.notebook_handler.extract_notebook_code(code_context)
124 |
125 | # Get AI response
126 | response = feedback_system.chat_about_code(message, code_context)
127 |
128 | # Save message and response
129 | chat_history.add_message(conversation_id, message, response)
130 |
131 | return jsonify({'response': response})
132 |
133 | except Exception as e:
134 | return jsonify({
135 | 'error': str(e)
136 | }), 400
137 |
138 | @app.route('/history/