universal-mcp-agents 0.1.13__py3-none-any.whl → 0.1.14__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.

@@ -18,7 +18,8 @@ from universal_mcp.agents.codeact0.prompts import (
18
18
  )
19
19
  from universal_mcp.agents.codeact0.sandbox import eval_unsafe, execute_ipython_cell
20
20
  from universal_mcp.agents.codeact0.state import CodeActState
21
- from universal_mcp.agents.codeact0.utils import filter_retry_on, inject_context
21
+ from universal_mcp.agents.utils import filter_retry_on
22
+ from universal_mcp.agents.codeact0.utils import inject_context
22
23
  from universal_mcp.agents.llm import load_chat_model
23
24
 
24
25
 
@@ -63,9 +64,9 @@ class CodeActAgent(BaseAgent):
63
64
  raise ValueError("Tools are configured but no registry is provided")
64
65
  # Langchain tools are fine
65
66
  exported_tools = await self.registry.export_tools(self.tools_config, ToolFormat.LANGCHAIN)
66
- exported_tools.extend([smart_print, data_extractor, ai_classify, call_llm])
67
- self.processed_tools = [t if isinstance(t, StructuredTool) else create_tool(t) for t in exported_tools]
68
- self.instructions, self.tools_context = create_default_prompt(self.processed_tools, self.instructions)
67
+ additional_tools= [smart_print, data_extractor, ai_classify, call_llm]
68
+ additional_tools = [t if isinstance(t, StructuredTool) else create_tool(t) for t in additional_tools]
69
+ self.instructions, self.tools_context = create_default_prompt(exported_tools, additional_tools, self.instructions)
69
70
 
70
71
  def call_model(state: CodeActState) -> Command[Literal["sandbox"]]:
71
72
  messages = [{"role": "system", "content": self.instructions}] + state["messages"]
@@ -0,0 +1,17 @@
1
+ import asyncio
2
+
3
+ from langgraph.checkpoint.memory import MemorySaver
4
+ from rich import print
5
+ from universal_mcp.agentr.registry import AgentrRegistry
6
+
7
+ from universal_mcp.agents.codeact0.agent import CodeActAgent
8
+ from universal_mcp.agents.utils import messages_to_list
9
+ async def agent():
10
+ agent_obj = CodeActAgent(
11
+ name="CodeAct Agent",
12
+ instructions="Be very concise in your answers.",
13
+ model="anthropic:claude-4-sonnet-20250514",
14
+ tools={"google_calendar": ["get_upcoming_events"], "exa" : ["search_with_filters"]},
15
+ registry=AgentrRegistry()
16
+ )
17
+ return await agent_obj._build_graph()
@@ -27,7 +27,7 @@ def smart_print(data: Any) -> None:
27
27
  Args:
28
28
  data: Either a dictionary with string keys, or a list of such dictionaries
29
29
  """
30
- light_copy(data)
30
+ print(light_copy(data))
31
31
 
32
32
 
33
33
  def creative_writer(
@@ -1,5 +1,5 @@
1
1
  uneditable_prompt = """
2
- You are Wingman, an AI Assistant created by AgentR. You are a creative, straight-forward and direct principal software engineer.
2
+ You are Wingmen, an AI Assistant created by AgentR. You are a creative, straight-forward and direct principal software engineer.
3
3
 
4
4
  Your job is to answer the user's question or perform the task they ask for.
5
5
  - Answer simple questions (which do not require you to write any code or access any external resources) directly. Note that any operation that involves using ONLY print functions should be answered directly.
@@ -7,19 +7,18 @@ Your job is to answer the user's question or perform the task they ask for.
7
7
  - You have access to `execute_ipython_cell` tool that allows you to execute Python code in an IPython notebook cell.
8
8
  - In writing or natural language processing tasks DO NOT answer directly. Instead use `execute_ipython_cell` tool with the AI functions provided to you for tasks like summarizing, text generation, classification, data extraction from text or unstructured data, etc.
9
9
  - The code you write will be executed in a sandbox environment, and you can use the output of previous executions in your code.
