Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
bb9d06b
Adding design documents and data flow descriptions for sub-workflows
alliscode Aug 15, 2025
c1d50b1
Updating docs.
alliscode Aug 15, 2025
bd23068
Sub-workflow implementation #1. Stuck because of singleton RequestInf…
alliscode Aug 19, 2025
5955c9a
Removed the singleton restriction on RequestInfoExecutor so enable su…
alliscode Aug 19, 2025
eb8723b
Scenarios seem to be working.
alliscode Aug 19, 2025
09d5da6
Sample improved.
alliscode Aug 20, 2025
64383d7
going to have intern add generic response wrappers.
alliscode Aug 20, 2025
2d02fc2
Wrapped responses working.
alliscode Aug 20, 2025
89d2bc5
Non-hardcoded routing is working.
alliscode Aug 20, 2025
7fee8ba
Sample showing external approved and not approved.
alliscode Aug 20, 2025
ecd3e5e
Cleaning up.
alliscode Aug 20, 2025
bb02c82
Updating some samples and user guide.
alliscode Aug 20, 2025
78cebba
Removing old design doc.
alliscode Aug 20, 2025
be677ee
Cleaning up.
alliscode Aug 20, 2025
e9fd564
Merging from main
alliscode Aug 20, 2025
e267374
Adding python-package-setup.md back.
alliscode Aug 20, 2025
f466a0e
Update python/packages/workflow/agent_framework_workflow/_executor.py
alliscode Aug 20, 2025
51aebb8
Update python/packages/workflow/agent_framework_workflow/_validation.py
alliscode Aug 20, 2025
f54c1e8
Removing prints.
alliscode Aug 20, 2025
dd84e38
Fixing lint and type issues.
alliscode Aug 21, 2025
ff09a5c
Fixing lint and type issues.
alliscode Aug 21, 2025
2210550
Merging from updates in main.
alliscode Aug 21, 2025
3ee8f93
Update python/packages/workflow/agent_framework_workflow/_executor.py
alliscode Aug 21, 2025
cc21378
Adding type hints to intercepts decorator.
alliscode Aug 21, 2025
73f7887
Adding type hints to intercepts decorator.
alliscode Aug 21, 2025
3eb8a8e
Removing unused files.
alliscode Aug 21, 2025
1f9fc0f
Fixing issue with sample 5 groupchat with hil.
alliscode Aug 21, 2025
367b188
Removing redundent samples.
alliscode Aug 21, 2025
39edc97
Merge branch 'main' into bentho/sub-workflows
alliscode Aug 21, 2025
9958add
Updates to ensure no conflicting request interceptors and to support …
alliscode Aug 22, 2025
e85a692
Merge branch 'main' into bentho/sub-workflows
alliscode Aug 22, 2025
2c1c531
Fixing pypi errors.
alliscode Aug 22, 2025
cef4cab
Merge branch 'main' into bentho/sub-workflows
ekzhu Aug 23, 2025
fbcba5e
clean up samples
ekzhu Aug 23, 2025
dd4632c
update samples to make it more clear
ekzhu Aug 23, 2025
c1a4088
warning for unhandled request info from sub workflow
ekzhu Aug 23, 2025
c6f9ea0
add logger info
ekzhu Aug 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixing lint and type issues.
  • Loading branch information
alliscode committed Aug 21, 2025
commit ff09a5cae064fc1f9921d46904257eaf0ebb17db
9 changes: 9 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"permissions": {
"allow": [
"Bash(uv run poe:*)"
],
"deny": [],
"ask": []
}
}
118 changes: 62 additions & 56 deletions python/packages/workflow/agent_framework_workflow/_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def __init__(self, id: str | None = None) -> None:
self._id = id or f"{self.__class__.__name__}/{uuid.uuid4()}"

self._handlers: dict[type, Callable[[Any, WorkflowContext], Any]] = {}
self._request_interceptors: dict[type | str, dict] = {}
self._request_interceptors: dict[type | str, list[dict[str, Any]]] = {}
self._discover_handlers()

