Python: Fixed declarative samples (#4051)

* Updated declarative kind mapping

* Fixed required property handling

* Updated inline yaml sample

* Fixed remaining declarative samples

* Added lazy initialization for PowerFx engine

* Small fix
This commit is contained in:
Dmytro Struk
2026-02-18 15:08:31 -08:00
committed by GitHub
Unverified
parent 1b87a07377
commit 57da1bcfeb
9 changed files with 173 additions and 16 deletions
@@ -452,7 +452,7 @@ class AgentFactory:
name=prompt_agent.name,
description=prompt_agent.description,
instructions=prompt_agent.instructions,
**chat_options,
default_options=chat_options, # type: ignore[arg-type]
)
async def create_agent_from_yaml_path_async(self, yaml_path: str | Path) -> Agent:
@@ -569,7 +569,7 @@ class AgentFactory:
name=prompt_agent.name,
description=prompt_agent.description,
instructions=prompt_agent.instructions,
**chat_options,
default_options=chat_options, # type: ignore[arg-type]
)
async def _create_agent_with_provider(self, prompt_agent: PromptAgent, mapping: ProviderTypeMapping) -> Agent:
@@ -5,20 +5,32 @@ import logging
import os
from collections.abc import MutableMapping
from contextvars import ContextVar
from typing import Any, Literal, TypeVar, Union
from typing import TYPE_CHECKING, Any, Literal, TypeVar, Union, overload
from agent_framework._serialization import SerializationMixin
try:
if TYPE_CHECKING:
from powerfx import Engine
engine: Engine | None = Engine()
except (ImportError, RuntimeError):
# ImportError: powerfx package not installed
# RuntimeError: .NET runtime not available or misconfigured
engine = None
_engine_initialized = False
_engine: Engine | None = None
def _get_engine() -> Engine | None:
"""Lazily initialize the PowerFx engine on first use."""
global _engine_initialized, _engine
if not _engine_initialized:
_engine_initialized = True
try:
from powerfx import Engine
_engine = Engine()
except (ImportError, RuntimeError):
# ImportError: powerfx package not installed
# RuntimeError: .NET runtime not available or misconfigured
pass
return _engine
from typing import overload
logger = logging.getLogger("agent_framework.declarative")
@@ -47,6 +59,7 @@ def _try_powerfx_eval(value: str | None, log_value: bool = True) -> str | None:
return value
if not value.startswith("="):
return value
engine = _get_engine()
if engine is None:
logger.warning(
"PowerFx engine not available for evaluating values starting with '='. "
@@ -110,8 +123,12 @@ class Property(SerializationMixin):
# We're being called on a subclass, use the normal from_dict
return SerializationMixin.from_dict.__func__(cls, value, dependencies=dependencies) # type: ignore[attr-defined, no-any-return]
# Filter out 'type' (if it exists) field which is not a Property parameter
value.pop("type", None)
# The YAML spec uses 'type' for the data type, but Property stores it as 'kind'
if "type" in value:
if "kind" not in value:
value["kind"] = value.pop("type")
else:
value.pop("type")
kind = value.get("kind", "")
if kind == "array":
return ArrayProperty.from_dict(value, dependencies=dependencies)
@@ -224,11 +241,21 @@ class PropertySchema(SerializationMixin):
"""Get a schema out of this PropertySchema to create pydantic models."""
json_schema = self.to_dict(exclude={"type"}, exclude_none=True)
new_props = {}
required_fields: list[str] = []
for prop in json_schema.get("properties", []):
prop_name = prop.pop("name")
prop["type"] = prop.pop("kind", None)
# Convert property-level 'required' boolean to a top-level 'required' array
if prop.pop("required", False):
required_fields.append(prop_name)
# Remove empty enum arrays
if not prop.get("enum"):
prop.pop("enum", None)
new_props[prop_name] = prop
json_schema["type"] = "object"
json_schema["properties"] = new_props
if required_fields:
json_schema["required"] = required_fields
return json_schema