lupantech commited on
Commit
71d0d94
·
1 Parent(s): 1a829ca

added user feedback

Browse files
Files changed (4) hide show
  1. app.py +57 -19
  2. app_v3_0216.py +0 -380
  3. octotools/models/initializer.py +2 -1
  4. utils.py +26 -0
app.py CHANGED
@@ -22,6 +22,8 @@ from octotools.models.memory import Memory
22
  from octotools.models.executor import Executor
23
  from octotools.models.utils import make_json_serializable
24
 
 
 
25
 
26
  class Solver:
27
  def __init__(
@@ -36,7 +38,6 @@ class Solver:
36
  verbose: bool = True,
37
  max_steps: int = 10,
38
  max_time: int = 60,
39
- output_json_dir: str = "results",
40
  root_cache_dir: str = "cache"
41
  ):
42
  self.planner = planner
@@ -48,7 +49,6 @@ class Solver:
48
  self.verbose = verbose
49
  self.max_steps = max_steps
50
  self.max_time = max_time
51
- self.output_json_dir = output_json_dir
52
  self.root_cache_dir = root_cache_dir
53
 
54
  self.output_types = output_types.lower().split(',')
@@ -72,14 +72,16 @@ class Solver:
72
  # img_bytes = img_bytes_io.getvalue() # Get bytes
73
 
74
  # Use image paths instead of bytes,
75
- os.makedirs(os.path.join(self.root_cache_dir, 'images'), exist_ok=True)
76
- img_path = os.path.join(self.root_cache_dir, 'images', str(uuid.uuid4()) + '.jpg')
 
 
77
  user_image.save(img_path)
78
  else:
79
  img_path = None
80
 
81
- # Set query cache
82
- _cache_dir = os.path.join(self.root_cache_dir)
83
  self.executor.set_query_cache_dir(_cache_dir)
84
 
85
  # Step 1: Display the received inputs
@@ -178,10 +180,12 @@ class Solver:
178
  yield messages
179
 
180
  # Step 8: Completion Message
181
- messages.append(ChatMessage(role="assistant", content="✅ Problem-solving process complete."))
182
  yield messages
183
 
184
 
 
 
185
  def parse_arguments():
186
  parser = argparse.ArgumentParser(description="Run the OctoTools demo with specified parameters.")
187
  parser.add_argument("--llm_engine_name", default="gpt-4o", help="LLM engine name.")
@@ -196,8 +200,10 @@ def parse_arguments():
196
  )
197
  parser.add_argument("--enabled_tools", default="Generalist_Solution_Generator_Tool", help="List of enabled tools.")
198
  parser.add_argument("--root_cache_dir", default="demo_solver_cache", help="Path to solver cache directory.")
199
- parser.add_argument("--output_json_dir", default="demo_results", help="Path to output JSON directory.")
200
  parser.add_argument("--verbose", type=bool, default=True, help="Enable verbose output.")
 
 
 
201
  return parser.parse_args()
202
 
203
 
