Azure / Azure/azure-functions-agents-runtime
Typing hardening: reduce defensive getattr / Any across the runtime
- Dominant language
- Python
- Stars
- 9
- Forks
- 7
- Avg merge
- 1d 21h
- Merged PRs (30d)
- 20
Description
### This issue is for a: (mark with an `x`)
```
- [ ] bug report -> please search issues before submitting
- [x] feature request
- [ ] documentation issue or request
- [ ] regression (a behavior that used to work and stopped in a new release)
```
### Minimal steps to reproduce
> N/A — code-quality / tech-debt follow-up, not a runtime bug.
### Any log messages given by the failure
> N/A
### Expected/desired behavior
> Strengthen type annotations across the runtime so defensive `getattr(...)` access and loose `Any` typing can be replaced with direct, statically-verified attribute access wherever the type is knowable. Annotate framework-return boundaries with concrete Microsoft Agent Framework types (`AgentResponse`, `FunctionTool`, etc.), tighten our own signatures/returns/fields, and remove `getattr`/`or ` fallbacks that strong typing renders dead — keeping `mypy --strict` clean with no new `# type: ignore`.
### OS and Version?
> N/A (all platforms)
### Versions
> `agent-framework-core==1.3.*` (ships `py.typed`; `azure_functions_agents.*` already runs mypy `strict = true`).
### Mention any other details that might be useful
**Context.** Surfaced during the FRD 0006 multi-agent delegation work. The *new* delegation code (delegate path in `runner.py`, `config` `SubagentRef`/validation, `registration/catalog.py`, `app.py` composition) was already brought to strict typing there; this issue tracks the remaining pre-existing footprint across the rest of the runtime, deliberately deferred rather than bundled into the delegation PR to keep it reviewable.
**Rough scope (src/ only, ~40 files):** ~50 `getattr(` and ~250 `Any` annotations. Highest-density: `runner.py` (~20 getattr / ~48 Any), `registration/_handlers.py` (8/17), `registration/_trigger_serialization.py` (5/5), `workflows/tools.py` (4/15), `registration/endpoints.py` (1/18), `_function_tool.py` (1/21), `config/validation.py` (3/1), `registration/capabilities.py` (2/7), `_observability.py` (0/14), `_blob_history.py` (0/12).
**Judgment-heavy, not mechanical — much of the `Any` is legitimate and should stay:**
- Arbitrary user JSON/config (`dict[str, Any]`: `args`, `workflows`, `metadata`, `input_schema`/`response_schema`, `TRIGGER_TYPES`).
- MAF tool union `ToolTypes = FunctionTool | MCPTool | Mapping[str, Any] | object` (the trailing `object` makes `list[Any]` accurate, not under-typing).
- Dynamic MAF `Contents` union parsing (needs `isinstance` dispatch).
- External libs without stubs (e.g. `azure.durable_functions` -> `workflow_durable_client: Any`).
**Suggested approach:** own dedicated PR (not folded into feature PRs); type at framework-return boundaries rather than scattering `cast()`; keep typed guards where values are genuinely dynamic; gate on `mypy --strict` for touched files, no behavior change.
Contributor guide
Research direction
Start with mypy --strict and the highest-density files named in the issue, especially runner.py, registration/_handlers.py, registration/_trigger_serialization.py, and workflows/tools.py. Trace framework-return boundaries and existing dynamic-value guards before changing annotations; done means touched runtime files remain strict-clean, with no new type ignores and no behavior change.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- azure, python
- Domain
- backend, developer-experience
- Issue type
- Refactor
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100