DocUA commited on
Commit
b4da720
·
1 Parent(s): 10a9a4e

налаштування AI Аналіз

Browse files
Files changed (4) hide show
  1. app.py +2 -1
  2. interface.py +82 -90
  3. modules/ai_analysis/llm_connector.py +85 -51
  4. requirements.txt +1 -1
app.py CHANGED
@@ -325,4 +325,5 @@ app = JiraAssistantApp()
325
  # Точка входу для запуску з командного рядка
326
  if __name__ == "__main__":
327
  from interface import launch_interface
328
- launch_interface(app)
 
 
325
  # Точка входу для запуску з командного рядка
326
  if __name__ == "__main__":
327
  from interface import launch_interface
328
+ interface = launch_interface(app)
329
+ interface.launch()
interface.py CHANGED
@@ -16,7 +16,7 @@ def launch_interface(app):
16
  """
17
  # Функція для обробки завантаження та аналізу CSV
18
  # Змініть функцію analyze_csv, щоб вона повертала тільки звіт та AI аналіз
19
- def analyze_csv(file_obj, inactive_days, include_ai):
20
  if file_obj is None:
21
  return "Помилка: файл не вибрано", None
22
 
@@ -40,34 +40,44 @@ def launch_interface(app):
40
  f.write(str(file_obj))
41
 
42
  # Аналіз даних
43
- api_key = os.getenv("OPENAI_API_KEY") if include_ai else None
 
 
 
 
 
 
44
  result = app.analyze_csv_file(
45
  temp_file_path,
46
  inactive_days=inactive_days,
47
  include_ai=include_ai,
48
- api_key=api_key
 
49
  )
50
 
51
- # Видалення тимчасового файлу
52
- try:
53
- os.remove(temp_file_path)
54
- except:
55
- pass
56
-
57
  if result.get("error"):
58
  return result.get("error"), None
59
-
60
- return (
61
- result.get("report", ""),
62
- result.get("ai_analysis", "AI аналіз буде доступний у наступних версіях додатку.")
63
- )
 
 
 
 
 
 
 
 
 
64
 
65
  except Exception as e:
66
  import traceback
67
  error_msg = f"Помилка аналізу: {str(e)}\n\n{traceback.format_exc()}"
68
  logger.error(error_msg)
69
  return error_msg, None
70
-
71
  # Функція для збереження звіту
72
  def save_report_handler(report_text, format_type, include_visualizations):
73
  if not report_text:
@@ -213,41 +223,72 @@ def launch_interface(app):
213
 
214
  include_ai = gr.Checkbox(label="Включити AI аналіз", value=False)
215
 
 
 
 
 
 
 
 
 
216
  analyze_btn = gr.Button("Аналізувати", variant="primary")
217
 
218
  with gr.Accordion("Збереження звіту", open=False):
219
- format_type = gr.Dropdown(
220
- choices=["markdown", "html", "pdf"],
221
- value="markdown",
222
  label="Формат звіту"
223
  )
224
  include_visualizations = gr.Checkbox(
225
- label="Включити візуалізації",
226
  value=True
227
  )
228
  save_btn = gr.Button("Зберегти звіт")
229
- save_output = gr.Textbox(label="Статус збереження")
230
 
231
  with gr.Column(scale=2):
232
- with gr.Tabs():
233
- with gr.Tab("Звіт"):
234
- report_output = gr.Markdown()
235
-
236
- with gr.Tab("AI Аналіз"):
237
- ai_output = gr.Markdown()
 
 
 
 
 
 
 
 
 
 
 
 
 
238
 
239
- # Встановлюємо обробники подій
 
 
 
 
 
 
 
240
  analyze_btn.click(
241
  analyze_csv,
242
- inputs=[file_input, inactive_days, include_ai],
243
  outputs=[report_output, ai_output]
244
  )
245
-
246
- save_btn.click(
247
- save_report_handler,
248
- inputs=[report_output, format_type, include_visualizations],
249
- outputs=[save_output]
 
 
250
  )
 
251
 
252
  # Нова вкладка для розширених візуалізацій
253
  with gr.Tab("Візуалізації"):
@@ -389,53 +430,6 @@ def launch_interface(app):
389
  lines=3
390
  )
391
  slack_send_btn = gr.Button("Надіслати у Slack", interactive=False)
392
-
393
- with gr.Tab("Налаштування"):
394
- gr.Markdown("## Налаштування програми")
395
-
396
- with gr.Accordion("Загальні налаштування", open=True):
397
- with gr.Row():
398
- theme_dropdown = gr.Dropdown(
399
- choices=["Світла", "Темна", "Системна"],
400
- value="Системна",
401
- label="Тема інтерфейсу"
402
- )
403
- language_dropdown = gr.Dropdown(
404
- choices=["Українська", "English"],
405
- value="Українська",
406
- label="Мова інтерфейсу"
407
- )
408
-
409
- chart_style = gr.Dropdown(
410
- choices=["ggplot", "seaborn", "bmh", "classic", "dark_background"],
411
- value="ggplot",
412
- label="Стиль графіків"
413
- )
414
-
415
- with gr.Accordion("Налаштування AI", open=True):
416
- with gr.Row():
417
- openai_api_key = gr.Textbox(
418
- label="OpenAI API ключ",
419
- placeholder="sk-...",
420
- type="password"
421
- )
422
- openai_model = gr.Dropdown(
423
- choices=["gpt-3.5-turbo", "gpt-4", "gpt-4o", "gpt-4o-mini"],
424
- value="gpt-3.5-turbo",
425
- label="Модель OpenAI"
426
- )
427
-
428
- with gr.Row():
429
- gemini_api_key = gr.Textbox(
430
- label="Google Gemini API ключ",
431
- placeholder="...",
432
- type="password"
433
- )
434
- gemini_model = gr.Dropdown(
435
- choices=["gemini-pro", "gemini-1.5-pro"],
436
- value="gemini-pro",
437
- label="Модель Gemini"
438
- )
439
 
440
  save_settings_btn = gr.Button("Зберегти налаштування", variant="primary")
441
  settings_status = gr.Textbox(label="Статус")
@@ -446,15 +440,13 @@ def launch_interface(app):
446
  inputs=[],
447
  outputs=[settings_status]
448
  )
 
 
 
 
 
 
 
449
 
450
  # Запуск інтерфейсу
451
- interface.launch()
452
-
453
- # Можливість запустити інтерфейс самостійно (для тестування)
454
- if __name__ == "__main__":
455
- import sys
456
- sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
457
-
458
- from app import JiraAssistantApp
459
- app_instance = JiraAssistantApp()
460
- launch_interface(app_instance)
 
16
  """
