lupantech commited on
Commit
ed01548
·
1 Parent(s): 06d02f8

recorded query data, user data, and step data

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitignore +2 -0
  2. app.py +195 -42
  3. demo_solver_cache/20250217_062225_8ce3e482/query_image.jpg +0 -0
  4. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_1.png +0 -0
  5. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_10.png +0 -0
  6. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_11.png +0 -0
  7. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_12.png +0 -0
  8. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_13.png +0 -0
  9. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_14.png +0 -0
  10. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_15.png +0 -0
  11. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_16.png +0 -0
  12. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_17.png +0 -0
  13. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_18.png +0 -0
  14. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_19.png +0 -0
  15. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_2.png +0 -0
  16. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_20.png +0 -0
  17. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_3.png +0 -0
  18. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_4.png +0 -0
  19. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_5.png +0 -0
  20. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_6.png +0 -0
  21. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_7.png +0 -0
  22. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_8.png +0 -0
  23. demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_9.png +0 -0
  24. demo_solver_cache/20250217_062225_8ce3e482/user_feedback.json +0 -22
  25. demo_solver_cache/20250217_063316_09285db1/query_image.jpg +0 -0
  26. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_1.png +0 -0
  27. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_10.png +0 -0
  28. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_11.png +0 -0
  29. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_12.png +0 -0
  30. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_13.png +0 -0
  31. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_14.png +0 -0
  32. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_15.png +0 -0
  33. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_16.png +0 -0
  34. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_17.png +0 -0
  35. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_18.png +0 -0
  36. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_19.png +0 -0
  37. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_2.png +0 -0
  38. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_20.png +0 -0
  39. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_3.png +0 -0
  40. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_4.png +0 -0
  41. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_5.png +0 -0
  42. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_6.png +0 -0
  43. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_7.png +0 -0
  44. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_8.png +0 -0
  45. demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_9.png +0 -0
  46. demo_solver_cache/20250217_063316_09285db1/user_feedback.json +0 -12
  47. demo_solver_cache/20250217_183323_b0e58b32/query_image.jpg +0 -0
  48. demo_solver_cache/20250217_183323_b0e58b32/user_feedback.json +0 -32
  49. feedback_dataset/feedback-20250217_212246.json +0 -0
  50. feedback_dataset/feedback-20250217_212401.json +0 -10
.gitignore CHANGED
@@ -175,4 +175,6 @@ detected_objects/
175
 
176
  # [Gradio]
177
  demo_solver_cache/
 
178
  backups/
 
 
175
 
176
  # [Gradio]
177
  demo_solver_cache/
178
+ solver_cache/
179
  backups/
180
+ data/
app.py CHANGED
@@ -22,41 +22,125 @@ from octotools.models.memory import Memory
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
- ########### Test Huggingface Dataset ###########
29
  from pathlib import Path
30
  from huggingface_hub import CommitScheduler
31
 
32
- # Add these near the top of the file with other constants
33
- DATASET_DIR = Path("feedback_dataset")
34
- DATASET_DIR.mkdir(parents=True, exist_ok=True)
35
- DATASET_PATH = DATASET_DIR / f"feedback-{time.strftime('%Y%m%d_%H%M%S')}.json"
36
-
37
  # Get Huggingface token from environment variable
38
  HF_TOKEN = os.getenv("HUGGINGFACE_TOKEN")
39
 
 
 
 
 
 
 
 
 
40
  scheduler = CommitScheduler(
41
  repo_id="lupantech/OctoTools-Gradio-Demo-User-Data",
42
  repo_type="dataset",
43
  folder_path=DATASET_DIR,
44
- path_in_repo="data",
45
  token=HF_TOKEN
46
  )
47
 
48
- def save_feedback(root_cache_dir: str, feedback_type: str, comment: str = None) -> None:
49
- """Save user feedback to Huggingface dataset"""
50
- with scheduler.lock:
51
- with DATASET_PATH.open("a") as f:
52
- feedback_data = {
53
- "query_id": os.path.basename(root_cache_dir),
54
- "feedback_type": feedback_type,
55
- "comment": comment,
56
- "datetime": time.strftime("%Y%m%d_%H%M%S")
57
- }
58
- json.dump(feedback_data, f)
59
- f.write("\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  ########### End of Test Huggingface Dataset ###########
61
 
62
  class Solver:
@@ -72,7 +156,7 @@ class Solver:
72
  verbose: bool = True,
73
  max_steps: int = 10,
74
  max_time: int = 60,
75
- root_cache_dir: str = "cache"
76
  ):