if not self._handlers:
Expand Down Expand Up @@ -115,7 +115,9 @@ def _discover_handlers(self) -> None:
"condition": getattr(attr, "_intercept_condition", None),
}
request_type = attr._intercepts_request # type: ignore
self._request_interceptors[request_type] = interceptor_info
if request_type not in self._request_interceptors:
self._request_interceptors[request_type] = []
self._request_interceptors[request_type].append(interceptor_info)

def _register_sub_workflow_handler(self) -> None:
"""Register automatic handler for SubWorkflowRequestInfo messages."""
Expand All @@ -133,7 +135,7 @@ async def _handle_sub_workflow_request(
This is only active for executors that have @intercepts_request methods.
"""
# Try to match against registered interceptors
for request_type, interceptor_info in self._request_interceptors.items():
for request_type, interceptor_list in self._request_interceptors.items():
matched = False

# Check type matching
Expand All @@ -148,62 +150,66 @@ async def _handle_sub_workflow_request(
matched = True

if matched:
# Check workflow scope if specified
from_workflow = interceptor_info["from_workflow"]
if from_workflow and request.sub_workflow_id != from_workflow:
continue # Skip this interceptor, wrong workflow

# Check additional condition
condition = interceptor_info["condition"]
if condition and not condition(request):
continue

# Call the interceptor method
method = interceptor_info["method"]
response = await method(request.data, ctx)

# Check if interceptor handled it or needs to forward
if isinstance(response, RequestResponse):
# Add automatic correlation info to the response
correlated_response = RequestResponse._with_correlation(response, request.data, request.request_id)

if correlated_response.is_handled:
# Send response back to sub-workflow
from ._runner_context import Message

response_message = Message(
source_id=self.id,
target_id=request.sub_workflow_id,
data=SubWorkflowResponse(
request_id=request.request_id,
data=correlated_response.data,
),
# Check each interceptor in the list for this request type
for interceptor_info in interceptor_list:
# Check workflow scope if specified
from_workflow = interceptor_info["from_workflow"]
if from_workflow and request.sub_workflow_id != from_workflow:
continue # Skip this interceptor, wrong workflow

# Check additional condition
condition = interceptor_info["condition"]
if condition and not condition(request):
continue

# Call the interceptor method
method = interceptor_info["method"]
response = await method(request.data, ctx)

# Check if interceptor handled it or needs to forward
if isinstance(response, RequestResponse):
# Add automatic correlation info to the response
correlated_response = RequestResponse._with_correlation(
response, request.data, request.request_id
)
await ctx.send_message(response_message)
else:
# Forward WITH CONTEXT PRESERVED
# Update the data if interceptor provided a modified request
if correlated_response.forward_request:
request.data = correlated_response.forward_request

# Send the inner request to RequestInfoExecutor to create external request
from ._runner_context import Message

forward_message = Message(
source_id=self.id,
data=request,
if correlated_response.is_handled:
# Send response back to sub-workflow
from ._runner_context import Message

response_message = Message(
source_id=self.id,
target_id=request.sub_workflow_id,
data=SubWorkflowResponse(
request_id=request.request_id,
data=correlated_response.data,
),
)
await ctx.send_message(response_message)
else:
# Forward WITH CONTEXT PRESERVED
# Update the data if interceptor provided a modified request
if correlated_response.forward_request:
request.data = correlated_response.forward_request

# Send the inner request to RequestInfoExecutor to create external request
from ._runner_context import Message

forward_message = Message(
source_id=self.id,
data=request,
)
await ctx.send_message(forward_message)
else:
# Legacy support: direct return means handled
await ctx.send_message(
SubWorkflowResponse(
request_id=request.request_id,
data=response,
),
target_id=request.sub_workflow_id,
)
await ctx.send_message(forward_message)
else:
# Legacy support: direct return means handled
await ctx.send_message(
SubWorkflowResponse(
request_id=request.request_id,
data=response,
),
target_id=request.sub_workflow_id,
)
return
return

# No interceptor found - forward inner request to RequestInfoExecutor
# This sends the original request to RequestInfoExecutor
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ async def _deliver_sub_workflow_requests(messages: list[Message]) -> None:
and executor._id != message.source_id
):
# Send directly to the intercepting executor
await executor.execute(sub_request, self._ctx)
await executor.execute(sub_request, self._ctx) # type: ignore[arg-type]
interceptor_found = True
break

Expand Down