17
  # Функція для обробки завантаження та аналізу CSV
18
  # Змініть функцію analyze_csv, щоб вона повертала тільки звіт та AI аналіз
19
+ def analyze_csv(file_obj, inactive_days, include_ai, model_type):
20
  if file_obj is None:
21
  return "Помилка: файл не вибрано", None
22
 
 
40
  f.write(str(file_obj))
41
 
42
  # Аналіз даних
43
+ api_key = None
44
+ if include_ai:
45
+ if model_type == "openai":
46
+ api_key = os.getenv("OPENAI_API_KEY")
47
+ elif model_type == "gemini":
48
+ api_key = os.getenv("GEMINI_API_KEY")
49
+
50
  result = app.analyze_csv_file(
51
  temp_file_path,
52
  inactive_days=inactive_days,
53
  include_ai=include_ai,
54
+ api_key=api_key,
55
+ model_type=model_type if include_ai else None
56
  )
57
 
 
 
 
 
 
 
58
  if result.get("error"):
59
  return result.get("error"), None
60
+
61
+ # Отримуємо звіт та AI аналіз
62
+ report = result.get("report", "")
63
+ ai_analysis = result.get("ai_analysis", "")
64
+
65
+ # Якщо AI аналіз не включений або порожній, повертаємо None для ai_output
66
+ if not include_ai or not ai_analysis:
67
+ ai_analysis = None
68
+
69
+ # Перевіряємо, чи не однакові звіт та AI аналіз
70
+ if ai_analysis == report:
71
+ ai_analysis = "Помилка: AI аналіз ідентичний звіту. Перевірте налаштування LLM."
72
+
73
+ return report, ai_analysis
74
 
