fixes Python: DevUI fails when uploading Pdf file (tested on Python Foundry Agent) (#2675)

Fixes #2652
This commit is contained in:
Victor Dibia
2025-12-08 09:00:57 -08:00
committed by GitHub
Unverified
parent 551219cb55
commit 0675000f4b
10 changed files with 417 additions and 132 deletions
@@ -182,14 +182,29 @@ class EntityDiscovery:
f"{entity_id}.workflow",
]
# Track import errors to provide meaningful feedback
import_errors: list[tuple[str, Exception]] = []
for pattern in import_patterns:
module = self._load_module_from_pattern(pattern)
module, error = self._load_module_from_pattern(pattern)
if error:
import_errors.append((pattern, error))
if module:
# Find entity in module - pass entity_id so registration uses correct ID
entity_obj = await self._find_entity_in_module(module, entity_id, str(dir_path))
if entity_obj:
return entity_obj
# If we have import errors, raise the most informative one
if import_errors:
# Prefer errors from the main module pattern (entity_id) or agent submodule
for pattern, error in import_errors:
if pattern == entity_id or pattern.endswith(".agent"):
raise ValueError(f"Failed to load entity '{entity_id}': {error}") from error
# Fall back to first error
pattern, error = import_errors[0]
raise ValueError(f"Failed to load entity '{entity_id}': {error}") from error
raise ValueError(f"No valid entity found in {dir_path}")
# File-based entity
module = self._load_module_from_file(dir_path, entity_id)
@@ -632,31 +647,32 @@ class EntityDiscovery:
return True
return False
def _load_module_from_pattern(self, pattern: str) -> Any | None:
def _load_module_from_pattern(self, pattern: str) -> tuple[Any | None, Exception | None]:
"""Load module using import pattern.
Args:
pattern: Import pattern to try
Returns:
Loaded module or None if failed
Tuple of (loaded module or None, error or None)
"""
try:
# Check if module exists first
spec = importlib.util.find_spec(pattern)
if spec is None:
return None
return None, None
module = importlib.import_module(pattern)
logger.debug(f"Successfully imported {pattern}")
return module
return module, None
except ModuleNotFoundError:
logger.debug(f"Import pattern {pattern} not found")
return None
return None, None
except Exception as e:
# Capture the actual error for better error messages
logger.warning(f"Error importing {pattern}: {e}")
return None
return None, e
def _load_module_from_file(self, file_path: Path, module_name: str) -> Any | None:
"""Load module directly from file path.
@@ -642,12 +642,26 @@ class AgentFrameworkExecutor:
media_type = "audio/mp4" if ext == "m4a" else f"audio/{ext}"
# Use file_data or file_url
# Include filename in additional_properties for OpenAI/Azure file handling
additional_props = {"filename": filename} if filename else None
if file_data:
# Assume file_data is base64, create data URI
data_uri = f"data:{media_type};base64,{file_data}"
contents.append(DataContent(uri=data_uri, media_type=media_type))
contents.append(
DataContent(
uri=data_uri,
media_type=media_type,
additional_properties=additional_props,
)
)
elif file_url:
contents.append(DataContent(uri=file_url, media_type=media_type))
contents.append(
DataContent(
uri=file_url,
media_type=media_type,
additional_properties=additional_props,
)
)
elif content_type == "function_approval_response":
# Handle function approval response (DevUI extension)
@@ -537,9 +537,14 @@ class DevServer:
except HTTPException:
raise
except ValueError as e:
# ValueError from load_entity indicates entity not found or invalid
# ValueError from load_entity - could be "not found" or "failed to load"
error_str = str(e)
error_msg = self._format_error(e, "Entity loading")
raise HTTPException(status_code=404, detail=error_msg) from e
# Use 404 for "not found", 422 for load failures (entity exists but can't load)
if "not found" in error_str.lower() and "failed to load" not in error_str.lower():
raise HTTPException(status_code=404, detail=error_msg) from e
# Entity exists but failed to load (e.g., missing env vars, import errors)
raise HTTPException(status_code=422, detail=error_msg) from e
except Exception as e:
error_msg = self._format_error(e, "Entity info retrieval")
raise HTTPException(status_code=500, detail=error_msg) from e
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long