universal-mcp-agents 0.1.23rc2__tar.gz → 0.1.23rc3__tar.gz
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-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/PKG-INFO +1 -1
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/pyproject.toml +1 -1
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/base.py +2 -2
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/agent.py +78 -69
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/prompts.py +16 -16
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/state.py +12 -12
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/tools.py +10 -19
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/uv.lock +1 -1
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/.github/workflows/evals.yml +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/.github/workflows/lint.yml +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/.github/workflows/release-please.yml +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/.github/workflows/tests.yml +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/.gitignore +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/.pre-commit-config.yaml +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/GEMINI.md +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/PROMPTS.md +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/README.md +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/bump_and_release.sh +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/__init__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/dataset.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/datasets/codeact.jsonl +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/datasets/exact.jsonl +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/datasets/tasks.jsonl +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/evaluators.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/prompts.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/run.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/utils.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/tests/test_agents.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/__init__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/bigtool/__init__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/bigtool/__main__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/bigtool/agent.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/bigtool/context.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/bigtool/graph.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/bigtool/prompts.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/bigtool/state.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/bigtool/tools.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/builder/__main__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/builder/builder.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/builder/helper.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/builder/prompts.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/builder/state.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/cli.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/__init__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/__main__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/config.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/langgraph_agent.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/llm_tool.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/sandbox.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/codeact0/utils.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/hil.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/llm.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/react.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/sandbox.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/shared/__main__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/shared/prompts.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/shared/tool_node.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/simple.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/utils.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/applications/filesystem/__init__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/applications/filesystem/app.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/applications/llm/__init__.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/applications/llm/app.py +0 -0
- {universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/applications/ui/app.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: universal-mcp-agents
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.23rc3
|
|
4
4
|
Summary: Add your description here
|
|
5
5
|
Project-URL: Homepage, https://github.com/universal-mcp/applications
|
|
6
6
|
Project-URL: Repository, https://github.com/universal-mcp/applications
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/base.py
RENAMED
|
@@ -66,9 +66,9 @@ class BaseAgent:
|
|
|
66
66
|
):
|
|
67
67
|
if event == "messages" and isinstance(meta, (tuple, list)) and len(meta) == 2:
|
|
68
68
|
payload, meta_dict = meta
|
|
69
|
-
|
|
69
|
+
is_agent_builder = isinstance(meta_dict, dict) and meta_dict.get("langgraph_node") == "agent_builder"
|
|
70
70
|
additional_kwargs = getattr(payload, "additional_kwargs", {}) or {}
|
|
71
|
-
if
|
|
71
|
+
if is_agent_builder and not additional_kwargs.get("stream"):
|
|
72
72
|
continue
|
|
73
73
|
if isinstance(payload, AIMessageChunk):
|
|
74
74
|
last_ai_chunk = payload
|
|
@@ -16,16 +16,16 @@ from universal_mcp.types import ToolFormat
|
|
|
16
16
|
from universal_mcp.agents.base import BaseAgent
|
|
17
17
|
from universal_mcp.agents.codeact0.llm_tool import smart_print
|
|
18
18
|
from universal_mcp.agents.codeact0.prompts import (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
AGENT_BUILDER_GENERATING_PROMPT,
|
|
20
|
+
AGENT_BUILDER_META_PROMPT,
|
|
21
|
+
AGENT_BUILDER_PLANNING_PROMPT,
|
|
22
22
|
create_default_prompt,
|
|
23
23
|
)
|
|
24
24
|
from universal_mcp.agents.codeact0.sandbox import eval_unsafe, execute_ipython_cell, handle_execute_ipython_cell
|
|
25
|
-
from universal_mcp.agents.codeact0.state import CodeActState,
|
|
25
|
+
from universal_mcp.agents.codeact0.state import CodeActState, AgentBuilderCode, AgentBuilderMeta, AgentBuilderPlan
|
|
26
26
|
from universal_mcp.agents.codeact0.tools import (
|
|
27
27
|
create_meta_tools,
|
|
28
|
-
|
|
28
|
+
enter_agent_builder_mode,
|
|
29
29
|
get_valid_tools,
|
|
30
30
|
)
|
|
31
31
|
from universal_mcp.agents.codeact0.utils import build_anthropic_cache_message, get_connected_apps_string
|
|
@@ -41,7 +41,7 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
41
41
|
model: str,
|
|
42
42
|
memory: BaseCheckpointSaver | None = None,
|
|
43
43
|
registry: ToolRegistry | None = None,
|
|
44
|
-
|
|
44
|
+
agent_builder_registry: object | None = None,
|
|
45
45
|
sandbox_timeout: int = 20,
|
|
46
46
|
**kwargs,
|
|
47
47
|
):
|
|
@@ -53,11 +53,11 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
53
53
|
**kwargs,
|
|
54
54
|
)
|
|
55
55
|
self.model_instance = load_chat_model(model)
|
|
56
|
-
self.
|
|
56
|
+
self.agent_builder_model_instance = load_chat_model("azure/gpt-4.1")
|
|
57
57
|
self.registry = registry
|
|
58
|
-
self.
|
|
59
|
-
self.
|
|
60
|
-
self.tools_config = self.
|
|
58
|
+
self.agent_builder_registry = agent_builder_registry
|
|
59
|
+
self.agent = agent_builder_registry.get_agent() if agent_builder_registry else None
|
|
60
|
+
self.tools_config = self.agent.tools if self.agent else {}
|
|
61
61
|
self.eval_fn = eval_unsafe
|
|
62
62
|
self.sandbox_timeout = sandbox_timeout
|
|
63
63
|
self.default_tools_config = {
|
|
@@ -91,7 +91,7 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
91
91
|
|
|
92
92
|
agent_facing_tools = [
|
|
93
93
|
execute_ipython_cell,
|
|
94
|
-
|
|
94
|
+
enter_agent_builder_mode,
|
|
95
95
|
meta_tools["search_functions"],
|
|
96
96
|
meta_tools["load_functions"],
|
|
97
97
|
]
|
|
@@ -120,7 +120,7 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
120
120
|
else:
|
|
121
121
|
return Command(update={"messages": [response], "model_with_tools": model_with_tools})
|
|
122
122
|
|
|
123
|
-
async def execute_tools(state: CodeActState) -> Command[Literal["call_model", "
|
|
123
|
+
async def execute_tools(state: CodeActState) -> Command[Literal["call_model", "agent_builder"]]:
|
|
124
124
|
"""Execute tool calls"""
|
|
125
125
|
last_message = state["messages"][-1]
|
|
126
126
|
tool_calls = last_message.tool_calls if isinstance(last_message, AIMessage) else []
|
|
@@ -128,6 +128,8 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
128
128
|
tool_messages = []
|
|
129
129
|
new_tool_ids = []
|
|
130
130
|
tool_result = ""
|
|
131
|
+
ask_user = False
|
|
132
|
+
ai_msg = ""
|
|
131
133
|
effective_previous_add_context = state.get("add_context", {})
|
|
132
134
|
effective_existing_context = state.get("context", {})
|
|
133
135
|
# logging.info(f"Initial new_tool_ids_for_context: {new_tool_ids_for_context}")
|
|
@@ -136,15 +138,15 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
136
138
|
tool_name = tool_call["name"]
|
|
137
139
|
tool_args = tool_call["args"]
|
|
138
140
|
try:
|
|
139
|
-
if tool_name == "
|
|
141
|
+
if tool_name == "enter_agent_builder_mode":
|
|
140
142
|
tool_message = ToolMessage(
|
|
141
|
-
content=json.dumps("Entered
|
|
143
|
+
content=json.dumps("Entered Agent Builder Mode."),
|
|
142
144
|
name=tool_call["name"],
|
|
143
145
|
tool_call_id=tool_call["id"],
|
|
144
146
|
)
|
|
145
147
|
return Command(
|
|
146
|
-
goto="
|
|
147
|
-
update={"
|
|
148
|
+
goto="agent_builder",
|
|
149
|
+
update={"agent_builder_mode": "planning", "messages": [tool_message]}, # Entered Agent Builder mode
|
|
148
150
|
)
|
|
149
151
|
elif tool_name == "execute_ipython_cell":
|
|
150
152
|
code = tool_call["args"]["snippet"]
|
|
@@ -160,24 +162,21 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
160
162
|
tool_result = output
|
|
161
163
|
elif tool_name == "load_functions":
|
|
162
164
|
# The tool now does all the work of validation and formatting.
|
|
163
|
-
tool_result = await meta_tools["load_functions"].ainvoke(tool_args)
|
|
164
|
-
|
|
165
|
+
tool_result, new_context_for_sandbox, valid_tools, unconnected_links = await meta_tools["load_functions"].ainvoke(tool_args)
|
|
165
166
|
# We still need to update the sandbox context for `execute_ipython_cell`
|
|
166
|
-
valid_tools, _ = await get_valid_tools(tool_ids=tool_args["tool_ids"], registry=self.registry)
|
|
167
167
|
new_tool_ids.extend(valid_tools)
|
|
168
168
|
if new_tool_ids:
|
|
169
|
-
newly_exported = await self.registry.export_tools(new_tool_ids, ToolFormat.LANGCHAIN)
|
|
170
|
-
_, new_context_for_sandbox = create_default_prompt(
|
|
171
|
-
newly_exported, [], "", "", None
|
|
172
|
-
) # is_initial_prompt is False by default
|
|
173
169
|
self.tools_context.update(new_context_for_sandbox)
|
|
170
|
+
if unconnected_links:
|
|
171
|
+
ask_user = True
|
|
172
|
+
ai_msg = f"Please login to the following app(s) using the following links and let me know in order to proceed:\n {unconnected_links} "
|
|
174
173
|
|
|
175
174
|
elif tool_name == "search_functions":
|
|
176
175
|
tool_result = await meta_tools["search_functions"].ainvoke(tool_args)
|
|
177
176
|
else:
|
|
178
177
|
raise Exception(
|
|
179
178
|
f"Unexpected tool call: {tool_call['name']}. "
|
|
180
|
-
"tool calls must be one of '
|
|
179
|
+
"tool calls must be one of 'enter_agent_builder_mode', 'execute_ipython_cell', 'load_functions', or 'search_functions'. For using functions, call them in code using 'execute_ipython_cell'."
|
|
181
180
|
)
|
|
182
181
|
except Exception as e:
|
|
183
182
|
tool_result = str(e)
|
|
@@ -188,6 +187,17 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
188
187
|
tool_call_id=tool_call["id"],
|
|
189
188
|
)
|
|
190
189
|
tool_messages.append(tool_message)
|
|
190
|
+
|
|
191
|
+
if ask_user:
|
|
192
|
+
tool_messages.append(AIMessage(content=ai_msg))
|
|
193
|
+
return Command(
|
|
194
|
+
update={
|
|
195
|
+
"messages": tool_messages,
|
|
196
|
+
"selected_tool_ids": new_tool_ids,
|
|
197
|
+
"context": effective_existing_context,
|
|
198
|
+
"add_context": effective_previous_add_context,
|
|
199
|
+
}
|
|
200
|
+
)
|
|
191
201
|
|
|
192
202
|
return Command(
|
|
193
203
|
goto="call_model",
|
|
@@ -199,17 +209,17 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
199
209
|
},
|
|
200
210
|
)
|
|
201
211
|
|
|
202
|
-
def
|
|
203
|
-
|
|
204
|
-
if
|
|
212
|
+
def agent_builder(state: CodeActState, writer: StreamWriter) -> Command[Literal["call_model"]]:
|
|
213
|
+
agent_builder_mode = state.get("agent_builder_mode")
|
|
214
|
+
if agent_builder_mode == "planning":
|
|
205
215
|
plan_id = str(uuid.uuid4())
|
|
206
|
-
writer({"type": "custom", id: plan_id, "name": "planning", "data": {"update": bool(self.
|
|
207
|
-
planning_instructions = self.instructions +
|
|
216
|
+
writer({"type": "custom", id: plan_id, "name": "planning", "data": {"update": bool(self.agent)}})
|
|
217
|
+
planning_instructions = self.instructions + AGENT_BUILDER_PLANNING_PROMPT
|
|
208
218
|
messages = [{"role": "system", "content": planning_instructions}] + state["messages"]
|
|
209
219
|
|
|
210
|
-
model_with_structured_output = self.
|
|
220
|
+
model_with_structured_output = self.agent_builder_model_instance.with_structured_output(AgentBuilderPlan)
|
|
211
221
|
response = model_with_structured_output.invoke(messages)
|
|
212
|
-
plan = cast(
|
|
222
|
+
plan = cast(AgentBuilderPlan, response)
|
|
213
223
|
|
|
214
224
|
writer({"type": "custom", id: plan_id, "name": "planning", "data": {"plan": plan.steps}})
|
|
215
225
|
return Command(
|
|
@@ -220,16 +230,16 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
220
230
|
additional_kwargs={
|
|
221
231
|
"type": "planning",
|
|
222
232
|
"plan": plan.steps,
|
|
223
|
-
"update": bool(self.
|
|
233
|
+
"update": bool(self.agent),
|
|
224
234
|
},
|
|
225
235
|
)
|
|
226
236
|
],
|
|
227
|
-
"
|
|
237
|
+
"agent_builder_mode": "confirming",
|
|
228
238
|
"plan": plan.steps,
|
|
229
239
|
}
|
|
230
240
|
)
|
|
231
241
|
|
|
232
|
-
elif
|
|
242
|
+
elif agent_builder_mode == "confirming":
|
|
233
243
|
# Deterministic routing based on three exact button inputs from UI
|
|
234
244
|
user_text = ""
|
|
235
245
|
for m in reversed(state["messages"]):
|
|
@@ -245,10 +255,10 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
245
255
|
if t == "yes, this is great":
|
|
246
256
|
self.meta_id = str(uuid.uuid4())
|
|
247
257
|
name, description = None, None
|
|
248
|
-
if self.
|
|
258
|
+
if self.agent:
|
|
249
259
|
# Update flow: use existing name/description and do not re-generate
|
|
250
|
-
name = getattr(self.
|
|
251
|
-
description = getattr(self.
|
|
260
|
+
name = getattr(self.agent, "name", None)
|
|
261
|
+
description = getattr(self.agent, "description", None)
|
|
252
262
|
writer(
|
|
253
263
|
{
|
|
254
264
|
"type": "custom",
|
|
@@ -264,12 +274,12 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
264
274
|
else:
|
|
265
275
|
writer({"type": "custom", id: self.meta_id, "name": "generating", "data": {"update": False}})
|
|
266
276
|
|
|
267
|
-
meta_instructions = self.instructions +
|
|
277
|
+
meta_instructions = self.instructions + AGENT_BUILDER_META_PROMPT
|
|
268
278
|
messages = [{"role": "system", "content": meta_instructions}] + state["messages"]
|
|
269
279
|
|
|
270
|
-
model_with_structured_output = self.
|
|
280
|
+
model_with_structured_output = self.agent_builder_model_instance.with_structured_output(AgentBuilderMeta)
|
|
271
281
|
meta_response = model_with_structured_output.invoke(messages)
|
|
272
|
-
meta = cast(
|
|
282
|
+
meta = cast(AgentBuilderMeta, meta_response)
|
|
273
283
|
name, description = meta.name, meta.description
|
|
274
284
|
|
|
275
285
|
# Emit intermediary UI update with created name/description
|
|
@@ -283,11 +293,11 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
283
293
|
)
|
|
284
294
|
|
|
285
295
|
return Command(
|
|
286
|
-
goto="
|
|
296
|
+
goto="agent_builder",
|
|
287
297
|
update={
|
|
288
|
-
"
|
|
289
|
-
"
|
|
290
|
-
"
|
|
298
|
+
"agent_builder_mode": "generating",
|
|
299
|
+
"agent_name": name,
|
|
300
|
+
"agent_description": description,
|
|
291
301
|
},
|
|
292
302
|
)
|
|
293
303
|
if t == "i would like to modify the plan":
|
|
@@ -295,52 +305,51 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
295
305
|
content="What would you like to change about the plan? Let me know and I'll update the plan accordingly.",
|
|
296
306
|
additional_kwargs={"stream": "true"},
|
|
297
307
|
)
|
|
298
|
-
return Command(update={"
|
|
308
|
+
return Command(update={"agent_builder_mode": "planning", "messages": [prompt_ai]})
|
|
299
309
|
if t == "let's do something else":
|
|
300
|
-
return Command(goto="call_model", update={"
|
|
310
|
+
return Command(goto="call_model", update={"agent_builder_mode": "inactive"})
|
|
301
311
|
|
|
302
312
|
# Fallback safe default
|
|
303
|
-
return Command(goto="call_model", update={"
|
|
313
|
+
return Command(goto="call_model", update={"agent_builder_mode": "inactive"})
|
|
304
314
|
|
|
305
|
-
elif
|
|
306
|
-
generating_instructions = self.instructions +
|
|
315
|
+
elif agent_builder_mode == "generating":
|
|
316
|
+
generating_instructions = self.instructions + AGENT_BUILDER_GENERATING_PROMPT
|
|
307
317
|
messages = [{"role": "system", "content": generating_instructions}] + state["messages"]
|
|
308
318
|
|
|
309
|
-
model_with_structured_output = self.
|
|
319
|
+
model_with_structured_output = self.agent_builder_model_instance.with_structured_output(AgentBuilderCode)
|
|
310
320
|
response = model_with_structured_output.invoke(messages)
|
|
311
|
-
func_code = cast(
|
|
321
|
+
func_code = cast(AgentBuilderCode, response).code
|
|
312
322
|
|
|
313
323
|
# Extract function name (handle both regular and async functions)
|
|
314
324
|
match = re.search(r"^\s*(?:async\s+)?def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(", func_code, re.MULTILINE)
|
|
315
325
|
if match:
|
|
316
326
|
function_name = match.group(1)
|
|
317
327
|
else:
|
|
318
|
-
function_name = "
|
|
328
|
+
function_name = "generated_agent"
|
|
319
329
|
|
|
320
330
|
# Use generated metadata if available
|
|
321
|
-
final_name = state.get("
|
|
322
|
-
final_description = state.get("
|
|
331
|
+
final_name = state.get("agent_name") or function_name
|
|
332
|
+
final_description = state.get("agent_description") or f"Generated agent: {function_name}"
|
|
323
333
|
|
|
324
334
|
# Save or update an Agent using the helper registry
|
|
325
335
|
try:
|
|
326
|
-
if not self.
|
|
327
|
-
raise ValueError("
|
|
336
|
+
if not self.agent_builder_registry:
|
|
337
|
+
raise ValueError("AgentBuilder registry is not configured")
|
|
328
338
|
|
|
329
339
|
# Build instructions payload embedding the plan and function code
|
|
330
340
|
instructions_payload = {
|
|
331
|
-
"
|
|
332
|
-
"
|
|
341
|
+
"plan": state["plan"],
|
|
342
|
+
"script": func_code,
|
|
333
343
|
}
|
|
334
344
|
|
|
335
345
|
# Convert tool ids list to dict
|
|
336
346
|
tool_dict = convert_tool_ids_to_dict(state["selected_tool_ids"])
|
|
337
347
|
|
|
338
|
-
res = self.
|
|
348
|
+
res = self.agent_builder_registry.upsert_agent(
|
|
339
349
|
name=final_name,
|
|
340
350
|
description=final_description,
|
|
341
351
|
instructions=instructions_payload,
|
|
342
352
|
tools=tool_dict,
|
|
343
|
-
visibility="private",
|
|
344
353
|
)
|
|
345
354
|
except Exception as e:
|
|
346
355
|
raise e
|
|
@@ -352,7 +361,7 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
352
361
|
"name": "generating",
|
|
353
362
|
"data": {
|
|
354
363
|
"id": str(res.id),
|
|
355
|
-
"update": bool(self.
|
|
364
|
+
"update": bool(self.agent),
|
|
356
365
|
"name": final_name,
|
|
357
366
|
"description": final_description,
|
|
358
367
|
},
|
|
@@ -363,16 +372,16 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
363
372
|
additional_kwargs={
|
|
364
373
|
"type": "generating",
|
|
365
374
|
"id": str(res.id),
|
|
366
|
-
"update": bool(self.
|
|
375
|
+
"update": bool(self.agent),
|
|
367
376
|
"name": final_name,
|
|
368
377
|
"description": final_description,
|
|
369
378
|
},
|
|
370
379
|
)
|
|
371
380
|
|
|
372
|
-
return Command(update={"messages": [mock_assistant_message], "
|
|
381
|
+
return Command(update={"messages": [mock_assistant_message], "agent_builder_mode": "normal"})
|
|
373
382
|
|
|
374
|
-
async def route_entry(state: CodeActState) -> Literal["call_model", "
|
|
375
|
-
"""Route to either normal mode or
|
|
383
|
+
async def route_entry(state: CodeActState) -> Literal["call_model", "agent_builder"]:
|
|
384
|
+
"""Route to either normal mode or agent builder creation"""
|
|
376
385
|
all_tools = await self.registry.export_tools(state["selected_tool_ids"], ToolFormat.LANGCHAIN)
|
|
377
386
|
# print(all_tools)
|
|
378
387
|
|
|
@@ -382,16 +391,16 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
382
391
|
self.additional_tools,
|
|
383
392
|
self.instructions,
|
|
384
393
|
await get_connected_apps_string(self.registry),
|
|
385
|
-
self.
|
|
394
|
+
self.agent,
|
|
386
395
|
is_initial_prompt=True,
|
|
387
396
|
)
|
|
388
|
-
if state.get("
|
|
389
|
-
return "
|
|
397
|
+
if state.get("agent_builder_mode") in ["planning", "confirming", "generating"]:
|
|
398
|
+
return "agent_builder"
|
|
390
399
|
return "call_model"
|
|
391
400
|
|
|
392
401
|
agent = StateGraph(state_schema=CodeActState)
|
|
393
402
|
agent.add_node(call_model, retry_policy=RetryPolicy(max_attempts=3, retry_on=filter_retry_on))
|
|
394
|
-
agent.add_node(
|
|
403
|
+
agent.add_node(agent_builder)
|
|
395
404
|
agent.add_node(execute_tools)
|
|
396
405
|
agent.add_conditional_edges(START, route_entry)
|
|
397
406
|
return agent.compile(checkpointer=self.memory)
|
|
@@ -63,16 +63,16 @@ Rules:
|
|
|
63
63
|
- Your final response should contain the complete answer to the user's request in a clear, well-formatted manner that directly addresses what they asked for.
|
|
64
64
|
"""
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
AGENT_BUILDER_PLANNING_PROMPT = """Now, you are tasked with creating a reusable agent from the user's previous workflow.
|
|
67
67
|
|
|
68
68
|
TASK: Analyze the conversation history and code execution to create a step-by-step plan for a reusable function.
|
|
69
69
|
Do not include the searching and loading of tools. Assume that the tools have already been loaded.
|
|
70
70
|
The plan is a sequence of steps.
|
|
71
|
-
You must output a JSON object with a single key "steps", which is a list of strings. Each string is a step in the
|
|
71
|
+
You must output a JSON object with a single key "steps", which is a list of strings. Each string is a step in the agent.
|
|
72
72
|
|
|
73
73
|
Your plan should:
|
|
74
74
|
1. Identify the key steps in the workflow
|
|
75
|
-
2. Mark user-specific variables that should become the main
|
|
75
|
+
2. Mark user-specific variables that should become the main agent function parameters using `variable_name` syntax. Intermediate variables should not be highlighted using ``
|
|
76
76
|
3. Keep the logic generic and reusable
|
|
77
77
|
4. Be clear and concise
|
|
78
78
|
|
|
@@ -90,26 +90,26 @@ Now create a plan based on the conversation history. Do not include any other te
|
|
|
90
90
|
"""
|
|
91
91
|
|
|
92
92
|
|
|
93
|
-
|
|
93
|
+
AGENT_BUILDER_GENERATING_PROMPT = """Now, you are tasked with generating the agent function.
|
|
94
94
|
Your response must be ONLY the Python code for the function.
|
|
95
95
|
Do not include any other text, markdown, or explanations in your response.
|
|
96
96
|
Your response should start with `def` or `async def`.
|
|
97
97
|
The function should be a single, complete piece of code that can be executed independently, based on previously executed code snippets that executed correctly.
|
|
98
|
-
The parameters of the function should be the same as the final confirmed
|
|
98
|
+
The parameters of the function should be the same as the final confirmed agent plan.
|
|
99
99
|
"""
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
You are preparing metadata for a reusable
|
|
102
|
+
AGENT_BUILDER_META_PROMPT = """
|
|
103
|
+
You are preparing metadata for a reusable agent based on the confirmed step-by-step plan.
|
|
104
104
|
|
|
105
|
-
TASK: Create a concise, human-friendly name and a short description for the
|
|
105
|
+
TASK: Create a concise, human-friendly name and a short description for the agent.
|
|
106
106
|
|
|
107
107
|
INPUTS:
|
|
108
108
|
- Conversation context and plan steps will be provided in prior messages
|
|
109
109
|
|
|
110
110
|
REQUIREMENTS:
|
|
111
111
|
1. Name: 3-6 words, Title Case, no punctuation except hyphens if needed
|
|
112
|
-
2. Description: Single sentence, <= 140 characters, clearly states what the
|
|
112
|
+
2. Description: Single sentence, <= 140 characters, clearly states what the agent does
|
|
113
113
|
|
|
114
114
|
OUTPUT: Return ONLY a JSON object with exactly these keys:
|
|
115
115
|
{
|
|
@@ -137,7 +137,7 @@ def create_default_prompt(
|
|
|
137
137
|
additional_tools: Sequence[StructuredTool],
|
|
138
138
|
base_prompt: str | None = None,
|
|
139
139
|
apps_string: str | None = None,
|
|
140
|
-
|
|
140
|
+
agent: object | None = None,
|
|
141
141
|
is_initial_prompt: bool = False,
|
|
142
142
|
):
|
|
143
143
|
if is_initial_prompt:
|
|
@@ -191,14 +191,14 @@ def create_default_prompt(
|
|
|
191
191
|
f"\n\nUse the following information/instructions while completing your tasks:\n\n{base_prompt}"
|
|
192
192
|
)
|
|
193
193
|
|
|
194
|
-
# Append existing
|
|
194
|
+
# Append existing agent (plan + code) if provided
|
|
195
195
|
try:
|
|
196
|
-
if
|
|
197
|
-
pb =
|
|
198
|
-
plan = pb.get("
|
|
199
|
-
code = pb.get("
|
|
196
|
+
if agent and hasattr(agent, "instructions"):
|
|
197
|
+
pb = agent.instructions or {}
|
|
198
|
+
plan = pb.get("plan")
|
|
199
|
+
code = pb.get("script")
|
|
200
200
|
if plan or code:
|
|
201
|
-
system_prompt += "\n\nExisting
|
|
201
|
+
system_prompt += "\n\nExisting Agent Provided:\n"
|
|
202
202
|
if plan:
|
|
203
203
|
if isinstance(plan, list):
|
|
204
204
|
plan_block = "\n".join(f"- {str(s)}" for s in plan)
|
|
@@ -4,16 +4,16 @@ from langgraph.prebuilt.chat_agent_executor import AgentState
|
|
|
4
4
|
from pydantic import BaseModel, Field
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
class
|
|
8
|
-
steps: list[str] = Field(description="The steps of the
|
|
7
|
+
class AgentBuilderPlan(BaseModel):
|
|
8
|
+
steps: list[str] = Field(description="The steps of the agent.")
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
class
|
|
12
|
-
code: str = Field(description="The Python code for the
|
|
11
|
+
class AgentBuilderCode(BaseModel):
|
|
12
|
+
code: str = Field(description="The Python code for the agent.")
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
class
|
|
16
|
-
name: str = Field(description="Concise, title-cased
|
|
15
|
+
class AgentBuilderMeta(BaseModel):
|
|
16
|
+
name: str = Field(description="Concise, title-cased agent name (3-6 words).")
|
|
17
17
|
description: str = Field(description="Short, one-sentence description (<= 140 chars).")
|
|
18
18
|
|
|
19
19
|
|
|
@@ -46,13 +46,13 @@ class CodeActState(AgentState):
|
|
|
46
46
|
"""Dictionary containing the execution context with available tools and variables."""
|
|
47
47
|
add_context: dict[str, Any]
|
|
48
48
|
"""Dictionary containing the additional context (functions, classes, imports) to be added to the execution context."""
|
|
49
|
-
|
|
50
|
-
"""State for the
|
|
49
|
+
agent_builder_mode: str | None
|
|
50
|
+
"""State for the agent builder agent."""
|
|
51
51
|
selected_tool_ids: Annotated[list[str], _enqueue]
|
|
52
52
|
"""Queue for tools exported from registry"""
|
|
53
53
|
plan: list[str] | None
|
|
54
|
-
"""Plan for the
|
|
55
|
-
|
|
56
|
-
"""Generated
|
|
57
|
-
|
|
54
|
+
"""Plan for the agent builder agent."""
|
|
55
|
+
agent_name: str | None
|
|
56
|
+
"""Generated agent name after confirmation."""
|
|
57
|
+
agent_description: str | None
|
|
58
58
|
"""Generated short description after confirmation."""
|
|
@@ -11,8 +11,8 @@ from universal_mcp.types import ToolFormat
|
|
|
11
11
|
from universal_mcp.agents.codeact0.prompts import create_default_prompt
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
def
|
|
15
|
-
"""Call this function to enter
|
|
14
|
+
def enter_agent_builder_mode():
|
|
15
|
+
"""Call this function to enter agent builder mode. Agent builder mode is when the user wants to store a repeated task as a script with some inputs for the future."""
|
|
16
16
|
return
|
|
17
17
|
|
|
18
18
|
|
|
@@ -212,21 +212,18 @@ def create_meta_tools(tool_registry: AgentrRegistry) -> dict[str, Any]:
|
|
|
212
212
|
return "No tool IDs provided to load."
|
|
213
213
|
|
|
214
214
|
# Step 1: Validate which tools are usable and get login links for others.
|
|
215
|
-
|
|
215
|
+
valid_tools, unconnected_links = await get_valid_tools(tool_ids=tool_ids, registry=tool_registry)
|
|
216
216
|
|
|
217
|
-
if not
|
|
217
|
+
if not valid_tools:
|
|
218
218
|
return "Error: None of the provided tool IDs could be validated or loaded."
|
|
219
219
|
|
|
220
220
|
# Step 2: Export the schemas of the valid tools.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
exported_tools = await temp_registry.export_tools(valid_tool_ids, ToolFormat.LANGCHAIN)
|
|
225
|
-
except Exception as e:
|
|
226
|
-
return f"Error exporting tools: {e}"
|
|
221
|
+
all_exported_tools = await tool_registry.export_tools(valid_tools, ToolFormat.LANGCHAIN)
|
|
222
|
+
exported_tools = [tool for tool in all_exported_tools if tool.name in valid_tools]
|
|
223
|
+
|
|
227
224
|
|
|
228
225
|
# Step 3: Build the informational string for the agent.
|
|
229
|
-
tool_definitions,
|
|
226
|
+
tool_definitions, new_tools_context = create_default_prompt(exported_tools, [], is_initial_prompt=False)
|
|
230
227
|
|
|
231
228
|
result_parts = [
|
|
232
229
|
f"Successfully loaded {len(exported_tools)} functions. They are now available for use inside `execute_ipython_cell`:",
|
|
@@ -234,15 +231,9 @@ def create_meta_tools(tool_registry: AgentrRegistry) -> dict[str, Any]:
|
|
|
234
231
|
]
|
|
235
232
|
|
|
236
233
|
response_string = "\n\n".join(result_parts)
|
|
234
|
+
unconnected_links = "\n".join(unconnected_links)
|
|
237
235
|
|
|
238
|
-
|
|
239
|
-
if unconnected_links:
|
|
240
|
-
links = "\n".join(unconnected_links)
|
|
241
|
-
response_string += (
|
|
242
|
-
f"\n\nPlease ask the user to log in to the following app(s) to use their full functionality:\n{links}"
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
return response_string
|
|
236
|
+
return response_string, new_tools_context, valid_tools, unconnected_links
|
|
246
237
|
|
|
247
238
|
@tool
|
|
248
239
|
async def web_search(query: str) -> dict:
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/.github/workflows/evals.yml
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/.github/workflows/lint.yml
RENAMED
|
File without changes
|
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/.github/workflows/tests.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/datasets/codeact.jsonl
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/datasets/exact.jsonl
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/evals/datasets/tasks.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/cli.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/hil.py
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/llm.py
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/react.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/simple.py
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.23rc2 → universal_mcp_agents-0.1.23rc3}/src/universal_mcp/agents/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|