-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
fix: aworkflow loop expansion, concurrent turn-tool race, MCP skill-gate discovery #3319
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -283,6 +283,18 @@ class MCP: | |
| ``` | ||
| """ | ||
|
|
||
| # Process-level registry of active MCP server names (sanitized prefixes), | ||
| # mirroring how tools/registry.py tracks tool names. Populated when a | ||
| # server is namespaced via with_tool_prefix(), so skills' capability | ||
| # validator can discover which MCP servers are actually connected | ||
| # (issue #3307) instead of always seeing an empty set. | ||
| _active_server_names: set = set() | ||
|
|
||
| @classmethod | ||
| def list_active_server_names(cls) -> set: | ||
|
Comment on lines
+286
to
+294
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a prefixed MCP server disconnects or belongs to another Knowledge Base Used: praisonai-agents Core Library |
||
| """Return the set of active MCP server names (sanitized prefixes).""" | ||
| return set(cls._active_server_names) | ||
|
|
||
| def __init__(self, command_or_string=None, args=None, *, command=None, timeout=60, debug=False, | ||
| allowed_tools: Optional[List[str]] = None, disabled_tools: Optional[List[str]] = None, **kwargs): | ||
| """ | ||
|
|
@@ -834,6 +846,14 @@ def with_tool_prefix(self, prefix: str) -> "MCP": | |
|
|
||
| self._tool_prefix = sanitized | ||
|
|
||
| # Track this server name so skills' capability validator can see that | ||
| # it is connected (issue #3307). Register both the caller-supplied | ||
| # name and its sanitized prefix, since skill requirements may use | ||
| # either spelling. | ||
| MCP._active_server_names.add(sanitized) | ||
| if prefix: | ||
| MCP._active_server_names.add(prefix) | ||
|
|
||
| # Rename already-generated callable tools. Dispatch inside each | ||
| # wrapper closes over the original tool name, so only the public | ||
| # __name__/__qualname__ needs updating for schema construction. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -616,11 +616,33 @@ async def aworkflow(self) -> AsyncGenerator[str, None]: | |
| start_task = list(self.tasks.values())[0] | ||
| logging.debug(f"No start task marked, using first task: {start_task.name}") | ||
|
|
||
| # If loop type and no input_file, default to tasks.csv | ||
| if start_task and start_task.task_type == "loop" and not start_task.input_file: | ||
| start_task.input_file = "tasks.csv" | ||
|
|
||
| # --- If loop + input_file, read file & create tasks using consolidated helper | ||
| # Mirrors the sync workflow() pre-expansion so async workflows with a | ||
| # CSV-driven loop start task run once per row instead of a single time. | ||
| if start_task and start_task.task_type == "loop" and getattr(start_task, "input_file", None): | ||
| try: | ||
| parent_loop_task = start_task | ||
| parent_input_file = parent_loop_task.input_file | ||
| self._create_loop_subtasks(parent_loop_task, decision_mode=True) | ||
| subtasks = [ | ||
| t for t in self.tasks.values() | ||
| if t.name.startswith(parent_loop_task.name + "_") | ||
| ] | ||
| if subtasks: | ||
| parent_loop_task.status = "completed" | ||
| parent_loop_task._subtasks_created = True | ||
| start_task = next((t for t in subtasks if t.is_start), subtasks[0]) | ||
| logging.info(f"Created {len(subtasks)} tasks from: {parent_input_file}") | ||
| except Exception as e: | ||
| logging.error(f"Failed to read file tasks: {e}") | ||
|
Comment on lines
+625
to
+641
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the async loop's CSV is missing, unreadable, or malformed, subtask creation fails but this block only logs the error and continues with the original loop task. The later fallback can advance to downstream tasks without processing any CSV rows, making the workflow report progress despite silently dropping the loop work. Knowledge Base Used: praisonai-agents Core Library |
||
|
|
||
| current_task = start_task | ||
| visited_tasks = set() | ||
|
|
||
| # TODO: start task with loop feature is not available in aworkflow method | ||
|
|
||
| while current_task: | ||
| current_iter += 1 | ||
| if current_iter > self.max_iter: | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.