75
  except Exception as e:
76
  import traceback
77
  error_msg = f"Помилка аналізу: {str(e)}\n\n{traceback.format_exc()}"
78
  logger.error(error_msg)
79
  return error_msg, None
80
+
81
  # Функція для збереження звіту
82
  def save_report_handler(report_text, format_type, include_visualizations):
83
  if not report_text:
 
223
 
224
  include_ai = gr.Checkbox(label="Включити AI аналіз", value=False)
225
 
226
+ # Додаємо вибір моделі для AI аналізу
227
+ model_type = gr.Radio(
228
+ choices=["gemini", "openai"],
229
+ value="gemini",
230
+ label="Модель для AI аналізу",
231
+ interactive=True
232
+ )
233
+
234
  analyze_btn = gr.Button("Аналізувати", variant="primary")
235
 
236
  with gr.Accordion("Збереження звіту", open=False):
237
+ format_type = gr.Radio(
238
+ choices=["txt", "md", "html", "pdf"],
239
+ value="txt",
240
  label="Формат звіту"
241
  )
242
  include_visualizations = gr.Checkbox(
243
+ label="Включити візуалізації",
244
  value=True
245
  )
246
  save_btn = gr.Button("Зберегти звіт")
247
+ save_status = gr.Textbox(label="Статус збереження")
248
 
249
  with gr.Column(scale=2):
250
+ report_output = gr.Textbox(
251
+ label="Звіт аналізу",
252
+ lines=20,
253
+ max_lines=30
254
+ )
255
+ ai_output = gr.Textbox(
256
+ label="AI аналіз",
257
+ lines=20,
258
+ max_lines=30,
259
+ visible=False # Початково приховано
260
+ )
261
+
262
+ # Додаємо залежність для відображення/приховування AI аналізу
263
+ include_ai.change(
264
+ lambda x: gr.update(visible=x),
265
+ inputs=[include_ai],
266
+ outputs=[ai_output]
267
+ )
268
+
269
 
270
+ # Функція для оновлення видимості AI аналізу
271
+ def update_ai_output(include_ai, ai_text):
272
+ if include_ai and ai_text:
273
+ return gr.update(visible=True, value=ai_text)
274
+ else:
275
+ return gr.update(visible=False, value="")
276
+
277
+ # Оновлюємо обробник події для аналізу
278
  analyze_btn.click(
279
  analyze_csv,
280
+ inputs=[file_input, inactive_days, include_ai, model_type],
281
  outputs=[report_output, ai_output]
282
  )
283
+
284
+ # Додаємо обробник для відображення/приховування AI аналізу
285
+ analyze_btn.click(
286
+ update_ai_output,
287
+ inputs=[include_ai, ai_output],
288
+ outputs=[ai_output],
289
+ queue=False # Виконується одразу після analyze_csv
290
  )
291
+
292
 
293
  # Нова вкладка для розширених візуалізацій
294
  with gr.Tab("Візуалізації"):
 
430
  lines=3
431
  )
432
  slack_send_btn = gr.Button("Надіслати у Slack", interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
433
 
434
  save_settings_btn = gr.Button("Зберегти налаштування", variant="primary")
435
  settings_status = gr.Textbox(label="Статус")
 
440
  inputs=[],
441
  outputs=[settings_status]
442
  )