10
- - Read and understand the output of the previous code snippet and use it to answer the user's request. Note that the code output is not visible to the user, so after the task is complete, you have to give the output to the user in a markdown format.
10
+ - Read and understand the output of the previous code snippet and use it to answer the user's request. Note that the code output is NOT visible to the user, so after the task is complete, you have to give the output to the user in a markdown format.
11
11
  - If needed, feel free to ask for more information from the user (without using the execute_ipython_cell tool) to clarify the task.
12
12
 
13
13
  GUIDELINES for writing code:
14
14
  - Variables defined at the top level of previous code snippets can be referenced in your code.
15
- - External functions which return a dict or list[dict] are ambiguous. Therefore, you MUST explore the structure of the returned data using `smart_print()` statements before using it, printing keys and values.
16
- - Ensure to not print large amounts of data, use string truncation to limit the output to a few lines when checking the data structure.
15
+ - External functions which return a dict or list[dict] are ambiguous. Therefore, you MUST explore the structure of the returned data using `smart_print()` statements before using it, printing keys and values. `smart_print` truncates long strings from data, preventing huge output logs.
17
16
  - When an operation involves running a fixed set of steps on a list of items, run one run correctly and then use a for loop to run the steps on each item in the list.
18
17
  - In a single code snippet, try to achieve as much as possible.
19
- - You can only import libraries that come pre-installed with Python, and these have to be imported at the top of every code snippet where required.
20
- - Wrap await calls in a function and call it using `asyncio.run` to use async functions.
18
+ - You can only import libraries that come pre-installed with Python.
19
+ - You must wrap await calls in an async function and call it using `asyncio.run`.
20
+ - For displaying final results to the user, you must present your output in markdown format, including image links, so that they are rendered and displayed to the user. The code output is NOT visible to the user.
21
21
 
