universal-mcp-agents 0.1.15__py3-none-any.whl → 0.1.16__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.
Potentially problematic release.
This version of universal-mcp-agents might be problematic. Click here for more details.
- universal_mcp/agents/codeact0/llm_tool.py +1 -0
- universal_mcp/agents/codeact0/playbook_agent.py +52 -41
- universal_mcp/agents/codeact0/state.py +2 -0
- universal_mcp/agents/codeact0/tools.py +12 -6
- universal_mcp/agents/codeact0/utils.py +1 -1
- universal_mcp/agents/utils.py +18 -0
- {universal_mcp_agents-0.1.15.dist-info → universal_mcp_agents-0.1.16.dist-info}/METADATA +1 -1
- {universal_mcp_agents-0.1.15.dist-info → universal_mcp_agents-0.1.16.dist-info}/RECORD +9 -9
- {universal_mcp_agents-0.1.15.dist-info → universal_mcp_agents-0.1.16.dist-info}/WHEEL +0 -0
|
@@ -25,7 +25,7 @@ from universal_mcp.agents.codeact0.state import CodeActState
|
|
|
25
25
|
from universal_mcp.agents.codeact0.tools import create_meta_tools, enter_playbook_mode, exit_playbook_mode, get_valid_tools
|
|
26
26
|
from universal_mcp.agents.codeact0.utils import inject_context, smart_truncate
|
|
27
27
|
from universal_mcp.agents.llm import load_chat_model
|
|
28
|
-
from universal_mcp.agents.utils import filter_retry_on, get_message_text
|
|
28
|
+
from universal_mcp.agents.utils import filter_retry_on, get_message_text, convert_tool_ids_to_dict
|
|
29
29
|
|
|
30
30
|
PLAYBOOK_PLANNING_PROMPT = """Now, you are tasked with creating a reusable playbook from the user's previous workflow.
|
|
31
31
|
|
|
@@ -69,6 +69,7 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
69
69
|
memory: BaseCheckpointSaver | None = None,
|
|
70
70
|
tools: ToolConfig | None = None,
|
|
71
71
|
registry: ToolRegistry | None = None,
|
|
72
|
+
playbook_registry: object | None = None,
|
|
72
73
|
sandbox_timeout: int = 20,
|
|
73
74
|
**kwargs,
|
|
74
75
|
):
|
|
@@ -82,33 +83,33 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
82
83
|
self.model_instance = load_chat_model(model, thinking=True)
|
|
83
84
|
self.tools_config = tools or []
|
|
84
85
|
self.registry = registry
|
|
86
|
+
self.playbook_registry = playbook_registry
|
|
85
87
|
self.eval_fn = eval_unsafe
|
|
86
88
|
self.sandbox_timeout = sandbox_timeout
|
|
87
89
|
self.processed_tools: list[StructuredTool | Callable] = []
|
|
88
90
|
|
|
89
91
|
async def _build_graph(self):
|
|
90
|
-
self.exported_tools = []
|
|
91
|
-
if self.tools_config:
|
|
92
|
-
# Convert dict format to list format if needed
|
|
93
|
-
if isinstance(self.tools_config, dict):
|
|
94
|
-
self.tools_config = [
|
|
95
|
-
f"{provider}__{tool}"
|
|
96
|
-
for provider, tools in self.tools_config.items()
|
|
97
|
-
for tool in tools
|
|
98
|
-
]
|
|
99
|
-
if not self.registry:
|
|
100
|
-
raise ValueError("Tools are configured but no registry is provided")
|
|
101
|
-
# Langchain tools are fine
|
|
102
|
-
self.exported_tools = await self.registry.export_tools(self.tools_config, ToolFormat.LANGCHAIN)
|
|
103
92
|
meta_tools = create_meta_tools(self.registry)
|
|
104
|
-
await self.registry.export_tools(["exa__search_with_filters"], ToolFormat.LANGCHAIN)
|
|
105
93
|
additional_tools = [smart_print, data_extractor, ai_classify, call_llm, meta_tools["web_search"]]
|
|
106
94
|
self.additional_tools = [t if isinstance(t, StructuredTool) else create_tool(t) for t in additional_tools]
|
|
107
|
-
|
|
108
|
-
self.exported_tools
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
95
|
+
async def call_model(state: CodeActState) -> Command[Literal["sandbox", "execute_tools"]]:
|
|
96
|
+
self.exported_tools = []
|
|
97
|
+
if self.tools_config:
|
|
98
|
+
# Convert dict format to list format if needed
|
|
99
|
+
if isinstance(self.tools_config, dict):
|
|
100
|
+
self.tools_config = [
|
|
101
|
+
f"{provider}__{tool}"
|
|
102
|
+
for provider, tools in self.tools_config.items()
|
|
103
|
+
for tool in tools
|
|
104
|
+
]
|
|
105
|
+
if not self.registry:
|
|
106
|
+
raise ValueError("Tools are configured but no registry is provided")
|
|
107
|
+
# Langchain tools are fine
|
|
108
|
+
self.tools_config.extend(state.get('selected_tool_ids',[]))
|
|
109
|
+
self.exported_tools = await self.registry.export_tools(self.tools_config, ToolFormat.LANGCHAIN)
|
|
110
|
+
self.final_instructions, self.tools_context = create_default_prompt(
|
|
111
|
+
self.exported_tools, self.additional_tools, self.instructions
|
|
112
|
+
)
|
|
112
113
|
messages = [{"role": "system", "content": self.final_instructions}] + state["messages"]
|
|
113
114
|
|
|
114
115
|
# Run the model and potentially loop for reflection
|
|
@@ -262,16 +263,16 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
262
263
|
# Extract plan from response text between triple backticks
|
|
263
264
|
plan_match = re.search(r'```(.*?)```', response_text, re.DOTALL)
|
|
264
265
|
if plan_match:
|
|
265
|
-
|
|
266
|
+
plan = plan_match.group(1).strip()
|
|
266
267
|
else:
|
|
267
|
-
|
|
268
|
-
return Command(update={"messages": [response], "playbook_mode": "confirming"})
|
|
268
|
+
plan = response_text.strip()
|
|
269
|
+
return Command(update={"messages": [response], "playbook_mode": "confirming", "plan": plan})
|
|
269
270
|
|
|
270
271
|
|
|
271
272
|
elif playbook_mode == "confirming":
|
|
272
273
|
confirmation_instructions = self.instructions + PLAYBOOK_CONFIRMING_PROMPT
|
|
273
274
|
messages = [{"role": "system", "content": confirmation_instructions}] + state["messages"]
|
|
274
|
-
response = self.model_instance.invoke(messages)
|
|
275
|
+
response = self.model_instance.invoke(messages, stream=False)
|
|
275
276
|
response = get_message_text(response)
|
|
276
277
|
if "true" in response.lower():
|
|
277
278
|
return Command(goto="playbook", update={"playbook_mode": "generating"})
|
|
@@ -296,24 +297,34 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
296
297
|
else:
|
|
297
298
|
function_name = "generated_playbook"
|
|
298
299
|
|
|
300
|
+
# Save or update an Agent using the helper registry
|
|
301
|
+
saved_note = ""
|
|
299
302
|
try:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
303
|
+
if not self.playbook_registry:
|
|
304
|
+
raise ValueError("Playbook registry is not configured")
|
|
305
|
+
|
|
306
|
+
# Build instructions payload embedding the plan and function code
|
|
307
|
+
instructions_payload = {
|
|
308
|
+
"playbookPlan": state["plan"],
|
|
309
|
+
"playbookScript": {
|
|
310
|
+
"name": function_name,
|
|
311
|
+
"code": func_code,
|
|
312
|
+
},
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
# Convert tool ids list to dict
|
|
316
|
+
tool_dict = convert_tool_ids_to_dict(state["selected_tool_ids"])
|
|
317
|
+
|
|
318
|
+
res = self.playbook_registry.create_agent(
|
|
319
|
+
name=function_name,
|
|
320
|
+
description=f"Generated playbook: {function_name}",
|
|
321
|
+
instructions=instructions_payload,
|
|
322
|
+
tools=tool_dict,
|
|
323
|
+
visibility="private",
|
|
324
|
+
)
|
|
325
|
+
saved_note = f"Successfully created your playbook! Check it out here: [View Playbook](https://wingmen.info/agents/{res.id})"
|
|
315
326
|
except Exception as e:
|
|
316
|
-
saved_note = f"Failed to save playbook
|
|
327
|
+
saved_note = f"Failed to save generated playbook as Agent '{function_name}': {e}"
|
|
317
328
|
|
|
318
329
|
# Mock tool call for exit_playbook_mode (for testing/demonstration)
|
|
319
330
|
mock_exit_tool_call = {
|
|
@@ -322,7 +333,7 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
322
333
|
"id": "mock_exit_playbook_123"
|
|
323
334
|
}
|
|
324
335
|
mock_assistant_message = AIMessage(
|
|
325
|
-
content=
|
|
336
|
+
content=saved_note,
|
|
326
337
|
tool_calls=[mock_exit_tool_call]
|
|
327
338
|
)
|
|
328
339
|
|
|
@@ -4,6 +4,7 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
from langchain_core.tools import tool
|
|
6
6
|
from universal_mcp.tools.registry import ToolRegistry
|
|
7
|
+
from universal_mcp.types import ToolFormat
|
|
7
8
|
|
|
8
9
|
MAX_LENGHT=100
|
|
9
10
|
|
|
@@ -30,8 +31,8 @@ def create_meta_tools(tool_registry: ToolRegistry) -> dict[str, Any]:
|
|
|
30
31
|
connections = await tool_registry.list_connected_apps()
|
|
31
32
|
connected_apps = {connection["app_id"] for connection in connections}
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
app_tools = defaultdict(set)
|
|
35
|
+
MAX_LENGTH = 20
|
|
35
36
|
|
|
36
37
|
# Process all queries concurrently
|
|
37
38
|
search_tasks = []
|
|
@@ -40,20 +41,24 @@ def create_meta_tools(tool_registry: ToolRegistry) -> dict[str, Any]:
|
|
|
40
41
|
|
|
41
42
|
query_results = await asyncio.gather(*search_tasks)
|
|
42
43
|
|
|
43
|
-
# Aggregate results with limit per app
|
|
44
|
+
# Aggregate results with limit per app and automatic deduplication
|
|
44
45
|
for tools_list in query_results:
|
|
45
46
|
for tool in tools_list:
|
|
46
47
|
app = tool["id"].split("__")[0]
|
|
47
|
-
|
|
48
|
+
tool_id = tool["id"]
|
|
49
|
+
|
|
50
|
+
# Check if within limit and add to set (automatically deduplicates)
|
|
51
|
+
if len(app_tools[app]) < MAX_LENGTH:
|
|
48
52
|
cleaned_desc = tool["description"].split("Context:")[0].strip()
|
|
49
|
-
app_tools[app].
|
|
53
|
+
app_tools[app].add(f"{tool_id}: {cleaned_desc}")
|
|
50
54
|
|
|
51
55
|
# Build result string efficiently
|
|
52
56
|
result_parts = []
|
|
53
57
|
for app, tools in app_tools.items():
|
|
54
58
|
app_status = "connected" if app in connected_apps else "NOT connected"
|
|
55
59
|
result_parts.append(f"Tools from {app} (status: {app_status} by user):")
|
|
56
|
-
for
|
|
60
|
+
# Convert set to sorted list for consistent output
|
|
61
|
+
for tool in sorted(tools):
|
|
57
62
|
result_parts.append(f" - {tool}")
|
|
58
63
|
result_parts.append("") # Empty line between apps
|
|
59
64
|
|
|
@@ -116,6 +121,7 @@ def create_meta_tools(tool_registry: ToolRegistry) -> dict[str, Any]:
|
|
|
116
121
|
Example:
|
|
117
122
|
results = await web_search(query="python programming")
|
|
118
123
|
"""
|
|
124
|
+
await tool_registry.export_tools(["exa__search_with_filters"], ToolFormat.LANGCHAIN)
|
|
119
125
|
response = await tool_registry.call_tool(
|
|
120
126
|
"exa__search_with_filters", {"query": query, "contents": {"summary": True}}
|
|
121
127
|
)
|
universal_mcp/agents/utils.py
CHANGED
|
@@ -214,3 +214,21 @@ def filter_retry_on(exc: Exception) -> bool:
|
|
|
214
214
|
|
|
215
215
|
# Default: do not retry unknown exceptions
|
|
216
216
|
return False
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def convert_tool_ids_to_dict(tool_ids: list[str]) -> dict[str, list[str]]:
|
|
220
|
+
"""Convert list of tool ids like 'provider__tool' into a provider->tools dict.
|
|
221
|
+
|
|
222
|
+
Any ids without the expected delimiter are ignored.
|
|
223
|
+
"""
|
|
224
|
+
provider_to_tools: dict[str, list[str]] = {}
|
|
225
|
+
for tool_id in tool_ids or []:
|
|
226
|
+
if "__" not in tool_id:
|
|
227
|
+
continue
|
|
228
|
+
provider, tool = tool_id.split("__", 1)
|
|
229
|
+
if not provider or not tool:
|
|
230
|
+
continue
|
|
231
|
+
if provider not in provider_to_tools:
|
|
232
|
+
provider_to_tools[provider] = []
|
|
233
|
+
provider_to_tools[provider].append(tool)
|
|
234
|
+
return provider_to_tools
|
|
@@ -5,7 +5,7 @@ universal_mcp/agents/hil.py,sha256=_5PCK6q0goGm8qylJq44aSp2MadP-yCPvhOJYKqWLMo,3
|
|
|
5
5
|
universal_mcp/agents/llm.py,sha256=hVRwjZs3MHl5_3BWedmurs2Jt1oZDfFX0Zj9F8KH7fk,1787
|
|
6
6
|
universal_mcp/agents/react.py,sha256=8XQvJ0HLVgc-K0qn9Ml48WGcgUGuIKtL67HatlT6Da0,3334
|
|
7
7
|
universal_mcp/agents/simple.py,sha256=NSATg5TWzsRNS7V3LFiDG28WSOCIwCdcC1g7NRwg2nM,2095
|
|
8
|
-
universal_mcp/agents/utils.py,sha256=
|
|
8
|
+
universal_mcp/agents/utils.py,sha256=P6W9k6XAOBp6tdjC2VTP4tE0B2M4-b1EDmr-ylJ47Pw,7765
|
|
9
9
|
universal_mcp/agents/bigtool/__init__.py,sha256=mZG8dsaCVyKlm82otxtiTA225GIFLUCUUYPEIPF24uw,2299
|
|
10
10
|
universal_mcp/agents/bigtool/__main__.py,sha256=0i-fbd2yQ90qa8n2nM3luqoJVN9Reh5HZXR5oK7SAck,445
|
|
11
11
|
universal_mcp/agents/bigtool/agent.py,sha256=mtCDNN8WjE2hjJjooDqusmbferKBHeJMHrhXUPUWaVc,252
|
|
@@ -32,19 +32,19 @@ universal_mcp/agents/codeact0/__main__.py,sha256=V2wLWW9ym3rtiSvPEs-N0Mki7G5dYHz
|
|
|
32
32
|
universal_mcp/agents/codeact0/agent.py,sha256=9BInAQr3csES-XHSscmeJlYJ3-wQUHPvLOf-6wFILUU,6695
|
|
33
33
|
universal_mcp/agents/codeact0/config.py,sha256=H-1woj_nhSDwf15F63WYn723y4qlRefXzGxuH81uYF0,2215
|
|
34
34
|
universal_mcp/agents/codeact0/langgraph_agent.py,sha256=ehjMV_Z1118pCFWB_Sa5H7XnUp0udsbUHjfjXjhIQM8,435
|
|
35
|
-
universal_mcp/agents/codeact0/llm_tool.py,sha256=
|
|
36
|
-
universal_mcp/agents/codeact0/playbook_agent.py,sha256=
|
|
35
|
+
universal_mcp/agents/codeact0/llm_tool.py,sha256=_eT0yreFMRbHheWo-gGcRJ4Co6jMRJZXJ1oVe6f_ZyQ,13816
|
|
36
|
+
universal_mcp/agents/codeact0/playbook_agent.py,sha256=YNuWQ_xK9KlOtb4kkHkxLl3CEJfmM3sUf8dwaSPyYcg,18089
|
|
37
37
|
universal_mcp/agents/codeact0/prompts.py,sha256=j8HxA3Rp-EZsms9qMBcRmFrUjeySrG1IWjqrNFXZZn8,9457
|
|
38
38
|
universal_mcp/agents/codeact0/sandbox.py,sha256=zMgHrWnQYkSkJb2MzfXvT3euCc4hvqzBE_EbX2_iLxA,3142
|
|
39
|
-
universal_mcp/agents/codeact0/state.py,sha256=
|
|
40
|
-
universal_mcp/agents/codeact0/tools.py,sha256=
|
|
41
|
-
universal_mcp/agents/codeact0/utils.py,sha256=
|
|
39
|
+
universal_mcp/agents/codeact0/state.py,sha256=Y-Rzn_S7--aXH18KPvyhqDqOOB-miu1lsAmLgmMlaAg,1259
|
|
40
|
+
universal_mcp/agents/codeact0/tools.py,sha256=Adh9Go87JnIk5U_rtBOHglFRPowk2JmRK9bumR4zJoo,7751
|
|
41
|
+
universal_mcp/agents/codeact0/utils.py,sha256=ACncBI9-JJ4Qe_4VHWnSziY3P2nE6Vb9pj71NPeVZ0o,15943
|
|
42
42
|
universal_mcp/agents/shared/__main__.py,sha256=XxH5qGDpgFWfq7fwQfgKULXGiUgeTp_YKfcxftuVZq8,1452
|
|
43
43
|
universal_mcp/agents/shared/prompts.py,sha256=yjP3zbbuKi87qCj21qwTTicz8TqtkKgnyGSeEjMu3ho,3761
|
|
44
44
|
universal_mcp/agents/shared/tool_node.py,sha256=DC9F-Ri28Pam0u3sXWNODVgmj9PtAEUb5qP1qOoGgfs,9169
|
|
45
45
|
universal_mcp/applications/llm/__init__.py,sha256=xnpxq4Wl_pevvwtSUtEwcty8_d61ywO1V2YnEXyCREY,46
|
|
46
46
|
universal_mcp/applications/llm/app.py,sha256=iNLU6z2LRZc01GfSKvV0vNzT1LhKAjq_UrSJEmjthjw,6032
|
|
47
47
|
universal_mcp/applications/ui/app.py,sha256=c7OkZsO2fRtndgAzAQbKu-1xXRuRp9Kjgml57YD2NR4,9459
|
|
48
|
-
universal_mcp_agents-0.1.
|
|
49
|
-
universal_mcp_agents-0.1.
|
|
50
|
-
universal_mcp_agents-0.1.
|
|
48
|
+
universal_mcp_agents-0.1.16.dist-info/METADATA,sha256=uIRCPa0Yr0Q1vmRnEuq3omcFLxX8fxE_mWy1WT7biIc,878
|
|
49
|
+
universal_mcp_agents-0.1.16.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
50
|
+
universal_mcp_agents-0.1.16.dist-info/RECORD,,
|
|
File without changes
|