mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Fix Python pyright package scoping and typing remediation (#4426)
* Fix Python pyright package scoping and typing remediation Implements issue #4407 by removing the root pyright include, adding package-level pyright includes, and resolving pyright/mypy typing issues across Python packages. Also cleans unnecessary casts and applies line-level, rule-specific ignores where external libraries are too dynamic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Reduce pyright cost in handoff cloning Simplify cloned_options construction in HandoffAgentExecutor to avoid expensive TypedDict narrowing/inference in _handoff.py, which was causing pyright to spend a long time in orchestrations. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix types * Fix lint and type-check regressions Resolve current Python package check failures across lint, pyright, and mypy after recent code changes, including purview/declarative pyright issues and multiple ruff simplification findings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fixed hooks * Stabilize package tests and test tasks Resolve cross-package non-integration test failures, simplify streaming type flow, harden locale/culture handling, and standardize package test poe tasks to exclude integration tests where applicable. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * lots of small fixes * Fix current Python test regressions Address current failing unit tests in azure-ai, bedrock, and azure-cosmos while keeping Bedrock parsing logic inline (no new static helper methods). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * small fixes * small fixes * removed pydantic from json * final updates * fix core * fix tests * fix obser --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
4a043c6c66
commit
55ddd841b7
@@ -15,7 +15,6 @@ from agent_framework import (
|
||||
from agent_framework import (
|
||||
FunctionTool as AFFunctionTool,
|
||||
)
|
||||
from agent_framework._tools import _create_model_from_json_schema # type: ignore
|
||||
from agent_framework.exceptions import AgentException
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@@ -34,7 +33,7 @@ from ._models import (
|
||||
RemoteConnection,
|
||||
Tool,
|
||||
WebSearchTool,
|
||||
_safe_mode_context,
|
||||
_safe_mode_context, # type: ignore[reportPrivateUsage]
|
||||
agent_schema_dispatch,
|
||||
)
|
||||
|
||||
@@ -445,7 +444,7 @@ class AgentFactory:
|
||||
if tools := self._parse_tools(prompt_agent.tools):
|
||||
chat_options["tools"] = tools
|
||||
if output_schema := prompt_agent.outputSchema:
|
||||
chat_options["response_format"] = _create_model_from_json_schema("agent", output_schema.to_json_schema())
|
||||
chat_options["response_format"] = output_schema.to_json_schema()
|
||||
# Step 3: Create the agent instance
|
||||
return Agent(
|
||||
client=client,
|
||||
@@ -563,7 +562,7 @@ class AgentFactory:
|
||||
if tools := self._parse_tools(prompt_agent.tools):
|
||||
chat_options["tools"] = tools
|
||||
if output_schema := prompt_agent.outputSchema:
|
||||
chat_options["response_format"] = _create_model_from_json_schema("agent", output_schema.to_json_schema())
|
||||
chat_options["response_format"] = output_schema.to_json_schema()
|
||||
return Agent(
|
||||
client=client,
|
||||
name=prompt_agent.name,
|
||||
@@ -598,6 +597,9 @@ class AgentFactory:
|
||||
case ApiKeyConnection():
|
||||
if prompt_agent.model.connection.endpoint:
|
||||
provider_kwargs["project_endpoint"] = prompt_agent.model.connection.endpoint
|
||||
case ReferenceConnection():
|
||||
# Reference connections are resolved by concrete providers when supported.
|
||||
pass
|
||||
|
||||
# Create the provider and use it to create the agent
|
||||
provider = provider_class(**provider_kwargs)
|
||||
@@ -608,8 +610,7 @@ class AgentFactory:
|
||||
# Parse response format into default_options
|
||||
default_options: dict[str, Any] | None = None
|
||||
if prompt_agent.outputSchema:
|
||||
response_format = _create_model_from_json_schema("agent", prompt_agent.outputSchema.to_json_schema())
|
||||
default_options = {"response_format": response_format}
|
||||
default_options = {"response_format": prompt_agent.outputSchema.to_json_schema()}
|
||||
|
||||
# Create the agent using the provider
|
||||
# The provider's create_agent returns a Agent directly
|
||||
|
||||
+54
-28
@@ -25,6 +25,7 @@ See: dotnet/src/Microsoft.Agents.AI.Workflows.Declarative/PowerFx/
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import locale
|
||||
import logging
|
||||
import sys
|
||||
import uuid
|
||||
@@ -103,6 +104,8 @@ DECLARATIVE_STATE_KEY = "_declarative_workflow_state"
|
||||
# Types that PowerFx can serialize directly
|
||||
# Note: Decimal is included because PowerFx returns Decimal for numeric values
|
||||
_POWERFX_SAFE_TYPES = (str, int, float, bool, type(None), _Decimal)
|
||||
_POWERFX_EVAL_LOCALE = "en-US"
|
||||
_POWERFX_NUMERIC_LOCALE_CANDIDATES = ("en_US.UTF-8", "en_US", "C")
|
||||
|
||||
|
||||
def _make_powerfx_safe(value: Any) -> Any:
|
||||
@@ -121,10 +124,12 @@ def _make_powerfx_safe(value: Any) -> Any:
|
||||
return value
|
||||
|
||||
if isinstance(value, dict):
|
||||
return {k: _make_powerfx_safe(v) for k, v in value.items()}
|
||||
value_dict = cast(Mapping[Any, Any], value)
|
||||
return {str(k): _make_powerfx_safe(v) for k, v in value_dict.items()}
|
||||
|
||||
if isinstance(value, list):
|
||||
return [_make_powerfx_safe(item) for item in value]
|
||||
value_list = cast(list[Any], value) # type: ignore[redundant-cast]
|
||||
return [_make_powerfx_safe(item) for item in value_list]
|
||||
|
||||
# Try to convert objects with __dict__ or dataclass-style attributes
|
||||
if hasattr(value, "__dict__"):
|
||||
@@ -382,21 +387,33 @@ class DeclarativeWorkflowState:
|
||||
f"Install dotnet and the powerfx package for full PowerFx support."
|
||||
)
|
||||
|
||||
engine = Engine()
|
||||
symbols = self._to_powerfx_symbols()
|
||||
# Use setlocale(category) query form so we can restore the exact prior value.
|
||||
# getlocale() returns a normalized tuple and is not always a lossless
|
||||
# round-trip for setlocale across platforms/locales.
|
||||
original_numeric_locale = locale.setlocale(locale.LC_NUMERIC)
|
||||
try:
|
||||
from System.Globalization import CultureInfo
|
||||
for locale_candidate in _POWERFX_NUMERIC_LOCALE_CANDIDATES:
|
||||
try:
|
||||
locale.setlocale(locale.LC_NUMERIC, locale_candidate)
|
||||
break
|
||||
except locale.Error:
|
||||
continue
|
||||
|
||||
original_culture = CultureInfo.CurrentCulture
|
||||
original_ui_culture = CultureInfo.CurrentUICulture
|
||||
en_us_culture = CultureInfo("en-US")
|
||||
CultureInfo.CurrentCulture = en_us_culture
|
||||
CultureInfo.CurrentUICulture = en_us_culture
|
||||
engine = Engine()
|
||||
try:
|
||||
return engine.eval(formula, symbols=symbols)
|
||||
from System.Globalization import ( # pyright: ignore[reportMissingImports]
|
||||
CultureInfo, # pyright: ignore[reportUnknownVariableType]
|
||||
)
|
||||
except ImportError:
|
||||
return engine.eval(formula, symbols=symbols, locale=_POWERFX_EVAL_LOCALE)
|
||||
|
||||
original_culture = cast(Any, CultureInfo.CurrentCulture) # pyright: ignore[reportUnknownMemberType]
|
||||
try:
|
||||
CultureInfo.CurrentCulture = CultureInfo(_POWERFX_EVAL_LOCALE) # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
|
||||
return engine.eval(formula, symbols=symbols, locale=_POWERFX_EVAL_LOCALE)
|
||||
finally:
|
||||
CultureInfo.CurrentCulture = original_culture
|
||||
CultureInfo.CurrentUICulture = original_ui_culture
|
||||
CultureInfo.CurrentCulture = original_culture # pyright: ignore[reportUnknownMemberType]
|
||||
except ValueError as e:
|
||||
error_msg = str(e)
|
||||
# Handle undefined variable errors gracefully by returning None
|
||||
@@ -405,6 +422,8 @@ class DeclarativeWorkflowState:
|
||||
logger.debug(f"PowerFx: undefined variable in expression '{formula}', returning None")
|
||||
return None
|
||||
raise
|
||||
finally:
|
||||
locale.setlocale(locale.LC_NUMERIC, original_numeric_locale)
|
||||
|
||||
def _eval_custom_function(self, formula: str) -> Any | None:
|
||||
"""Handle custom functions not supported by the Python PowerFx library.
|
||||
@@ -424,7 +443,7 @@ class DeclarativeWorkflowState:
|
||||
args_str = match.group(1)
|
||||
# Parse comma-separated arguments (handling nested parentheses)
|
||||
args = self._parse_function_args(args_str)
|
||||
evaluated_args = []
|
||||
evaluated_args: list[str] = []
|
||||
for arg in args:
|
||||
arg = arg.strip()
|
||||
if arg.startswith('"') and arg.endswith('"'):
|
||||
@@ -576,37 +595,44 @@ class DeclarativeWorkflowState:
|
||||
"""
|
||||
messages: Any = self.eval(f"={inner_expr}")
|
||||
if isinstance(messages, list) and messages:
|
||||
last_msg: Any = messages[-1]
|
||||
message_list = cast(list[Any], messages) # type: ignore[redundant-cast]
|
||||
last_msg: Any = message_list[-1]
|
||||
if isinstance(last_msg, dict):
|
||||
last_msg_dict = cast(dict[str, Any], last_msg)
|
||||
# Try "text" key first (simple dict format)
|
||||
if "text" in last_msg:
|
||||
return str(last_msg["text"])
|
||||
if "text" in last_msg_dict:
|
||||
return str(last_msg_dict["text"])
|
||||
# Try extracting from "contents" (Message dict format)
|
||||
# Message.text concatenates text from all TextContent items
|
||||
contents = last_msg.get("contents", [])
|
||||
if isinstance(contents, list):
|
||||
text_parts = []
|
||||
contents_obj = last_msg_dict.get("contents", [])
|
||||
if isinstance(contents_obj, list):
|
||||
contents = cast(list[Any], contents_obj) # type: ignore[redundant-cast]
|
||||
text_parts: list[str] = []
|
||||
for content in contents:
|
||||
if isinstance(content, dict):
|
||||
content_dict = cast(dict[str, Any], content)
|
||||
# TextContent has a "text" key
|
||||
if content.get("type") == "text" or "text" in content:
|
||||
text_parts.append(str(content.get("text", "")))
|
||||
elif hasattr(content, "text"):
|
||||
text_parts.append(str(getattr(content, "text", "")))
|
||||
if content_dict.get("type") == "text" or "text" in content_dict:
|
||||
text_parts.append(str(content_dict.get("text", "")))
|
||||
else:
|
||||
content_obj: object = content
|
||||
if hasattr(content_obj, "text"):
|
||||
text_parts.append(str(getattr(content_obj, "text", "")))
|
||||
if text_parts:
|
||||
return " ".join(text_parts)
|
||||
return ""
|
||||
if hasattr(last_msg, "text"):
|
||||
return str(getattr(last_msg, "text", ""))
|
||||
last_msg_obj: object = last_msg
|
||||
if hasattr(last_msg_obj, "text"):
|
||||
return str(getattr(last_msg_obj, "text", ""))
|
||||
return ""
|
||||
|
||||
def _parse_function_args(self, args_str: str) -> list[str]:
|
||||
"""Parse comma-separated function arguments, handling nested parentheses and strings."""
|
||||
args = []
|
||||
current = []
|
||||
args: list[str] = []
|
||||
current: list[str] = []
|
||||
depth = 0
|
||||
in_string = False
|
||||
string_char = None
|
||||
string_char: str | None = None
|
||||
|
||||
for char in args_str:
|
||||
if char in ('"', "'") and not in_string:
|
||||
|
||||
+4
-3
@@ -14,7 +14,7 @@ action definitions and creates a proper workflow graph with:
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
|
||||
from agent_framework import (
|
||||
Workflow,
|
||||
@@ -983,8 +983,9 @@ class DeclarativeWorkflowBuilder:
|
||||
last_executor = chain[-1]
|
||||
|
||||
# Skip terminators — they handle their own control flow
|
||||
action_def = getattr(last_executor, "_action_def", {})
|
||||
if isinstance(action_def, dict) and action_def.get("kind", "") in TERMINATOR_ACTIONS:
|
||||
action_def_obj = getattr(last_executor, "_action_def", {})
|
||||
action_def = cast(dict[str, Any], action_def_obj) if isinstance(action_def_obj, dict) else {}
|
||||
if action_def.get("kind", "") in TERMINATOR_ACTIONS:
|
||||
return None
|
||||
|
||||
# Check if last executor is a structure with branch_exits
|
||||
|
||||
+2
-2
@@ -188,9 +188,9 @@ def _validate_conversation_history(messages: list[Message], agent_name: str) ->
|
||||
tool_result_ids: set[str] = set()
|
||||
|
||||
for i, msg in enumerate(messages):
|
||||
if not hasattr(msg, "contents") or msg.contents is None:
|
||||
if not (contents := getattr(msg, "contents", None)):
|
||||
continue
|
||||
for content in msg.contents:
|
||||
for content in contents:
|
||||
if content.type == "function_call" and content.call_id:
|
||||
tool_call_ids.add(content.call_id)
|
||||
logger.debug(
|
||||
|
||||
+55
-28
@@ -7,7 +7,8 @@ Each action becomes a node in the workflow graph.
|
||||
"""
|
||||
|
||||
import uuid
|
||||
from typing import Any
|
||||
from collections.abc import Mapping
|
||||
from typing import Any, cast
|
||||
|
||||
from agent_framework import (
|
||||
WorkflowContext,
|
||||
@@ -28,9 +29,12 @@ def _get_variable_path(action_def: dict[str, Any], key: str = "variable") -> str
|
||||
variable = action_def.get(key)
|
||||
if isinstance(variable, str):
|
||||
return variable
|
||||
if isinstance(variable, dict):
|
||||
return variable.get("path")
|
||||
return action_def.get("path")
|
||||
if isinstance(variable, Mapping):
|
||||
path = variable.get("path") # type: ignore[reportUnknownVariableType]
|
||||
return path if isinstance(path, str) else None
|
||||
|
||||
fallback_path = action_def.get("path")
|
||||
return fallback_path if isinstance(fallback_path, str) else None
|
||||
|
||||
|
||||
class SetValueExecutor(DeclarativeActionExecutor):
|
||||
@@ -150,16 +154,23 @@ class SetMultipleVariablesExecutor(DeclarativeActionExecutor):
|
||||
"""Handle the SetMultipleVariables action."""
|
||||
state = await self._ensure_state_initialized(ctx, trigger)
|
||||
|
||||
assignments = self._action_def.get("assignments", [])
|
||||
assignments = cast(
|
||||
list[Mapping[str, Any]],
|
||||
self._action_def.get("assignments") if isinstance(self._action_def.get("assignments"), list) else [],
|
||||
)
|
||||
for assignment in assignments:
|
||||
if not isinstance(assignment, Mapping):
|
||||
continue
|
||||
variable = assignment.get("variable")
|
||||
path: str | None
|
||||
if isinstance(variable, str):
|
||||
path = variable
|
||||
elif isinstance(variable, dict):
|
||||
path = variable.get("path")
|
||||
elif isinstance(variable, Mapping):
|
||||
path_value = variable.get("path") # type: ignore[reportUnknownMemberType]
|
||||
path = path_value if isinstance(path_value, str) else None
|
||||
else:
|
||||
path = assignment.get("path")
|
||||
fallback_path = assignment.get("path")
|
||||
path = fallback_path if isinstance(fallback_path, str) else None
|
||||
value = assignment.get("value")
|
||||
if path:
|
||||
evaluated_value = state.eval_if_expression(value)
|
||||
@@ -249,7 +260,10 @@ class SendActivityExecutor(DeclarativeActionExecutor):
|
||||
activity = self._action_def.get("activity", "")
|
||||
|
||||
# Activity can be a string directly or a dict with a "text" field
|
||||
text = activity.get("text", "") if isinstance(activity, dict) else activity
|
||||
if isinstance(activity, Mapping):
|
||||
text: Any = activity.get("text", "") # type: ignore[reportUnknownMemberType]
|
||||
else:
|
||||
text = activity
|
||||
|
||||
if isinstance(text, str):
|
||||
# First evaluate any =expression syntax
|
||||
@@ -260,7 +274,7 @@ class SendActivityExecutor(DeclarativeActionExecutor):
|
||||
|
||||
# Yield the text as workflow output
|
||||
if text:
|
||||
await ctx.yield_output(str(text))
|
||||
await ctx.yield_output(str(text)) # type: ignore[reportUnknownArgumentType]
|
||||
|
||||
await ctx.send_message(ActionComplete())
|
||||
|
||||
@@ -336,11 +350,14 @@ class EditTableExecutor(DeclarativeActionExecutor):
|
||||
|
||||
if table_path:
|
||||
# Get current table value
|
||||
current_table = state.get(table_path)
|
||||
if current_table is None:
|
||||
current_table_value = state.get(table_path)
|
||||
current_table: list[Any]
|
||||
if current_table_value is None:
|
||||
current_table = []
|
||||
elif not isinstance(current_table, list):
|
||||
current_table = [current_table]
|
||||
elif isinstance(current_table_value, list):
|
||||
current_table = list(current_table_value) # type: ignore[reportUnknownArgumentType]
|
||||
else:
|
||||
current_table = [current_table_value]
|
||||
|
||||
if operation == "add" or operation == "insert":
|
||||
evaluated_value = state.eval_if_expression(value)
|
||||
@@ -413,11 +430,14 @@ class EditTableV2Executor(DeclarativeActionExecutor):
|
||||
|
||||
if table_path:
|
||||
# Get current table value
|
||||
current_table = state.get(table_path)
|
||||
if current_table is None:
|
||||
current_table_value = state.get(table_path)
|
||||
current_table: list[Any]
|
||||
if current_table_value is None:
|
||||
current_table = []
|
||||
elif not isinstance(current_table, list):
|
||||
current_table = [current_table]
|
||||
elif isinstance(current_table_value, list):
|
||||
current_table = list(current_table_value) # type: ignore[reportUnknownArgumentType]
|
||||
else:
|
||||
current_table = [current_table_value]
|
||||
|
||||
if operation == "add":
|
||||
evaluated_item = state.eval_if_expression(item)
|
||||
@@ -433,9 +453,12 @@ class EditTableV2Executor(DeclarativeActionExecutor):
|
||||
evaluated_item = state.eval_if_expression(item)
|
||||
if key_field and isinstance(evaluated_item, dict):
|
||||
# Remove by key match
|
||||
key_value = evaluated_item.get(key_field)
|
||||
evaluated_item_dict = cast(dict[str, Any], evaluated_item)
|
||||
key_value = evaluated_item_dict.get(key_field)
|
||||
current_table = [
|
||||
r for r in current_table if not (isinstance(r, dict) and r.get(key_field) == key_value)
|
||||
r
|
||||
for r in current_table
|
||||
if not (isinstance(r, dict) and cast(dict[str, Any], r).get(key_field) == key_value)
|
||||
]
|
||||
elif evaluated_item in current_table:
|
||||
current_table.remove(evaluated_item)
|
||||
@@ -451,11 +474,11 @@ class EditTableV2Executor(DeclarativeActionExecutor):
|
||||
elif operation == "addorupdate":
|
||||
evaluated_item = state.eval_if_expression(item)
|
||||
if key_field and isinstance(evaluated_item, dict):
|
||||
key_value = evaluated_item.get(key_field)
|
||||
key_value = evaluated_item.get(key_field) # type: ignore[reportUnknownArgumentType]
|
||||
# Find existing item with same key
|
||||
found_idx = -1
|
||||
for i, r in enumerate(current_table):
|
||||
if isinstance(r, dict) and r.get(key_field) == key_value:
|
||||
if isinstance(r, dict) and cast(dict[str, Any], r).get(key_field) == key_value:
|
||||
found_idx = i
|
||||
break
|
||||
if found_idx >= 0:
|
||||
@@ -476,9 +499,9 @@ class EditTableV2Executor(DeclarativeActionExecutor):
|
||||
if 0 <= idx < len(current_table):
|
||||
current_table[idx] = evaluated_item
|
||||
elif key_field and isinstance(evaluated_item, dict):
|
||||
key_value = evaluated_item.get(key_field)
|
||||
key_value = evaluated_item.get(key_field) # type: ignore[reportUnknownArgumentType]
|
||||
for i, r in enumerate(current_table):
|
||||
if isinstance(r, dict) and r.get(key_field) == key_value:
|
||||
if isinstance(r, dict) and cast(dict[str, Any], r).get(key_field) == key_value:
|
||||
current_table[i] = evaluated_item
|
||||
break
|
||||
|
||||
@@ -568,11 +591,13 @@ class ParseValueExecutor(DeclarativeActionExecutor):
|
||||
if value is None:
|
||||
return {}
|
||||
if isinstance(value, dict):
|
||||
return value
|
||||
return cast(dict[str, Any], value)
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
parsed = json.loads(value)
|
||||
return parsed if isinstance(parsed, dict) else {"value": parsed}
|
||||
if isinstance(parsed, dict):
|
||||
return cast(dict[str, Any], parsed)
|
||||
return {"value": parsed}
|
||||
except json.JSONDecodeError:
|
||||
return {"value": value}
|
||||
return {"value": value}
|
||||
@@ -581,11 +606,13 @@ class ParseValueExecutor(DeclarativeActionExecutor):
|
||||
if value is None:
|
||||
return []
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
return cast(list[Any], value) # type: ignore[redundant-cast]
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
parsed = json.loads(value)
|
||||
return parsed if isinstance(parsed, list) else [parsed]
|
||||
if isinstance(parsed, list):
|
||||
return cast(list[Any], parsed) # type: ignore[redundant-cast]
|
||||
return [parsed]
|
||||
except json.JSONDecodeError:
|
||||
return [value]
|
||||
return [value]
|
||||
|
||||
+7
-6
@@ -15,9 +15,11 @@ import json
|
||||
import logging
|
||||
import uuid
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass, field
|
||||
from inspect import isawaitable
|
||||
from typing import Any
|
||||
from typing import Any, cast
|
||||
from collections.abc import Callable
|
||||
|
||||
from agent_framework import (
|
||||
Content,
|
||||
@@ -127,7 +129,7 @@ class ToolInvocationResult:
|
||||
success: bool
|
||||
result: Any = None
|
||||
error: str | None = None
|
||||
messages: list[Message] = field(default_factory=list)
|
||||
messages: list[Message] = field(default_factory=cast(Callable[..., list[Message]], list))
|
||||
rejected: bool = False
|
||||
rejection_reason: str | None = None
|
||||
|
||||
@@ -267,15 +269,14 @@ class BaseToolExecutor(DeclarativeActionExecutor):
|
||||
Returns:
|
||||
Tuple of (messages_var, result_var, auto_send)
|
||||
"""
|
||||
output_config = self._action_def.get("output", {})
|
||||
output_config: dict[str, str | bool] = self._action_def.get("output", {})
|
||||
|
||||
if not isinstance(output_config, dict):
|
||||
if not isinstance(output_config, Mapping):
|
||||
return None, None, True
|
||||
|
||||
messages_var = output_config.get("messages")
|
||||
result_var = output_config.get("result")
|
||||
auto_send = bool(output_config.get("autoSend", True))
|
||||
|
||||
return (
|
||||
str(messages_var) if messages_var else None,
|
||||
str(result_var) if result_var else None,
|
||||
@@ -494,7 +495,7 @@ class BaseToolExecutor(DeclarativeActionExecutor):
|
||||
type(arguments_def).__name__,
|
||||
)
|
||||
elif isinstance(arguments_def, dict):
|
||||
for key, value in arguments_def.items():
|
||||
for key, value in arguments_def.items(): # type: ignore[reportUnknownVariableType]
|
||||
arguments[key] = state.eval_if_expression(value)
|
||||
|
||||
# Check if approval is required
|
||||
|
||||
+17
-15
@@ -44,14 +44,16 @@ def message_text(messages: Any) -> str:
|
||||
content: Any = messages_dict.get("content", "")
|
||||
if isinstance(content, str):
|
||||
return content
|
||||
if hasattr(content, "text"):
|
||||
return str(content.text)
|
||||
text_attr = getattr(content, "text", None)
|
||||
if text_attr is not None:
|
||||
return str(text_attr)
|
||||
return str(content) if content else ""
|
||||
|
||||
if isinstance(messages, list):
|
||||
# List of messages - concatenate all text
|
||||
texts: list[str] = []
|
||||
for msg in messages:
|
||||
message_list = cast(list[Any], messages) # type: ignore[redundant-cast]
|
||||
for msg in message_list:
|
||||
if isinstance(msg, str):
|
||||
texts.append(msg)
|
||||
elif isinstance(msg, dict):
|
||||
@@ -61,14 +63,16 @@ def message_text(messages: Any) -> str:
|
||||
texts.append(msg_content)
|
||||
elif msg_content:
|
||||
texts.append(str(msg_content))
|
||||
elif hasattr(msg, "content"):
|
||||
msg_obj_content: Any = msg.content
|
||||
if isinstance(msg_obj_content, str):
|
||||
texts.append(msg_obj_content)
|
||||
elif hasattr(msg_obj_content, "text"):
|
||||
texts.append(str(msg_obj_content.text))
|
||||
elif msg_obj_content:
|
||||
texts.append(str(msg_obj_content))
|
||||
else:
|
||||
msg_obj: object = msg
|
||||
if hasattr(msg_obj, "content"):
|
||||
msg_obj_content: Any = getattr(msg_obj, "content", None)
|
||||
if isinstance(msg_obj_content, str):
|
||||
texts.append(msg_obj_content)
|
||||
elif (msg_obj_text := getattr(msg_obj_content, "text", None)) is not None:
|
||||
texts.append(str(msg_obj_text))
|
||||
elif msg_obj_content:
|
||||
texts.append(str(msg_obj_content))
|
||||
return " ".join(texts)
|
||||
|
||||
# Try to get text attribute
|
||||
@@ -191,10 +195,8 @@ def is_blank(value: Any) -> bool:
|
||||
return True
|
||||
if isinstance(value, str) and not value.strip():
|
||||
return True
|
||||
if isinstance(value, list):
|
||||
return len(value) == 0
|
||||
if isinstance(value, dict):
|
||||
return len(value) == 0
|
||||
if isinstance(value, (list, dict)):
|
||||
return len(value) == 0 # type: ignore[reportUnknownArgumentType]
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@@ -284,8 +284,9 @@ class WorkflowState:
|
||||
if existing is None:
|
||||
self.set(path, [value])
|
||||
elif isinstance(existing, list):
|
||||
existing.append(value)
|
||||
self.set(path, existing)
|
||||
existing_list = cast(list[Any], existing) # type: ignore[redundant-cast]
|
||||
existing_list.append(value)
|
||||
self.set(path, existing_list)
|
||||
else:
|
||||
raise ValueError(f"Cannot append to non-list at path '{path}'")
|
||||
|
||||
@@ -614,9 +615,9 @@ class WorkflowState:
|
||||
if isinstance(value, str):
|
||||
return self.eval(value)
|
||||
if isinstance(value, dict):
|
||||
return {str(k): self.eval_if_expression(v) for k, v in value.items()}
|
||||
return {str(k): self.eval_if_expression(v) for k, v in value.items()} # type: ignore[reportUnknownVariableType]
|
||||
if isinstance(value, list):
|
||||
return [self.eval_if_expression(item) for item in value]
|
||||
return [self.eval_if_expression(item) for item in value] # type: ignore[reportUnknownVariableType]
|
||||
return value
|
||||
|
||||
def reset_local(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user