diff --git a/src/core/env_server/interfaces.py b/src/core/env_server/interfaces.py index caa2d76d..fbb783e7 100644 --- a/src/core/env_server/interfaces.py +++ b/src/core/env_server/interfaces.py @@ -5,9 +5,9 @@ # LICENSE file in the root directory of this source tree. from abc import ABC, abstractmethod -from typing import Any, Protocol, TypedDict +from typing import Any, List, Protocol, TypedDict -from .types import Action, Observation, State +from .types import Action, Observation, State, ToolDefinition class Message(TypedDict): @@ -116,3 +116,15 @@ def _apply_transform(self, observation: Observation) -> Observation: if self.transform is not None: return self.transform(observation) return observation + + def actions(self) -> List[ToolDefinition]: + """Return list of available actions in this environment. + + This method enables action discovery for any environment type. + Actions can represent tools, code execution, game moves, navigation, + or any domain-specific operations. + + For backward compatibility, environments can return an empty list, + though implementing this method is strongly encouraged. + """ + return [] diff --git a/src/core/env_server/types.py b/src/core/env_server/types.py index 70da9f3c..5efda9f0 100644 --- a/src/core/env_server/types.py +++ b/src/core/env_server/types.py @@ -19,6 +19,28 @@ class Action: metadata: Dict[str, Any] = field(default_factory=dict) +@dataclass(kw_only=True) +class ToolCallAction(Action): + """Action representing a named operation with typed parameters. + + Inspired by MCP's tool-calling pattern, but generalized to represent + ANY environment action - not just tool calls or code execution. + + Examples: + - Tool calls: tool_name="search_web", parameters={"query": "..."} + - Code execution: tool_name="execute_code", parameters={"code": "..."} + - Game moves: tool_name="move_piece", parameters={"from": "e2", "to": "e4"} + - Navigation: tool_name="go_north", parameters={} + - Configuration: tool_name="set_timeout", parameters={"seconds": 30} + + This is the standard action type for all OpenEnv environments. + Environments dispatch based on tool_name to handle different action types. + """ + + tool_name: str + parameters: Dict[str, Any] = field(default_factory=dict) + + @dataclass(kw_only=True) class Observation: """Base class for all environment observations.""" @@ -48,10 +70,60 @@ class CodeExecResult: @dataclass class EnvironmentMetadata: """Metadata about an environment for documentation and UI purposes.""" - + name: str description: str readme_content: Optional[str] = None version: Optional[str] = None author: Optional[str] = None documentation_url: Optional[str] = None + + +@dataclass +class ToolParameter: + """Definition of a tool parameter.""" + + name: str + type: str # JSON Schema type: "string", "number", "boolean", "object", "array" + description: str + required: bool = True + default: Any = None + + +@dataclass +class ToolDefinition: + """Specification of an action that can be taken in an environment. + + Inspired by MCP's tool definition format and compatible with LLM tool-calling + APIs (Claude, OpenAI, etc.), but represents ANY action type - not just tools. + + This can describe: + - External tool calls (search_web, read_file) + - Code execution (execute_python, run_bash) + - Game actions (move_piece, attack, defend) + - Navigation commands (go_north, turn_left) + - Configuration changes (set_parameter, update_config) + - Any domain-specific action + """ + + name: str + description: str + parameters: List["ToolParameter"] + + def to_json_schema(self) -> Dict[str, Any]: + """Convert to JSON Schema format for LLM tool calling.""" + return { + "name": self.name, + "description": self.description, + "input_schema": { + "type": "object", + "properties": { + p.name: { + "type": p.type, + "description": p.description, + } + for p in self.parameters + }, + "required": [p.name for p in self.parameters if p.required], + }, + }