@@ -207,6 +213,15 @@ def solve_problem_gradio(user_query, user_image, max_steps=10, max_time=60, api_
207
  Streams responses from `solver.stream_solve_user_problem` for real-time UI updates.
208
  """
209
 
 
 
 
 
 
 
 
 
 
210
  if api_key is None:
211
  return [["assistant", "⚠️ Error: OpenAI API Key is required."]]
212
 
@@ -237,7 +252,7 @@ def solve_problem_gradio(user_query, user_image, max_steps=10, max_time=60, api_
237
  # Instantiate Executor
238
  executor = Executor(
239
  llm_engine_name=llm_model_engine,
240
- root_cache_dir=args.root_cache_dir,
241
  enable_signal=False,
242
  api_key=api_key
243
  )
@@ -253,8 +268,7 @@ def solve_problem_gradio(user_query, user_image, max_steps=10, max_time=60, api_
253
  verbose=args.verbose,
254
  max_steps=max_steps,
255
  max_time=max_time,
256
- output_json_dir=args.output_json_dir,
257
- root_cache_dir=args.root_cache_dir
258
  )
259
 
260
  if solver is None:
@@ -352,23 +366,47 @@ def main(args):
352
  # Right column for the output
353
  with gr.Column(scale=3):
354
  chatbot_output = gr.Chatbot(type="messages", label="Step-wise Problem-Solving Output (Deep Thinking)", height=500)
355
- # chatbot_output.like(lambda x: print(f"User liked: {x}"))
356
 
357
  # TODO: Add actions to the buttons
358
  with gr.Row(elem_id="buttons") as button_row:
359
- upvote_btn = gr.Button(value="👍 Upvote", interactive=True, variant="primary")
360
- downvote_btn = gr.Button(value="👎 Downvote", interactive=True, variant="primary")
361
- stop_btn = gr.Button(value="⛔️ Stop", interactive=True)
362
- clear_btn = gr.Button(value="🗑️ Clear history", interactive=True)
363
 
 
364
  with gr.Row():
365
  comment_textbox = gr.Textbox(value="",
366
  placeholder="Feel free to add any comments here. Thanks for using OctoTools!",
367
- label="💬 Comment", interactive=True)
368
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
  # Bottom row for examples
370
  with gr.Row():
371
  with gr.Column(scale=5):
 
 
 
 
372
  gr.Examples(
373
  examples=[
374
  [ None, "Who is the president of the United States?", ["Google_Search_Tool"]],
@@ -383,7 +421,7 @@ def main(args):
383
 
384
  ],
385
  inputs=[user_image, user_query, enabled_tools],
386
- label="Try these examples with suggested tools."
387
  )
388
 
389
  # Link button click to function
@@ -410,8 +448,8 @@ if __name__ == "__main__":
410
 
411
  "Image_Captioner_Tool",
412
  "Object_Detector_Tool",
413
- "Text_Detector_Tool",
414
  "Relevant_Patch_Zoomer_Tool",
 
415
 
416
  "Python_Code_Generator_Tool",
417
 
 
22
  from octotools.models.executor import Executor
23
  from octotools.models.utils import make_json_serializable
24
 
25
+ from utils import save_feedback
26
+
27
 
28
  class Solver:
29
  def __init__(
 
38
  verbose: bool = True,
39
  max_steps: int = 10,
40
  max_time: int = 60,
 
41
  root_cache_dir: str = "cache"
42
  ):
43
  self.planner = planner
 
49
  self.verbose = verbose
50
  self.max_steps = max_steps
51
  self.max_time = max_time
 
52
  self.root_cache_dir = root_cache_dir
53
 
54
  self.output_types = output_types.lower().split(',')
 
72
  # img_bytes = img_bytes_io.getvalue() # Get bytes
73
 
74
  # Use image paths instead of bytes,
75
+ # os.makedirs(os.path.join(self.root_cache_dir, 'images'), exist_ok=True)
76
+ # img_path = os.path.join(self.root_cache_dir, 'images', str(uuid.uuid4()) + '.jpg')
77
+
78
+ img_path = os.path.join(self.root_cache_dir, 'query_image.jpg')
79
  user_image.save(img_path)
80
  else:
81
  img_path = None
82
 
83
+ # Set tool cache directory
84
+ _cache_dir = os.path.join(self.root_cache_dir, "tool_cache") # NOTE: This is the directory for tool cache
85
  self.executor.set_query_cache_dir(_cache_dir)
86
 
87
  # Step 1: Display the received inputs
 
180
  yield messages
181
 
182
  # Step 8: Completion Message
183
+ messages.append(ChatMessage(role="assistant", content="✅ Problem-solving process completed."))
184
  yield messages
185
 
186
 
187
+
188
+
189
  def parse_arguments():
190
  parser = argparse.ArgumentParser(description="Run the OctoTools demo with specified parameters.")
191
  parser.add_argument("--llm_engine_name", default="gpt-4o", help="LLM engine name.")
 
200
  )
201
  parser.add_argument("--enabled_tools", default="Generalist_Solution_Generator_Tool", help="List of enabled tools.")
202
  parser.add_argument("--root_cache_dir", default="demo_solver_cache", help="Path to solver cache directory.")
 
203
  parser.add_argument("--verbose", type=bool, default=True, help="Enable verbose output.")
204
+
205
+ # NOTE: Add new arguments
206
+ parser.add_argument("--openai_api_source", default="we_provided", choices=["we_provided", "user_provided"], help="Source of OpenAI API key.")
207
  return parser.parse_args()
208
 
209
 
 
213
  Streams responses from `solver.stream_solve_user_problem` for real-time UI updates.
214
  """
215
 
216
+ # Generate shorter ID (Date and first 8 characters of UUID)
217
+ query_id = time.strftime("%Y%m%d_%H%M%S") + "_" + str(uuid.uuid4())[:8] # e.g, 20250217_612f2474
218
+ print(f"Query ID: {query_id}")
219
+
220
+ # Create a directory for the query ID
221
+ query_dir = os.path.join(args.root_cache_dir, query_id)
222
+ os.makedirs(query_dir, exist_ok=True)
223
+ args.root_cache_dir = query_dir
224
+
225
  if api_key is None:
226
  return [["assistant", "⚠️ Error: OpenAI API Key is required."]]
227
 
 
252
  # Instantiate Executor
253
  executor = Executor(
254
  llm_engine_name=llm_model_engine,
255
+ root_cache_dir=args.root_cache_dir, # NOTE
256
  enable_signal=False,
257
  api_key=api_key
258
  )
 
268
  verbose=args.verbose,
269
  max_steps=max_steps,
270
  max_time=max_time,
271
+ root_cache_dir=args.root_cache_dir # NOTE
 
272
  )