77
  self.planner = planner
78
  self.memory = memory
@@ -83,7 +167,7 @@ class Solver:
83
  self.verbose = verbose
84
  self.max_steps = max_steps
85
  self.max_time = max_time
86
- self.root_cache_dir = root_cache_dir
87
 
88
  self.output_types = output_types.lower().split(',')
89
  assert all(output_type in ["base", "final", "direct"] for output_type in self.output_types), "Invalid output type. Supported types are 'base', 'final', 'direct'."
@@ -109,14 +193,14 @@ class Solver:
109
  # os.makedirs(os.path.join(self.root_cache_dir, 'images'), exist_ok=True)
110
  # img_path = os.path.join(self.root_cache_dir, 'images', str(uuid.uuid4()) + '.jpg')
111
 
112
- img_path = os.path.join(self.root_cache_dir, 'query_image.jpg')
113
  user_image.save(img_path)
114
  else:
115
  img_path = None
116
 
117
  # Set tool cache directory
118
- _cache_dir = os.path.join(self.root_cache_dir, "tool_cache") # NOTE: This is the directory for tool cache
119
- self.executor.set_query_cache_dir(_cache_dir)
120
 
121
  # Step 1: Display the received inputs
122
  if user_image:
@@ -145,6 +229,13 @@ class Solver:
145
  metadata={"title": "🔍 Query Analysis"}))
146
  yield messages
147
 
 
 
 
 
 
 
 
148
  # Step 5: Execution loop (similar to your step-by-step solver)
149
  while step_count < self.max_steps and (time.time() - start_time) < self.max_time:
150
  step_count += 1
@@ -158,6 +249,14 @@ class Solver:
158
  user_query, img_path, query_analysis, self.memory, step_count, self.max_steps
159
  )
160
  context, sub_goal, tool_name = self.planner.extract_context_subgoal_and_tool(next_step)
 
 
 
 
 
 
 
 
161
 
162
  # Display the step information
