1717
1818# List of default tools to include in every environment
1919DEFAULT_TOOLS = ["web_search" , "bash" , "python_execute" , "browser_use" , "terminate" ]
20-
20+ # Define valid action types
21+ VALID_ACTIONS = {
22+ "web_search" : {"param" : "query" },
23+ "bash" : {"param" : "command" },
24+ "python_execute" : {"param" : "code" },
25+ "browser_use" : {"param" : "input" },
26+ "terminate" : {"param" : "answer" }
27+ }
2128
2229
2330# GaiaEnvServer class to manage environment instances
@@ -239,32 +246,45 @@ def step(self, env_id: str, action: str):
239246 with self .env_locks [env_id ]:
240247 env = self .env_instances [env_id ]
241248
242- # Parse and process the action
243- action_type , action_input = self ._parse_action (action )
249+ try :
250+ # Parse and process the action
251+ action_type , action_input = self ._parse_action (action )
244252
245- # Update state
246- env ["state" ]["steps_taken" ] += 1
253+ # Update state
254+ env ["state" ]["steps_taken" ] += 1
247255
248- # Process the action using available tools
249- observation , reward , done = self ._process_action (env_id , action_type , action_input )
256+ # Process the action using available tools
257+ observation , reward , done = self ._process_action (env_id , action_type , action_input )
250258
251- # Store action and result in memory
252- env ["state" ]["memory" ].append ({
253- "action" : action ,
254- "result" : observation ,
255- "step" : env ["state" ]["steps_taken" ]
256- })
259+ # Store action and result in memory
260+ env ["state" ]["memory" ].append ({
261+ "action" : action ,
262+ "result" : observation ,
263+ "step" : env ["state" ]["steps_taken" ]
264+ })
257265
258- # Update state with results
259- env ["state" ]["reward" ] += reward
260- env ["state" ]["done" ] = done
266+ # Update state with results
267+ env ["state" ]["reward" ] += reward
268+ env ["state" ]["done" ] = done
261269
262- info = {
263- "steps_taken" : env ["state" ]["steps_taken" ],
264- "action_processed" : action ,
265- }
270+ info = {
271+ "steps_taken" : env ["state" ]["steps_taken" ],
272+ "action_processed" : action ,
273+ }
266274
267- return observation , reward , done , info
275+ return observation , reward , done , info
276+
277+ except ValueError as e :
278+ # Handle validation errors
279+ error_msg = str (e )
280+ observation = f"Error: { error_msg } "
281+ reward = - 0.1 # Penalty for invalid action
282+ done = False
283+ info = {
284+ "steps_taken" : env ["state" ]["steps_taken" ],
285+ "error" : error_msg
286+ }
287+ return observation , reward , done , info
268288
269289 def _parse_action (self , action : str ) -> Tuple [str , Dict [str , Any ]]:
270290 """
@@ -275,53 +295,71 @@ def _parse_action(self, action: str) -> Tuple[str, Dict[str, Any]]:
275295
276296 Returns:
277297 tuple: (action_type, action_parameters)
298+
299+ Raises:
300+ ValueError: If action format is invalid
278301 """
279302 try :
303+ # Split by newlines and find the action line
304+ lines = action .split ('\n ' )
305+ action_line = None
306+ for line in lines :
307+ if line .strip ().startswith ('Action:' ):
308+ action_line = line .strip ()
309+ break
310+
311+ if not action_line :
312+ raise ValueError ("No valid action found in the input" )
313+
280314 # Parse actions in format "Action: action_type with Action Input: action_input"
281- if "Action:" in action and "Action Input:" in action :
282- action_parts = action .split ("Action Input:" , 1 )
315+ if "Action:" in action_line and "Action Input:" in action_line :
316+ action_parts = action_line .split ("Action Input:" , 1 )
283317 action_type = action_parts [0 ].replace ("Action:" , "" ).strip ()
284318 action_input = action_parts [1 ].strip ()
285- return action_type , {"query" : action_input } if action_type == "web_search" else {
286- "command" : action_input } if action_type == "bash" else {
287- "code" : action_input } if action_type == "python_execute" else {"input" : action_input }
319+
320+ # Validate action type
321+ if action_type not in VALID_ACTIONS :
322+ raise ValueError (f"Invalid action type: { action_type } . Valid types are: { ', ' .join (VALID_ACTIONS .keys ())} " )
323+
324+ # Return with appropriate parameter name
325+ param_name = VALID_ACTIONS [action_type ]["param" ]
326+ return action_type , {param_name : action_input }
288327
289328 # Parse JSON format with tool_name and parameters
290- elif action .strip ().startswith ("{" ) and action .strip ().endswith ("}" ):
329+ elif action_line .strip ().startswith ("{" ) and action_line .strip ().endswith ("}" ):
291330 try :
292- action_json = json .loads (action )
293- if "tool_name" in action_json :
294- tool_name = action_json .pop ("tool_name" )
295- return tool_name , action_json
331+ action_json = json .loads (action_line )
332+ if "tool_name" not in action_json :
333+ raise ValueError ("JSON format must include 'tool_name' field" )
334+
335+ tool_name = action_json .pop ("tool_name" )
336+ if tool_name not in VALID_ACTIONS :
337+ raise ValueError (f"Invalid tool name: { tool_name } . Valid tools are: { ', ' .join (VALID_ACTIONS .keys ())} " )
338+
339+ return tool_name , action_json
296340 except json .JSONDecodeError :
297- pass
341+ raise ValueError ( "Invalid JSON format" )
298342
299343 # Parse direct tool calls (e.g., "web_search: query")
300- elif ":" in action :
301- tool_name , input_text = action .split (":" , 1 )
344+ elif ":" in action_line :
345+ tool_name , input_text = action_line .split (":" , 1 )
302346 tool_name = tool_name .strip ()
303347 input_text = input_text .strip ()
304348
305- # Map to appropriate parameter name based on tool
306- if tool_name .lower () in ["web_search" , "search_web" ]:
307- return tool_name , {"query" : input_text }
308- elif tool_name .lower () == "bash" :
309- return tool_name , {"command" : input_text }
310- elif tool_name .lower () == "python_execute" :
311- return tool_name , {"code" : input_text }
312- elif tool_name .lower () == "browser_use" :
313- return tool_name , {"url" : input_text }
314- elif tool_name .lower () == "terminate" :
315- return "terminate" , {"answer" : input_text }
316- else :
317- return tool_name , {"input" : input_text }
349+ if tool_name not in VALID_ACTIONS :
350+ raise ValueError (f"Invalid tool name: { tool_name } . Valid tools are: { ', ' .join (VALID_ACTIONS .keys ())} " )
318351
319- # Handle plain text as a default case
320- return "unknown" , {"input" : action }
352+ param_name = VALID_ACTIONS [tool_name ]["param" ]
353+ return tool_name , {param_name : input_text }
354+
355+ else :
356+ raise ValueError ("Invalid action format. Must be one of:\n " +
357+ "1. 'Action: tool_name with Action Input: input'\n " +
358+ "2. JSON format with tool_name and parameters\n " +
359+ "3. 'tool_name: input'" )
321360
322361 except Exception as e :
323- print (f"Error parsing action: { e } " )
324- return "unknown" , {"input" : action }
362+ raise ValueError (f"Error parsing action: { str (e )} " )
325363
326364 def _process_action (self , env_id : str , action_type : str , action_input : Dict [str , Any ]) -> Tuple [str , float , bool ]:
327365 """
@@ -495,6 +533,7 @@ def _format_new_task(self, env_id: str):
495533 observation += "\n Use tools in the format: Action: tool_name with Action Input: your_input\n "
496534 observation += "\n Give me one action."
497535
536+
498537 return observation
499538
500539 def _format_observation (self , env_id : str ):
0 commit comments