273
 
274
  if solver is None:
 
366
  # Right column for the output
367
  with gr.Column(scale=3):
368
  chatbot_output = gr.Chatbot(type="messages", label="Step-wise Problem-Solving Output (Deep Thinking)", height=500)
 
369
 
370
  # TODO: Add actions to the buttons
371
  with gr.Row(elem_id="buttons") as button_row:
372
+ upvote_btn = gr.Button(value="👍 Upvote", interactive=True, variant="primary") # TODO
373
+ downvote_btn = gr.Button(value="👎 Downvote", interactive=True, variant="primary") # TODO
374
+ stop_btn = gr.Button(value="⛔️ Stop", interactive=True) # TODO
375
+ clear_btn = gr.Button(value="🗑️ Clear history", interactive=True) # TODO
376
 
377
+ # TODO: Add comment textbox
378
  with gr.Row():
379
  comment_textbox = gr.Textbox(value="",
380
  placeholder="Feel free to add any comments here. Thanks for using OctoTools!",
381
+ label="💬 Comment (Type and press Enter to submit.)", interactive=True) # TODO
382
 
383
+ # Update the button click handlers
384
+ upvote_btn.click(
385
+ fn=lambda: save_feedback(args.root_cache_dir, "upvote"),
386
+ inputs=[],
387
+ outputs=[]
388
+ )
389
+
390
+ downvote_btn.click(
391
+ fn=lambda: save_feedback(args.root_cache_dir, "downvote"),
392
+ inputs=[],
393
+ outputs=[]
394
+ )
395
+
396
+ # Add handler for comment submission
397
+ comment_textbox.submit(
398
+ fn=lambda comment: save_feedback(args.root_cache_dir, comment),
399
+ inputs=[comment_textbox],
400
+ outputs=[]
401
+ )
402
+
403
  # Bottom row for examples
404
  with gr.Row():
405
  with gr.Column(scale=5):