163
  messages.append(ChatMessage(
@@ -183,6 +282,21 @@ class Solver:
183
  result = self.executor.execute_tool_command(tool_name, command)
184
  result = make_json_serializable(result)
185
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  messages.append(ChatMessage(
187
  role="assistant",
188
  content=f"{json.dumps(result, indent=4)}",
@@ -194,6 +308,14 @@ class Solver:
194
  stop_verification = self.planner.verificate_memory(user_query, img_path, query_analysis, self.memory)
195
  conclusion = self.planner.extract_conclusion(stop_verification)
196
 
 
 
 
 
 
 
 
 
197
  messages.append(ChatMessage(
198
  role="assistant",
199
  content=f"🛑 Step {step_count} Conclusion: {conclusion}"))
@@ -208,15 +330,29 @@ class Solver:
208
  messages.append(ChatMessage(role="assistant", content=f"🎯 Final Output:\n{final_output}"))
209
  yield messages
210
 
 
 
 
 
 
 
 
211
  if 'direct' in self.output_types:
212
  direct_output = self.planner.generate_direct_output(user_query, img_path, self.memory)
213
  messages.append(ChatMessage(role="assistant", content=f"🔹 Direct Output:\n{direct_output}"))
214
  yield messages
215
 
 
 
 
 
 
 
 
216
  # Step 8: Completion Message
217
  messages.append(ChatMessage(role="assistant", content="✅ Problem-solving process completed."))
218
  yield messages
219
-
220
 
221
  def parse_arguments():
222
  parser = argparse.ArgumentParser(description="Run the OctoTools demo with specified parameters.")
@@ -230,7 +366,8 @@ def parse_arguments():
230
  help="Comma-separated list of required outputs (base,final,direct)"
231
  )
232
  parser.add_argument("--enabled_tools", default="Generalist_Solution_Generator_Tool", help="List of enabled tools.")
233
- parser.add_argument("--root_cache_dir", default="demo_solver_cache", help="Path to solver cache directory.")
 
234
  parser.add_argument("--verbose", type=bool, default=True, help="Enable verbose output.")
235
 
236
  # NOTE: Add new arguments
@@ -245,18 +382,28 @@ def solve_problem_gradio(user_query, user_image, max_steps=10, max_time=60, api_
245
  Streams responses from `solver.stream_solve_user_problem` for real-time UI updates.
246
  """
247
 
248
- # Generate shorter ID (Date and first 8 characters of UUID)
249
  query_id = time.strftime("%Y%m%d_%H%M%S") + "_" + str(uuid.uuid4())[:8] # e.g, 20250217_062225_612f2474
250
  print(f"Query ID: {query_id}")
251
 
 
 
 
 
252
  # Create a directory for the query ID
253
- query_dir = os.path.join(args.root_cache_dir, query_id)
254
- os.makedirs(query_dir, exist_ok=True)
255
- args.root_cache_dir = query_dir
256
 
257
  if api_key is None:
258
  return [["assistant", "⚠️ Error: OpenAI API Key is required."]]
259
 
 
 
 
 
 
 
 
260
  # # Initialize Tools
261
  # enabled_tools = args.enabled_tools.split(",") if args.enabled_tools else []
262
 
@@ -284,7 +431,7 @@ def solve_problem_gradio(user_query, user_image, max_steps=10, max_time=60, api_
284
  # Instantiate Executor
285
  executor = Executor(
286
  llm_engine_name=llm_model_engine,
287
- root_cache_dir=args.root_cache_dir, # NOTE
288
  enable_signal=False,
289
  api_key=api_key
290
  )
@@ -300,16 +447,23 @@ def solve_problem_gradio(user_query, user_image, max_steps=10, max_time=60, api_
300
  verbose=args.verbose,
301
  max_steps=max_steps,
302
  max_time=max_time,
303
- root_cache_dir=args.root_cache_dir # NOTE
304
  )
305
 
306
  if solver is None:
307
  return [["assistant", "⚠️ Error: Solver is not initialized. Please restart the application."]]
308
 
 
309
  messages = [] # Initialize message list
310
  for message_batch in solver.stream_solve_user_problem(user_query, user_image, api_key, messages):
311
  yield [msg for msg in message_batch] # Ensure correct format for Gradio Chatbot
312
 
 
 
 
 
 
 
313
 
314
  def main(args):
315
  #################### Gradio Interface ####################
@@ -325,8 +479,8 @@ def main(args):
325
 
326
  [Website](https://octotools.github.io/) |
327
  [Github](https://github.com/octotools/octotools) |
328
- [arXiv](https://arxiv.org/abs/2502.xxxxx) |
329
- [Paper](https://arxiv.org/pdf/2502.xxxxx) |
330
  [Tool Cards](https://octotools.github.io/#tool-cards) |
331
  [Example Visualizations](https://octotools.github.io/#visualization) |
332
  [Discord](https://discord.gg/NMJx66DC)
@@ -424,20 +578,20 @@ def main(args):
424
 
425
  # Update the button click handlers
426
  upvote_btn.click(
427
- fn=lambda: save_feedback(args.root_cache_dir, "upvote"),
428
  inputs=[],
429
  outputs=[]
430
  )
431
 
432
  downvote_btn.click(
433
- fn=lambda: save_feedback(args.root_cache_dir, "downvote"),
434
  inputs=[],
435
  outputs=[]
436
  )
437
 
438
  # Add handler for comment submission
439
  comment_textbox.submit(
440
- fn=lambda comment: save_feedback(args.root_cache_dir, comment),
441
  inputs=[comment_textbox],
442
  outputs=[]
443
  )
@@ -481,9 +635,6 @@ def main(args):
481
  if __name__ == "__main__":
482
  args = parse_arguments()
483
 
484
- # Manually set enabled tools
485
- # args.enabled_tools = "Generalist_Solution_Generator_Tool"
486
-
487
  # All tools
488
  all_tools = [
489
  "Generalist_Solution_Generator_Tool",
@@ -504,5 +655,7 @@ if __name__ == "__main__":
504
  ]
505
  args.enabled_tools = ",".join(all_tools)
506
 
 
 
507
  main(args)
508
 
 
22
  from octotools.models.executor import Executor
23
  from octotools.models.utils import make_json_serializable
24
 
 
25
 
 
 
26
  from pathlib import Path
27
  from huggingface_hub import CommitScheduler
28
 
 
 
 
 
 
29
  # Get Huggingface token from environment variable
30
  HF_TOKEN = os.getenv("HUGGINGFACE_TOKEN")
31
 
32
+ ########### Test Huggingface Dataset ###########
33
+ # Update the HuggingFace dataset constants
34
+ DATASET_DIR = Path("solver_cache") # the directory to save the dataset
35
+ DATASET_DIR.mkdir(parents=True, exist_ok=True)
36
+
37
+ global QUERY_ID
38
+ QUERY_ID = None
39
+
40
  scheduler = CommitScheduler(
41
  repo_id="lupantech/OctoTools-Gradio-Demo-User-Data",
42
  repo_type="dataset",
43
  folder_path=DATASET_DIR,
44
+ path_in_repo="solver_cache", # Update path in repo
45
  token=HF_TOKEN
46
  )
47
 
48
+
49
+ def save_query_data(query_id: str, query: str, image_path: str) -> None:
50
+ """Save query data to Huggingface dataset"""
51
+ # Save query metadata
52
+ query_cache_dir = DATASET_DIR / query_id
53
+ query_cache_dir.mkdir(parents=True, exist_ok=True)
54
+ query_file = query_cache_dir / "query_metadata.json"
55
+
56
+ query_metadata = {
57
+ "query_id": query_id,
58
+ "query_text": query,
59
+ "datetime": time.strftime("%Y%m%d_%H%M%S"),
60
+ "image_path": image_path if image_path else None
61
+ }
62
+
63
+ print(f"Saving query metadata to {query_file}")
64
+ with query_file.open("w") as f:
65
+ json.dump(query_metadata, f, indent=4)
66
+
67
+ # # NOTE: As we are using the same name for the query cache directory as the dataset directory,
68
+ # # NOTE: we don't need to copy the content from the query cache directory to the query directory.
69
+ # # Copy all content from root_cache_dir to query_dir
70
+ # import shutil
71
+ # shutil.copytree(args.root_cache_dir, query_data_dir, dirs_exist_ok=True)
72
+
73
+
74
+ def save_feedback(query_id: str, feedback_type: str, feedback_text: str = None) -> None:
75
+ """
76
+ Save user feedback to the query directory.
77
+
78
+ Args:
79
+ query_id: Unique identifier for the query
80
+ feedback_type: Type of feedback ('upvote', 'downvote', or 'comment')
81
+ feedback_text: Optional text feedback from user
82
+ """
83
+
84
+ feedback_data_dir = DATASET_DIR / query_id
85
+ feedback_data_dir.mkdir(parents=True, exist_ok=True)
86
+
87
+ feedback_data = {
88
+ "query_id": query_id,
89
+ "feedback_type": feedback_type,
90
+ "feedback_text": feedback_text,
91
+ "datetime": time.strftime("%Y%m%d_%H%M%S")
92
+ }
93
+
94
+ # Save feedback in the query directory
95
+ feedback_file = feedback_data_dir / "feedback.json"
96
+ print(f"Saving feedback to {feedback_file}")
97
+
98
+ # If feedback file exists, update it
99
+ if feedback_file.exists():
100
+ with feedback_file.open("r") as f:
101
+ existing_feedback = json.load(f)
102
+ # Convert to list if it's a single feedback entry
103
+ if not isinstance(existing_feedback, list):
104
+ existing_feedback = [existing_feedback]
105
+ existing_feedback.append(feedback_data)
106
+ feedback_data = existing_feedback
107
+
108
+ # Write feedback data
109
+ with feedback_file.open("w") as f:
110
+ json.dump(feedback_data, f, indent=4)
111
+
112
+
113
+ def save_steps_data(query_id: str, memory: Memory) -> None:
114
+ """Save steps data to Huggingface dataset"""
115
+ steps_file = DATASET_DIR / query_id / "all_steps.json"
116
+
117
+ memory_actions = memory.get_actions()
118
+ memory_actions = make_json_serializable(memory_actions) # NOTE: make the memory actions serializable
119
+ print("Memory actions: ", memory_actions)
120
+
121
+ with steps_file.open("w") as f:
122
+ json.dump(memory_actions, f, indent=4)
123
+
124
+
125
+ def save_module_data(query_id: str, key: str, value: Any) -> None:
126
+ """Save module data to Huggingface dataset"""
127
+ try:
128
+ key = key.replace(" ", "_").lower()
129
+ module_file = DATASET_DIR / query_id / f"{key}.json"
130
+ value = make_json_serializable(value) # NOTE: make the value serializable
131
+ with module_file.open("a") as f:
132
+ json.dump(value, f, indent=4)
133
+ except Exception as e:
134
+ print(f"Warning: Failed to save as JSON: {e}")
135
+ # Fallback to saving as text file
136
+ text_file = DATASET_DIR / query_id / f"{key}.txt"
137
+ try:
138
+ with text_file.open("a") as f:
139
+ f.write(str(value) + "\n")
140
+ print(f"Successfully saved as text file: {text_file}")
141
+ except Exception as e:
142
+ print(f"Error: Failed to save as text file: {e}")
143
+
144
  ########### End of Test Huggingface Dataset ###########
145
 
146
  class Solver:
 
156
  verbose: bool = True,
157
  max_steps: int = 10,
158
  max_time: int = 60,
159
+ query_cache_dir: str = "solver_cache"
160
  ):
161
  self.planner = planner
162
  self.memory = memory
 
167
  self.verbose = verbose
168
  self.max_steps = max_steps
169
  self.max_time = max_time
170
+ self.query_cache_dir = query_cache_dir
171
 
172
  self.output_types = output_types.lower().split(',')
173
  assert all(output_type in ["base", "final", "direct"] for output_type in self.output_types), "Invalid output type. Supported types are 'base', 'final', 'direct'."
 
193
  # os.makedirs(os.path.join(self.root_cache_dir, 'images'), exist_ok=True)
194
  # img_path = os.path.join(self.root_cache_dir, 'images', str(uuid.uuid4()) + '.jpg')
195
 
196
+ img_path = os.path.join(self.query_cache_dir, 'query_image.jpg')
197
  user_image.save(img_path)
198
  else:
199
  img_path = None
200
 
201
  # Set tool cache directory
202
+ _tool_cache_dir = os.path.join(self.query_cache_dir, "tool_cache") # NOTE: This is the directory for tool cache
203
+ self.executor.set_query_cache_dir(_tool_cache_dir) # NOTE: set query cache directory
204
 
205
  # Step 1: Display the received inputs
206
  if user_image:
 
229
  metadata={"title": "🔍 Query Analysis"}))
230
  yield messages
231
 
232
+ # Save the query analysis data
233
+ query_analysis_data = {
234
+ "query_analysis": query_analysis,
235
+ "time": round(time.time() - start_time, 5)
236
+ }
237
+ save_module_data(QUERY_ID, "step_0_query_analysis", query_analysis_data)
238
+
239
  # Step 5: Execution loop (similar to your step-by-step solver)
240
  while step_count < self.max_steps and (time.time() - start_time) < self.max_time:
241
  step_count += 1
 
249
  user_query, img_path, query_analysis, self.memory, step_count, self.max_steps
250
  )
251
  context, sub_goal, tool_name = self.planner.extract_context_subgoal_and_tool(next_step)
252
+ step_data = {
253
+ "step_count": step_count,
254
+ "context": context,
255
+ "sub_goal": sub_goal,
256
+ "tool_name": tool_name,
257
+ "time": round(time.time() - start_time, 5)
258
+ }
259
+ save_module_data(QUERY_ID, f"step_{step_count}_action_prediction", step_data)
260
 
261
  # Display the step information
262
  messages.append(ChatMessage(
 
282
  result = self.executor.execute_tool_command(tool_name, command)
283
  result = make_json_serializable(result)
284
 
285
+ # Save the command generation data
286
+ command_generation_data = {
287
+ "explanation": explanation,
288
+ "command": command,
289
+ "time": round(time.time() - start_time, 5)
290
+ }
291
+ save_module_data(QUERY_ID, f"step_{step_count}_command_generation", command_generation_data)
292
+
293
+ # Save the command execution data
294
+ command_execution_data = {
295
+ "result": result,
296
+ "time": round(time.time() - start_time, 5)
297
+ }
298
+ save_module_data(QUERY_ID, f"step_{step_count}_command_execution", command_execution_data)
299
+
300
  messages.append(ChatMessage(
301
  role="assistant",
302
  content=f"{json.dumps(result, indent=4)}",
 
308
  stop_verification = self.planner.verificate_memory(user_query, img_path, query_analysis, self.memory)
309
  conclusion = self.planner.extract_conclusion(stop_verification)
310
 
311
+ # Save the context verification data
312
+ context_verification_data = {
313
+ "stop_verification": stop_verification,
314
+ "conclusion": conclusion,
315
+ "time": round(time.time() - start_time, 5)
316
+ }
317
+ save_module_data(QUERY_ID, f"step_{step_count}_context_verification", context_verification_data)
318
+
319
  messages.append(ChatMessage(
320
  role="assistant",
321
  content=f"🛑 Step {step_count} Conclusion: {conclusion}"))
 
330
  messages.append(ChatMessage(role="assistant", content=f"🎯 Final Output:\n{final_output}"))
331
  yield messages
332
 
333
+ # Save the final output data
334
+ final_output_data = {
335
+ "final_output": final_output,
336
+ "time": round(time.time() - start_time, 5)
337
+ }
338
+ save_module_data(QUERY_ID, "final_output", final_output_data)
339
+
340
  if 'direct' in self.output_types:
341
  direct_output = self.planner.generate_direct_output(user_query, img_path, self.memory)
342
  messages.append(ChatMessage(role="assistant", content=f"🔹 Direct Output:\n{direct_output}"))
343
  yield messages
344
 
345
+ # Save the direct output data
346
+ direct_output_data = {
347
+ "direct_output": direct_output,
348
+ "time": round(time.time() - start_time, 5)
349
+ }
350
+ save_module_data(QUERY_ID, "direct_output", direct_output_data)
351
+
352
  # Step 8: Completion Message
353
  messages.append(ChatMessage(role="assistant", content="✅ Problem-solving process completed."))
354
  yield messages
355
+
356
 
357
  def parse_arguments():
358
  parser = argparse.ArgumentParser(description="Run the OctoTools demo with specified parameters.")
 
366
  help="Comma-separated list of required outputs (base,final,direct)"
367
  )
368
  parser.add_argument("--enabled_tools", default="Generalist_Solution_Generator_Tool", help="List of enabled tools.")
369
+ parser.add_argument("--root_cache_dir", default="solver_cache", help="Path to solver cache directory.")
370
+ parser.add_argument("--query_id", default=None, help="Query ID.")
371
  parser.add_argument("--verbose", type=bool, default=True, help="Enable verbose output.")
372
 
373
  # NOTE: Add new arguments
 
382
  Streams responses from `solver.stream_solve_user_problem` for real-time UI updates.
383
  """
384
 
385
+ # Generate Unique Query ID (Date and first 8 characters of UUID)
386
  query_id = time.strftime("%Y%m%d_%H%M%S") + "_" + str(uuid.uuid4())[:8] # e.g, 20250217_062225_612f2474
387
  print(f"Query ID: {query_id}")
388
 
389
+ # NOTE: update the global variable to save the query ID
390
+ global QUERY_ID
391
+ QUERY_ID = query_id
392
+
393
  # Create a directory for the query ID
394
+ query_cache_dir = os.path.join(DATASET_DIR.name, query_id) # NOTE
395
+ os.makedirs(query_cache_dir, exist_ok=True)
 
396
 
397
  if api_key is None:
398
  return [["assistant", "⚠️ Error: OpenAI API Key is required."]]
399
 
400
+ # Save the query data
401
+ save_query_data(
402
+ query_id=query_id,
403
+ query=user_query,
404
+ image_path=os.path.join(query_cache_dir, 'query_image.jpg') if user_image else None
405
+ )
406
+
407
  # # Initialize Tools
408
  # enabled_tools = args.enabled_tools.split(",") if args.enabled_tools else []
409
 
 
431
  # Instantiate Executor
432
  executor = Executor(
433
  llm_engine_name=llm_model_engine,
434
+ query_cache_dir=query_cache_dir, # NOTE
435
  enable_signal=False,
436
  api_key=api_key
437
  )
 
447
  verbose=args.verbose,
448
  max_steps=max_steps,
449
  max_time=max_time,
450
+ query_cache_dir=query_cache_dir # NOTE
451
  )
452
 
453
  if solver is None:
454
  return [["assistant", "⚠️ Error: Solver is not initialized. Please restart the application."]]
455
 
456
+
457
  messages = [] # Initialize message list
458
  for message_batch in solver.stream_solve_user_problem(user_query, user_image, api_key, messages):
459
  yield [msg for msg in message_batch] # Ensure correct format for Gradio Chatbot
460
 
461
+ # Save steps
462
+ save_steps_data(
463
+ query_id=query_id,
464
+ memory=memory
465
+ )
466
+
467
 
468
  def main(args):
469
  #################### Gradio Interface ####################
 
479
 
480
  [Website](https://octotools.github.io/) |
481
  [Github](https://github.com/octotools/octotools) |
482
+ [arXiv](https://arxiv.org/abs/2502.11271) |
483
+ [Paper](https://arxiv.org/pdf/2502.11271) |
484
  [Tool Cards](https://octotools.github.io/#tool-cards) |
485
  [Example Visualizations](https://octotools.github.io/#visualization) |
486
  [Discord](https://discord.gg/NMJx66DC)
 
578
 
579
  # Update the button click handlers
580
  upvote_btn.click(
581
+ fn=lambda: save_feedback(QUERY_ID, "upvote"),
582
  inputs=[],
583
  outputs=[]
584
  )
585
 
586
  downvote_btn.click(
587
+ fn=lambda: save_feedback(QUERY_ID, "downvote"),
588
  inputs=[],
589
  outputs=[]
590
  )
591
 
592
  # Add handler for comment submission
593
  comment_textbox.submit(
594
+ fn=lambda comment: save_feedback(QUERY_ID, "comment", comment),
595
  inputs=[comment_textbox],
596
  outputs=[]
597
  )
 
635
  if __name__ == "__main__":
636
  args = parse_arguments()
637
 
 
 
 
638
  # All tools
639
  all_tools = [
640
  "Generalist_Solution_Generator_Tool",
 
655
  ]
656
  args.enabled_tools = ",".join(all_tools)
657
 
658
+ # NOTE: Use the same name for the query cache directory as the dataset directory
659
+ args.root_cache_dir = DATASET_DIR.name
660
  main(args)
661
 
demo_solver_cache/20250217_062225_8ce3e482/query_image.jpg DELETED
Binary file (42 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_1.png DELETED
Binary file (7.75 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_10.png DELETED
Binary file (7.6 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_11.png DELETED
Binary file (7.77 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_12.png DELETED
Binary file (7.71 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_13.png DELETED
Binary file (7.6 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_14.png DELETED
Binary file (7.47 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_15.png DELETED
Binary file (8.05 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_16.png DELETED
Binary file (7.86 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_17.png DELETED
Binary file (7.88 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_18.png DELETED
Binary file (7.76 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_19.png DELETED
Binary file (8.02 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_2.png DELETED
Binary file (7.65 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_20.png DELETED
Binary file (8.03 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_3.png DELETED
Binary file (7.92 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_4.png DELETED
Binary file (7.71 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_5.png DELETED
Binary file (7.6 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_6.png DELETED
Binary file (7.82 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_7.png DELETED
Binary file (7.53 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_8.png DELETED
Binary file (7.67 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/query_image_baseball_9.png DELETED
Binary file (7.41 kB)
 
demo_solver_cache/20250217_062225_8ce3e482/user_feedback.json DELETED
@@ -1,22 +0,0 @@
1
- [
2
- {
3
- "timestamp": "20250217_062307",
4
- "feedback_type": "upvote",
5
- "comment": null
6
- },
7
- {
8
- "timestamp": "20250217_062315",
9
- "feedback_type": "downvote",
10
- "comment": null
11
- },
12
- {
13
- "timestamp": "20250217_062322",
14
- "feedback_type": "upvote",
15
- "comment": null
16
- },
17
- {
18
- "timestamp": "20250217_062333",
19
- "feedback_type": "It is helpful!",
20
- "comment": null
21
- }
22
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
demo_solver_cache/20250217_063316_09285db1/query_image.jpg DELETED
Binary file (42 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_1.png DELETED
Binary file (7.75 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_10.png DELETED
Binary file (7.6 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_11.png DELETED
Binary file (7.77 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_12.png DELETED
Binary file (7.71 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_13.png DELETED
Binary file (7.6 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_14.png DELETED
Binary file (7.47 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_15.png DELETED
Binary file (8.05 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_16.png DELETED
Binary file (7.86 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_17.png DELETED
Binary file (7.88 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_18.png DELETED
Binary file (7.76 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_19.png DELETED
Binary file (8.02 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_2.png DELETED
Binary file (7.65 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_20.png DELETED
Binary file (8.03 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_3.png DELETED
Binary file (7.92 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_4.png DELETED
Binary file (7.71 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_5.png DELETED
Binary file (7.6 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_6.png DELETED
Binary file (7.82 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_7.png DELETED
Binary file (7.53 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_8.png DELETED
Binary file (7.67 kB)
 
demo_solver_cache/20250217_063316_09285db1/tools/query_image_baseball_9.png DELETED
Binary file (7.41 kB)
 
demo_solver_cache/20250217_063316_09285db1/user_feedback.json DELETED
@@ -1,12 +0,0 @@
1
- [
2
- {
3
- "timestamp": "20250217_063350",
4
- "feedback_type": "upvote",
5
- "comment": null
6
- },
7
- {
8
- "timestamp": "20250217_063359",
9
- "feedback_type": "Thanks! It is interesting!",
10
- "comment": null
11
- }
12
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
demo_solver_cache/20250217_183323_b0e58b32/query_image.jpg DELETED
Binary file (42 kB)
 
demo_solver_cache/20250217_183323_b0e58b32/user_feedback.json DELETED
@@ -1,32 +0,0 @@
1
- [
2
- {
3
- "timestamp": "20250217_190313",
4
- "feedback_type": "upvote",
5
- "comment": null
6
- },
7
- {
8
- "timestamp": "20250217_190319",
9
- "feedback_type": "downvote",
10
- "comment": null
11
- },
12
- {
13
- "timestamp": "20250217_190321",
14
- "feedback_type": "upvote",
15
- "comment": null
16
- },
17
- {
18
- "timestamp": "20250217_190322",
19
- "feedback_type": "downvote",
20
- "comment": null
21
- },
22
- {
23
- "timestamp": "20250217_190338",
24
- "feedback_type": "Thanks! It is interesting!",
25
- "comment": null
26
- },
27
- {
28
- "timestamp": "20250217_190341",
29
- "feedback_type": "Thanks! It is interesting!",
30
- "comment": null
31
- }
32
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
feedback_dataset/feedback-20250217_212246.json DELETED
File without changes
feedback_dataset/feedback-20250217_212401.json DELETED
@@ -1,10 +0,0 @@
1
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "upvote", "comment": null, "datetime": "20250217_212450"}
2
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "upvote", "comment": null, "datetime": "20250217_212452"}
3
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "It is good!", "comment": null, "datetime": "20250217_212459"}
4
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "upvote", "comment": null, "datetime": "20250217_212523"}
5
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "upvote", "comment": null, "datetime": "20250217_212524"}
6
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "upvote", "comment": null, "datetime": "20250217_212524"}
7
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "downvote", "comment": null, "datetime": "20250217_212524"}
8
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "It is good!", "comment": null, "datetime": "20250217_212526"}
9
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "upvote", "comment": null, "datetime": "20250217_212619"}
10
- {"query_id": "20250217_212439_f48ed6ff", "feedback_type": "It is good!", "comment": null, "datetime": "20250217_212650"}