443
+ # Додаємо залежність для відображення/приховування вибору моделі
444
+ # Додаємо залежність для відображення/приховування вибору моделі
445
+ include_ai.change(
446
+ lambda x: gr.update(visible=x),
447
+ inputs=[include_ai],
448
+ outputs=[model_type]
449
+ )
450
 
451
  # Запуск інтерфейсу
452
+ interface.launch()
 
 
 
 
 
 
 
 
 
modules/ai_analysis/llm_connector.py CHANGED
@@ -3,7 +3,8 @@ import json
3
  import logging
4
  import requests
5
  from typing import Dict, List, Any, Optional
6
- import openai
 
7
  from datetime import datetime, timedelta
8
 
9
  logger = logging.getLogger(__name__)
@@ -201,60 +202,93 @@ class LLMConnector:
201
  if not self.api_key:
202
  return "Не вказано API ключ Gemini"
203
 
204
- # API endpoint
205
- url = f"https://generativelanguage.googleapis.com/v1beta/models/{self.model}:generateContent?key={self.api_key}"
206
-
207
- # Формування запиту
208
- payload = {
209
- "contents": [
210
- {
211
- "parts": [
212
- {
213
- "text": """Ви аналітик Jira з досвідом у процесах розробки ПЗ.
214
- Проаналізуйте надані дані про тікети та надайте корисні інсайти та рекомендації
215
- для покращення процесу. Будьте конкретними та орієнтованими на дії.
216
- Виділіть сильні та слабкі сторони, а також потенційні ризики та можливості.
217
- Аналіз повинен бути структурованим і легким для сприйняття менеджерами проекту."""
218
- }
219
- ]
220
- },
221
- {
222
- "parts": [
223
- {
224
- "text": f"Проаналізуйте наступні дані Jira та надайте рекомендації:\n\n{data_summary}"
225
- }
226
- ]
227
- }
228
- ],
229
- "generationConfig": {
230
- "temperature": temperature,
231
- "maxOutputTokens": 2048
232
- }
233
- }
234
 
235
- # Відправлення запиту
236
- headers = {"Content-Type": "application/json"}
237
- response = requests.post(url, headers=headers, json=payload)
238
 
239
- # Обробка відповіді
240
- if response.status_code == 200:
241
- response_data = response.json()
242
-
243
- # Отримання тексту відповіді
244
- if 'candidates' in response_data and len(response_data['candidates']) > 0:
245
- if 'content' in response_data['candidates'][0]:
246
- content = response_data['candidates'][0]['content']
247
- if 'parts' in content and len(content['parts']) > 0:
248
- result = content['parts'][0].get('text', '')
249
- logger.info("Успішно отримано аналіз від Gemini")
250
- return result
251
-
252
- logger.error(f"Помилка при взаємодії з Gemini: {response.text}")
253
- return f"Помилка при взаємодії з Gemini: статус {response.status_code}"
 
 
254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  except Exception as e:
256
- logger.error(f"Помилка при взаємодії з Gemini: {e}")
257
- return f"Помилка при взаємодії з Gemini: {str(e)}"
 
 
258
 
259
  def ask_question(self, question, context=None, temperature=0.3):