406
+ gr.Markdown("")
407
+ gr.Markdown("""
408
+ ## 💡 Try these examples with suggested tools.
409
+ """)
410
  gr.Examples(
411
  examples=[
412
  [ None, "Who is the president of the United States?", ["Google_Search_Tool"]],
 
421
 
422
  ],
423
  inputs=[user_image, user_query, enabled_tools],
424
+ # label="Try these examples with suggested tools."
425
  )
426
 
427
  # Link button click to function
 
448
 
449
  "Image_Captioner_Tool",
450
  "Object_Detector_Tool",
 
451
  "Relevant_Patch_Zoomer_Tool",
452
+ "Text_Detector_Tool",
453
 
454
  "Python_Code_Generator_Tool",
455
 
app_v3_0216.py DELETED
@@ -1,380 +0,0 @@
1
- import os
2
- import sys
3
- import json
4
- import argparse
5
- import time
6
- import io
7
- import uuid
8
- from PIL import Image
9
- from typing import List, Dict, Any, Iterator
10
-
11
- import gradio as gr
12
- from gradio import ChatMessage
13
-
14
- # Add the project root to the Python path
15
- current_dir = os.path.dirname(os.path.abspath(__file__))
16
- project_root = os.path.dirname(os.path.dirname(os.path.dirname(current_dir)))
17
- sys.path.insert(0, project_root)
18
-
19
- from octotools.models.initializer import Initializer
20
- from octotools.models.planner import Planner
21
- from octotools.models.memory import Memory
22
- from octotools.models.executor import Executor
23
- from octotools.models.utils import make_json_serializable
24
-
25
-
26
- class Solver:
27
- def __init__(
28
- self,
29
- planner,
30
- memory,
31
- executor,
32
- task: str,
33
- task_description: str,
34
- output_types: str = "base,final,direct",
35
- index: int = 0,
36
- verbose: bool = True,
37
- max_steps: int = 10,
38
- max_time: int = 60,
39
- output_json_dir: str = "results",
40
- root_cache_dir: str = "cache"
41
- ):
42
- self.planner = planner
43
- self.memory = memory
44
- self.executor = executor
45
- self.task = task
46
- self.task_description = task_description
47
- self.index = index
48
- self.verbose = verbose
49
- self.max_steps = max_steps
50
- self.max_time = max_time
51
- self.output_json_dir = output_json_dir
52
- self.root_cache_dir = root_cache_dir
53
-
54
- self.output_types = output_types.lower().split(',')
55
- assert all(output_type in ["base", "final", "direct"] for output_type in self.output_types), "Invalid output type. Supported types are 'base', 'final', 'direct'."
56
-
57
-
58
- def stream_solve_user_problem(self, user_query: str, user_image: Image.Image, api_key: str, messages: List[ChatMessage]) -> Iterator[List[ChatMessage]]:
59
- """
60
- Streams intermediate thoughts and final responses for the problem-solving process based on user input.
61
-
62
- Args:
63
- user_query (str): The text query input from the user.
64
- user_image (Image.Image): The uploaded image from the user (PIL Image object).
65
- messages (list): A list of ChatMessage objects to store the streamed responses.
66
- """
67
-
68
- if user_image:
69
- # # Convert PIL Image to bytes (for processing)
70
- # img_bytes_io = io.BytesIO()
71
- # user_image.save(img_bytes_io, format="PNG") # Convert image to PNG bytes
72
- # img_bytes = img_bytes_io.getvalue() # Get bytes
73
-
74
- # Use image paths instead of bytes,
75
- os.makedirs(os.path.join(self.root_cache_dir, 'images'), exist_ok=True)
76
- img_path = os.path.join(self.root_cache_dir, 'images', str(uuid.uuid4()) + '.jpg')
77
- user_image.save(img_path)
78
- else:
79
- img_path = None
80
-
81
- # Set query cache
82
- _cache_dir = os.path.join(self.root_cache_dir)
83
- self.executor.set_query_cache_dir(_cache_dir)
84
-
85
- # Step 1: Display the received inputs
86
- if user_image:
87
- messages.append(ChatMessage(role="assistant", content=f"📝 Received Query: {user_query}\n🖼️ Image Uploaded"))
88
- else:
89
- messages.append(ChatMessage(role="assistant", content=f"📝 Received Query: {user_query}"))
90
- yield messages
91
-
92
- # # Step 2: Add "thinking" status while processing
93
- # messages.append(ChatMessage(
94
- # role="assistant",
95
- # content="",
96
- # metadata={"title": "⏳ Thinking: Processing input..."}
97
- # ))
98
-
99
- # Step 3: Initialize problem-solving state
100
- start_time = time.time()
101
- step_count = 0
102
- json_data = {"query": user_query, "image": "Image received as bytes"}
103
-
104
- # Step 4: Query Analysis
105
- query_analysis = self.planner.analyze_query(user_query, img_path)
106
- json_data["query_analysis"] = query_analysis
107
- messages.append(ChatMessage(role="assistant",
108
- content=f"{query_analysis}",
109
- metadata={"title": "🔍 Query Analysis"}))
110
- yield messages
111
-
112
- # Step 5: Execution loop (similar to your step-by-step solver)
113
- while step_count < self.max_steps and (time.time() - start_time) < self.max_time:
114
- step_count += 1
115
- # messages.append(ChatMessage(role="assistant",
116
- # content=f"Generating next step...",
117
- # metadata={"title": f"🔄 Step {step_count}"}))
118
- yield messages
119
-
120
- # Generate the next step
121
- next_step = self.planner.generate_next_step(
122
- user_query, img_path, query_analysis, self.memory, step_count, self.max_steps
123
- )
124
- context, sub_goal, tool_name = self.planner.extract_context_subgoal_and_tool(next_step)
125
-
126
- # Display the step information
127
- messages.append(ChatMessage(
128
- role="assistant",
129
- content=f"- Context: {context}\n- Sub-goal: {sub_goal}\n- Tool: {tool_name}",
130
- metadata={"title": f"📌 Step {step_count}: {tool_name}"}
131
- ))
132
- yield messages
133
-
134
- # Handle tool execution or errors
135
- if tool_name not in self.planner.available_tools:
136
- messages.append(ChatMessage(
137
- role="assistant",
138
- content=f"⚠️ Error: Tool '{tool_name}' is not available."))
139
- yield messages
140
- continue
141
-
142
- # Execute the tool command
143
- tool_command = self.executor.generate_tool_command(
144
- user_query, img_path, context, sub_goal, tool_name, self.planner.toolbox_metadata[tool_name]
145
- )
146
- explanation, command = self.executor.extract_explanation_and_command(tool_command)
147
- result = self.executor.execute_tool_command(tool_name, command)
148
- result = make_json_serializable(result)
149
-
150
- messages.append(ChatMessage(
151
- role="assistant",
152
- content=f"{json.dumps(result, indent=4)}",
153
- metadata={"title": f"✅ Step {step_count} Result: {tool_name}"}))
154
- yield messages
155
-
156
- # Step 6: Memory update and stopping condition
157
- self.memory.add_action(step_count, tool_name, sub_goal, tool_command, result)
158
- stop_verification = self.planner.verificate_memory(user_query, img_path, query_analysis, self.memory)
159
- conclusion = self.planner.extract_conclusion(stop_verification)
160
-
161
- messages.append(ChatMessage(
162
- role="assistant",
163
- content=f"🛑 Step {step_count} Conclusion: {conclusion}"))
164
- yield messages
165
-
166
- if conclusion == 'STOP':
167
- break
168
-
169
- # Step 7: Generate Final Output (if needed)
170
- if 'final' in self.output_types:
171
- final_output = self.planner.generate_final_output(user_query, img_path, self.memory)
172
- messages.append(ChatMessage(role="assistant", content=f"🎯 Final Output:\n{final_output}"))
173
- yield messages
174
-
175
- if 'direct' in self.output_types:
176
- direct_output = self.planner.generate_direct_output(user_query, img_path, self.memory)
177
- messages.append(ChatMessage(role="assistant", content=f"🔹 Direct Output:\n{direct_output}"))
178
- yield messages
179
-
180
- # Step 8: Completion Message
181
- messages.append(ChatMessage(role="assistant", content="✅ Problem-solving process complete."))
182
- yield messages
183
-
184
-
185
- def parse_arguments():
186
- parser = argparse.ArgumentParser(description="Run the OctoTools demo with specified parameters.")
187
- parser.add_argument("--llm_engine_name", default="gpt-4o", help="LLM engine name.")
188
- parser.add_argument("--max_tokens", type=int, default=2000, help="Maximum tokens for LLM generation.")
189
- parser.add_argument("--run_baseline_only", type=bool, default=False, help="Run only the baseline (no toolbox).")
190
- parser.add_argument("--task", default="minitoolbench", help="Task to run.")
191
- parser.add_argument("--task_description", default="", help="Task description.")
192
- parser.add_argument(
193
- "--output_types",
194
- default="base,final,direct",
195
- help="Comma-separated list of required outputs (base,final,direct)"
196
- )
197
- parser.add_argument("--enabled_tools", default="Generalist_Solution_Generator_Tool", help="List of enabled tools.")
198
- parser.add_argument("--root_cache_dir", default="demo_solver_cache", help="Path to solver cache directory.")
199
- parser.add_argument("--output_json_dir", default="demo_results", help="Path to output JSON directory.")
200
- parser.add_argument("--verbose", type=bool, default=True, help="Enable verbose output.")
201
- return parser.parse_args()
202
-
203
-
204
- def solve_problem_gradio(user_query, user_image, max_steps=10, max_time=60, api_key=None, llm_model_engine=None, enabled_tools=None):
205
- """
206
- Wrapper function to connect the solver to Gradio.
207
- Streams responses from `solver.stream_solve_user_problem` for real-time UI updates.
208
- """
209
-
210
- if api_key is None:
211
- return [["assistant", "⚠️ Error: OpenAI API Key is required."]]
212
-
213
- # Initialize Tools
214
- enabled_tools = args.enabled_tools.split(",") if args.enabled_tools else []
215
-
216
- # Hack enabled_tools
217
- enabled_tools = ["Generalist_Solution_Generator_Tool"]
218
- # Instantiate Initializer
219
- initializer = Initializer(
220
- enabled_tools=enabled_tools,
221
- model_string=llm_model_engine,
222
- api_key=api_key
223
- )
224
-
225
- # Instantiate Planner
226
- planner = Planner(
227
- llm_engine_name=llm_model_engine,
228
- toolbox_metadata=initializer.toolbox_metadata,
229
- available_tools=initializer.available_tools,
230
- api_key=api_key
231
- )
232
-
233
- # Instantiate Memory
234
- memory = Memory()
235
-
236
- # Instantiate Executor
237
- executor = Executor(
238
- llm_engine_name=llm_model_engine,
239
- root_cache_dir=args.root_cache_dir,
240
- enable_signal=False,
241
- api_key=api_key
242
- )
243
-
244
- # Instantiate Solver
245
- solver = Solver(
246
- planner=planner,
247
- memory=memory,
248
- executor=executor,
249
- task=args.task,
250
- task_description=args.task_description,
251
- output_types=args.output_types, # Add new parameter
252
- verbose=args.verbose,
253
- max_steps=max_steps,
254
- max_time=max_time,
255
- output_json_dir=args.output_json_dir,
256
- root_cache_dir=args.root_cache_dir
257
- )
258
-
259
- if solver is None:
260
- return [["assistant", "⚠️ Error: Solver is not initialized. Please restart the application."]]
261
-
262
- messages = [] # Initialize message list
263
- for message_batch in solver.stream_solve_user_problem(user_query, user_image, api_key, messages):
264
- yield [msg for msg in message_batch] # Ensure correct format for Gradio Chatbot
265
-
266
-
267
- def main(args):
268
- #################### Gradio Interface ####################
269
- with gr.Blocks() as demo:
270
- gr.Markdown("# 🐙 Chat with OctoTools: An Agentic Framework for Complex Reasoning") # Title
271
- # gr.Markdown("[![OctoTools](https://img.shields.io/badge/OctoTools-Agentic%20Framework%20for%20Complex%20Reasoning-blue)](https://octotools.github.io/)") # Title
272
- gr.Markdown("""
273
- **OctoTools** is a training-free, user-friendly, and easily extensible open-source agentic framework designed to tackle complex reasoning across diverse domains.
274
- It introduces standardized **tool cards** to encapsulate tool functionality, a **planner** for both high-level and low-level planning, and an **executor** to carry out tool usage.
275
-
276
- [Website](https://octotools.github.io/) |
277
- [Github](https://github.com/octotools/octotools) |
278
- [arXiv](https://github.com/octotools/octotools/assets/paper.pdf) |
279
- [Paper](https://github.com/octotools/octotools/assets/paper.pdf) |
280
- [Tool Cards](https://octotools.github.io/#tool-cards) |
281
- [Example Visualizations](https://octotools.github.io/#visualization)
282
- """)
283
-
284
- with gr.Row():
285
- # Left column for settings
286
- with gr.Column(scale=1):
287
- with gr.Row():
288
- api_key = gr.Textbox(
289
- show_label=True,
290
- placeholder="Your API key will not be stored in any way.",
291
- type="password",
292
- label="OpenAI API Key",
293
- # container=False
294
- )
295
-
296
- llm_model_engine = gr.Dropdown(
297
- choices=["gpt-4o", "gpt-4o-2024-11-20", "gpt-4o-2024-08-06", "gpt-4o-2024-05-13",
298
- "gpt-4o-mini", "gpt-4o-mini-2024-07-18"],
299
- value="gpt-4o",
300
- label="LLM Model"
301
- )
302
- with gr.Row():
303
- max_steps = gr.Slider(value=5, minimum=1, maximum=10, step=1, label="Max Steps")
304
- max_time = gr.Slider(value=180, minimum=60, maximum=300, step=30, label="Max Time (seconds)")
305
-
306
- with gr.Row():
307
- enabled_tools = gr.CheckboxGroup(
308
- choices=all_tools,
309
- value=all_tools,
310
- label="Enabled Tools",
311
- )
312
-
313
-
314
-
315
- # Middle column for the query
316
- with gr.Column(scale=2):
317
- user_image = gr.Image(type="pil", label="Upload an image (optional)", height=500) # Accepts multiple formats
318
-
319
- with gr.Row():
320
- user_query = gr.Textbox( placeholder="Type your question here...", label="Question")
321
-
322
- with gr.Row():
323
- run_button = gr.Button("Run") # Run button
324
-
325
- # Right column for the output
326
- with gr.Column(scale=3):
327
- chatbot_output = gr.Chatbot(type="messages", label="Step-wise problem-solving output (Deep Thinking)", height=500)
328
- # chatbot_output.like(lambda x: print(f"User liked: {x}"))
329
-
330
- # TODO: Add actions to the buttons
331
- with gr.Row(elem_id="buttons") as button_row:
332
- upvote_btn = gr.Button(value="👍 Upvote", interactive=True)
333
- downvote_btn = gr.Button(value="👎 Downvote", interactive=True)
334
- clear_btn = gr.Button(value="🗑️ Clear history", interactive=True)
335
-
336
- with gr.Row():
337
- comment_textbox = gr.Textbox(value="",
338
- placeholder="Feel free to add any comments here. Thanks for using OctoTools!",
339
- label="💬 Comment", interactive=True)
340
-
341
- # Link button click to function
342
- run_button.click(
343
- fn=solve_problem_gradio,
344
- inputs=[user_query, user_image, max_steps, max_time, api_key, llm_model_engine, enabled_tools],
345
- outputs=chatbot_output
346
- )
347
- #################### Gradio Interface ####################
348
-
349
- # Launch the Gradio app
350
- demo.launch()
351
-
352
-
353
- if __name__ == "__main__":
354
- args = parse_arguments()
355
-
356
- # Manually set enabled tools
357
- # args.enabled_tools = "Generalist_Solution_Generator_Tool"
358
-
359
- # All tools
360
- all_tools = [
361
- "Generalist_Solution_Generator_Tool",
362
-
363
- "Image_Captioner_Tool",
364
- "Object_Detector_Tool",
365
- "Text_Detector_Tool",
366
- "Relevant_Patch_Zoomer_Tool",
367
-
368
- "Python_Code_Generator_Tool",
369
-
370
- "ArXiv_Paper_Searcher_Tool",
371
- "Google_Search_Tool",
372
- "Nature_News_Fetcher_Tool",
373
- "Pubmed_Search_Tool",
374
- "URL_Text_Extractor_Tool",
375
- "Wikipedia_Knowledge_Searcher_Tool"
376
- ]
377
- args.enabled_tools = ",".join(all_tools)
378
-
379
- main(args)
380
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
octotools/models/initializer.py CHANGED
@@ -17,6 +17,7 @@ class Initializer:
17
  print("\nInitializing OctoTools...")
