Spaces:
Runtime error
Runtime error
Підключено Gemini, але потребує агентної роботи
Browse files- Gradio_UI.py +42 -20
- agent.py +116 -51
- app.py +32 -31
- config.py +0 -112
- model_init.py +295 -0
- models_config.json +11 -61
- requirements.txt +2 -1
- test_gemini.py +47 -0
Gradio_UI.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
import logging
|
| 3 |
from pathlib import Path
|
| 4 |
-
from config import ModelConfig
|
| 5 |
from typing import Tuple, List
|
| 6 |
|
| 7 |
logger = logging.getLogger(__name__)
|
|
@@ -11,24 +10,33 @@ class GradioUI:
|
|
| 11 |
self.agent = agent
|
| 12 |
self.file_upload_folder = Path(file_upload_folder)
|
| 13 |
self.file_upload_folder.mkdir(exist_ok=True)
|
| 14 |
-
self.model_config = ModelConfig()
|
| 15 |
|
| 16 |
def build_interface(self):
|
| 17 |
with gr.Blocks(theme=gr.themes.Soft()) as interface:
|
| 18 |
-
#
|
| 19 |
with gr.Row():
|
| 20 |
-
model_choices = self.
|
| 21 |
model_dropdown = gr.Dropdown(
|
| 22 |
choices=[f"{key} - {desc}" for key, desc in model_choices],
|
| 23 |
-
value=f"{self.
|
| 24 |
label="Оберіть модель",
|
| 25 |
info="Оберіть модель для обробки запитів"
|
| 26 |
)
|
| 27 |
|
| 28 |
with gr.Row():
|
|
|
|
| 29 |
chatbot = gr.Chatbot(
|
| 30 |
label="Research Assistant",
|
| 31 |
-
height=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
show_copy_button=True
|
| 33 |
)
|
| 34 |
|
|
@@ -55,15 +63,14 @@ class GradioUI:
|
|
| 55 |
clear_btn = gr.Button("Очистити")
|
| 56 |
toggle_upload_btn = gr.Button("Завантажити файл")
|
| 57 |
|
| 58 |
-
#
|
| 59 |
state = gr.State([])
|
| 60 |
file_history = gr.State([])
|
| 61 |
-
current_model = gr.State(self.
|
| 62 |
|
| 63 |
def change_model(choice):
|
| 64 |
model_key = choice.split(" - ")[0]
|
| 65 |
-
|
| 66 |
-
self.agent.update_model(model_config)
|
| 67 |
return model_key
|
| 68 |
|
| 69 |
model_dropdown.change(
|
|
@@ -104,38 +111,53 @@ class GradioUI:
|
|
| 104 |
if files:
|
| 105 |
message += f"\nДоступні файли для аналізу: {', '.join(files)}"
|
| 106 |
chat_history.append((message, None))
|
| 107 |
-
return "", chat_history
|
| 108 |
|
| 109 |
-
def bot_response(chat_history, files):
|
| 110 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 111 |
response = self.agent.process_query(
|
| 112 |
chat_history[-1][0],
|
| 113 |
available_files=files if files else None
|
| 114 |
)
|
|
|
|
|
|
|
| 115 |
chat_history[-1] = (chat_history[-1][0], response)
|
| 116 |
-
|
|
|
|
|
|
|
| 117 |
except Exception as e:
|
| 118 |
logger.error(f"Помилка відповіді: {e}")
|
| 119 |
-
|
| 120 |
-
|
|
|
|
| 121 |
|
| 122 |
submit_btn.click(
|
| 123 |
user_message,
|
| 124 |
[text_input, state, file_history],
|
| 125 |
-
[text_input, chatbot]
|
| 126 |
).then(
|
| 127 |
bot_response,
|
| 128 |
-
[chatbot, file_history],
|
| 129 |
-
[chatbot]
|
| 130 |
)
|
| 131 |
|
| 132 |
def clear_chat():
|
| 133 |
-
return [], []
|
| 134 |
|
| 135 |
clear_btn.click(
|
| 136 |
clear_chat,
|
| 137 |
None,
|
| 138 |
-
[chatbot, file_history],
|
| 139 |
queue=False
|
| 140 |
)
|
| 141 |
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
import logging
|
| 3 |
from pathlib import Path
|
|
|
|
| 4 |
from typing import Tuple, List
|
| 5 |
|
| 6 |
logger = logging.getLogger(__name__)
|
|
|
|
| 10 |
self.agent = agent
|
| 11 |
self.file_upload_folder = Path(file_upload_folder)
|
| 12 |
self.file_upload_folder.mkdir(exist_ok=True)
|
|
|
|
| 13 |
|
| 14 |
def build_interface(self):
|
| 15 |
with gr.Blocks(theme=gr.themes.Soft()) as interface:
|
| 16 |
+
# Вибір моделі
|
| 17 |
with gr.Row():
|
| 18 |
+
model_choices = self.agent.model_initializer.get_available_models()
|
| 19 |
model_dropdown = gr.Dropdown(
|
| 20 |
choices=[f"{key} - {desc}" for key, desc in model_choices],
|
| 21 |
+
value=f"{self.agent.model_initializer.config['default_model']} - {dict(model_choices)[self.agent.model_initializer.config['default_model']]}",
|
| 22 |
label="Оберіть модель",
|
| 23 |
info="Оберіть модель для обробки запитів"
|
| 24 |
)
|
| 25 |
|
| 26 |
with gr.Row():
|
| 27 |
+
# Основний чат
|
| 28 |
chatbot = gr.Chatbot(
|
| 29 |
label="Research Assistant",
|
| 30 |
+
height=400,
|
| 31 |
+
show_copy_button=True
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
+
# Лог проміжних кроків
|
| 35 |
+
steps_log = gr.Textbox(
|
| 36 |
+
label="Process Steps",
|
| 37 |
+
lines=10,
|
| 38 |
+
max_lines=20,
|
| 39 |
+
interactive=False,
|
| 40 |
show_copy_button=True
|
| 41 |
)
|
| 42 |
|
|
|
|
| 63 |
clear_btn = gr.Button("Очистити")
|
| 64 |
toggle_upload_btn = gr.Button("Завантажити файл")
|
| 65 |
|
| 66 |
+
# Зберігання стану
|
| 67 |
state = gr.State([])
|
| 68 |
file_history = gr.State([])
|
| 69 |
+
current_model = gr.State(self.agent.model_initializer.config['default_model'])
|
| 70 |
|
| 71 |
def change_model(choice):
|
| 72 |
model_key = choice.split(" - ")[0]
|
| 73 |
+
self.agent.update_model(model_key)
|
|
|
|
| 74 |
return model_key
|
| 75 |
|
| 76 |
model_dropdown.change(
|
|
|
|
| 111 |
if files:
|
| 112 |
message += f"\nДоступні файли для аналізу: {', '.join(files)}"
|
| 113 |
chat_history.append((message, None))
|
| 114 |
+
return "", chat_history, "Початок обробки запиту...\n"
|
| 115 |
|
| 116 |
+
def bot_response(chat_history, files, steps_text):
|
| 117 |
try:
|
| 118 |
+
# Оновлюємо лог кроків
|
| 119 |
+
steps = []
|
| 120 |
+
def step_callback(step_info):
|
| 121 |
+
steps.append(f"Step {len(steps)+1}: {step_info}")
|
| 122 |
+
return "\n".join(steps)
|
| 123 |
+
|
| 124 |
+
# Додаємо callback до агента
|
| 125 |
+
self.agent.step_callback = step_callback
|
| 126 |
+
|
| 127 |
+
# Обробляємо запит
|
| 128 |
response = self.agent.process_query(
|
| 129 |
chat_history[-1][0],
|
| 130 |
available_files=files if files else None
|
| 131 |
)
|
| 132 |
+
|
| 133 |
+
# Оновлюємо історію чату
|
| 134 |
chat_history[-1] = (chat_history[-1][0], response)
|
| 135 |
+
|
| 136 |
+
# Повертаємо оновлену історію та лог кроків
|
| 137 |
+
return chat_history, "\n".join(steps) + "\n\nОбробку завершено."
|
| 138 |
except Exception as e:
|
| 139 |
logger.error(f"Помилка відповіді: {e}")
|
| 140 |
+
error_msg = f"Помилка: {str(e)}"
|
| 141 |
+
chat_history[-1] = (chat_history[-1][0], error_msg)
|
| 142 |
+
return chat_history, steps_text + f"\nПомилка: {str(e)}"
|
| 143 |
|
| 144 |
submit_btn.click(
|
| 145 |
user_message,
|
| 146 |
[text_input, state, file_history],
|
| 147 |
+
[text_input, chatbot, steps_log]
|
| 148 |
).then(
|
| 149 |
bot_response,
|
| 150 |
+
[chatbot, file_history, steps_log],
|
| 151 |
+
[chatbot, steps_log]
|
| 152 |
)
|
| 153 |
|
| 154 |
def clear_chat():
|
| 155 |
+
return [], [], ""
|
| 156 |
|
| 157 |
clear_btn.click(
|
| 158 |
clear_chat,
|
| 159 |
None,
|
| 160 |
+
[chatbot, file_history, steps_log],
|
| 161 |
queue=False
|
| 162 |
)
|
| 163 |
|
agent.py
CHANGED
|
@@ -8,35 +8,127 @@ logger = logging.getLogger(__name__)
|
|
| 8 |
|
| 9 |
class ResearchAgent(CodeAgent):
|
| 10 |
"""
|
| 11 |
-
|
| 12 |
-
Inherits from CodeAgent and specializes in academic research tasks.
|
| 13 |
"""
|
| 14 |
|
| 15 |
def __init__(self, model, tools, **kwargs):
|
| 16 |
"""
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
Args:
|
| 20 |
-
model: The language model to use
|
| 21 |
-
tools: List of available tools
|
| 22 |
-
**kwargs: Additional arguments passed to CodeAgent
|
| 23 |
"""
|
| 24 |
super().__init__(model=model, tools=tools, **kwargs)
|
| 25 |
self.available_tools = {tool.name: tool for tool in tools}
|
| 26 |
-
|
|
|
|
|
|
|
| 27 |
|
| 28 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
"""
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
-
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
"""
|
| 38 |
try:
|
| 39 |
-
# Default sections for research report
|
| 40 |
sections = [
|
| 41 |
"Executive Summary",
|
| 42 |
"Introduction",
|
|
@@ -47,75 +139,48 @@ class ResearchAgent(CodeAgent):
|
|
| 47 |
"References"
|
| 48 |
]
|
| 49 |
|
| 50 |
-
# Create report header
|
| 51 |
report = [
|
| 52 |
"# Науковий звіт",
|
| 53 |
f"*Згенеровано: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n"
|
| 54 |
]
|
| 55 |
|
| 56 |
-
# Add each section
|
| 57 |
for section in sections:
|
| 58 |
section_content = content.get(section, f"Розділ {section} не надано")
|
| 59 |
report.extend([
|
| 60 |
f"## {section}",
|
| 61 |
section_content,
|
| 62 |
-
""
|
| 63 |
])
|
| 64 |
|
| 65 |
return "\n".join(report)
|
| 66 |
|
| 67 |
except Exception as e:
|
| 68 |
logger.error(f"Error formatting research report: {e}")
|
| 69 |
-
return str(content)
|
| 70 |
-
|
| 71 |
-
def update_model(self, model_config):
|
| 72 |
-
"""Update the model configuration"""
|
| 73 |
-
try:
|
| 74 |
-
from smolagents import HfApiModel
|
| 75 |
-
self.model = HfApiModel(
|
| 76 |
-
model_id=model_config['model_id'],
|
| 77 |
-
token=os.getenv('HF_API_TOKEN'),
|
| 78 |
-
temperature=model_config['parameters'].get('temperature', 0.7)
|
| 79 |
-
)
|
| 80 |
-
logger.info(f"Model updated to: {model_config['model_id']}")
|
| 81 |
-
except Exception as e:
|
| 82 |
-
logger.error(f"Error updating model: {e}")
|
| 83 |
-
raise
|
| 84 |
|
| 85 |
def process_query(self, query: str, available_files: Optional[List[str]] = None) -> str:
|
| 86 |
"""
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
Args:
|
| 90 |
-
query (str): The research query to process
|
| 91 |
-
available_files (Optional[List[str]]): List of available file paths
|
| 92 |
-
|
| 93 |
-
Returns:
|
| 94 |
-
str: Formatted research results
|
| 95 |
"""
|
| 96 |
try:
|
| 97 |
-
logger.info(f"
|
| 98 |
|
| 99 |
-
#
|
| 100 |
result = self.run(
|
| 101 |
task=query,
|
| 102 |
-
stream=False,
|
| 103 |
-
reset=True
|
| 104 |
)
|
| 105 |
|
| 106 |
-
# Validate results
|
| 107 |
if not result:
|
| 108 |
return "Не вдалося отримати результати. Будь ласка, спробуйте переформулювати запит."
|
| 109 |
|
| 110 |
-
# If result is already a string, return it
|
| 111 |
if isinstance(result, str):
|
| 112 |
return result
|
| 113 |
|
| 114 |
-
# If result is a dict, format it as a report
|
| 115 |
if isinstance(result, dict):
|
| 116 |
return self.format_research_report(result)
|
| 117 |
|
| 118 |
-
# Default case
|
| 119 |
return str(result)
|
| 120 |
|
| 121 |
except Exception as e:
|
|
|
|
| 8 |
|
| 9 |
class ResearchAgent(CodeAgent):
|
| 10 |
"""
|
| 11 |
+
Дослідницький агент для наукового пошуку та аналізу.
|
|
|
|
| 12 |
"""
|
| 13 |
|
| 14 |
def __init__(self, model, tools, **kwargs):
|
| 15 |
"""
|
| 16 |
+
Ініціалізація дослідницького агента.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
"""
|
| 18 |
super().__init__(model=model, tools=tools, **kwargs)
|
| 19 |
self.available_tools = {tool.name: tool for tool in tools}
|
| 20 |
+
self.model_initializer = None
|
| 21 |
+
self.step_callback = None
|
| 22 |
+
logger.info(f"ResearchAgent ініціалізовано з інструментами: {list(self.available_tools.keys())}")
|
| 23 |
|
| 24 |
+
def _update_steps(self, step_info: str):
|
| 25 |
+
"""Оновлення інформації про кроки"""
|
| 26 |
+
if self.step_callback:
|
| 27 |
+
self.step_callback(step_info)
|
| 28 |
+
|
| 29 |
+
def run(self, task: str, stream=False, reset=True) -> str:
|
| 30 |
"""
|
| 31 |
+
Перевизначений метод run для кращої обробки запитів.
|
| 32 |
+
"""
|
| 33 |
+
try:
|
| 34 |
+
self._update_steps("Початок обробки запиту")
|
| 35 |
+
|
| 36 |
+
# Step 1: Web Search
|
| 37 |
+
self._update_steps("Пошук інформації в інтернеті")
|
| 38 |
+
search_results = self.available_tools['web_search'](query=task)
|
| 39 |
+
|
| 40 |
+
# Step 2: Формування запиту до моделі
|
| 41 |
+
prompt = (
|
| 42 |
+
"As a research assistant, analyze the following information and create a detailed report.\n\n"
|
| 43 |
+
f"Task: {task}\n\n"
|
| 44 |
+
f"Search Results:\n{search_results}\n\n"
|
| 45 |
+
"Create a comprehensive report with references to sources."
|
| 46 |
+
)
|
| 47 |
+
|
| 48 |
+
self._update_steps("Генерація звіту на основі знайденої інформації")
|
| 49 |
+
result = self.model(prompt)
|
| 50 |
|
| 51 |
+
self._update_steps("Форматування фінального звіту")
|
| 52 |
+
return result
|
| 53 |
+
|
| 54 |
+
except Exception as e:
|
| 55 |
+
error_msg = f"Error in run method: {str(e)}"
|
| 56 |
+
logger.error(error_msg)
|
| 57 |
+
self._update_steps(f"Помилка: {error_msg}")
|
| 58 |
+
return error_msg
|
| 59 |
+
|
| 60 |
+
def process_query(self, query: str, available_files: Optional[List[str]] = None) -> str:
|
| 61 |
+
"""
|
| 62 |
+
Обробка дослідницького запиту.
|
| 63 |
+
"""
|
| 64 |
+
try:
|
| 65 |
+
logger.info(f"Обробка дослідницького запиту: {query}")
|
| 66 |
+
self._update_steps("Початок обробки запиту")
|
| 67 |
+
|
| 68 |
+
# Виконання запиту
|
| 69 |
+
result = self.run(
|
| 70 |
+
task=query,
|
| 71 |
+
stream=False,
|
| 72 |
+
reset=True
|
| 73 |
+
)
|
| 74 |
+
|
| 75 |
+
if not result:
|
| 76 |
+
self._update_steps("Не отримано результатів")
|
| 77 |
+
return "Не вдалося отримати результати. Будь ласка, спробуйте переформулювати запит."
|
| 78 |
+
|
| 79 |
+
self._update_steps("Запит успішно оброблено")
|
| 80 |
+
|
| 81 |
+
if isinstance(result, str):
|
| 82 |
+
return result
|
| 83 |
+
|
| 84 |
+
if isinstance(result, dict):
|
| 85 |
+
return self.format_research_report(result)
|
| 86 |
+
|
| 87 |
+
return str(result)
|
| 88 |
+
|
| 89 |
+
except Exception as e:
|
| 90 |
+
error_msg = f"Помилка при обробці запиту: {str(e)}"
|
| 91 |
+
logger.error(error_msg)
|
| 92 |
+
self._update_steps(f"Помилка: {error_msg}")
|
| 93 |
+
return error_msg
|
| 94 |
+
"""
|
| 95 |
+
Дослідницький агент для наукового пошуку та аналізу.
|
| 96 |
+
"""
|
| 97 |
+
|
| 98 |
+
def __init__(self, model, tools, **kwargs):
|
| 99 |
+
"""
|
| 100 |
+
Ініціалізація дослідницького агента.
|
| 101 |
+
"""
|
| 102 |
+
super().__init__(model=model, tools=tools, **kwargs)
|
| 103 |
+
self.available_tools = {tool.name: tool for tool in tools}
|
| 104 |
+
self.model_initializer = None
|
| 105 |
+
logger.info(f"ResearchAgent ініціалізовано з інструментами: {list(self.available_tools.keys())}")
|
| 106 |
+
|
| 107 |
+
def run(self, task: str, stream=False, reset=True) -> str:
|
| 108 |
+
"""
|
| 109 |
+
Перевизначений метод run для кращої обробки запитів.
|
| 110 |
+
"""
|
| 111 |
+
try:
|
| 112 |
+
# Формуємо простий текстовий запит
|
| 113 |
+
prompt = (
|
| 114 |
+
"You are a research assistant. Your task is to:\n"
|
| 115 |
+
f"{task}\n\n"
|
| 116 |
+
"Provide a detailed response."
|
| 117 |
+
)
|
| 118 |
+
logger.info(f"Sending prompt to model: {prompt[:100]}...")
|
| 119 |
+
|
| 120 |
+
result = self.model(prompt)
|
| 121 |
+
return result
|
| 122 |
+
|
| 123 |
+
except Exception as e:
|
| 124 |
+
logger.error(f"Error in run method: {str(e)}")
|
| 125 |
+
return f"Error processing request: {str(e)}"
|
| 126 |
+
|
| 127 |
+
def format_research_report(self, content: Dict[str, Any]) -> str:
|
| 128 |
+
"""
|
| 129 |
+
Форматування результатів у звіт.
|
| 130 |
"""
|
| 131 |
try:
|
|
|
|
| 132 |
sections = [
|
| 133 |
"Executive Summary",
|
| 134 |
"Introduction",
|
|
|
|
| 139 |
"References"
|
| 140 |
]
|
| 141 |
|
|
|
|
| 142 |
report = [
|
| 143 |
"# Науковий звіт",
|
| 144 |
f"*Згенеровано: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n"
|
| 145 |
]
|
| 146 |
|
|
|
|
| 147 |
for section in sections:
|
| 148 |
section_content = content.get(section, f"Розділ {section} не надано")
|
| 149 |
report.extend([
|
| 150 |
f"## {section}",
|
| 151 |
section_content,
|
| 152 |
+
""
|
| 153 |
])
|
| 154 |
|
| 155 |
return "\n".join(report)
|
| 156 |
|
| 157 |
except Exception as e:
|
| 158 |
logger.error(f"Error formatting research report: {e}")
|
| 159 |
+
return str(content)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 160 |
|
| 161 |
def process_query(self, query: str, available_files: Optional[List[str]] = None) -> str:
|
| 162 |
"""
|
| 163 |
+
Обробка дослідницького запиту.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 164 |
"""
|
| 165 |
try:
|
| 166 |
+
logger.info(f"Обробка дослідницького запиту: {query}")
|
| 167 |
|
| 168 |
+
# Виконання запиту
|
| 169 |
result = self.run(
|
| 170 |
task=query,
|
| 171 |
+
stream=False,
|
| 172 |
+
reset=True
|
| 173 |
)
|
| 174 |
|
|
|
|
| 175 |
if not result:
|
| 176 |
return "Не вдалося отримати результати. Будь ласка, спробуйте переформулювати запит."
|
| 177 |
|
|
|
|
| 178 |
if isinstance(result, str):
|
| 179 |
return result
|
| 180 |
|
|
|
|
| 181 |
if isinstance(result, dict):
|
| 182 |
return self.format_research_report(result)
|
| 183 |
|
|
|
|
| 184 |
return str(result)
|
| 185 |
|
| 186 |
except Exception as e:
|
app.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
| 1 |
-
from smolagents import HfApiModel
|
| 2 |
from tools.web_search import DuckDuckGoSearchTool
|
| 3 |
from tools.final_answer import FinalAnswerTool
|
| 4 |
from tools.healthcare_llm_visualizer import HealthcareLLMVisualizerTool
|
| 5 |
from Gradio_UI import GradioUI
|
| 6 |
from agent import ResearchAgent
|
|
|
|
| 7 |
import os
|
| 8 |
from dotenv import load_dotenv
|
| 9 |
import logging
|
| 10 |
-
from
|
| 11 |
|
| 12 |
-
#
|
| 13 |
logging.basicConfig(
|
| 14 |
level=logging.INFO,
|
| 15 |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
@@ -20,12 +20,12 @@ logging.basicConfig(
|
|
| 20 |
)
|
| 21 |
logger = logging.getLogger(__name__)
|
| 22 |
|
| 23 |
-
#
|
| 24 |
load_dotenv()
|
| 25 |
|
| 26 |
def initialize_tools():
|
| 27 |
-
"""
|
| 28 |
-
logger.info("
|
| 29 |
try:
|
| 30 |
tools = [
|
| 31 |
DuckDuckGoSearchTool(
|
|
@@ -34,50 +34,51 @@ def initialize_tools():
|
|
| 34 |
FinalAnswerTool(),
|
| 35 |
HealthcareLLMVisualizerTool()
|
| 36 |
]
|
| 37 |
-
logger.info("
|
| 38 |
return tools
|
| 39 |
except Exception as e:
|
| 40 |
-
logger.error(f"
|
| 41 |
raise
|
| 42 |
|
| 43 |
-
def initialize_model():
|
| 44 |
-
"""
|
| 45 |
-
logger.info("
|
| 46 |
try:
|
| 47 |
-
|
| 48 |
-
|
|
|
|
| 49 |
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
)
|
| 56 |
-
logger.info(f"Model initialized: {default_model['model_id']}")
|
| 57 |
-
return model
|
| 58 |
except Exception as e:
|
| 59 |
-
logger.error(f"
|
| 60 |
raise
|
| 61 |
|
| 62 |
def main():
|
| 63 |
-
"""
|
| 64 |
try:
|
| 65 |
-
logger.info("
|
| 66 |
|
| 67 |
-
#
|
| 68 |
tools = initialize_tools()
|
| 69 |
-
model = initialize_model()
|
| 70 |
|
| 71 |
-
#
|
| 72 |
agent = ResearchAgent(
|
| 73 |
model=model,
|
| 74 |
tools=tools,
|
| 75 |
-
max_steps=int(os.getenv('MAX_STEPS', 10)),
|
| 76 |
verbosity_level=int(os.getenv('VERBOSITY_LEVEL', 1))
|
| 77 |
)
|
| 78 |
|
| 79 |
-
#
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
| 81 |
ui = GradioUI(agent)
|
| 82 |
ui.launch(
|
| 83 |
debug=os.getenv('DEBUG_MODE', 'False').lower() == 'true',
|
|
@@ -87,7 +88,7 @@ def main():
|
|
| 87 |
)
|
| 88 |
|
| 89 |
except Exception as e:
|
| 90 |
-
logger.error(f"
|
| 91 |
raise
|
| 92 |
|
| 93 |
if __name__ == "__main__":
|
|
|
|
|
|
|
| 1 |
from tools.web_search import DuckDuckGoSearchTool
|
| 2 |
from tools.final_answer import FinalAnswerTool
|
| 3 |
from tools.healthcare_llm_visualizer import HealthcareLLMVisualizerTool
|
| 4 |
from Gradio_UI import GradioUI
|
| 5 |
from agent import ResearchAgent
|
| 6 |
+
from model_init import ModelInitializer # Новий імпорт
|
| 7 |
import os
|
| 8 |
from dotenv import load_dotenv
|
| 9 |
import logging
|
| 10 |
+
from pathlib import Path
|
| 11 |
|
| 12 |
+
# Налаштування логування
|
| 13 |
logging.basicConfig(
|
| 14 |
level=logging.INFO,
|
| 15 |
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
|
|
|
| 20 |
)
|
| 21 |
logger = logging.getLogger(__name__)
|
| 22 |
|
| 23 |
+
# Завантаження змінних середовища
|
| 24 |
load_dotenv()
|
| 25 |
|
| 26 |
def initialize_tools():
|
| 27 |
+
"""Ініціалізація всіх доступних інструментів"""
|
| 28 |
+
logger.info("Ініціалізація інструментів...")
|
| 29 |
try:
|
| 30 |
tools = [
|
| 31 |
DuckDuckGoSearchTool(
|
|
|
|
| 34 |
FinalAnswerTool(),
|
| 35 |
HealthcareLLMVisualizerTool()
|
| 36 |
]
|
| 37 |
+
logger.info("Інструменти успішно ініціалізовано")
|
| 38 |
return tools
|
| 39 |
except Exception as e:
|
| 40 |
+
logger.error(f"Помилка ініціалізації інструментів: {e}")
|
| 41 |
raise
|
| 42 |
|
| 43 |
+
def initialize_model(model_key=None):
|
| 44 |
+
"""Ініціалізація моделі"""
|
| 45 |
+
logger.info("Ініціалізація мовної моделі...")
|
| 46 |
try:
|
| 47 |
+
config_path = Path("models_config.json")
|
| 48 |
+
model_initializer = ModelInitializer(config_path)
|
| 49 |
+
model = model_initializer.initialize_model(model_key)
|
| 50 |
|
| 51 |
+
if model_key:
|
| 52 |
+
logger.info(f"Модель ініціалізовано: {model_key}")
|
| 53 |
+
else:
|
| 54 |
+
logger.info("Ініціалізовано модель за замовчуванням")
|
| 55 |
+
return model, model_initializer
|
|
|
|
|
|
|
|
|
|
| 56 |
except Exception as e:
|
| 57 |
+
logger.error(f"Помилка ініціалізації моделі: {e}")
|
| 58 |
raise
|
| 59 |
|
| 60 |
def main():
|
| 61 |
+
"""Головна точка входу в програму"""
|
| 62 |
try:
|
| 63 |
+
logger.info("Запуск Research Agent...")
|
| 64 |
|
| 65 |
+
# Ініціалізація компонентів
|
| 66 |
tools = initialize_tools()
|
| 67 |
+
model, model_initializer = initialize_model()
|
| 68 |
|
| 69 |
+
# Ініціалізація дослідницького агента
|
| 70 |
agent = ResearchAgent(
|
| 71 |
model=model,
|
| 72 |
tools=tools,
|
| 73 |
+
max_steps=int(os.getenv('MAX_STEPS', 10)),
|
| 74 |
verbosity_level=int(os.getenv('VERBOSITY_LEVEL', 1))
|
| 75 |
)
|
| 76 |
|
| 77 |
+
# Додавання model_initializer до агента для можливості зміни моделі
|
| 78 |
+
agent.model_initializer = model_initializer
|
| 79 |
+
|
| 80 |
+
# Запуск інтерфейсу
|
| 81 |
+
logger.info("Запуск Gradio інтерфейсу...")
|
| 82 |
ui = GradioUI(agent)
|
| 83 |
ui.launch(
|
| 84 |
debug=os.getenv('DEBUG_MODE', 'False').lower() == 'true',
|
|
|
|
| 88 |
)
|
| 89 |
|
| 90 |
except Exception as e:
|
| 91 |
+
logger.error(f"Помилка запуску програми: {e}")
|
| 92 |
raise
|
| 93 |
|
| 94 |
if __name__ == "__main__":
|
config.py
DELETED
|
@@ -1,112 +0,0 @@
|
|
| 1 |
-
import json
|
| 2 |
-
from pathlib import Path
|
| 3 |
-
|
| 4 |
-
class ModelConfig:
|
| 5 |
-
def __init__(self, config_path='models_config.json'): # Changed path
|
| 6 |
-
self.config_path = Path(config_path)
|
| 7 |
-
self.ensure_config_exists()
|
| 8 |
-
self.load_config()
|
| 9 |
-
|
| 10 |
-
def ensure_config_exists(self):
|
| 11 |
-
"""Ensure config file exists"""
|
| 12 |
-
if not self.config_path.exists():
|
| 13 |
-
default_config = {
|
| 14 |
-
"models": {
|
| 15 |
-
"deepseek-r1": {
|
| 16 |
-
"model_id": "deepseek-ai/DeepSeek-R1",
|
| 17 |
-
"description": "Найбільш рейтингова модель за кількістю лайків",
|
| 18 |
-
"likes": 8659,
|
| 19 |
-
"downloads": 3468420,
|
| 20 |
-
"parameters": {
|
| 21 |
-
"max_tokens": 2048,
|
| 22 |
-
"temperature": 0.7,
|
| 23 |
-
"top_p": 0.95,
|
| 24 |
-
"frequency_penalty": 0.0,
|
| 25 |
-
"presence_penalty": 0.0
|
| 26 |
-
},
|
| 27 |
-
"recommended_use": ["складні дослідницькі завдання", "аналітика", "генерація тексту"]
|
| 28 |
-
},
|
| 29 |
-
"llama-3-instruct": {
|
| 30 |
-
"model_id": "meta-llama/Llama-3.1-8B-Instruct",
|
| 31 |
-
"description": "Потужна інструктивна модель від Meta",
|
| 32 |
-
"likes": 3612,
|
| 33 |
-
"downloads": 6035070,
|
| 34 |
-
"parameters": {
|
| 35 |
-
"max_tokens": 4096,
|
| 36 |
-
"temperature": 0.8,
|
| 37 |
-
"top_p": 0.9,
|
| 38 |
-
"frequency_penalty": 0.0,
|
| 39 |
-
"presence_penalty": 0.0
|
| 40 |
-
},
|
| 41 |
-
"recommended_use": ["діалоги", "інструкції", "навчальні матеріали"]
|
| 42 |
-
},
|
| 43 |
-
"mistral-instruct": {
|
| 44 |
-
"model_id": "mistralai/Mistral-7B-Instruct-v0.2",
|
| 45 |
-
"description": "Оптимізована інструктивна модель",
|
| 46 |
-
"likes": 2650,
|
| 47 |
-
"downloads": 3415777,
|
| 48 |
-
"parameters": {
|
| 49 |
-
"max_tokens": 2048,
|
| 50 |
-
"temperature": 0.3,
|
| 51 |
-
"top_p": 0.9,
|
| 52 |
-
"frequency_penalty": 0.0,
|
| 53 |
-
"presence_penalty": 0.0
|
| 54 |
-
},
|
| 55 |
-
"recommended_use": ["наукові дослідження", "структуровані відповіді"]
|
| 56 |
-
},
|
| 57 |
-
"gpt2": {
|
| 58 |
-
"model_id": "openai-community/gpt2",
|
| 59 |
-
"description": "Найбільш завантажувана модель",
|
| 60 |
-
"likes": 2565,
|
| 61 |
-
"downloads": 18299067,
|
| 62 |
-
"parameters": {
|
| 63 |
-
"max_tokens": 1024,
|
| 64 |
-
"temperature": 0.9,
|
| 65 |
-
"top_p": 0.9,
|
| 66 |
-
"frequency_penalty": 0.0,
|
| 67 |
-
"presence_penalty": 0.0
|
| 68 |
-
},
|
| 69 |
-
"recommended_use": ["базова генерація тексту", "прості завдання"]
|
| 70 |
-
},
|
| 71 |
-
"llama-3-1b": {
|
| 72 |
-
"model_id": "meta-llama/Llama-3.2-1B",
|
| 73 |
-
"description": "Легка версія Llama 3",
|
| 74 |
-
"likes": 1559,
|
| 75 |
-
"downloads": 8244484,
|
| 76 |
-
"parameters": {
|
| 77 |
-
"max_tokens": 2048,
|
| 78 |
-
"temperature": 0.8,
|
| 79 |
-
"top_p": 0.9,
|
| 80 |
-
"frequency_penalty": 0.0,
|
| 81 |
-
"presence_penalty": 0.0
|
| 82 |
-
},
|
| 83 |
-
"recommended_use": ["швидкі відповіді", "базова генерація"]
|
| 84 |
-
}
|
| 85 |
-
},
|
| 86 |
-
"default_model": "mistral-instruct",
|
| 87 |
-
"model_selection_criteria": {
|
| 88 |
-
"research": ["deepseek-r1", "mistral-instruct", "llama-3-instruct"],
|
| 89 |
-
"general": ["gpt2", "llama-3-1b"],
|
| 90 |
-
"instruction": ["mistral-instruct", "llama-3-instruct"]
|
| 91 |
-
}
|
| 92 |
-
}
|
| 93 |
-
self.config_path.write_text(json.dumps(default_config, indent=4, ensure_ascii=False))
|
| 94 |
-
|
| 95 |
-
def load_config(self):
|
| 96 |
-
"""Load configuration from file"""
|
| 97 |
-
self.config = json.loads(self.config_path.read_text())
|
| 98 |
-
|
| 99 |
-
def get_model_config(self, model_key=None):
|
| 100 |
-
"""Get configuration for specific model or default model"""
|
| 101 |
-
if model_key is None:
|
| 102 |
-
model_key = self.config['default_model']
|
| 103 |
-
return self.config['models'].get(model_key)
|
| 104 |
-
|
| 105 |
-
def get_available_models(self):
|
| 106 |
-
"""Get list of available models"""
|
| 107 |
-
return [(key, model['description'])
|
| 108 |
-
for key, model in self.config['models'].items()]
|
| 109 |
-
|
| 110 |
-
def get_default_model(self):
|
| 111 |
-
"""Get default model key"""
|
| 112 |
-
return self.config['default_model']
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
model_init.py
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import Optional, Dict, Any
|
| 2 |
+
import os
|
| 3 |
+
import google.generativeai as genai
|
| 4 |
+
from huggingface_hub import HfApi
|
| 5 |
+
import logging
|
| 6 |
+
from smolagents import HfApiModel
|
| 7 |
+
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
class ModelWrapper:
|
| 11 |
+
"""Спрощена обгортка для моделей"""
|
| 12 |
+
|
| 13 |
+
def __init__(self, model, model_type):
|
| 14 |
+
self.model = model
|
| 15 |
+
self.model_type = model_type
|
| 16 |
+
|
| 17 |
+
def __call__(self, prompt, **kwargs):
|
| 18 |
+
try:
|
| 19 |
+
if self.model_type == 'gemini':
|
| 20 |
+
# Якщо prompt - це словник з роллю і контентом
|
| 21 |
+
if isinstance(prompt, dict) and 'content' in prompt:
|
| 22 |
+
text = prompt['content']
|
| 23 |
+
# Якщо prompt - це список повідомлень
|
| 24 |
+
elif isinstance(prompt, list):
|
| 25 |
+
# Беремо останнє повідомлення
|
| 26 |
+
last_message = prompt[-1]
|
| 27 |
+
text = last_message.get('content', '') if isinstance(last_message, dict) else str(last_message)
|
| 28 |
+
else:
|
| 29 |
+
text = str(prompt)
|
| 30 |
+
|
| 31 |
+
logger.info(f"Prompt для Gemini: {text[:100]}...")
|
| 32 |
+
response = self.model.generate_content(text)
|
| 33 |
+
return response.text
|
| 34 |
+
else:
|
| 35 |
+
kwargs.pop('stop_sequences', None) # Видаляємо stop_sequences для HF моделей
|
| 36 |
+
return self.model(prompt, **kwargs)
|
| 37 |
+
|
| 38 |
+
except Exception as e:
|
| 39 |
+
logger.error(f"Помилка ModelWrapper: {str(e)}")
|
| 40 |
+
logger.error(f"Тип prompt: {type(prompt)}")
|
| 41 |
+
logger.error(f"Prompt: {str(prompt)[:200]}")
|
| 42 |
+
raise
|
| 43 |
+
"""Обгортка для уніфікації інтерфейсу різних моделей"""
|
| 44 |
+
|
| 45 |
+
def __init__(self, model, model_type):
|
| 46 |
+
self.model = model
|
| 47 |
+
self.model_type = model_type
|
| 48 |
+
|
| 49 |
+
def _extract_text_from_input(self, input_data):
|
| 50 |
+
"""Витягує текст з різних форматів вхідних даних"""
|
| 51 |
+
try:
|
| 52 |
+
if isinstance(input_data, str):
|
| 53 |
+
return input_data
|
| 54 |
+
|
| 55 |
+
elif isinstance(input_data, list):
|
| 56 |
+
# Обробка списку повідомлень
|
| 57 |
+
messages = []
|
| 58 |
+
for msg in input_data:
|
| 59 |
+
if isinstance(msg, dict):
|
| 60 |
+
# Витягуємо контент з повідомлення
|
| 61 |
+
content = msg.get('content', '')
|
| 62 |
+
if isinstance(content, list):
|
| 63 |
+
# Якщо контент - список, обробляємо кожен елемент
|
| 64 |
+
for item in content:
|
| 65 |
+
if isinstance(item, dict) and 'text' in item:
|
| 66 |
+
messages.append(item['text'])
|
| 67 |
+
else:
|
| 68 |
+
messages.append(str(item))
|
| 69 |
+
else:
|
| 70 |
+
messages.append(str(content))
|
| 71 |
+
else:
|
| 72 |
+
messages.append(str(msg))
|
| 73 |
+
return ' '.join(messages)
|
| 74 |
+
|
| 75 |
+
elif isinstance(input_data, dict):
|
| 76 |
+
# Обробка одиночного повідомлення
|
| 77 |
+
content = input_data.get('content', '')
|
| 78 |
+
if isinstance(content, list):
|
| 79 |
+
return ' '.join(item.get('text', str(item)) for item in content if isinstance(item, dict))
|
| 80 |
+
return str(content)
|
| 81 |
+
|
| 82 |
+
return str(input_data)
|
| 83 |
+
|
| 84 |
+
except Exception as e:
|
| 85 |
+
logger.error(f"Помилка при обробці вхідних даних: {e}")
|
| 86 |
+
logger.error(f"Тип даних: {type(input_data)}")
|
| 87 |
+
logger.error(f"Дані: {str(input_data)[:200]}")
|
| 88 |
+
return str(input_data)
|
| 89 |
+
|
| 90 |
+
def __call__(self, prompt: str, **kwargs) -> str:
|
| 91 |
+
"""
|
| 92 |
+
Виклик моделі з підтримкою додаткових параметрів.
|
| 93 |
+
|
| 94 |
+
Args:
|
| 95 |
+
prompt: Текст запиту
|
| 96 |
+
**kwargs: Додаткові параметри (ігноруються для Gemini)
|
| 97 |
+
"""
|
| 98 |
+
try:
|
| 99 |
+
if self.model_type == 'gemini':
|
| 100 |
+
# Для Gemini ігноруємо додаткові параметри і просто передаємо текст
|
| 101 |
+
text = self._extract_text_from_input(prompt)
|
| 102 |
+
logger.info(f"Gemini отримав запит: {text[:200]}...") # Логуємо перші 200 символів
|
| 103 |
+
|
| 104 |
+
response = self.model.generate_content(text)
|
| 105 |
+
|
| 106 |
+
if response and hasattr(response, 'text'):
|
| 107 |
+
logger.info("Gemini успішно згенерував відповідь")
|
| 108 |
+
return response.text
|
| 109 |
+
else:
|
| 110 |
+
error_msg = "Gemini повернув порожню або неправильну відповідь"
|
| 111 |
+
logger.error(error_msg)
|
| 112 |
+
raise ValueError(error_msg)
|
| 113 |
+
else: # huggingface model
|
| 114 |
+
# Видаляємо stop_sequences, якщо він є
|
| 115 |
+
kwargs.pop('stop_sequences', None)
|
| 116 |
+
return self.model(prompt, **kwargs)
|
| 117 |
+
except Exception as e:
|
| 118 |
+
logger.error(f"Помилка при виклику моделі {self.model_type}: {e}")
|
| 119 |
+
logger.error(f"Тип вхідних даних: {type(prompt)}")
|
| 120 |
+
logger.error(f"Вміст вхідних даних: {str(prompt)[:200]}")
|
| 121 |
+
raise
|
| 122 |
+
"""Обгортка для уніфікації інтерфейсу різних моделей"""
|
| 123 |
+
|
| 124 |
+
def __init__(self, model, model_type):
|
| 125 |
+
self.model = model
|
| 126 |
+
self.model_type = model_type
|
| 127 |
+
|
| 128 |
+
def _extract_text_from_input(self, input_data):
|
| 129 |
+
"""Витягує текст з різних форматів вхідних даних"""
|
| 130 |
+
if isinstance(input_data, str):
|
| 131 |
+
return input_data
|
| 132 |
+
elif isinstance(input_data, dict):
|
| 133 |
+
return input_data.get('content', str(input_data))
|
| 134 |
+
elif isinstance(input_data, list):
|
| 135 |
+
return ' '.join(self._extract_text_from_input(item) for item in input_data)
|
| 136 |
+
return str(input_data)
|
| 137 |
+
|
| 138 |
+
def __call__(self, prompt: str, **kwargs) -> str:
|
| 139 |
+
"""
|
| 140 |
+
Виклик моделі з підтримкою додаткових параметрів.
|
| 141 |
+
|
| 142 |
+
Args:
|
| 143 |
+
prompt: Текст запиту
|
| 144 |
+
**kwargs: Додаткові параметри (ігноруються для Gemini)
|
| 145 |
+
"""
|
| 146 |
+
try:
|
| 147 |
+
if self.model_type == 'gemini':
|
| 148 |
+
# Для Gemini ігноруємо додаткові параметри і просто передаємо текст
|
| 149 |
+
text = self._extract_text_from_input(prompt)
|
| 150 |
+
logger.info(f"Gemini отримав запит: {text[:200]}...") # Логуємо перші 200 символів
|
| 151 |
+
|
| 152 |
+
response = self.model.generate_content(text)
|
| 153 |
+
|
| 154 |
+
if response and hasattr(response, 'text'):
|
| 155 |
+
logger.info("Gemini успішно згенерував відповідь")
|
| 156 |
+
return response.text
|
| 157 |
+
else:
|
| 158 |
+
error_msg = "Gemini повернув порожню або неправильну відповідь"
|
| 159 |
+
logger.error(error_msg)
|
| 160 |
+
raise ValueError(error_msg)
|
| 161 |
+
else: # huggingface model
|
| 162 |
+
# Видаляємо stop_sequences, якщо він є
|
| 163 |
+
kwargs.pop('stop_sequences', None)
|
| 164 |
+
return self.model(prompt, **kwargs)
|
| 165 |
+
except Exception as e:
|
| 166 |
+
logger.error(f"Помилка при виклику моделі {self.model_type}: {e}")
|
| 167 |
+
logger.error(f"Тип вхідних даних: {type(prompt)}")
|
| 168 |
+
logger.error(f"Вміст вхідних даних: {str(prompt)[:200]}") # Логуємо перші 200 символів
|
| 169 |
+
raise
|
| 170 |
+
|
| 171 |
+
class ModelInitializer:
|
| 172 |
+
def __init__(self, config_path: str = 'models_config.json'):
|
| 173 |
+
self.config = self._load_config(config_path)
|
| 174 |
+
self._setup_api_keys()
|
| 175 |
+
|
| 176 |
+
def _setup_api_keys(self):
|
| 177 |
+
"""Налаштування API ключів"""
|
| 178 |
+
self.hf_api_token = os.getenv('HF_API_TOKEN')
|
| 179 |
+
self.gemini_api_key = os.getenv('GEMINI_API_KEY')
|
| 180 |
+
if self.gemini_api_key:
|
| 181 |
+
genai.configure(api_key=self.gemini_api_key)
|
| 182 |
+
logger.info("API ключ Gemini успішно налаштовано")
|
| 183 |
+
else:
|
| 184 |
+
logger.warning("API ключ Gemini не знайдено")
|
| 185 |
+
|
| 186 |
+
def _load_config(self, config_path: str) -> Dict[str, Any]:
|
| 187 |
+
"""Завантаження конфігурації моделей"""
|
| 188 |
+
import json
|
| 189 |
+
try:
|
| 190 |
+
with open(config_path, 'r', encoding='utf-8') as f:
|
| 191 |
+
config = json.load(f)
|
| 192 |
+
logger.info(f"Конфігурацію успішно завантажено з {config_path}")
|
| 193 |
+
return config
|
| 194 |
+
except Exception as e:
|
| 195 |
+
logger.error(f"Помилка завантаження конфігурації: {e}")
|
| 196 |
+
raise
|
| 197 |
+
|
| 198 |
+
def initialize_model(self, model_key: Optional[str] = None) -> Any:
|
| 199 |
+
"""Ініціалізація вибраної моделі"""
|
| 200 |
+
try:
|
| 201 |
+
if model_key is None:
|
| 202 |
+
model_key = self.config['default_model']
|
| 203 |
+
logger.info(f"Використовуємо модель за замовчуванням: {model_key}")
|
| 204 |
+
|
| 205 |
+
model_config = self.config['models'].get(model_key)
|
| 206 |
+
if not model_config:
|
| 207 |
+
error_msg = f"Модель {model_key} не знайдена в конфігурації"
|
| 208 |
+
logger.error(error_msg)
|
| 209 |
+
raise ValueError(error_msg)
|
| 210 |
+
|
| 211 |
+
if model_key == 'gemini-flash':
|
| 212 |
+
model = self._initialize_gemini(model_config)
|
| 213 |
+
logger.info("Ініціалізовано Gemini модель")
|
| 214 |
+
return ModelWrapper(model, 'gemini')
|
| 215 |
+
else:
|
| 216 |
+
model = self._initialize_huggingface(model_config)
|
| 217 |
+
logger.info("Ініціалізовано HuggingFace модель")
|
| 218 |
+
return ModelWrapper(model, 'huggingface')
|
| 219 |
+
except Exception as e:
|
| 220 |
+
error_msg = f"Помилка ініціалізації моделі: {e}"
|
| 221 |
+
logger.error(error_msg)
|
| 222 |
+
raise ValueError(error_msg)
|
| 223 |
+
|
| 224 |
+
def _initialize_gemini(self, config: Dict[str, Any]) -> Any:
|
| 225 |
+
"""Ініціалізація Gemini моделі"""
|
| 226 |
+
if not self.gemini_api_key:
|
| 227 |
+
raise ValueError("GEMINI_API_KEY не знайдено в змінних середовища")
|
| 228 |
+
|
| 229 |
+
try:
|
| 230 |
+
# Налаштування безпеки (вимикаємо всі обмеження для наукових досліджень)
|
| 231 |
+
safety_settings = [
|
| 232 |
+
{
|
| 233 |
+
"category": "HARM_CATEGORY_HARASSMENT",
|
| 234 |
+
"threshold": "BLOCK_NONE"
|
| 235 |
+
},
|
| 236 |
+
{
|
| 237 |
+
"category": "HARM_CATEGORY_HATE_SPEECH",
|
| 238 |
+
"threshold": "BLOCK_NONE"
|
| 239 |
+
},
|
| 240 |
+
{
|
| 241 |
+
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
|
| 242 |
+
"threshold": "BLOCK_NONE"
|
| 243 |
+
},
|
| 244 |
+
{
|
| 245 |
+
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
|
| 246 |
+
"threshold": "BLOCK_NONE"
|
| 247 |
+
}
|
| 248 |
+
]
|
| 249 |
+
|
| 250 |
+
# Створюємо модель з мінімальними обмеженнями
|
| 251 |
+
model = genai.GenerativeModel(
|
| 252 |
+
model_name='gemini-pro',
|
| 253 |
+
safety_settings=safety_settings
|
| 254 |
+
)
|
| 255 |
+
|
| 256 |
+
# Тестуємо модель
|
| 257 |
+
try:
|
| 258 |
+
logger.info("Тестування з'єднання з Gemini...")
|
| 259 |
+
test_response = model.generate_content("Test connection")
|
| 260 |
+
if test_response and hasattr(test_response, 'text'):
|
| 261 |
+
logger.info("Gemini модель успішно ініціалізовано та протестовано")
|
| 262 |
+
return model
|
| 263 |
+
else:
|
| 264 |
+
raise ValueError("Тестова генерація не повернула текст")
|
| 265 |
+
except Exception as e:
|
| 266 |
+
raise ValueError(f"Помилка тестування Gemini: {str(e)}")
|
| 267 |
+
|
| 268 |
+
except Exception as e:
|
| 269 |
+
error_msg = f"Помилка ініціалізації Gemini: {str(e)}"
|
| 270 |
+
logger.error(error_msg)
|
| 271 |
+
raise ValueError(error_msg)
|
| 272 |
+
|
| 273 |
+
def _initialize_huggingface(self, config: Dict[str, Any]) -> Any:
|
| 274 |
+
"""Ініціалізація Hugging Face моделі"""
|
| 275 |
+
if not self.hf_api_token:
|
| 276 |
+
raise ValueError("HF_API_TOKEN не знайдено в змінних середовища")
|
| 277 |
+
|
| 278 |
+
try:
|
| 279 |
+
model = HfApiModel(
|
| 280 |
+
model_id=config['model_id'],
|
| 281 |
+
token=self.hf_api_token,
|
| 282 |
+
temperature=config['parameters']['temperature'],
|
| 283 |
+
max_tokens=config['parameters']['max_tokens']
|
| 284 |
+
)
|
| 285 |
+
|
| 286 |
+
return model
|
| 287 |
+
except Exception as e:
|
| 288 |
+
error_msg = f"Помилка ініціалізації HuggingFace: {str(e)}"
|
| 289 |
+
logger.error(error_msg)
|
| 290 |
+
raise ValueError(error_msg)
|
| 291 |
+
|
| 292 |
+
def get_available_models(self) -> list:
|
| 293 |
+
"""Отримання списку доступних моделей"""
|
| 294 |
+
return [(key, model['description'])
|
| 295 |
+
for key, model in self.config['models'].items()]
|
models_config.json
CHANGED
|
@@ -1,80 +1,30 @@
|
|
| 1 |
{
|
| 2 |
"models": {
|
| 3 |
-
"
|
| 4 |
-
"model_id": "
|
| 5 |
-
"description": "
|
| 6 |
-
"likes": 8659,
|
| 7 |
-
"downloads": 3468420,
|
| 8 |
"parameters": {
|
| 9 |
-
"max_tokens":
|
| 10 |
-
"temperature": 0
|
| 11 |
"top_p": 0.95,
|
| 12 |
-
"
|
| 13 |
-
|
| 14 |
-
},
|
| 15 |
-
"recommended_use": ["складні дослідницькі завдання", "аналітика", "генерація тексту"]
|
| 16 |
-
},
|
| 17 |
-
"llama-3-instruct": {
|
| 18 |
-
"model_id": "meta-llama/Llama-3.1-8B-Instruct",
|
| 19 |
-
"description": "Потужна інструктивна модель від Meta",
|
| 20 |
-
"likes": 3612,
|
| 21 |
-
"downloads": 6035070,
|
| 22 |
-
"parameters": {
|
| 23 |
-
"max_tokens": 4096,
|
| 24 |
-
"temperature": 0.8,
|
| 25 |
-
"top_p": 0.9,
|
| 26 |
-
"frequency_penalty": 0.0,
|
| 27 |
-
"presence_penalty": 0.0
|
| 28 |
-
},
|
| 29 |
-
"recommended_use": ["діалоги", "інструкції", "навчальні матеріали"]
|
| 30 |
},
|
| 31 |
"mistral-instruct": {
|
| 32 |
"model_id": "mistralai/Mistral-7B-Instruct-v0.2",
|
| 33 |
"description": "Оптимізована інструктивна модель",
|
| 34 |
-
"likes": 2650,
|
| 35 |
-
"downloads": 3415777,
|
| 36 |
"parameters": {
|
| 37 |
"max_tokens": 2048,
|
| 38 |
"temperature": 0.3,
|
| 39 |
"top_p": 0.9,
|
| 40 |
"frequency_penalty": 0.0,
|
| 41 |
"presence_penalty": 0.0
|
| 42 |
-
}
|
| 43 |
-
"recommended_use": ["наукові дослідження", "структуровані відповіді"]
|
| 44 |
-
},
|
| 45 |
-
"gpt2": {
|
| 46 |
-
"model_id": "openai-community/gpt2",
|
| 47 |
-
"description": "Найбільш завантажувана модель",
|
| 48 |
-
"likes": 2565,
|
| 49 |
-
"downloads": 18299067,
|
| 50 |
-
"parameters": {
|
| 51 |
-
"max_tokens": 1024,
|
| 52 |
-
"temperature": 0.9,
|
| 53 |
-
"top_p": 0.9,
|
| 54 |
-
"frequency_penalty": 0.0,
|
| 55 |
-
"presence_penalty": 0.0
|
| 56 |
-
},
|
| 57 |
-
"recommended_use": ["базова генерація тексту", "прості завдання"]
|
| 58 |
-
},
|
| 59 |
-
"llama-3-1b": {
|
| 60 |
-
"model_id": "meta-llama/Llama-3.2-1B",
|
| 61 |
-
"description": "Легка версія Llama 3",
|
| 62 |
-
"likes": 1559,
|
| 63 |
-
"downloads": 8244484,
|
| 64 |
-
"parameters": {
|
| 65 |
-
"max_tokens": 2048,
|
| 66 |
-
"temperature": 0.8,
|
| 67 |
-
"top_p": 0.9,
|
| 68 |
-
"frequency_penalty": 0.0,
|
| 69 |
-
"presence_penalty": 0.0
|
| 70 |
-
},
|
| 71 |
-
"recommended_use": ["швидкі відповіді", "базова генерація"]
|
| 72 |
}
|
| 73 |
},
|
| 74 |
-
"default_model": "
|
| 75 |
"model_selection_criteria": {
|
| 76 |
-
"research": ["
|
| 77 |
-
"
|
| 78 |
-
"instruction": ["mistral-instruct", "llama-3-instruct"]
|
| 79 |
}
|
| 80 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"models": {
|
| 3 |
+
"gemini-flash": {
|
| 4 |
+
"model_id": "gemini-pro",
|
| 5 |
+
"description": "Швидка та потужна модель від Google",
|
|
|
|
|
|
|
| 6 |
"parameters": {
|
| 7 |
+
"max_tokens": 8192,
|
| 8 |
+
"temperature": 1.0,
|
| 9 |
"top_p": 0.95,
|
| 10 |
+
"top_k": 40
|
| 11 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
},
|
| 13 |
"mistral-instruct": {
|
| 14 |
"model_id": "mistralai/Mistral-7B-Instruct-v0.2",
|
| 15 |
"description": "Оптимізована інструктивна модель",
|
|
|
|
|
|
|
| 16 |
"parameters": {
|
| 17 |
"max_tokens": 2048,
|
| 18 |
"temperature": 0.3,
|
| 19 |
"top_p": 0.9,
|
| 20 |
"frequency_penalty": 0.0,
|
| 21 |
"presence_penalty": 0.0
|
| 22 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
}
|
| 24 |
},
|
| 25 |
+
"default_model": "gemini-flash",
|
| 26 |
"model_selection_criteria": {
|
| 27 |
+
"research": ["gemini-flash", "mistral-instruct"],
|
| 28 |
+
"instruction": ["gemini-flash", "mistral-instruct"]
|
|
|
|
| 29 |
}
|
| 30 |
}
|
requirements.txt
CHANGED
|
@@ -6,4 +6,5 @@ duckduckgo-search>=3.0.0
|
|
| 6 |
markdownify>=0.11.0
|
| 7 |
requests>=2.28.0
|
| 8 |
pandas>=1.3.0
|
| 9 |
-
numpy>=1.21.0
|
|
|
|
|
|
| 6 |
markdownify>=0.11.0
|
| 7 |
requests>=2.28.0
|
| 8 |
pandas>=1.3.0
|
| 9 |
+
numpy>=1.21.0
|
| 10 |
+
google-generativeai>=0.3.0
|
test_gemini.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import google.generativeai as genai
|
| 3 |
+
from dotenv import load_dotenv
|
| 4 |
+
import logging
|
| 5 |
+
|
| 6 |
+
# Налаштування логування
|
| 7 |
+
logging.basicConfig(level=logging.INFO)
|
| 8 |
+
logger = logging.getLogger(__name__)
|
| 9 |
+
|
| 10 |
+
def test_gemini():
|
| 11 |
+
try:
|
| 12 |
+
# Завантаження API ключа
|
| 13 |
+
load_dotenv()
|
| 14 |
+
api_key = os.getenv('GEMINI_API_KEY')
|
| 15 |
+
if not api_key:
|
| 16 |
+
raise ValueError("GEMINI_API_KEY не знайдено")
|
| 17 |
+
|
| 18 |
+
# Конфігурація Gemini
|
| 19 |
+
genai.configure(api_key=api_key)
|
| 20 |
+
|
| 21 |
+
# Створення моделі
|
| 22 |
+
model = genai.GenerativeModel('gemini-pro')
|
| 23 |
+
|
| 24 |
+
# Тестовий запит
|
| 25 |
+
prompt = "What is 2+2? Answer in one word."
|
| 26 |
+
logger.info(f"Sending test prompt: {prompt}")
|
| 27 |
+
|
| 28 |
+
# Генерація відповіді
|
| 29 |
+
response = model.generate_content(prompt)
|
| 30 |
+
|
| 31 |
+
if response and hasattr(response, 'text'):
|
| 32 |
+
logger.info(f"Response received: {response.text}")
|
| 33 |
+
return True
|
| 34 |
+
else:
|
| 35 |
+
logger.error("No valid response received")
|
| 36 |
+
return False
|
| 37 |
+
|
| 38 |
+
except Exception as e:
|
| 39 |
+
logger.error(f"Error testing Gemini: {str(e)}")
|
| 40 |
+
return False
|
| 41 |
+
|
| 42 |
+
if __name__ == "__main__":
|
| 43 |
+
print("Testing Gemini connection...")
|
| 44 |
+
if test_gemini():
|
| 45 |
+
print("Test successful!")
|
| 46 |
+
else:
|
| 47 |
+
print("Test failed!")
|