universal-mcp 0.1.24rc2__py3-none-any.whl → 0.1.24rc4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. universal_mcp/agentr/README.md +201 -0
  2. universal_mcp/agentr/__init__.py +6 -0
  3. universal_mcp/agentr/agentr.py +30 -0
  4. universal_mcp/{utils/agentr.py → agentr/client.py} +19 -3
  5. universal_mcp/agentr/integration.py +104 -0
  6. universal_mcp/agentr/registry.py +91 -0
  7. universal_mcp/agentr/server.py +51 -0
  8. universal_mcp/agents/__init__.py +6 -0
  9. universal_mcp/agents/auto.py +576 -0
  10. universal_mcp/agents/base.py +88 -0
  11. universal_mcp/agents/cli.py +27 -0
  12. universal_mcp/agents/codeact/__init__.py +243 -0
  13. universal_mcp/agents/codeact/sandbox.py +27 -0
  14. universal_mcp/agents/codeact/test.py +15 -0
  15. universal_mcp/agents/codeact/utils.py +61 -0
  16. universal_mcp/agents/hil.py +104 -0
  17. universal_mcp/agents/llm.py +10 -0
  18. universal_mcp/agents/react.py +58 -0
  19. universal_mcp/agents/simple.py +40 -0
  20. universal_mcp/agents/utils.py +111 -0
  21. universal_mcp/analytics.py +5 -7
  22. universal_mcp/applications/__init__.py +42 -75
  23. universal_mcp/applications/application.py +1 -1
  24. universal_mcp/applications/sample/app.py +245 -0
  25. universal_mcp/cli.py +10 -3
  26. universal_mcp/config.py +33 -7
  27. universal_mcp/exceptions.py +4 -0
  28. universal_mcp/integrations/__init__.py +0 -15
  29. universal_mcp/integrations/integration.py +9 -91
  30. universal_mcp/servers/__init__.py +2 -14
  31. universal_mcp/servers/server.py +10 -51
  32. universal_mcp/tools/__init__.py +3 -0
  33. universal_mcp/tools/adapters.py +20 -11
  34. universal_mcp/tools/manager.py +29 -56
  35. universal_mcp/tools/registry.py +41 -0
  36. universal_mcp/tools/tools.py +22 -1
  37. universal_mcp/types.py +10 -0
  38. universal_mcp/utils/common.py +245 -0
  39. universal_mcp/utils/openapi/api_generator.py +46 -18
  40. universal_mcp/utils/openapi/cli.py +445 -19
  41. universal_mcp/utils/openapi/openapi.py +284 -21
  42. universal_mcp/utils/openapi/postprocessor.py +275 -0
  43. universal_mcp/utils/openapi/preprocessor.py +1 -1
  44. universal_mcp/utils/openapi/test_generator.py +287 -0
  45. universal_mcp/utils/prompts.py +188 -341
  46. universal_mcp/utils/testing.py +190 -2
  47. {universal_mcp-0.1.24rc2.dist-info → universal_mcp-0.1.24rc4.dist-info}/METADATA +17 -3
  48. universal_mcp-0.1.24rc4.dist-info/RECORD +71 -0
  49. universal_mcp/applications/sample_tool_app.py +0 -80
  50. universal_mcp/client/agents/__init__.py +0 -4
  51. universal_mcp/client/agents/base.py +0 -38
  52. universal_mcp/client/agents/llm.py +0 -115
  53. universal_mcp/client/agents/react.py +0 -67
  54. universal_mcp/client/cli.py +0 -181
  55. universal_mcp-0.1.24rc2.dist-info/RECORD +0 -53
  56. {universal_mcp-0.1.24rc2.dist-info → universal_mcp-0.1.24rc4.dist-info}/WHEEL +0 -0
  57. {universal_mcp-0.1.24rc2.dist-info → universal_mcp-0.1.24rc4.dist-info}/entry_points.txt +0 -0
  58. {universal_mcp-0.1.24rc2.dist-info → universal_mcp-0.1.24rc4.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,201 @@
1
+ # AgentR Python SDK
2
+
3
+ The official Python SDK for the AgentR platform, a component of the Universal MCP framework.
4
+ Currently in beta, breaking changes are expected.
5
+
6
+ The AgentR Python SDK provides convenient access to the AgentR REST API from any Python 3.10+
7
+ application, allowing for dynamic loading and management of tools and integrations.
8
+
9
+ ## Installation
10
+ ```bash
11
+ pip install universal-mcp
12
+ ```
13
+
14
+ ## Usage
15
+ The AgentR platform is designed to seamlessly integrate a wide array of tools into your agentic applications. The primary entry point for this is the `Agentr` class, which provides a high-level interface for loading and listing tools.
16
+
17
+ ### High-Level Client (`Agentr`)
18
+ This is the recommended way to get started. It abstracts away the details of the registry and tool management.
19
+
20
+ ```python
21
+ import os
22
+ from universal_mcp.agentr import Agentr
23
+ from universal_mcp.tools import ToolFormat
24
+
25
+ # Initialize the main client
26
+ # It reads from environment variables by default (AGENTR_API_KEY, AGENTR_BASE_URL)
27
+ agentr = Agentr(
28
+ api_key=os.environ.get("AGENTR_API_KEY")
29
+ )
30
+
31
+ # Load specific tools from the AgentR server into the tool manager
32
+ agentr.load_tools(["reddit_search_subreddits", "google_drive_list_files"])
33
+
34
+ # List the tools that are now loaded and ready to be used
35
+ # You can specify a format compatible with your LLM (e.g., OPENAI)
36
+ tools = agentr.list_tools(format=ToolFormat.OPENAI)
37
+ print(tools)
38
+ ```
39
+
40
+ ### Low-Level API
41
+
42
+ For more granular control over the AgentR platform, you can use the lower-level components directly.
43
+
44
+ ### AgentrClient
45
+ The `AgentrClient` provides direct access to the AgentR REST API endpoints.
46
+
47
+ #### Methods
48
+ ```python
49
+ import os
50
+ from universal_mcp.agentr import AgentrClient
51
+ from universal_mcp.exceptions import NotAuthorizedError
52
+
53
+ # Initialize the low-level client
54
+ client = AgentrClient(
55
+ api_key=os.environ.get("AGENTR_API_KEY")
56
+ )
57
+
58
+ # Fetch all available applications from the AgentR server
59
+ apps = client.fetch_apps()
60
+ print(apps)
61
+
62
+ # Get credentials for a specific integration
63
+ # This will raise a NotAuthorizedError if the user needs to authenticate
64
+ try:
65
+ credentials = client.get_credentials("reddit")
66
+ print("Reddit credentials found.")
67
+ except NotAuthorizedError as e:
68
+ print(e) # "Please ask the user to visit the following url to authorize..."
69
+
70
+ # Example of fetching a single app and its actions
71
+ if apps:
72
+ app_id = apps[0].id # Assuming AppConfig has an 'id' attribute
73
+
74
+ # Fetch a single app's configuration
75
+ app_config = client.fetch_app(app_id)
76
+ print(f"Fetched config for app {app_id}:", app_config)
77
+
78
+ # List all actions for that app
79
+ actions = client.list_actions(app_id)
80
+ print(f"Actions for app {app_id}:", actions)
81
+
82
+ # List all apps (returns raw JSON data)
83
+ all_apps = client.list_all_apps()
84
+ print("All available apps:", all_apps)
85
+ ```
86
+
87
+ ### AgentrIntegration
88
+ This class handles the authentication and authorization flow for a single integration (e.g., "reddit"). It's used under the hood by applications to acquire credentials.
89
+
90
+ #### Methods
91
+ ```python
92
+ from universal_mcp.agentr import AgentrIntegration, AgentrClient
93
+ from universal_mcp.exceptions import NotAuthorizedError
94
+
95
+ client = AgentrClient()
96
+
97
+ # Create an integration for a specific service
98
+ reddit_integration = AgentrIntegration(name="reddit", client=client)
99
+
100
+ # If credentials are not present, this will raise NotAuthorizedError
101
+ try:
102
+ creds = reddit_integration.credentials
103
+ print("Successfully retrieved credentials.")
104
+ except NotAuthorizedError:
105
+ # Get the URL to send the user to for authentication
106
+ auth_url = reddit_integration.authorize()
107
+ print(f"Please authorize here: {auth_url}")
108
+
109
+ # You can also use the get_credentials() method
110
+ try:
111
+ creds = reddit_integration.get_credentials()
112
+ print("Successfully retrieved credentials again.")
113
+ except NotAuthorizedError:
114
+ print("Still not authorized.")
115
+ ```
116
+
117
+ ### AgentrRegistry
118
+ The registry is responsible for discovering which tools are available on the AgentR platform.
119
+
120
+ #### Methods
121
+ ```python
122
+ import asyncio
123
+ from universal_mcp.agentr import AgentrRegistry, AgentrClient
124
+
125
+ client = AgentrClient()
126
+ registry = AgentrRegistry(client=client)
127
+
128
+ async def main():
129
+ # List all apps available on the AgentR platform
130
+ available_apps = await registry.list_apps()
131
+ print(available_apps)
132
+
133
+ if available_apps:
134
+ app_id = available_apps[0]['id']
135
+ # Get details for a specific app
136
+ app_details = await registry.get_app_details(app_id)
137
+ print(f"Details for {app_id}:", app_details)
138
+
139
+ # The load_tools method is used internally by the high-level Agentr client
140
+ # but can be called directly if needed.
141
+ # from universal_mcp.tools import ToolManager
142
+ # tool_manager = ToolManager()
143
+ # registry.load_tools(["reddit_search_subreddits"], tool_manager)
144
+ # print(tool_manager.list_tools())
145
+
146
+
147
+ if __name__ == "__main__":
148
+ asyncio.run(main())
149
+ ```
150
+
151
+ ### AgentrServer
152
+ For server-side deployments, `AgentrServer` can be used to load all configured applications and their tools from an AgentR instance on startup.
153
+
154
+ ```python
155
+ from universal_mcp.config import ServerConfig
156
+ from universal_mcp.agentr.server import AgentrServer
157
+
158
+ # Configuration for the server
159
+ config = ServerConfig(
160
+ type="agentr",
161
+ api_key="your-agentr-api-key"
162
+ )
163
+
164
+ # The server will automatically fetch and register all tools on initialization
165
+ server = AgentrServer(config=config)
166
+
167
+ # The tool manager is now populated with tools from the AgentR instance
168
+ tool_manager = server.tool_manager
169
+ print(tool_manager.list_tools())
170
+ ```
171
+
172
+ ## Executing Tools
173
+ Once tools are loaded, you can execute them using the `call_tool` method on the `ToolManager` instance, which is available via `agentr.manager`.
174
+
175
+ ```python
176
+ import os
177
+ import asyncio
178
+ from universal_mcp.agentr import Agentr
179
+
180
+ async def main():
181
+ # 1. Initialize Agentr
182
+ agentr = Agentr(api_key=os.environ.get("AGENTR_API_KEY"))
183
+
184
+ # 2. Load the tool(s) you want to use
185
+ tool_name = "reddit_search_subreddits"
186
+ agentr.load_tools([tool_name])
187
+
188
+ # 3. Execute the tool using the tool manager
189
+ try:
190
+ # Note the 'await' since call_tool is an async method
191
+ result = await agentr.manager.call_tool(
192
+ name=tool_name,
193
+ arguments={"query": "elon musk", "limit": 5, "sort": "relevance"}
194
+ )
195
+ print("Execution result:", result)
196
+ except Exception as e:
197
+ print(f"An error occurred: {e}")
198
+
199
+ if __name__ == "__main__":
200
+ asyncio.run(main())
201
+ ```
@@ -0,0 +1,6 @@
1
+ from .agentr import Agentr
2
+ from .client import AgentrClient
3
+ from .integration import AgentrIntegration
4
+ from .registry import AgentrRegistry
5
+
6
+ __all__ = ["Agentr", "AgentrClient", "AgentrRegistry", "AgentrIntegration"]
@@ -0,0 +1,30 @@
1
+ import os
2
+
3
+ from universal_mcp.tools import Tool, ToolFormat, ToolManager
4
+
5
+ from .client import AgentrClient
6
+ from .registry import AgentrRegistry
7
+
8
+
9
+ class Agentr:
10
+ def __init__(
11
+ self,
12
+ api_key: str | None = None,
13
+ base_url: str | None = None,
14
+ registry: AgentrRegistry | None = None,
15
+ format: ToolFormat | None = None,
16
+ manager: ToolManager | None = None,
17
+ ):
18
+ self.api_key = api_key or os.getenv("AGENTR_API_KEY")
19
+ self.base_url = base_url or os.getenv("AGENTR_BASE_URL")
20
+ self.client = AgentrClient(api_key=self.api_key, base_url=self.base_url)
21
+ self.registry = registry or AgentrRegistry(client=self.client)
22
+ self.format = format or ToolFormat.NATIVE
23
+ self.manager = manager or ToolManager()
24
+
25
+ def load_tools(self, tool_names: list[str]) -> None:
26
+ self.registry.load_tools(tool_names, self.manager)
27
+ return
28
+
29
+ def list_tools(self, format: ToolFormat | None = None) -> list[Tool]:
30
+ return self.manager.list_tools(format=format or self.format)
@@ -82,6 +82,22 @@ class AgentrClient:
82
82
  data = response.json()
83
83
  return [AppConfig.model_validate(app) for app in data]
84
84
 
85
+ def fetch_app(self, app_id: str) -> dict:
86
+ """Fetch a specific app from AgentR API.
87
+
88
+ Args:
89
+ app_id (str): ID of the app to fetch
90
+
91
+ Returns:
92
+ dict: App configuration data
93
+
94
+ Raises:
95
+ httpx.HTTPError: If API request fails
96
+ """
97
+ response = self.client.get(f"/apps/{app_id}/")
98
+ response.raise_for_status()
99
+ return response.json()
100
+
85
101
  def list_all_apps(self) -> list:
86
102
  """List all apps from AgentR API.
87
103
 
@@ -92,16 +108,16 @@ class AgentrClient:
92
108
  response.raise_for_status()
93
109
  return response.json()
94
110
 
95
- def list_actions(self, app_name: str):
111
+ def list_actions(self, app_id: str):
96
112
  """List actions for an app.
97
113
 
98
114
  Args:
99
- app_name (str): Name of the app to list actions for
115
+ app_id (str): ID of the app to list actions for
100
116
 
101
117
  Returns:
102
118
  List of action configurations
103
119
  """
104
120
 
105
- response = self.client.get(f"/apps/{app_name}/actions/")
121
+ response = self.client.get(f"/apps/{app_id}/actions/")
106
122
  response.raise_for_status()
107
123
  return response.json()
@@ -0,0 +1,104 @@
1
+ from universal_mcp.integrations.integration import Integration
2
+
3
+ from .client import AgentrClient
4
+
5
+
6
+ class AgentrIntegration(Integration):
7
+ """Manages authentication and authorization via the AgentR platform.
8
+
9
+ This integration uses an `AgentrClient` to interact with the AgentR API
10
+ for operations like retrieving authorization URLs and fetching stored
11
+ credentials. It simplifies integration with services supported by AgentR.
12
+
13
+ Attributes:
14
+ name (str): Name of the integration (e.g., "github", "google").
15
+ store (BaseStore): Store, typically not used directly by this class
16
+ as AgentR manages the primary credential storage.
17
+ client (AgentrClient): Client for communicating with the AgentR API.
18
+ _credentials (dict | None): Cached credentials.
19
+ """
20
+
21
+ def __init__(
22
+ self,
23
+ name: str,
24
+ client: AgentrClient | None = None,
25
+ api_key: str | None = None,
26
+ base_url: str | None = None,
27
+ **kwargs,
28
+ ):
29
+ """Initializes the AgentRIntegration.
30
+
31
+ Args:
32
+ name (str): The name of the service integration as configured on
33
+ the AgentR platform (e.g., "github").
34
+ client (AgentrClient | None, optional): The AgentR client. If not provided,
35
+ a new `AgentrClient` will be created.
36
+ api_key (str | None, optional): API key for AgentR. If not provided,
37
+ will be loaded from environment variables.
38
+ base_url (str | None, optional): Base URL for AgentR API. If not provided,
39
+ will be loaded from environment variables.
40
+ **kwargs: Additional arguments passed to the parent `Integration`.
41
+ """
42
+ super().__init__(name, **kwargs)
43
+ self.type = "agentr"
44
+ self.client = client or AgentrClient(api_key=api_key, base_url=base_url)
45
+ self._credentials = None
46
+
47
+ def set_credentials(self, credentials: dict[str, str] | None = None) -> str:
48
+ """Not used for direct credential setting; initiates authorization instead.
49
+
50
+ For AgentR integrations, credentials are set via the AgentR platform's
51
+ OAuth flow. This method effectively redirects to the `authorize` flow.
52
+
53
+ Args:
54
+ credentials (dict | None, optional): Not used by this implementation.
55
+
56
+ Returns:
57
+ str: The authorization URL or message from the `authorize()` method.
58
+ """
59
+ raise NotImplementedError("AgentR integrations do not support direct credential setting")
60
+
61
+ @property
62
+ def credentials(self):
63
+ """Retrieves credentials from the AgentR API, with caching.
64
+
65
+ If credentials are not cached locally (in `_credentials`), this property
66
+ fetches them from the AgentR platform using `self.client.get_credentials`.
67
+
68
+ Returns:
69
+ dict: The credentials dictionary obtained from AgentR.
70
+
71
+ Raises:
72
+ NotAuthorizedError: If credentials are not found (e.g., 404 from AgentR).
73
+ httpx.HTTPStatusError: For other API errors from AgentR.
74
+ """
75
+ if self._credentials is not None:
76
+ return self._credentials
77
+ self._credentials = self.client.get_credentials(self.name)
78
+ return self._credentials
79
+
80
+ def get_credentials(self):
81
+ """Retrieves credentials from the AgentR API. Alias for `credentials` property.
82
+
83
+ Returns:
84
+ dict: The credentials dictionary obtained from AgentR.
85
+
86
+ Raises:
87
+ NotAuthorizedError: If credentials are not found.
88
+ httpx.HTTPStatusError: For other API errors.
89
+ """
90
+ return self.credentials
91
+
92
+ def authorize(self) -> str:
93
+ """Retrieves the authorization URL from the AgentR platform.
94
+
95
+ This URL should be presented to the user to initiate the OAuth flow
96
+ managed by AgentR for the service associated with `self.name`.
97
+
98
+ Returns:
99
+ str: The authorization URL.
100
+
101
+ Raises:
102
+ httpx.HTTPStatusError: If the API request to AgentR fails.
103
+ """
104
+ return self.client.get_authorization_url(self.name)
@@ -0,0 +1,91 @@
1
+ from loguru import logger
2
+
3
+ from universal_mcp.agentr.client import AgentrClient
4
+ from universal_mcp.applications import app_from_slug
5
+ from universal_mcp.tools.manager import ToolManager, _get_app_and_tool_name
6
+ from universal_mcp.tools.registry import ToolRegistry
7
+
8
+ from .integration import AgentrIntegration
9
+
10
+
11
+ class AgentrRegistry(ToolRegistry):
12
+ """Platform manager implementation for AgentR platform."""
13
+
14
+ def __init__(self, client: AgentrClient | None = None, **kwargs):
15
+ """Initialize the AgentR platform manager."""
16
+
17
+ self.client = client or AgentrClient(**kwargs)
18
+ logger.debug("AgentrRegistry initialized successfully")
19
+
20
+ async def list_apps(self) -> list[dict[str, str]]:
21
+ """Get list of available apps from AgentR.
22
+
23
+ Returns:
24
+ List of app dictionaries with id, name, description, and available fields
25
+ """
26
+ try:
27
+ all_apps = await self.client.list_all_apps()
28
+ available_apps = [
29
+ {"id": app["id"], "name": app["name"], "description": app.get("description", "")}
30
+ for app in all_apps
31
+ if app.get("available", False)
32
+ ]
33
+ logger.info(f"Found {len(available_apps)} available apps from AgentR")
34
+ return available_apps
35
+ except Exception as e:
36
+ logger.error(f"Error fetching apps from AgentR: {e}")
37
+ return []
38
+
39
+ async def get_app_details(self, app_id: str) -> dict[str, str]:
40
+ """Get detailed information about a specific app from AgentR.
41
+
42
+ Args:
43
+ app_id: The ID of the app to get details for
44
+
45
+ Returns:
46
+ Dictionary containing app details
47
+ """
48
+ try:
49
+ app_info = await self.client.fetch_app(app_id)
50
+ return {
51
+ "id": app_info.get("id"),
52
+ "name": app_info.get("name"),
53
+ "description": app_info.get("description"),
54
+ "category": app_info.get("category"),
55
+ "available": app_info.get("available", True),
56
+ }
57
+ except Exception as e:
58
+ logger.error(f"Error getting details for app {app_id}: {e}")
59
+ return {
60
+ "id": app_id,
61
+ "name": app_id,
62
+ "description": "Error loading details",
63
+ "category": "Unknown",
64
+ "available": True,
65
+ }
66
+
67
+ def load_tools(self, tools: list[str] | None, tool_manager: ToolManager) -> None:
68
+ """Load tools from AgentR and register them as tools.
69
+
70
+ Args:
71
+ tools: The list of tools to load ( prefixed with app name )
72
+ tool_manager: The tool manager to register tools with
73
+ """
74
+ if not tools:
75
+ return
76
+ logger.info(f"Loading all actions for app: {tools}")
77
+ # Group all tools by app_name, tools
78
+ tools_by_app = {}
79
+ for tool_name in tools:
80
+ app_name, _ = _get_app_and_tool_name(tool_name)
81
+ if app_name not in tools_by_app:
82
+ tools_by_app[app_name] = []
83
+ tools_by_app[app_name].append(tool_name)
84
+
85
+ for app_name, tool_names in tools_by_app.items():
86
+ app = app_from_slug(app_name)
87
+ integration = AgentrIntegration(name=app_name)
88
+ # TODO: Import with name param, some apps are written incorrectly and hence passing name fails
89
+ app_instance = app(integration=integration)
90
+ tool_manager.register_tools_from_app(app_instance, tool_names=tool_names)
91
+ return
@@ -0,0 +1,51 @@
1
+ from loguru import logger
2
+
3
+ from universal_mcp.applications import app_from_config
4
+ from universal_mcp.config import AppConfig, ServerConfig
5
+ from universal_mcp.servers.server import BaseServer
6
+ from universal_mcp.tools import ToolManager
7
+
8
+ from .client import AgentrClient
9
+ from .integration import AgentrIntegration
10
+
11
+
12
+ def load_from_agentr_server(client: AgentrClient, tool_manager: ToolManager) -> None:
13
+ """Load apps from AgentR server and register their tools."""
14
+ try:
15
+ apps = client.fetch_apps()
16
+ for app in apps:
17
+ try:
18
+ app_config = AppConfig.model_validate(app)
19
+ integration = (
20
+ AgentrIntegration(name=app_config.integration.name, client=client) # type: ignore
21
+ if app_config.integration
22
+ else None
23
+ )
24
+ app_instance = app_from_config(app_config)(integration=integration)
25
+ tool_manager.register_tools_from_app(app_instance, app_config.actions)
26
+ logger.info(f"Loaded app from AgentR: {app_config.name}")
27
+ except Exception as e:
28
+ logger.error(f"Failed to load app from AgentR: {e}", exc_info=True)
29
+ except Exception as e:
30
+ logger.error(f"Failed to fetch apps from AgentR: {e}", exc_info=True)
31
+ raise
32
+
33
+
34
+ class AgentrServer(BaseServer):
35
+ """Server that loads apps from AgentR server."""
36
+
37
+ def __init__(self, config: ServerConfig, **kwargs):
38
+ super().__init__(config, **kwargs)
39
+ self._tools_loaded = False
40
+ self.api_key = config.api_key.get_secret_value() if config.api_key else None
41
+ self.base_url = config.base_url
42
+ self.client = AgentrClient(api_key=self.api_key, base_url=self.base_url)
43
+
44
+ @property
45
+ def tool_manager(self) -> ToolManager:
46
+ if self._tool_manager is None:
47
+ self._tool_manager = ToolManager(warn_on_duplicate_tools=True)
48
+ if not self._tools_loaded:
49
+ load_from_agentr_server(self.client, self._tool_manager)
50
+ self._tools_loaded = True
51
+ return self._tool_manager
@@ -0,0 +1,6 @@
1
+ from .auto import AutoAgent
2
+ from .base import BaseAgent
3
+ from .react import ReactAgent
4
+ from .simple import SimpleAgent
5
+
6
+ __all__ = ["BaseAgent", "ReactAgent", "SimpleAgent", "AutoAgent"]