18
  print(f"Enabled tools: {self.enabled_tools}")
19
  print(f"LLM model string: {self.model_string}")
 
20
  self._set_up_tools()
21
 
22
  def get_project_root(self):
@@ -48,7 +49,7 @@ class Initializer:
48
 
49
  for root, dirs, files in os.walk(tools_dir):
50
  # print(f"\nScanning directory: {root}")
51
- if 'tool.py' in files and os.path.basename(root) in self.available_tools:
52
  file = 'tool.py'
53
  module_path = os.path.join(root, file)
54
  module_name = os.path.splitext(file)[0]
 
17
  print("\nInitializing OctoTools...")
18
  print(f"Enabled tools: {self.enabled_tools}")
19
  print(f"LLM model string: {self.model_string}")
20
+
21
  self._set_up_tools()
22
 
23
  def get_project_root(self):
 
49
 
50
  for root, dirs, files in os.walk(tools_dir):
51
  # print(f"\nScanning directory: {root}")
52
+ if 'tool.py' in files and os.path.basename(root) in self.available_tools: # NOTE
53
  file = 'tool.py'
54
  module_path = os.path.join(root, file)
55
  module_name = os.path.splitext(file)[0]
utils.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import json
3
+ import time
4
+
5
+
6
+ def save_feedback(cache_dir: str, feedback_type: str, comment: str = None):
7
+ print(f"==> Saving feedback to {cache_dir}")
8
+ print(f"==> Feedback type: {feedback_type}")
9
+ print(f"==> Comment: {comment}")
10
+
11
+ feedback_file = os.path.join(cache_dir, "user_feedback.json")
12
+ if os.path.exists(feedback_file):
13
+ with open(feedback_file, 'r') as f:
14
+ feedback_data = json.load(f)
15
+ else:
16
+ feedback_data = []
17
+
18
+ feedback_data.append({
19
+ "timestamp": time.strftime("%Y%m%d_%H%M%S"),
20
+ "feedback_type": feedback_type,
21
+ "comment": comment
22
+ })
23
+
24
+ # Save feedback
25
+ with open(feedback_file, 'w') as f:
26
+ json.dump(feedback_data, f, indent=4)