universal-mcp-agents 0.1.24rc1__tar.gz → 0.1.24rc2__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.24rc1 → universal_mcp_agents-0.1.24rc2}/PKG-INFO +1 -1
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/pyproject.toml +1 -1
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/agent.py +14 -17
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/prompts.py +9 -3
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/sandbox.py +2 -2
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/tools.py +1 -1
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/utils.py +47 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/applications/llm/app.py +62 -11
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/uv.lock +1 -1
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/.github/workflows/evals.yml +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/.github/workflows/lint.yml +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/.github/workflows/release-please.yml +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/.github/workflows/tests.yml +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/.gitignore +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/.pre-commit-config.yaml +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/GEMINI.md +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/PROMPTS.md +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/README.md +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/bump_and_release.sh +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/__init__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/dataset.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/datasets/exact.jsonl +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/datasets/tasks.jsonl +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/datasets/test.jsonl +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/evaluators.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/prompts.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/run.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/utils.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/tests/test_agents.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/tests/test_sandbox.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/__init__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/base.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/bigtool/__init__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/bigtool/__main__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/bigtool/agent.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/bigtool/context.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/bigtool/graph.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/bigtool/prompts.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/bigtool/state.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/bigtool/tools.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/builder/__main__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/builder/builder.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/builder/helper.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/builder/prompts.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/builder/state.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/cli.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/__init__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/__main__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/config.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/langgraph_agent.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/llm_tool.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/codeact0/state.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/hil.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/llm.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/react.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/sandbox.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/shared/__main__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/shared/prompts.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/shared/tool_node.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/simple.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/utils.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/applications/filesystem/__init__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/applications/filesystem/app.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/applications/llm/__init__.py +0 -0
- {universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/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.24rc2
|
|
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
|
|
@@ -27,7 +27,7 @@ from universal_mcp.agents.codeact0.tools import (
|
|
|
27
27
|
create_meta_tools,
|
|
28
28
|
enter_agent_builder_mode,
|
|
29
29
|
)
|
|
30
|
-
from universal_mcp.agents.codeact0.utils import build_anthropic_cache_message, get_connected_apps_string, strip_thinking
|
|
30
|
+
from universal_mcp.agents.codeact0.utils import build_anthropic_cache_message, get_connected_apps_string, strip_thinking, extract_plan_parameters
|
|
31
31
|
from universal_mcp.agents.llm import load_chat_model
|
|
32
32
|
from universal_mcp.agents.utils import convert_tool_ids_to_dict, filter_retry_on, get_message_text
|
|
33
33
|
|
|
@@ -349,16 +349,23 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
349
349
|
# Use generated metadata if available
|
|
350
350
|
final_name = state.get("agent_name") or function_name
|
|
351
351
|
final_description = state.get("agent_description") or f"Generated agent: {function_name}"
|
|
352
|
+
add_context = state.get("add_context", {})
|
|
353
|
+
if "functions" not in add_context:
|
|
354
|
+
add_context["functions"] = []
|
|
355
|
+
add_context["functions"].append(func_code)
|
|
352
356
|
|
|
353
357
|
# Save or update an Agent using the helper registry
|
|
354
358
|
try:
|
|
355
359
|
if not self.agent_builder_registry:
|
|
356
360
|
raise ValueError("AgentBuilder registry is not configured")
|
|
357
361
|
|
|
362
|
+
plan_params = extract_plan_parameters(state["plan"])
|
|
363
|
+
|
|
358
364
|
# Build instructions payload embedding the plan and function code
|
|
359
365
|
instructions_payload = {
|
|
360
366
|
"plan": state["plan"],
|
|
361
367
|
"script": func_code,
|
|
368
|
+
"params": plan_params,
|
|
362
369
|
}
|
|
363
370
|
|
|
364
371
|
# Convert tool ids list to dict
|
|
@@ -395,22 +402,10 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
395
402
|
tool_call_id="exit_builder_1",
|
|
396
403
|
)
|
|
397
404
|
if self.eval_mode:
|
|
398
|
-
human_msg = HumanMessage(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
return Command(
|
|
402
|
-
goto="call_model",
|
|
403
|
-
update={
|
|
404
|
-
"messages": [mock_assistant_message, mock_exit_tool_response, human_msg],
|
|
405
|
-
"agent_builder_mode": "normal",
|
|
406
|
-
},
|
|
407
|
-
)
|
|
408
|
-
return Command(
|
|
409
|
-
update={
|
|
410
|
-
"messages": [mock_assistant_message, mock_exit_tool_response],
|
|
411
|
-
"agent_builder_mode": "normal",
|
|
412
|
-
}
|
|
413
|
-
)
|
|
405
|
+
human_msg = HumanMessage(content="Call the generated agent function (without redeclaring it) and check whether it works as expected")
|
|
406
|
+
return Command(goto="call_model", update={"messages": [mock_assistant_message, mock_exit_tool_response, human_msg], "agent_builder_mode": "normal", "add_context": add_context})
|
|
407
|
+
else:
|
|
408
|
+
return Command(update={"messages": [mock_assistant_message, mock_exit_tool_response], "agent_builder_mode": "normal", "add_context": add_context})
|
|
414
409
|
|
|
415
410
|
writer(
|
|
416
411
|
{
|
|
@@ -422,6 +417,7 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
422
417
|
"update": bool(self.agent),
|
|
423
418
|
"name": final_name,
|
|
424
419
|
"description": final_description,
|
|
420
|
+
"add_context": add_context,
|
|
425
421
|
},
|
|
426
422
|
}
|
|
427
423
|
)
|
|
@@ -450,6 +446,7 @@ class CodeActPlaybookAgent(BaseAgent):
|
|
|
450
446
|
update={
|
|
451
447
|
"messages": [mock_assistant_message, mock_exit_tool_response],
|
|
452
448
|
"agent_builder_mode": "normal",
|
|
449
|
+
"add_context": add_context,
|
|
453
450
|
}
|
|
454
451
|
)
|
|
455
452
|
|
|
@@ -9,7 +9,6 @@ Your job is to answer the user's question or perform the task they ask for.
|
|
|
9
9
|
- 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 in the chat. NEVER write a string or sequences of strings yourself and print it.
|
|
10
10
|
- For task requiring operations or access to external resources, you should achieve the task by executing Python code snippets.
|
|
11
11
|
- You have access to `execute_ipython_cell` tool that allows you to execute Python code in an IPython notebook cell.
|
|
12
|
-
- For writing, text/document generation (like HTML/markdown document generation) or language processing tasks DO NOT answer directly. Instead you MUST use `execute_ipython_cell` tool with the llm functions provided to you for tasks like summarizing, text generation, classification, data extraction from text or unstructured data, etc. Avoid hardcoded approaches to classification, data extraction, or creative writing.
|
|
13
12
|
- You also have access to two tools for finding and loading more python functions- `search_functions` and `load_functions`, which you must use for finding functions for using different external applications or additional functionality.
|
|
14
13
|
- Prioritize connected applications over unconnected ones from the output of `search_functions`. However, if the user specifically asks for an application, you MUST use that irrespective of connection status.
|
|
15
14
|
- When multiple relevant apps are connected, or none of the apps are connected, YOU MUST ask the user to choose the application(s). The search results may also inform you when such a case occurs, and you must stop and ask the user if multiple apps are relevant.
|
|
@@ -19,6 +18,7 @@ Your job is to answer the user's question or perform the task they ask for.
|
|
|
19
18
|
- Structure your code into multiple small, well-defined functions within a single execution snippet. This ensures modularity and makes it easier to debug or update specific logic without rewriting or re-executing large portions of code. You can only rewrite the function/portion that you need to edit since the others are retained in context.
|
|
20
19
|
- Every snippet you execute using `execute_ipython_cell` MUST follow this structure:
|
|
21
20
|
- Break down logic into 3-5 small helper functions (30 lines each max)
|
|
21
|
+
- For definining large constants (multiline strings, dictionaries, etc) do it either at the global level or in a separate helper function responsible only for defining the object. Ensures that you do not have to rewrite large parts of the code multiple times during debugging/modifying.
|
|
22
22
|
- Each helper function should do ONE thing
|
|
23
23
|
- Example:
|
|
24
24
|
def _helper_function_1(...):
|
|
@@ -38,7 +38,7 @@ Your job is to answer the user's question or perform the task they ask for.
|
|
|
38
38
|
**Code Writing Rules:**
|
|
39
39
|
- The code you write will be executed in a sandbox environment, and you can use the output of previous executions in your code. Variables, defined functions, imports, loaded functions are retained.
|
|
40
40
|
- DO NOT use the code execution to communicate/show anything to the user. The user is not able to see the output of the code cells, it is only for your processing and actions. Similarly, you should only use print/smart_print for your own analysis, the user does not get the output.
|
|
41
|
-
- Whenever you need to generate a large body of text, such as a document, an HTML file, or a report, use llm functions and save the output file using save/upload functions. Do not generate text yourself and do not print the entire text in order to save your memory.
|
|
41
|
+
- Whenever you need to generate a large body of text, such as a document, an HTML file, or a report, use llm functions (see critical section below on LLM functions) and save the output file using save/upload functions. Do not generate text yourself and do not print the entire text in order to save your memory.
|
|
42
42
|
- 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.
|
|
43
43
|
- 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.
|
|
44
44
|
- You can only import libraries that come pre-installed with Python. However, do consider searching for external functions first, using the search and load tools to access them in the code.
|
|
@@ -50,12 +50,17 @@ Your job is to answer the user's question or perform the task they ask for.
|
|
|
50
50
|
- Final output summarization after analysis
|
|
51
51
|
- Anything that's just formatted print statements
|
|
52
52
|
|
|
53
|
+
**Critical:LLM Function Usage Rules:**
|
|
54
|
+
- For text creation or document generation (e.g., HTML, Markdown, textual content for document/email), do not respond directly; you must use execute_ipython_cell with llm__generate_text.
|
|
55
|
+
- For any data extraction, text analysis, or classification task, always use the LLM functions (llm__extract_data, llm__classify_data, or llm__call_llm)-never rely on regex, manual parsing, or heuristic methods.
|
|
56
|
+
- Use llm__call_llm only as a fallback when the other functions don't match the task exactly.
|
|
57
|
+
|
|
53
58
|
**Final Output Requirements:**
|
|
54
59
|
- Once you have all the information about the task, return the text directly to user in markdown format. Do NOT call `execute_ipython_cell` or any LLM tools again just for summarization. Do NOT use llm__generate_text for this purpose.
|
|
55
60
|
- Always respond in github flavoured markdown format.
|
|
56
61
|
- For charts and diagrams, use mermaid chart in markdown directly.
|
|
57
62
|
- 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.
|
|
58
|
-
- For file types like images, audio, documents, etc., you must use the `upload_file` tool to upload the file to the server and render the link in the markdown response.
|
|
63
|
+
- For file types like images, audio, documents, etc., you must use the `upload_file` tool to upload the file to the server and render the link/path in the markdown response.
|
|
59
64
|
"""
|
|
60
65
|
|
|
61
66
|
AGENT_BUILDER_PLANNING_PROMPT = """TASK: Analyze the conversation history and code execution to create a step-by-step non-technical plan for a reusable function.
|
|
@@ -115,6 +120,7 @@ Rules-
|
|
|
115
120
|
2) One top-level orchestrator function that calls the step functions in sequence to achieve the plan objectives.
|
|
116
121
|
- The orchestrator function's parameters **must exactly match the external variables** in the agent plan (the ones marked with backticks `` `variable_name` ``). Provide defaults exactly as specified in the plan when present. Variables in italics (i.e. enclosed in *...*) are internal and must not be orchestrator parameters.
|
|
117
122
|
- The orchestrator function MUST be declared with `def` or `async def` and be directly runnable with a single Python command (e.g., `image_generator(...)`). If it is async, assume the caller will `await` it.
|
|
123
|
+
- NEVER use asyncio or asyncio.run(). The code is executed in a ipython environment, so using await is enough.
|
|
118
124
|
- Step functions should accept only the inputs they need, return explicit outputs, and pass intermediate results forward via return values—not globals.
|
|
119
125
|
- Name functions in snake_case derived from their purpose/step. Use keyword arguments in calls; avoid positional-only calls.
|
|
120
126
|
- Keep the code self-contained and executable. Put imports at the top of the code. Do not nest functions unless strictly necessary.
|
|
@@ -125,8 +125,8 @@ async def handle_execute_ipython_cell(
|
|
|
125
125
|
|
|
126
126
|
Returns (output, new_context, new_add_context).
|
|
127
127
|
"""
|
|
128
|
-
|
|
129
|
-
context =
|
|
128
|
+
context = {**tools_context, **effective_existing_context}
|
|
129
|
+
context = inject_context(effective_previous_add_context, context)
|
|
130
130
|
if inspect.iscoroutinefunction(eval_fn):
|
|
131
131
|
output, new_context, new_add_context = await eval_fn(code, context, effective_previous_add_context, 180)
|
|
132
132
|
else:
|
|
@@ -420,7 +420,7 @@ def create_meta_tools(tool_registry: AgentrRegistry) -> dict[str, Any]:
|
|
|
420
420
|
|
|
421
421
|
def upload_file(file_name: str, mime_type: str, base64_data: str) -> dict:
|
|
422
422
|
"""
|
|
423
|
-
Uploads a file to the server
|
|
423
|
+
Uploads a file to the server.
|
|
424
424
|
|
|
425
425
|
Args:
|
|
426
426
|
file_name (str): The name of the file to upload.
|
|
@@ -503,3 +503,50 @@ async def get_connected_apps_string(registry) -> str:
|
|
|
503
503
|
return "\n".join(apps_list)
|
|
504
504
|
except Exception:
|
|
505
505
|
return "Unable to retrieve connected applications."
|
|
506
|
+
|
|
507
|
+
def extract_plan_parameters(plan_steps: list[str]) -> list[dict[str, Any]]:
|
|
508
|
+
"""
|
|
509
|
+
Extracts parameters from plan steps and formats them into a list of OpenAPI-like parameter objects.
|
|
510
|
+
|
|
511
|
+
Parses parameters enclosed in backticks, identifying their name, if they are required, and any default values.
|
|
512
|
+
e.g., `variable` -> {"name": "variable", "required": True}
|
|
513
|
+
e.g., `variable(default = 'value')` -> {"name": "variable", "required": False, "default": "value"}
|
|
514
|
+
"""
|
|
515
|
+
parameters_map: dict[str, Any] = {}
|
|
516
|
+
# Regex to find anything inside backticks
|
|
517
|
+
outer_pattern = re.compile(r"`([^`]+)`")
|
|
518
|
+
# Regex to parse parameters with default values
|
|
519
|
+
inner_pattern = re.compile(r"^\s*(\w+)\s*\(\s*default\s*=\s*(.+)\s*\)\s*$")
|
|
520
|
+
|
|
521
|
+
for step in plan_steps:
|
|
522
|
+
matches = outer_pattern.findall(step)
|
|
523
|
+
for match in matches:
|
|
524
|
+
param_str = match.strip()
|
|
525
|
+
inner_match = inner_pattern.match(param_str)
|
|
526
|
+
|
|
527
|
+
if inner_match:
|
|
528
|
+
# Parameter with a default value
|
|
529
|
+
name, default_val_str = inner_match.groups()
|
|
530
|
+
default_value: Any
|
|
531
|
+
try:
|
|
532
|
+
# Safely evaluate the default value (e.g., 'string', 123, True)
|
|
533
|
+
default_value = ast.literal_eval(default_val_str)
|
|
534
|
+
except (ValueError, SyntaxError):
|
|
535
|
+
# If it's not a valid literal, treat it as a string
|
|
536
|
+
default_value = default_val_str
|
|
537
|
+
parameters_map[name] = {"required": False, "default": default_value}
|
|
538
|
+
else:
|
|
539
|
+
# Required parameter (no default value)
|
|
540
|
+
name = param_str
|
|
541
|
+
# Only set as required if it hasn't been defined with a default already
|
|
542
|
+
if name not in parameters_map:
|
|
543
|
+
parameters_map[name] = {"required": True}
|
|
544
|
+
|
|
545
|
+
# Convert the map to the final list format
|
|
546
|
+
final_parameters = []
|
|
547
|
+
for name, details in sorted(parameters_map.items()):
|
|
548
|
+
param_obj = {"name": name}
|
|
549
|
+
param_obj.update(details)
|
|
550
|
+
final_parameters.append(param_obj)
|
|
551
|
+
|
|
552
|
+
return final_parameters
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
|
-
from typing import Any, Literal, cast
|
|
2
|
+
from typing import Any, Literal, cast, Optional, List
|
|
3
3
|
|
|
4
4
|
from langchain.agents import create_agent
|
|
5
|
-
from pydantic import BaseModel, Field
|
|
5
|
+
from pydantic import BaseModel, Field, create_model
|
|
6
6
|
from universal_mcp.applications.application import BaseApplication
|
|
7
7
|
|
|
8
8
|
from universal_mcp.agents.llm import load_chat_model
|
|
@@ -10,6 +10,59 @@ from universal_mcp.agents.llm import load_chat_model
|
|
|
10
10
|
MAX_RETRIES = 3
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def _pydantic_model_from_json_schema(schema: dict[str, Any]) -> type[BaseModel]:
|
|
14
|
+
"""Create a Pydantic model from a JSON schema (subset support).
|
|
15
|
+
|
|
16
|
+
Supported keywords: type, properties, required, items, description, title.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def to_type(subschema: dict[str, Any]) -> Any:
|
|
20
|
+
stype = subschema.get("type")
|
|
21
|
+
if stype == "object" or (stype is None and "properties" in subschema):
|
|
22
|
+
title = subschema.get("title", "SubObject")
|
|
23
|
+
props: dict[str, dict[str, Any]] = subschema.get("properties", {})
|
|
24
|
+
required: list[str] = subschema.get("required", [])
|
|
25
|
+
fields: dict[str, tuple[Any, Any]] = {}
|
|
26
|
+
for name, prop_schema in props.items():
|
|
27
|
+
t = to_type(prop_schema)
|
|
28
|
+
if name in required:
|
|
29
|
+
fields[name] = (t, ...)
|
|
30
|
+
else:
|
|
31
|
+
fields[name] = (Optional[t], None) # type: ignore[index]
|
|
32
|
+
return create_model(title, **fields) # type: ignore[return-value]
|
|
33
|
+
if stype == "array":
|
|
34
|
+
item_schema = subschema.get("items", {"type": "string"})
|
|
35
|
+
return List[to_type(item_schema)] # type: ignore[index]
|
|
36
|
+
if stype == "string":
|
|
37
|
+
return str
|
|
38
|
+
if stype == "integer":
|
|
39
|
+
return int
|
|
40
|
+
if stype == "number":
|
|
41
|
+
return float
|
|
42
|
+
if stype == "boolean":
|
|
43
|
+
return bool
|
|
44
|
+
if stype == "null":
|
|
45
|
+
return Optional[Any]
|
|
46
|
+
# Fallback to Any for unsupported/omitted types
|
|
47
|
+
return Any
|
|
48
|
+
|
|
49
|
+
title = schema.get("title", "Output")
|
|
50
|
+
if schema.get("type") == "object" or "properties" in schema:
|
|
51
|
+
props = schema.get("properties", {})
|
|
52
|
+
required = schema.get("required", [])
|
|
53
|
+
fields: dict[str, tuple[Any, Any]] = {}
|
|
54
|
+
for name, prop_schema in props.items():
|
|
55
|
+
t = to_type(prop_schema)
|
|
56
|
+
if name in required:
|
|
57
|
+
fields[name] = (t, ...)
|
|
58
|
+
else:
|
|
59
|
+
fields[name] = (Optional[t], None) # type: ignore[index]
|
|
60
|
+
return create_model(title, **fields) # type: ignore[return-value]
|
|
61
|
+
# Non-object root types
|
|
62
|
+
root_type = to_type(schema)
|
|
63
|
+
return create_model(title, __root__=(root_type, ...)) # type: ignore[return-value]
|
|
64
|
+
|
|
65
|
+
|
|
13
66
|
def _get_context_as_string(source: Any | list[Any] | dict[str, Any]) -> str:
|
|
14
67
|
"""Converts context to a string representation.
|
|
15
68
|
|
|
@@ -231,12 +284,13 @@ class LlmApp(BaseApplication):
|
|
|
231
284
|
|
|
232
285
|
model = load_chat_model("azure/gpt-5-mini", temperature=0, disable_streaming=True, tags=("quiet",))
|
|
233
286
|
|
|
287
|
+
PModel = _pydantic_model_from_json_schema(output_schema)
|
|
234
288
|
response = (
|
|
235
|
-
model.with_structured_output(
|
|
289
|
+
model.with_structured_output(PModel)
|
|
236
290
|
.with_retry(stop_after_attempt=MAX_RETRIES)
|
|
237
291
|
.invoke(prompt, stream=False)
|
|
238
292
|
)
|
|
239
|
-
return cast(dict[str, Any], response)
|
|
293
|
+
return cast(dict[str, Any], response.model_dump())
|
|
240
294
|
|
|
241
295
|
def call_llm(
|
|
242
296
|
self,
|
|
@@ -284,13 +338,10 @@ class LlmApp(BaseApplication):
|
|
|
284
338
|
|
|
285
339
|
model = load_chat_model("azure/gpt-5-mini", temperature=0, disable_streaming=True, tags=("quiet",))
|
|
286
340
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
)
|
|
292
|
-
result = agent.invoke({"messages": [{"role": "user", "content": prompt}]}, stream=False)
|
|
293
|
-
return result["structured_response"]
|
|
341
|
+
PModel = _pydantic_model_from_json_schema(output_schema)
|
|
342
|
+
model_with_structure = model.with_structured_output(PModel)
|
|
343
|
+
response = model_with_structure.with_retry(stop_after_attempt=MAX_RETRIES).invoke(prompt, stream=False)
|
|
344
|
+
return cast(dict[str, Any], response.model_dump())
|
|
294
345
|
|
|
295
346
|
def list_tools(self):
|
|
296
347
|
return [
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/.github/workflows/evals.yml
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/.github/workflows/lint.yml
RENAMED
|
File without changes
|
|
File without changes
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/.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.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/datasets/exact.jsonl
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/datasets/tasks.jsonl
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/evals/datasets/test.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
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/base.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/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
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/hil.py
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/llm.py
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/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.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/simple.py
RENAMED
|
File without changes
|
{universal_mcp_agents-0.1.24rc1 → universal_mcp_agents-0.1.24rc2}/src/universal_mcp/agents/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|