Extends the canonical structured-output pattern from the Portfolio Manager
to the other two decision-making agents. Each of the three agents now
returns a typed Pydantic instance via llm.with_structured_output() in a
single primary call, and a render helper turns the result into the same
markdown shape downstream agents and saved reports already consume.
- ResearchPlan: 5-tier recommendation, conversational rationale, concrete
strategic actions for the trader.
- TraderProposal: 3-tier action (transaction direction is naturally Buy /
Hold / Sell — position sizing happens later at the Portfolio Manager),
reasoning, and optional entry_price / stop_loss / position_sizing.
Rendered output preserves the trailing "FINAL TRANSACTION PROPOSAL:
**BUY/HOLD/SELL**" line for backward compatibility with the analyst
stop-signal text.
- PortfolioDecision: 5-tier rating, executive summary, investment thesis,
optional price_target / time_horizon (unchanged).
The shared try-structured-then-fallback pattern is extracted into
tradingagents/agents/utils/structured.py (bind_structured +
invoke_structured_or_freetext) so all three agents go through the same
code path and log the same warning when a provider lacks structured
output and the agent falls back to free-text generation.
Net effect for users: every saved markdown report (research/manager.md,
trading/trader.md, portfolio/decision.md) now has consistent section
headers across runs and providers, easier to scan.
Net effect for the runtime: the rating extraction round-trip is gone —
the rating comes from the structured response itself, not a second
LLM call. SignalProcessor was already simplified to a heuristic adapter
in the previous commit.
11 new tests in tests/test_structured_agents.py cover the Trader and
Research Manager render functions, structured-output happy paths, and
free-text fallback. Full suite: 88 tests pass in ~2s without API keys.