260
  """
 
3
  import logging
4
  import requests
5
  from typing import Dict, List, Any, Optional
6
+
7
+
8
  from datetime import datetime, timedelta
9
 
10
  logger = logging.getLogger(__name__)
 
202
  if not self.api_key:
203
  return "Не вказано API ключ Gemini"
204
 
205
+ # Імпортуємо необхідні бібліотеки
206
+ try:
207
+ from google import genai
208
+ from google.genai import types
209
+ except ImportError:
210
+ logger.error("Бібліотека google-generativeai не встановлена")
211
+ return "Помилка: бібліотека google-generativeai не встановлена. Встановіть її командою: pip install google-generativeai"
212
+
213
+ # Налаштування клієнта
214
+ client = genai.Client(api_key=self.api_key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
+ # Додаємо логування для діагностики
217
+ logger.info(f"Відправляємо запит до Gemini API. Довжина запиту: {len(data_summary)} символів")
 
218
 
219
+ # Системна інструкція
220
+ system_instruction = """Ви аналітик Jira з досвідом у процесах розробки ПЗ.
221
+ Проаналізуйте надані дані про тікети та надайте корисні інсайти та рекомендації
222
+ для покращення процесу. Будьте конкретними та орієнтованими на дії.
223
+ Виділіть сильні та слабкі сторони, а також потенційні ризики та можливості.
224
+ Аналіз повинен бути структурованим і легким для сприйняття менеджерами проекту."""
225
+
226
+ # Запит користувача
227
+ user_prompt = f"Проаналізуйте наступні дані Jira та надайте рекомендації:\n\n{data_summary}"
228
+
229
+ # Створення запиту
230
+ contents = [
231
+ types.Content(
232
+ role="user",
233
+ parts=[types.Part.from_text(text=user_prompt)]
234
+ )
235
+ ]
236
 
237
+ # Налаштування генерації з системною інструкцією
238
+ generate_content_config = types.GenerateContentConfig(
239
+ temperature=temperature,
240
+ top_p=0.95,
241
+ top_k=40,
242
+ max_output_tokens=2048,
243
+ response_mime_type="text/plain",
244
+ system_instruction=[
245
+ types.Part.from_text(text=system_instruction)
246
+ ],
247
+ )
248
+
249
+ # Відправка запиту
250
+ try:
251
+ response = client.models.generate_content(
252
+ model=self.model,
253
+ contents=contents,
254
+ config=generate_content_config,
255
+ )
256
+
257
+ # Перевірка відповіді
258
+ if hasattr(response, 'text') and response.text:
259
+ logger.info("Успішно отримано аналіз від Gemini")
260
+ return response.text
261
+ else:
262
+ logger.error("Порожня відповідь від Gemini API")
263
+ return "Помилка: порожня відповідь від Gemini API"
264
+
265
+ except Exception as api_error:
266
+ logger.error(f"Помилка API Gemini: {str(api_error)}")
267
+
268
+ # Спробуємо з іншою моделлю, якщо поточна не працює
269
+ try:
270
+ logger.info("Спроба з альтернативною моделлю gemini-1.5-flash")
271
+ alternative_model = "gemini-1.5-flash"
272
+ response = client.models.generate_content(
273
+ model=alternative_model,
274
+ contents=contents,
275
+ config=generate_content_config,
276
+ )
277
+
278
+ if hasattr(response, 'text') and response.text:
279
+ logger.info("Успішно отримано аналіз від альтернативної моделі Gemini")
280
+ return response.text
281
+ else:
282
+ return "Помилка: порожня відповідь від альтернативної моделі Gemini API"
283
+ except Exception as retry_error:
284
+ logger.error(f"Повторна помилка API Gemini: {str(retry_error)}")
285
+ return f"Помилка при взаємодії з Gemini: {str(api_error)}"
286
+
287
  except Exception as e:
288
+ import traceback
289
+ error_msg = f"Помилка при взаємодії з Gemini: {str(e)}\n\n{traceback.format_exc()}"
290
+ logger.error(error_msg)
291
+ return error_msg
292
 
293
  def ask_question(self, question, context=None, temperature=0.3):
294
  """
requirements.txt CHANGED
@@ -7,6 +7,6 @@ seaborn>=0.12.2
7
  python-dotenv>=1.0.0
8
  markdown>=3.4.4
9
  pathlib>=1.0.1
10
- google-generativeai>=0.3.2
11
  openai>=1.12.0
12
  httpx>=0.27.0
 
7
  python-dotenv>=1.0.0
8
  markdown>=3.4.4
9
  pathlib>=1.0.1
10
+ google-genai>=0.3.2
11
  openai>=1.12.0
12
  httpx>=0.27.0