22
- NOTE: If any function throws an error requiring authentication, provide the user with a Markdown link to the authentication page and prompt them to authenticate.
23
22
  """
24
23
  import inspect
25
24
  import re
@@ -27,6 +26,7 @@ from collections.abc import Sequence
27
26
  from datetime import datetime
28
27
 
29
28
  from langchain_core.tools import StructuredTool
29
+ from universal_mcp.agents.codeact0.utils import schema_to_signature
30
30
 
31
31
 
32
32
  def make_safe_function_name(name: str) -> str:
@@ -119,38 +119,49 @@ def indent(text, prefix, predicate=None):
119
119
  return "".join(prefixed_lines)
120
120
 
121
121
 
122
+
122
123
  def create_default_prompt(
123
124
  tools: Sequence[StructuredTool],
125
+ additional_tools : Sequence[StructuredTool],
124
126
  base_prompt: str | None = None,
125
127
  ):
126
- system_prompt = uneditable_prompt.strip() + (
127
- "\n\nIn addition to the Python Standard Library, you can use the following external functions:\n"
128
+ system_prompt = uneditable_prompt.strip() + (
129
+ "\n\nIn addition to the Python Standard Library, you can use the following external functions:"
130
+ "\n"
128
131
  )
129
132
  tools_context = {}
130
133
  for tool in tools:
131
- # Create a safe function name
132
- safe_name = make_safe_function_name(tool.name)
133
- # Use coroutine if it exists, otherwise use func
134
134
  if hasattr(tool, "coroutine") and tool.coroutine is not None:
135
135
  tool_callable = tool.coroutine
136
- signature = inspect.signature(tool_callable)
137
136
  is_async = True
138
137
  elif hasattr(tool, "func") and tool.func is not None:
139
138
  tool_callable = tool.func
140
- signature = inspect.signature(tool_callable)
141
139
  is_async = False
142
- else:
143
- raise ValueError(f"Tool {tool.name} does not have a callable coroutine or function.")
140
+ system_prompt += f'''{"async " if is_async else ""}{schema_to_signature(tool.args, tool.name)}:
141
+ """{tool.description}"""
142
+ ...
143
+ '''
144
+ safe_name = make_safe_function_name(tool.name)
144
145
  tools_context[safe_name] = tool_callable
145
- system_prompt += f'''
146
- {"async " if is_async else ""}def {safe_name}{str(signature)}:
147
- """
148
- {indent(dedent(tool.description), " ")}
149
- """
146
+
147
+ for tool in additional_tools:
148
+ if hasattr(tool, "coroutine") and tool.coroutine is not None:
149
+ tool_callable = tool.coroutine
150
+ is_async = True
151
+ elif hasattr(tool, "func") and tool.func is not None:
152
+ tool_callable = tool.func
153
+ is_async = False
154
+ system_prompt += f'''{"async " if is_async else ""}def {tool.name} {str(inspect.signature(tool_callable))}:
155
+ """{tool.description}"""
150
156
  ...
151
- '''
152
- system_prompt += f"\n\nThe current time is {datetime.now().strftime('%H:%M:%S')}"
157
+ '''
158
+ safe_name = make_safe_function_name(tool.name)
159
+ tools_context[safe_name] = tool_callable
160
+
153
161
  if base_prompt and base_prompt.strip():
154
162
  system_prompt += f"Your goal is to perform the following task:\n\n{base_prompt}"
155
163
 
156
164
  return system_prompt, tools_context
165
+
166
+
167
+
@@ -1,13 +1,14 @@
1
1
  base_prompt: 'Fetch all open issues from the GitHub repository "microsoft/vscode" and add them to a new Google Sheet. Then create corresponding tasks in ClickUp for each issue with descriptions, tags, and "In Progress" status. Delete processed rows from the sheet after creating ClickUp tasks.'
2
2
  tools:
3
3
  - google_sheet__get_values
4
+ - google_sheet__create_spreadsheet
5
+ - google_sheet__write_values_to_sheet
6
+ - google_sheet__delete_dimensions
7
+ - google_sheet__append_values
8
+ - google_sheet__update_values
4
9
  - clickup__tasks_create_new_task
5
10
  - clickup__spaces_get_details
6
11
  - clickup__lists_get_list_details
7
12
  - clickup__tasks_get_list_tasks
8
- - google_sheet__delete_dimensions
9
- - google_sheet__update_values
10
- - google_sheet__get_spreadsheet_metadata
11
- - google_sheet__batch_get_values_by_range
12
- - github__list_issues
13
+ - github__search_issues
13
14
  - github__update_issue
@@ -3,8 +3,6 @@ from collections.abc import Sequence
3
3
  from typing import Any
4
4
 
5
5
  from langchain_core.messages import BaseMessage
6
- from pydantic import ValidationError
7
- from requests import JSONDecodeError
8
6
 
9
7
 
10
8
  def light_copy(data):
@@ -74,45 +72,6 @@ def make_safe_function_name(name: str) -> str:
74
72
  safe_name = "unnamed_tool"
75
73
  return safe_name
76
74
 
77
-
78
- def filter_retry_on(exc: Exception) -> bool:
79
- import httpx
80
- import requests
81
-
82
- if isinstance(
83
- exc,
84
- (
85
- ConnectionError,
86
- JSONDecodeError,
87
- ValidationError,
88
- ),
89
- ):
90
- return True
91
- if isinstance(
92
- exc,
93
- (
94
- ValueError,
95
- TypeError,
96
- ArithmeticError,
97
- ImportError,
98
- LookupError,
99
- NameError,
100
- SyntaxError,
101
- RuntimeError,
102
- ReferenceError,
103
- StopIteration,
104
- StopAsyncIteration,
105
- OSError,
106
- ),
107
- ):
108
- return False
109
- if isinstance(exc, httpx.HTTPStatusError):
110
- return 500 <= exc.response.status_code < 600
111
- if isinstance(exc, requests.HTTPError):
112
- return 500 <= exc.response.status_code < 600 if exc.response else True
113
- return True
114
-
115
-
116
75
  def derive_context(code: str, context: dict[str, Any]) -> dict[str, Any]:
117
76
  """
118
77
  Derive context from code by extracting classes, functions, and import statements.
@@ -176,23 +135,20 @@ def derive_context(code: str, context: dict[str, Any]) -> dict[str, Any]:
176
135
 
177
136
  if class_def not in context["classes"]:
178
137
  context["classes"].append(class_def)
