DocUA commited on
Commit
d1f023e
·
1 Parent(s): ff6980a

Робочий варіант, але потребує суттєвого доопрацювання

Browse files
=0.11.0 ADDED
File without changes
Gradio_UI.py CHANGED
@@ -1,7 +1,8 @@
1
  import gradio as gr
2
  import logging
3
  from pathlib import Path
4
- import os
 
5
 
6
  logger = logging.getLogger(__name__)
7
 
@@ -10,9 +11,20 @@ class GradioUI:
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
  with gr.Row():
17
  chatbot = gr.Chatbot(
18
  label="Research Assistant",
@@ -20,7 +32,6 @@ class GradioUI:
20
  show_copy_button=True
21
  )
22
 
23
- # Hidden by default file upload section
24
  with gr.Row(visible=False) as file_upload_row:
25
  upload_file = gr.File(
26
  label="Upload File",
@@ -34,21 +45,33 @@ class GradioUI:
34
 
35
  with gr.Row():
36
  text_input = gr.Textbox(
37
- label="Enter your research query",
38
- placeholder="Enter your query here...",
39
  lines=2
40
  )
41
 
42
  with gr.Row():
43
- submit_btn = gr.Button("Submit", variant="primary")
44
- clear_btn = gr.Button("Clear")
45
- toggle_upload_btn = gr.Button("Toggle File Upload")
46
 
47
  # Store conversation state
48
  state = gr.State([])
49
  file_history = gr.State([])
 
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
- # Event handlers
52
  def toggle_upload(visible):
53
  return not visible
54
 
@@ -62,15 +85,14 @@ class GradioUI:
62
  if file:
63
  try:
64
  file_path = self.file_upload_folder / file.name
65
- # Save file
66
  with open(file_path, 'wb') as f:
67
  f.write(file.read())
68
  history.append(str(file_path))
69
- return gr.update(value=f"File uploaded: {file.name}"), history
70
  except Exception as e:
71
- logger.error(f"Upload error: {e}")
72
- return gr.update(value=f"Upload failed: {str(e)}"), history
73
- return gr.update(value="No file selected"), history
74
 
75
  upload_file.change(
76
  fn=process_upload,
@@ -80,7 +102,7 @@ class GradioUI:
80
 
81
  def user_message(message, chat_history, files):
82
  if files:
83
- message += f"\nAvailable files for analysis: {', '.join(files)}"
84
  chat_history.append((message, None))
85
  return "", chat_history
86
 
@@ -93,11 +115,10 @@ class GradioUI:
93
  chat_history[-1] = (chat_history[-1][0], response)
94
  return chat_history
95
  except Exception as e:
96
- logger.error(f"Error in bot response: {e}")
97
- chat_history[-1] = (chat_history[-1][0], f"Error: {str(e)}")
98
  return chat_history
99
 
100
- # Submit handling
101
  submit_btn.click(
102
  user_message,
103
  [text_input, state, file_history],
@@ -108,7 +129,6 @@ class GradioUI:
108
  [chatbot]
109
  )
110
 
111
- # Clear handling
112
  def clear_chat():
113
  return [], []
114
 
@@ -123,11 +143,4 @@ class GradioUI:
123
 
124
  def launch(self, **kwargs):
125
  interface = self.build_interface()
126
- interface.launch(**kwargs)
127
-
128
- if __name__ == "__main__":
129
- # Example usage
130
- from agent import ResearchAgent
131
- agent = ResearchAgent()
132
- ui = GradioUI(agent)
133
- ui.launch(share=True)
 
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__)
8
 
 
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
+ # Model selection dropdown
19
+ with gr.Row():
20
+ model_choices = self.model_config.get_available_models()
21
+ model_dropdown = gr.Dropdown(
22
+ choices=[f"{key} - {desc}" for key, desc in model_choices],
23
+ value=f"{self.model_config.get_default_model()} - {dict(model_choices)[self.model_config.get_default_model()]}",
24
+ label="Оберіть модель",
25
+ info="Оберіть модель для обробки запитів"
26
+ )
27
+
28
  with gr.Row():
29
  chatbot = gr.Chatbot(
30
  label="Research Assistant",
 
32
  show_copy_button=True
33
  )
34
 
 
35
  with gr.Row(visible=False) as file_upload_row:
36
  upload_file = gr.File(
37
  label="Upload File",
 
45
 
46
  with gr.Row():
47
  text_input = gr.Textbox(
48
+ label="Введіть ваш запит",
49
+ placeholder="Введіть ваш запит тут...",
50
  lines=2
51
  )
52
 
53
  with gr.Row():
54
+ submit_btn = gr.Button("Надіслати", variant="primary")
55
+ clear_btn = gr.Button("Очистити")
56
+ toggle_upload_btn = gr.Button("Завантажити файл")
57
 
58
  # Store conversation state
59
  state = gr.State([])
60
  file_history = gr.State([])
61
+ current_model = gr.State(self.model_config.get_default_model())
62
+
63
+ def change_model(choice):
64
+ model_key = choice.split(" - ")[0]
65
+ model_config = self.model_config.get_model_config(model_key)
66
+ self.agent.update_model(model_config)
67
+ return model_key
68
+
69
+ model_dropdown.change(
70
+ fn=change_model,
71
+ inputs=[model_dropdown],
72
+ outputs=[current_model]
73
+ )
74
 
 
75
  def toggle_upload(visible):
76
  return not visible
77
 
 
85
  if file:
86
  try:
87
  file_path = self.file_upload_folder / file.name
 
88
  with open(file_path, 'wb') as f:
89
  f.write(file.read())
90
  history.append(str(file_path))
91
+ return gr.update(value=f"Файл завантажено: {file.name}"), history
92
  except Exception as e:
93
+ logger.error(f"Помилка завантаження: {e}")
94
+ return gr.update(value=f"Помилка завантаження: {str(e)}"), history
95
+ return gr.update(value="Файл не вибрано"), history
96
 
97
  upload_file.change(
98
  fn=process_upload,
 
102
 
103
  def user_message(message, chat_history, files):
104
  if files:
105
+ message += f"\nДоступні файли для аналізу: {', '.join(files)}"
106
  chat_history.append((message, None))
107
  return "", chat_history
108
 
 
115
  chat_history[-1] = (chat_history[-1][0], response)
116
  return chat_history
117
  except Exception as e:
118
+ logger.error(f"Помилка відповіді: {e}")
119
+ chat_history[-1] = (chat_history[-1][0], f"Помилка: {str(e)}")
120
  return chat_history
121
 
 
122
  submit_btn.click(
123
  user_message,
124
  [text_input, state, file_history],
 
129
  [chatbot]
130
  )
131
 
 
132
  def clear_chat():
133
  return [], []
134
 
 
143
 
144
  def launch(self, **kwargs):
145
  interface = self.build_interface()
146
+ interface.launch(**kwargs)
 
 
 
 
 
 
 
agent.py CHANGED
@@ -2,6 +2,7 @@ from smolagents import CodeAgent
2
  import logging
3
  from typing import Optional, List, Dict, Any
4
  from datetime import datetime
 
5
 
6
  logger = logging.getLogger(__name__)
7
 
@@ -67,54 +68,19 @@ class ResearchAgent(CodeAgent):
67
  logger.error(f"Error formatting research report: {e}")
68
  return str(content) # Return raw content if formatting fails
69
 
70
- def prepare_query_context(self, query: str, available_files: Optional[List[str]] = None) -> str:
71
- """
72
- Prepare the context for the research query.
73
-
74
- Args:
75
- query (str): Original research query
76
- available_files (Optional[List[str]]): List of available files
77
-
78
- Returns:
79
- str: Prepared query context
80
- """
81
- context_parts = [
82
- query,
83
- "\nІнструкції для виконання:",
84
- "1. Використовуйте web_search для пошуку актуальної наукової інформації",
85
- "2. Аналізуйте знайдені джерела та підсумовуйте ключові висновки",
86
- "3. Формуйте структурований звіт з усіма необхідними розділами",
87
- "4. Обов'язково вказуйте посилання на використані джерела"
88
- ]
89
-
90
- if available_files:
91
- context_parts.append(f"\nДоступні файли для аналізу: {', '.join(available_files)}")
92
-
93
- return "\n".join(context_parts)
94
-
95
- def validate_search_results(self, results: str) -> bool:
96
- """
97
- Validate that search results are meaningful.
98
-
99
- Args:
100
- results (str): Search results to validate
101
-
102
- Returns:
103
- bool: True if results are valid, False otherwise
104
- """
105
- if not results or len(results.strip()) < 100:
106
- return False
107
-
108
- # Check for common error indicators
109
- error_indicators = [
110
- "no results found",
111
- "error",
112
- "failed",
113
- "unauthorized",
114
- "invalid"
115
- ]
116
-
117
- return not any(indicator in results.lower() for indicator in error_indicators)
118
 
119
  def process_query(self, query: str, available_files: Optional[List[str]] = None) -> str:
120
  """
@@ -130,12 +96,9 @@ class ResearchAgent(CodeAgent):
130
  try:
131
  logger.info(f"Processing research query: {query}")
132
 
133
- # Prepare context
134
- context = self.prepare_query_context(query, available_files)
135
-
136
  # Execute query
137
  result = self.run(
138
- task=context,
139
  stream=False, # We want complete results
140
  reset=True # Fresh start for each query
141
  )
@@ -158,39 +121,4 @@ class ResearchAgent(CodeAgent):
158
  except Exception as e:
159
  error_msg = f"Помилка при обробці запиту: {str(e)}"
160
  logger.error(error_msg)
161
- return error_msg
162
-
163
- def add_tool(self, tool) -> None:
164
- """
165
- Add a new tool to the agent's toolkit.
166
-
167
- Args:
168
- tool: Tool instance to add
169
- """
170
- try:
171
- self.available_tools[tool.name] = tool
172
- self.tools.append(tool)
173
- logger.info(f"Added new tool: {tool.name}")
174
- except Exception as e:
175
- logger.error(f"Error adding tool: {e}")
176
- raise
177
-
178
- def remove_tool(self, tool_name: str) -> None:
179
- """
180
- Remove a tool from the agent's toolkit.
181
-
182
- Args:
183
- tool_name (str): Name of the tool to remove
184
- """
185
- try:
186
- if tool_name in self.available_tools:
187
- tool = self.available_tools.pop(tool_name)
188
- self.tools.remove(tool)
189
- logger.info(f"Removed tool: {tool_name}")
190
- except Exception as e:
191
- logger.error(f"Error removing tool: {e}")
192
- raise
193
-
194
- def __str__(self) -> str:
195
- """String representation of the agent"""
196
- return f"ResearchAgent(tools={list(self.available_tools.keys())})"
 
2
  import logging
3
  from typing import Optional, List, Dict, Any
4
  from datetime import datetime
5
+ import os
6
 
7
  logger = logging.getLogger(__name__)
8
 
 
68
  logger.error(f"Error formatting research report: {e}")
69
  return str(content) # Return raw content if formatting fails
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
  """
 
96
  try:
97
  logger.info(f"Processing research query: {query}")
98
 
 
 
 
99
  # Execute query
100
  result = self.run(
101
+ task=query,
102
  stream=False, # We want complete results
103
  reset=True # Fresh start for each query
104
  )
 
121
  except Exception as e:
122
  error_msg = f"Помилка при обробці запиту: {str(e)}"
123
  logger.error(error_msg)
124
+ return error_msg
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
app.py CHANGED
@@ -7,6 +7,7 @@ from agent import ResearchAgent
7
  import os
8
  from dotenv import load_dotenv
9
  import logging
 
10
 
11
  # Configure logging
12
  logging.basicConfig(
@@ -43,12 +44,16 @@ def initialize_model():
43
  """Initialize the language model"""
44
  logger.info("Initializing language model...")
45
  try:
 
 
 
46
  model = HfApiModel(
47
- model_id=os.getenv('MODEL_ID', "mistralai/Mistral-7B-Instruct-v0.2"),
48
  token=os.getenv('HF_API_TOKEN'),
49
- temperature=float(os.getenv('TEMPERATURE', 0.3))
 
50
  )
51
- logger.info(f"Model initialized: {model.model_id}")
52
  return model
53
  except Exception as e:
54
  logger.error(f"Error initializing model: {e}")
@@ -63,11 +68,11 @@ def main():
63
  tools = initialize_tools()
64
  model = initialize_model()
65
 
66
- # Initialize research agent
67
  agent = ResearchAgent(
68
  model=model,
69
  tools=tools,
70
- max_steps=int(os.getenv('MAX_STEPS', 6)),
71
  verbosity_level=int(os.getenv('VERBOSITY_LEVEL', 1))
72
  )
73
 
 
7
  import os
8
  from dotenv import load_dotenv
9
  import logging
10
+ from config import ModelConfig
11
 
12
  # Configure logging
13
  logging.basicConfig(
 
44
  """Initialize the language model"""
45
  logger.info("Initializing language model...")
46
  try:
47
+ model_config = ModelConfig()
48
+ default_model = model_config.get_model_config()
49
+
50
  model = HfApiModel(
51
+ model_id=default_model['model_id'],
52
  token=os.getenv('HF_API_TOKEN'),
53
+ temperature=default_model['parameters'].get('temperature', 0.7),
54
+ max_tokens=default_model['parameters'].get('max_tokens', 2048)
55
  )
56
+ logger.info(f"Model initialized: {default_model['model_id']}")
57
  return model
58
  except Exception as e:
59
  logger.error(f"Error initializing model: {e}")
 
68
  tools = initialize_tools()
69
  model = initialize_model()
70
 
71
+ # Initialize research agent with increased max_steps
72
  agent = ResearchAgent(
73
  model=model,
74
  tools=tools,
75
+ max_steps=int(os.getenv('MAX_STEPS', 10)), # Increased from 6 to 10
76
  verbosity_level=int(os.getenv('VERBOSITY_LEVEL', 1))
77
  )
78
 
config.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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']
models_config.json ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "models": {
3
+ "deepseek-r1": {
4
+ "model_id": "deepseek-ai/DeepSeek-R1",
5
+ "description": "Найбільш рейтингова модель за кількістю лайків",
6
+ "likes": 8659,
7
+ "downloads": 3468420,
8
+ "parameters": {
9
+ "max_tokens": 2048,
10
+ "temperature": 0.7,
11
+ "top_p": 0.95,
12
+ "frequency_penalty": 0.0,
13
+ "presence_penalty": 0.0
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": "mistral-instruct",
75
+ "model_selection_criteria": {
76
+ "research": ["deepseek-r1", "mistral-instruct", "llama-3-instruct"],
77
+ "general": ["gpt2", "llama-3-1b"],
78
+ "instruction": ["mistral-instruct", "llama-3-instruct"]
79
+ }
80
+ }
requirements.txt CHANGED
@@ -1,5 +1,10 @@
1
- markdownify
2
- smolagents
3
- requests
4
- duckduckgo_search
5
- pandas
 
 
 
 
 
 
1
+ smolagents==0.1.0
2
+ gradio>=4.0.0
3
+ python-dotenv>=0.19.0
4
+ huggingface-hub>=0.19.0
5
+ duckduckgo-search>=3.0.0
6
+ markdownify>=0.11.0
7
+ requests>=2.28.0
8
+ logging>=0.5.1.2
9
+ pandas>=1.3.0
10
+ numpy>=1.21.0
tools/__init__.py CHANGED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .web_search import DuckDuckGoSearchTool
2
+ from .final_answer import FinalAnswerTool
3
+ from .healthcare_llm_visualizer import HealthcareLLMVisualizerTool
4
+ from .visit_webpage import VisitWebpageTool
5
+
6
+ __all__ = [
7
+ 'DuckDuckGoSearchTool',
8
+ 'FinalAnswerTool',
9
+ 'HealthcareLLMVisualizerTool',
10
+ 'VisitWebpageTool'
11
+ ]
tools/final_answer.py CHANGED
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, Union
2
  from smolagents.tools import Tool
3
  import logging
4
  from datetime import datetime
@@ -14,14 +14,11 @@ class FinalAnswerTool(Tool):
14
  'description': 'The final research report content'
15
  }
16
  }
17
- output_type = "any"
18
 
19
- def _format_report(self, content: Union[str, Dict]) -> str:
20
- """Format content as a proper research report"""
21
- if isinstance(content, str):
22
- return content
23
-
24
- required_sections = [
25
  "Executive Summary",
26
  "Introduction",
27
  "Methodology",
@@ -31,28 +28,55 @@ class FinalAnswerTool(Tool):
31
  "References"
32
  ]
33
 
34
- # Ensure all required sections are present
35
- for section in required_sections:
36
- if section not in content:
37
- content[section] = f"{section} section was not provided"
38
-
39
- # Create formatted report
40
- report = [
41
- f"# Research Report",
42
- f"*Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n",
43
- ]
44
 
45
- # Add each section
46
- for section in required_sections:
47
- report.extend([
 
 
 
 
 
 
48
  f"## {section}",
49
- content[section],
50
  "" # Empty line for better readability
51
  ])
 
 
 
 
 
 
 
 
 
 
 
52
 
53
- return "\n".join(report)
 
 
 
 
 
 
54
 
55
- def forward(self, answer: Any) -> Any:
 
 
 
 
 
 
 
56
  """Process and return the final answer"""
57
  logger.info("Formatting final research report")
58
  try:
@@ -62,8 +86,4 @@ class FinalAnswerTool(Tool):
62
  except Exception as e:
63
  error_msg = f"Error formatting research report: {str(e)}"
64
  logger.error(error_msg)
65
- return error_msg
66
-
67
- def __init__(self, *args, **kwargs):
68
- self.is_initialized = False
69
- super().__init__(*args, **kwargs)
 
1
+ from typing import Any, Dict, Union, List
2
  from smolagents.tools import Tool
3
  import logging
4
  from datetime import datetime
 
14
  'description': 'The final research report content'
15
  }
16
  }
17
+ output_type = "string" # Changed from 'str' to 'string'
18
 
19
+ def __init__(self):
20
+ super().__init__()
21
+ self.sections = [
 
 
 
22
  "Executive Summary",
23
  "Introduction",
24
  "Methodology",
 
28
  "References"
29
  ]
30
 
31
+ def _format_list_content(self, content: List) -> str:
32
+ """Format list content into readable text"""
33
+ try:
34
+ return "\n".join([f"- {item}" for item in content])
35
+ except Exception as e:
36
+ logger.error(f"Error formatting list content: {e}")
37
+ return str(content)
 
 
 
38
 
39
+ def _format_dict_content(self, content: Dict) -> str:
40
+ """Format dictionary content into sections"""
41
+ formatted_sections = []
42
+
43
+ for section in self.sections:
44
+ section_content = content.get(section, f"{section} section was not provided")
45
+ if isinstance(section_content, list):
46
+ section_content = self._format_list_content(section_content)
47
+ formatted_sections.extend([
48
  f"## {section}",
49
+ str(section_content),
50
  "" # Empty line for better readability
51
  ])
52
+
53
+ return "\n".join(formatted_sections)
54
+
55
+ def _format_report(self, content: Any) -> str:
56
+ """Format content as a proper research report"""
57
+ try:
58
+ # Create report header
59
+ report_parts = [
60
+ "# Науковий звіт",
61
+ f"*Згенеровано: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}*\n"
62
+ ]
63
 
64
+ # Handle different content types
65
+ if isinstance(content, dict):
66
+ report_parts.append(self._format_dict_content(content))
67
+ elif isinstance(content, list):
68
+ report_parts.append(self._format_list_content(content))
69
+ else:
70
+ report_parts.append(str(content))
71
 
72
+ return "\n".join(report_parts)
73
+
74
+ except Exception as e:
75
+ error_msg = f"Error formatting report: {str(e)}"
76
+ logger.error(error_msg)
77
+ return f"# Error in Report Generation\n\n{error_msg}\n\nRaw content:\n{str(content)}"
78
+
79
+ def forward(self, answer: Any) -> str:
80
  """Process and return the final answer"""
81
  logger.info("Formatting final research report")
82
  try:
 
86
  except Exception as e:
87
  error_msg = f"Error formatting research report: {str(e)}"
88
  logger.error(error_msg)
89
+ return error_msg
 
 
 
 
tools/healthcare_llm_visualizer.py CHANGED
@@ -6,13 +6,13 @@ class HealthcareLLMVisualizerTool(Tool):
6
  description = "Creates interactive visualizations for analyzing LLM applications in Healthcare"
7
  inputs = {
8
  'data': {
9
- 'type': 'object',
10
  'description': 'Data for visualization in format: {"items": [{"category": "name", "value": number}]}'
11
  }
12
  }
13
- output_type = "string"
14
 
15
- def prepare_data(self, raw_data: Dict) -> List[Dict[str, Any]]:
16
  """Convert raw data into format suitable for visualization"""
17
  categories = {}
18
 
 
6
  description = "Creates interactive visualizations for analyzing LLM applications in Healthcare"
7
  inputs = {
8
  'data': {
9
+ 'type': 'object', # Using 'object' for dictionary input
10
  'description': 'Data for visualization in format: {"items": [{"category": "name", "value": number}]}'
11
  }
12
  }
13
+ output_type = "string" # Changed from 'str' to 'string'
14
 
15
+ def prepare_data(self, raw_data: Dict) -> Dict[str, List[Dict[str, Any]]]:
16
  """Convert raw data into format suitable for visualization"""
17
  categories = {}
18
 
tools/visit_webpage.py CHANGED
@@ -1,13 +1,19 @@
1
  from typing import Any, Optional
2
  from smolagents.tools import Tool
3
  import requests
 
4
  import markdownify
5
  import smolagents
6
 
7
  class VisitWebpageTool(Tool):
8
  name = "visit_webpage"
9
  description = "Visits a webpage at the given url and reads its content as a markdown string. Use this to browse webpages."
10
- inputs = {'url': {'type': 'string', 'description': 'The url of the webpage to visit.'}}
 
 
 
 
 
11
  output_type = "string"
12
 
13
  def forward(self, url: str) -> str:
@@ -15,7 +21,6 @@ class VisitWebpageTool(Tool):
15
  import requests
16
  from markdownify import markdownify
17
  from requests.exceptions import RequestException
18
-
19
  from smolagents.utils import truncate_content
20
  except ImportError as e:
21
  raise ImportError(
@@ -42,4 +47,4 @@ class VisitWebpageTool(Tool):
42
  return f"An unexpected error occurred: {str(e)}"
43
 
44
  def __init__(self, *args, **kwargs):
45
- self.is_initialized = False
 
1
  from typing import Any, Optional
2
  from smolagents.tools import Tool
3
  import requests
4
+ import re
5
  import markdownify
6
  import smolagents
7
 
8
  class VisitWebpageTool(Tool):
9
  name = "visit_webpage"
10
  description = "Visits a webpage at the given url and reads its content as a markdown string. Use this to browse webpages."
11
+ inputs = {
12
+ 'url': {
13
+ 'type': 'string',
14
+ 'description': 'The url of the webpage to visit.'
15
+ }
16
+ }
17
  output_type = "string"
18
 
19
  def forward(self, url: str) -> str:
 
21
  import requests
22
  from markdownify import markdownify
23
  from requests.exceptions import RequestException
 
24
  from smolagents.utils import truncate_content
25
  except ImportError as e:
26
  raise ImportError(
 
47
  return f"An unexpected error occurred: {str(e)}"
48
 
49
  def __init__(self, *args, **kwargs):
50
+ self.is_initialized = False
tools/web_search.py CHANGED
@@ -1,20 +1,14 @@
1
- from typing import Any, Optional, List, Dict
2
  from smolagents.tools import Tool
3
  import duckduckgo_search
4
- import logging
5
- import re
6
- from datetime import datetime
7
- from collections import defaultdict
8
-
9
- logger = logging.getLogger(__name__)
10
 
11
  class DuckDuckGoSearchTool(Tool):
12
  name = "web_search"
13
- description = "Performs comprehensive web searches with focus on academic and scientific sources"
14
  inputs = {
15
  'query': {
16
  'type': 'string',
17
- 'description': 'The search query to perform'
18
  }
19
  }
20
  output_type = "string"
@@ -26,95 +20,24 @@ class DuckDuckGoSearchTool(Tool):
26
  from duckduckgo_search import DDGS
27
  except ImportError as e:
28
  raise ImportError(
29
- "Required package `duckduckgo_search` not found. Install with: pip install duckduckgo-search"
30
  ) from e
31
  self.ddgs = DDGS(**kwargs)
32
 
33
- def _extract_date(self, text: str) -> Optional[str]:
34
- """Extract publication date from text if available"""
35
- try:
36
- # Common date patterns
37
- patterns = [
38
- r'\b(20\d{2})\b', # Year pattern
39
- r'\b(19\d{2})\b', # Year pattern for older papers
40
- r'\b(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[a-z]* \d{1,2},? 20\d{2}\b'
41
- ]
42
-
43
- for pattern in patterns:
44
- match = re.search(pattern, text)
45
- if match:
46
- return match.group(0)
47
- return None
48
- except Exception as e:
49
- logger.error(f"Error extracting date: {e}")
50
- return None
51
-
52
- def _parse_search_result(self, result: Dict[str, str]) -> Dict[str, Any]:
53
- """Parse a single search result safely"""
54
- try:
55
- title = result.get('title', '').strip()
56
- url = result.get('link', '')
57
- description = result.get('body', '').strip()
58
- date = self._extract_date(description) or 'Date not found'
59
-
60
- return {
61
- 'title': title,
62
- 'url': url,
63
- 'description': description,
64
- 'date': date
65
- }
66
- except Exception as e:
67
- logger.error(f"Error parsing search result: {e}")
68
- return {}
69
-
70
- def _format_results(self, results: List[Dict[str, str]]) -> str:
71
- """Format search results with academic focus"""
72
- if not results:
73
- return "No results found. Consider refining your search terms."
74
-
75
- formatted_output = ["## Search Results\n"]
76
 
 
77
  for result in results:
78
- parsed = self._parse_search_result(result)
79
- if parsed and parsed.get('title'):
80
- formatted_output.extend([
81
- f"### {parsed['title']}",
82
- f"**Source:** [{parsed['url']}]({parsed['url']})",
83
- f"**Date:** {parsed['date']}",
84
- f"**Summary:** {parsed['description']}\n"
85
- ])
86
-
87
- return "\n".join(formatted_output)
88
-
89
- def forward(self, query: str) -> str:
90
- """Execute search and return formatted results"""
91
- logger.info(f"Performing web search for query: {query}")
92
- try:
93
- # Add academic focus to search if not present
94
- academic_terms = ['research', 'study', 'journal', 'paper']
95
- if not any(term in query.lower() for term in academic_terms):
96
- query = f"{query} research study"
97
-
98
- # Execute search with error handling
99
- try:
100
- results = list(self.ddgs.text(query, max_results=self.max_results))
101
- if not results:
102
- return "No results found. Try modifying your search terms."
103
- except Exception as e:
104
- logger.error(f"Search execution error: {e}")
105
- return f"Error performing search: {str(e)}"
106
-
107
- # Format results
108
- formatted_output = self._format_results(results)
109
- logger.info("Search completed successfully")
110
- return formatted_output
111
-
112
- except Exception as e:
113
- error_msg = f"Error during web search: {str(e)}"
114
- logger.error(error_msg)
115
- return error_msg
116
-
117
- def __del__(self):
118
- """Cleanup when object is destroyed"""
119
- if hasattr(self, 'ddgs'):
120
- self.ddgs = None
 
1
+ from typing import Any, Optional
2
  from smolagents.tools import Tool
3
  import duckduckgo_search
 
 
 
 
 
 
4
 
5
  class DuckDuckGoSearchTool(Tool):
6
  name = "web_search"
7
+ description = "Performs a duckduckgo web search based on your query (think a Google search) then returns the top search results."
8
  inputs = {
9
  'query': {
10
  'type': 'string',
11
+ 'description': 'The search query to perform.'
12
  }
13
  }
14
  output_type = "string"
 
20
  from duckduckgo_search import DDGS
21
  except ImportError as e:
22
  raise ImportError(
23
+ "You must install package `duckduckgo_search` to run this tool: for instance run `pip install duckduckgo-search`."
24
  ) from e
25
  self.ddgs = DDGS(**kwargs)
26
 
27
+ def forward(self, query: str) -> str:
28
+ results = list(self.ddgs.text(query, max_results=self.max_results))
29
+ if len(results) == 0:
30
+ raise Exception("No results found! Try a less restrictive/shorter query.")
31
+
32
+ # Print result structure for debugging
33
+ if results:
34
+ print("Result keys:", results[0].keys())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
 
36
+ postprocessed_results = []
37
  for result in results:
38
+ title = result.get('title', '')
39
+ url = result.get('link', '') or result.get('href', '')
40
+ body = result.get('body', '') or result.get('snippet', '')
41
+ postprocessed_results.append(f"[{title}]({url})\n{body}")
42
+
43
+ return "## Search Results\n\n" + "\n\n".join(postprocessed_results)