mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Default DevUI workflows to string input when start node is auto-wrapped agent (#1143)
* DevUI workflows default to string input when start node is AgentExecutor * Remove unnecessary sample * Fix lint issue
This commit is contained in:
committed by
GitHub
Unverified
parent
5a208ab1e1
commit
c543dbaa92
@@ -7,7 +7,7 @@ import logging
|
||||
import os
|
||||
import uuid
|
||||
from collections.abc import AsyncGenerator
|
||||
from typing import Any
|
||||
from typing import Any, get_origin
|
||||
|
||||
from agent_framework import AgentThread
|
||||
|
||||
@@ -638,6 +638,54 @@ class AgentFrameworkExecutor:
|
||||
logger.warning(f"Error parsing workflow input: {e}")
|
||||
return raw_input
|
||||
|
||||
def _get_start_executor_message_types(self, workflow: Any) -> tuple[Any | None, list[Any]]:
|
||||
"""Return start executor and its declared input types."""
|
||||
try:
|
||||
start_executor = workflow.get_start_executor()
|
||||
except Exception as exc: # pragma: no cover - defensive logging path
|
||||
logger.debug(f"Unable to access workflow start executor: {exc}")
|
||||
return None, []
|
||||
|
||||
if not start_executor:
|
||||
return None, []
|
||||
|
||||
message_types: list[Any] = []
|
||||
|
||||
try:
|
||||
input_types = getattr(start_executor, "input_types", None)
|
||||
except Exception as exc: # pragma: no cover - defensive logging path
|
||||
logger.debug(f"Failed to read executor input_types: {exc}")
|
||||
else:
|
||||
if input_types:
|
||||
message_types = list(input_types)
|
||||
|
||||
if not message_types and hasattr(start_executor, "_handlers"):
|
||||
try:
|
||||
handlers = start_executor._handlers
|
||||
if isinstance(handlers, dict):
|
||||
message_types = list(handlers.keys())
|
||||
except Exception as exc: # pragma: no cover - defensive logging path
|
||||
logger.debug(f"Failed to read executor handlers: {exc}")
|
||||
|
||||
return start_executor, message_types
|
||||
|
||||
def _select_primary_input_type(self, message_types: list[Any]) -> Any | None:
|
||||
"""Choose the most user-friendly input type for workflow kick-off."""
|
||||
if not message_types:
|
||||
return None
|
||||
|
||||
preferred = (str, dict)
|
||||
|
||||
for candidate in preferred:
|
||||
for message_type in message_types:
|
||||
if message_type is candidate:
|
||||
return candidate
|
||||
origin = get_origin(message_type)
|
||||
if origin is candidate:
|
||||
return candidate
|
||||
|
||||
return message_types[0]
|
||||
|
||||
def _parse_structured_workflow_input(self, workflow: Any, input_data: dict[str, Any]) -> Any:
|
||||
"""Parse structured input data for workflow execution.
|
||||
|
||||
@@ -650,18 +698,20 @@ class AgentFrameworkExecutor:
|
||||
"""
|
||||
try:
|
||||
# Get the start executor and its input type
|
||||
start_executor = workflow.get_start_executor()
|
||||
if not start_executor or not hasattr(start_executor, "_handlers"):
|
||||
start_executor, message_types = self._get_start_executor_message_types(workflow)
|
||||
if not start_executor:
|
||||
logger.debug("Cannot determine input type for workflow - using raw dict")
|
||||
return input_data
|
||||
|
||||
message_types = list(start_executor._handlers.keys())
|
||||
if not message_types:
|
||||
logger.debug("No message types found for start executor - using raw dict")
|
||||
return input_data
|
||||
|
||||
# Get the first (primary) input type
|
||||
input_type = message_types[0]
|
||||
input_type = self._select_primary_input_type(message_types)
|
||||
if input_type is None:
|
||||
logger.debug("Could not select primary input type for workflow - using raw dict")
|
||||
return input_data
|
||||
|
||||
# If input type is dict, return as-is
|
||||
if input_type is dict:
|
||||
@@ -715,18 +765,20 @@ class AgentFrameworkExecutor:
|
||||
"""
|
||||
try:
|
||||
# Get the start executor and its input type
|
||||
start_executor = workflow.get_start_executor()
|
||||
if not start_executor or not hasattr(start_executor, "_handlers"):
|
||||
start_executor, message_types = self._get_start_executor_message_types(workflow)
|
||||
if not start_executor:
|
||||
logger.debug("Cannot determine input type for workflow - using raw string")
|
||||
return raw_input
|
||||
|
||||
message_types = list(start_executor._handlers.keys())
|
||||
if not message_types:
|
||||
logger.debug("No message types found for start executor - using raw string")
|
||||
return raw_input
|
||||
|
||||
# Get the first (primary) input type
|
||||
input_type = message_types[0]
|
||||
input_type = self._select_primary_input_type(message_types)
|
||||
if input_type is None:
|
||||
logger.debug("Could not select primary input type for workflow - using raw string")
|
||||
return raw_input
|
||||
|
||||
# If input type is str, return as-is
|
||||
if input_type is str:
|
||||
|
||||
@@ -7,7 +7,7 @@ import json
|
||||
import logging
|
||||
from collections.abc import AsyncGenerator
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import Any
|
||||
from typing import Any, get_origin
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Request
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
@@ -23,6 +23,47 @@ from .models._discovery_models import DiscoveryResponse, EntityInfo
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _extract_executor_message_types(executor: Any) -> list[Any]:
|
||||
"""Return declared input types for the given executor."""
|
||||
message_types: list[Any] = []
|
||||
|
||||
try:
|
||||
input_types = getattr(executor, "input_types", None)
|
||||
except Exception as exc: # pragma: no cover - defensive logging path
|
||||
logger.debug(f"Failed to access executor input_types: {exc}")
|
||||
else:
|
||||
if input_types:
|
||||
message_types = list(input_types)
|
||||
|
||||
if not message_types and hasattr(executor, "_handlers"):
|
||||
try:
|
||||
handlers = executor._handlers
|
||||
if isinstance(handlers, dict):
|
||||
message_types = list(handlers.keys())
|
||||
except Exception as exc: # pragma: no cover - defensive logging path
|
||||
logger.debug(f"Failed to read executor handlers: {exc}")
|
||||
|
||||
return message_types
|
||||
|
||||
|
||||
def _select_primary_input_type(message_types: list[Any]) -> Any | None:
|
||||
"""Choose the most user-friendly input type for rendering workflow inputs."""
|
||||
if not message_types:
|
||||
return None
|
||||
|
||||
preferred = (str, dict)
|
||||
|
||||
for candidate in preferred:
|
||||
for message_type in message_types:
|
||||
if message_type is candidate:
|
||||
return candidate
|
||||
origin = get_origin(message_type)
|
||||
if origin is candidate:
|
||||
return candidate
|
||||
|
||||
return message_types[0]
|
||||
|
||||
|
||||
class DevServer:
|
||||
"""Development Server - OpenAI compatible API server for debugging agents."""
|
||||
|
||||
@@ -223,23 +264,36 @@ class DevServer:
|
||||
|
||||
try:
|
||||
start_executor = entity_obj.get_start_executor()
|
||||
if start_executor and hasattr(start_executor, "_handlers"):
|
||||
message_types = list(start_executor._handlers.keys())
|
||||
if message_types:
|
||||
input_type = message_types[0]
|
||||
except Exception as e:
|
||||
logger.debug(f"Could not extract input info for workflow {entity_id}: {e}")
|
||||
else:
|
||||
if start_executor:
|
||||
start_executor_id = getattr(start_executor, "executor_id", "") or getattr(
|
||||
start_executor, "id", ""
|
||||
)
|
||||
|
||||
message_types = _extract_executor_message_types(start_executor)
|
||||
input_type = _select_primary_input_type(message_types)
|
||||
|
||||
if input_type:
|
||||
input_type_name = getattr(input_type, "__name__", str(input_type))
|
||||
|
||||
# Basic schema generation for common types
|
||||
if input_type is str:
|
||||
input_schema = {"type": "string"}
|
||||
elif input_type is dict:
|
||||
input_schema = {"type": "object"}
|
||||
elif hasattr(input_type, "model_json_schema"):
|
||||
input_schema = input_type.model_json_schema()
|
||||
try:
|
||||
input_schema = input_type.model_json_schema()
|
||||
except Exception as exc: # pragma: no cover - defensive path
|
||||
logger.debug(f"model_json_schema() failed for workflow {entity_id}: {exc}")
|
||||
elif hasattr(input_type, "__annotations__"):
|
||||
input_schema = {"type": "object"}
|
||||
|
||||
start_executor_id = getattr(start_executor, "executor_id", "")
|
||||
except Exception as e:
|
||||
logger.debug(f"Could not extract input info for workflow {entity_id}: {e}")
|
||||
if not input_schema:
|
||||
input_schema = {"type": "string"}
|
||||
if input_type_name == "Unknown":
|
||||
input_type_name = "string"
|
||||
|
||||
# Get executor list
|
||||
executor_list = []
|
||||
|
||||
Reference in New Issue
Block a user