179
-
180
- # Extract function definitions (only top-level functions, not class methods)
138
+
139
+ # Extract function definitions (including async)
181
140
  for node in ast.walk(tree):
182
- if isinstance(node, ast.FunctionDef):
183
- # Get the function definition as a string
141
+ if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
184
142
  func_lines = code.split("\n")[node.lineno - 1 : node.end_lineno]
185
143
  func_def = "\n".join(func_lines)
186
144
 
187
- # Check if this is a top-level function by looking at indentation
188
- # Top-level functions should start at column 0 (no indentation)
145
+ # Only top-level functions (col_offset == 0)
189
146
  if node.col_offset == 0:
190
- # Clean up the function definition (remove leading/trailing whitespace)
191
147
  func_def = func_def.strip()
192
-
193
148
  if func_def not in context["functions"]:
194
149
  context["functions"].append(func_def)
195
150
 
151
+
196
152
  except SyntaxError:
197
153
  # If the code has syntax errors, try a simpler regex-based approach
198
154
 
@@ -351,24 +307,47 @@ def inject_context(
351
307
  exec(function_definition, namespace)
352
308
  except Exception:
353
309
  # If execution fails, try to extract function name and create placeholder
354
- import re
355
-
356
- func_match = re.search(r"def\s+(\w+)", function_definition)
310
+ func_match = re.search(r"(async\s+)?def\s+(\w+)", function_definition)
357
311
  if func_match:
358
- func_name = func_match.group(1)
312
+ func_name = func_match.group(2)
313
+ is_async = bool(func_match.group(1))
359
314
 
360
- # Create a placeholder function
361
- def placeholder_func(*args, **kwargs):
362
- raise NotImplementedError(f"Function '{func_name}' failed to load: {str(e)}")
315
+ if is_async:
316
+ async def placeholder_func(*args, **kwargs):
317
+ raise NotImplementedError(f"Async function '{func_name}' failed to load: {str(e)}")
318
+ else:
319
+ def placeholder_func(*args, **kwargs):
320
+ raise NotImplementedError(f"Function '{func_name}' failed to load: {str(e)}")
363
321
 
364
322
  placeholder_func.__name__ = func_name
365
323
  namespace[func_name] = placeholder_func
366
- else:
367
- # If we can't extract function name, create a generic placeholder
368
- def generic_placeholder_func(*args, **kwargs):
369
- raise NotImplementedError(f"Function definition failed to load: {str(e)}")
370
-
371
- generic_placeholder_func.__name__ = f"func_{len(namespace)}"
372
- namespace[generic_placeholder_func.__name__] = generic_placeholder_func
373
324
 
374
325
  return namespace
326
+
327
+ def schema_to_signature(schema: dict, func_name="my_function") -> str:
328
+ type_map = {
329
+ "integer": "int",
330
+ "string": "str",
331
+ "boolean": "bool",
332
+ "null": "None",
333
+ }
334
+
335
+ params = []
336
+ for name, meta in schema.items():
337
+ # figure out type
338
+ if "type" in meta:
339
+ typ = type_map.get(meta["type"], "Any")
340
+ elif "anyOf" in meta:
341
+ types = [type_map.get(t["type"], "Any") for t in meta["anyOf"]]
342
+ typ = " | ".join(set(types))
343
+ else:
344
+ typ = "Any"
345
+
346
+ default = meta.get("default", None)
347
+ default_repr = repr(default)
348
+
349
+ params.append(f"{name}: {typ} = {default_repr}")
350
+
351
+ # join into signature
352
+ param_str = ",\n ".join(params)
353
+ return f"def {func_name}(\n {param_str},\n):"
@@ -0,0 +1,43 @@
1
+ import asyncio
2
+
3
+ from rich import print
4
+ from universal_mcp.agentr.registry import AgentrRegistry
5
+ from universal_mcp.agents.llm import load_chat_model
6
+ from universal_mcp.agents.shared.tool_node import build_tool_node_graph
7
+ from universal_mcp.logger import setup_logger
8
+
9
+
10
+ async def main():
11
+ """
12
+ An example of how to run the tool_node graph independently.
13
+ """
14
+ setup_logger(level="INFO")
15
+
16
+ user_input = "What are the topics of my meetings today from Google Calendar and who are the attendees? Give a 1-line context for each attendee using LinkedIn or web search."
17
+
18
+ print(f"▶️ User Task: [bold cyan]'{user_input}'[/bold cyan]\n")
19
+
20
+ llm = load_chat_model("azure/gpt-4.1", thinking=False)
21
+ registry = AgentrRegistry()
22
+
23
+ graph = build_tool_node_graph(llm=llm, registry=registry)
24
+
25
+ initial_state = {"original_task": user_input}
26
+
27
+ print("🚀 Invoking the tool selection graph...")
28
+ final_state = await graph.ainvoke(initial_state)
29
+
30
+ execution_plan = final_state.get("execution_plan")
31
+
32
+ print("\n[bold green]✅ Graph execution complete![/bold green]")
33
+ print("\n--- Final Execution Plan (Selected Tools) ---")
34
+ if execution_plan:
35
+ print(execution_plan)
36
+ else:
37
+ print("[bold red]No execution plan was created.[/bold red]")
38
+ if messages := final_state.get("messages"):
39
+ print(f"Final Message: {messages[-1].content}")
40
+
41
+
42
+ if __name__ == "__main__":
43
+ asyncio.run(main())
@@ -1,132 +1,83 @@
1
- TASK_DECOMPOSITION_PROMPT = """
2
- You are an expert planner. Your goal is to consolidate a complex user request into the minimum number of high-level sub-tasks required. Each sub-task should correspond to a major, consolidated action within a single target application.
1
+ TOOL_SEARCH_QUERIES_PROMPT = """
2
+ You are an expert at breaking down a complex user task into a list of simple, atomic search queries for finding tools. Your goal is to generate a list of queries that will cover all aspects of the user's request.
3
3
 
4
4
  **CORE PRINCIPLES:**
5
- 1. **App-Centric Grouping:** Group all related actions for a single application into ONE sub-task.
6
- 2. **Focus on Data Handoffs:** A good decomposition often involves one sub-task to *retrieve* information and a subsequent sub-task to *use* that information.
7
- 3. **Assume Internal Capabilities:** Do NOT create sub-tasks for abstract cognitive work like 'summarize' or 'analyze'.
8
- 4. **Simplify Single Actions:** If the user's task is already a single, simple action, the output should be a single sub-task that concisely describes that action. Do not make it identical to the user's input.
9
- 5. **General purpose sub tasks:** You also need to realise that these subtasks are going to be used to search for tools and apps. And the names and description of these tools and apps are going to be general in nature so the sub tasks should not be too specific. The task which you will get may be specific in nature but the sub taks must be general.
5
+ 1. **Deconstruct the Task:** Analyze the user's request and identify the distinct actions or sub-tasks required.
6
+ 2. **Include Application Context:** If the user mentions a specific application (e.g., Gmail, Google Docs, Exa), include it in the query.
7
+ 3. **Focus on the Action:** Each query must describe a general capability. It should combine the core action (verb) and the general type of object it acts on (e.g., "create document", "get pull requests", "web search").
8
+ 4. **STRIP SPECIFIC DETAILS:** **This is critical.** Do NOT include specific data, parameters, names, or details from the user's prompt in your queries. Your goal is to find a general tool, not to run the specific command.
9
+
10
10
  **--- EXAMPLES ---**
11
11
 
12
12
  **EXAMPLE 1:**
13
13
  - **User Task:** "Create a Google Doc summarizing the last 5 merged pull requests in my GitHub repo universal-mcp/universal-mcp."
14
- - **CORRECT DECOMPOSITION:**
15
- - "Fetch the last 5 merged pull requests from the GitHub repository 'universal-mcp/universal-mcp'."
16
- - "Create a new Google Doc containing the summary of the pull requests."
14
+ - **CORRECT QUERIES:**
15
+ - "github get pull requests from repository"
16
+ - "google docs create document"
17
+ - "google docs append text to document"
18
+ - **INCORRECT QUERIES:**
19
+ - "github get pull requests from universal-mcp/universal-mcp" (Contains specific repo name)
20
+ - "google docs create 'summary' document" (Contains specific document title)
21
+
17
22
 
18
23
  **EXAMPLE 2:**
19
- - **User Task:** "Find the best restaurants in Goa using perplexity web search."
20
- - **CORRECT DECOMPOSITION:**
21
- - "Perform a web search using Perplexity to find the best restaurants in Goa."
24
+ - **User Task:** "Find the best restaurants in Goa using exa web search, then email the list to my friend at test@example.com."
25
+ - **CORRECT QUERIES:**
26
+ - "exa web search"
27
+ - "send email"
28
+ - **INCORRECT QUERIES:**
29
+ - "exa search for best restaurants in Goa" (Contains specific search details)
30
+ - "email list to test@example.com" (Contains specific parameters)
31
+
32
+ **EXAMPLE 3:**
33
+ - **User Task:** "add an event to my google calendar at 2pm called 'Walk in the park'?"
34
+ - **CORRECT QUERIES:**
35
+ - "google calendar create calendar event"
36
+ - **INCORRECT QUERIES:**
37
+ - "google calendar create event 'Walk in the park' at 2pm" (Contains specific event details)
22
38
 
23
39
  **--- YOUR TASK ---**
24
40
 
25
41
  **USER TASK:**
26
42
  "{task}"
27
43
 
28
- **YOUR DECOMPOSITION (as a list of strings):**
44
+ **YOUR SEARCH QUERIES (as a list of strings):**
29
45
  """
30
46
 
31
-
32
- APP_SEARCH_QUERY_PROMPT = """
33
- You are an expert at selecting an application to perform a specific sub-task. Your goal is to generate a concise query for an app search engine.
34
-
35
- Analyze the current sub-task in the context of the original user goal and the ENTIRE PLAN so far.
36
-
37
- **CORE INSTRUCTION:** If any application already used in the plan is capable of performing the current sub-task, your query MUST BE the name of that application to ensure continuity and efficiency. Otherwise, generate a concise query for the category of application needed.
38
-
39
- **--- EXAMPLES ---**
40
-
41
- **EXAMPLE 1: Reusing an app from two steps ago**
42
- - **Original User Task:** "Find my latest order confirmation in Gmail, search for reviews of the main product on perplexity, and then send an email to ankit@agentr.dev telling about the reviews"
43
- - **Plan So Far:**
44
- - The sub-task 'Find order confirmation in Gmail' was assigned to app 'google_mail'.
45
- - The sub-task 'Search for product reviews on perplexity' was assigned to app 'perplexity'.
46
- - **Current Sub-task:** "send an email to ankit@agentr.dev"
47
- - **CORRECT QUERY:** "google_mail"
48
-
49
- **EXAMPLE 2: First Step (No previous context)**
50
- - **Original User Task:** "Find the best restaurants in Goa."
51
- - **Plan So Far:** None. This is the first step.
52
- - **Current Sub-task:** "Perform a web search to find the best restaurants in Goa."
53
- - **CORRECT QUERY:** "web search"
54
-
55
- **--- YOUR TASK ---**
56
-
57
- **Original User Task:**
58
- "{original_task}"
59
-
60
- **Plan So Far:**
61
- {plan_context}
62
-
63
- **Current Sub-task:**
64
- "{sub_task}"
65
-
66
- **YOUR CONCISE APP SEARCH QUERY:**
67
- """
68
-
69
-
70
- TOOL_SEARCH_QUERY_PROMPT = """
71
- You are an expert at summarizing the core *action* of a sub-task into a concise query for finding a tool. This query should ignore any application names.
47
+ APP_SELECTION_PROMPT = """
48
+ You are an AI assistant that selects the most appropriate applications (apps) from a list to accomplish a user's task.
72
49
 
73
50
  **INSTRUCTIONS:**
74
- 1. Focus only on the verb or action being performed in the sub-task.
75
- 2. Include key entities related to the action.
76
- 3. Do NOT include the names of applications (e.g., "Perplexity", "Gmail").
77
- 4. You also need to realise that this query is going to be used to search for tools in a particular app. And the names and description of these tools are going to be general in nature so the query should not be too specific. The sub task which you will get may be specific in nature but the query must be general.
78
-
79
- **EXAMPLES:**
80
- - **Sub-task:** "Perform a web search using Perplexity to find the best restaurants in Goa."
81
- - **Query:** "web search for restaurants"
82
-
83
- - **Sub-task:** "Fetch all marketing emails received from Gmail in the last 7 days."
84
- - **Query:** "get emails by date"
85
-
86
- - **Sub-task:** "Create a new Google Doc and append a summary."
87
- - **Query:** "create document, append text"
88
-
89
- **SUB-TASK:**
90
- "{sub_task}"
91
-
92
- **YOUR CONCISE TOOL SEARCH QUERY:**
93
- """
94
-
95
- REVISE_DECOMPOSITION_PROMPT = """
96
- You are an expert planner who revises plans that have failed. Your previous attempt to break down a task resulted in a sub-task that could not be matched with any available tools.
97
-
98
- **INSTRUCTIONS:**
99
- 1. Analyze the original user task and the failed sub-task.
100
- 2. Generate a NEW, alternative decomposition of the original task.
101
- 3. This new plan should try to achieve the same overall goal but with different, perhaps broader or more combined, sub-tasks to increase the chance of finding a suitable tool.
51
+ 1. Carefully review the original user task to understand the complete goal.
52
+ 2. Examine the list of available apps, their IDs, and their descriptions.
53
+ 3. Select ALL app IDs that are necessary to complete the entire task.
54
+ 4. If the user's task mentions a specific app, you MUST select it.
55
+ 5. If no apps are a good fit, return an empty list.
102
56
 
103
57
  **ORIGINAL USER TASK:**
104
58
  "{task}"
105
59
 
106
- **FAILED SUB-TASK FROM PREVIOUS PLAN:**
107
- "{failed_sub_task}"
60
+ **AVAILABLE APPS:**
61
+ {app_candidates}
108
62
 
109
- **YOUR NEW, REVISED DECOMPOSITION (as a list of strings):**
63
+ **YOUR SELECTED APP ID(s) (as a list of strings):**
110
64
  """
111
65
 
112
-
113
66
  TOOL_SELECTION_PROMPT = """
114
- You are an AI assistant that selects the most appropriate tool(s) from a list to accomplish a specific sub-task.
67
+ You are an AI assistant that selects the most appropriate tool(s) from a list to accomplish a user's overall task.
115
68
 
116
69
  **INSTRUCTIONS:**
117
- 1. Carefully review the sub-task to understand the required action.
118
- 2. Examine the list of available tools and their descriptions.
119
- 3. Select the best tool ID that matches the sub-task. You are encouraged to select multiple tools if there are multiple tools with similar capabilties
120
- or names. It is always good to have more tools than having insufficent tools.
121
- 4. If no tool is a good fit, return an empty list.
122
- 5. Only return the tool IDs.
123
- 6. You should understand that the sub task maybe specific in nature but the tools are made to be general purpose and therefore the tool_candidates you will get will be very general purpose but that should not stop you from selecting the tools as these tools will be given to a very smart agent who will be able to use these tools for the specific sub-taks
70
+ 1. Carefully review the original user task to understand the complete goal.
71
+ 2. Examine the list of available tools, their IDs, and their descriptions. These tools have been found using a search based on the task.
72
+ 3. Select all tool IDs that are necessary to complete the entire task. It is critical to select all tools needed for a multi-step task.
73
+ 4. If no tools are a good fit for the task, return an empty list.
74
+ 5. Only return the tool IDs. The tools are general purpose, but you are smart enough to see how they can be used for the specific task.
124
75
 
125
- **SUB-TASK:**
126
- "{sub_task}"
76
+ **ORIGINAL USER TASK:**
77
+ "{task}"
127
78
 
128
79
  **AVAILABLE TOOLS:**
129
80
  {tool_candidates}
130
81
 
131
- **YOUR SELECTED TOOL ID(s):**
132
- """
82
+ **YOUR SELECTED TOOL ID(s) (as a list of strings):**
83
+ """