mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-06-17 05:16:14 +03:00
Three related changes that take the rating pipeline from heuristic-only to type-safe at the source. 1) Research Manager prompt now uses the same 5-tier scale (Buy / Overweight / Hold / Underweight / Sell) as the Portfolio Manager, signal_processing, and the memory log. The prior 3-tier wording (Buy / Sell / Hold) was the only remaining inconsistency in the pipeline. 2) Centralise the 5-tier vocabulary and the heuristic prose-rating parser into tradingagents/agents/utils/rating.py. Both the memory log and the signal processor now share the same parser instead of duplicating regex and word-walker logic. 3) Make structured output a first-class part of the Portfolio Manager's primary call. The PM uses llm.with_structured_output(PortfolioDecision) so each provider's native structured-output mode (json_schema for OpenAI/xAI, response_schema for Gemini, tool-use for Anthropic, function_calling for OpenAI-compatible providers) yields a typed Pydantic instance directly. A render helper turns that instance back into the same markdown shape downstream consumers (memory log, CLI display, saved reports) already expect, so no other code has to know the PM now produces structured output. Providers without structured support fall back gracefully to free-text + the deterministic heuristic. The previous SignalProcessor had been making a second LLM call to re-extract the rating from the PM's prose; that round-trip is now eliminated. SignalProcessor is a thin adapter over parse_rating(), makes zero LLM calls, and stays for backwards compatibility with process_signal() callers. Schema (PortfolioDecision) captures rating + executive_summary + investment_thesis + optional price_target + time_horizon, with field descriptions doubling as output instructions. Agent prose remains the primary artifact; structured output is layered onto the PM only because it is the one agent whose output has machine-readable downstream consumers. 15 new tests cover the heuristic parser (markdown-bold edge cases that had no coverage before), the structured PM happy path, the free-text fallback path, and that SignalProcessor never invokes the LLM. Full suite: 77 tests pass in ~2s without API keys.
32 lines
1.3 KiB
Python
32 lines
1.3 KiB
Python
"""Extract the 5-tier portfolio rating from the Portfolio Manager's decision.
|
|
|
|
The Portfolio Manager produces a typed ``PortfolioDecision`` via structured
|
|
output and renders it to markdown that always carries a ``**Rating**: X``
|
|
header (see :func:`tradingagents.agents.schemas.render_pm_decision`). The
|
|
deterministic heuristic in :mod:`tradingagents.agents.utils.rating` is more
|
|
than sufficient to extract that rating; no extra LLM call is needed.
|
|
|
|
This module exists for backwards compatibility with callers that expect a
|
|
``SignalProcessor.process_signal(text)`` interface.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from tradingagents.agents.utils.rating import parse_rating
|
|
|
|
|
|
class SignalProcessor:
|
|
"""Read the 5-tier rating out of a Portfolio Manager decision."""
|
|
|
|
def __init__(self, quick_thinking_llm: Any = None):
|
|
# The LLM argument is accepted for backwards compatibility but no
|
|
# longer used: the PM's structured output guarantees the rating is
|
|
# parseable from the rendered markdown without a second LLM call.
|
|
self.quick_thinking_llm = quick_thinking_llm
|
|
|
|
def process_signal(self, full_signal: str) -> str:
|
|
"""Return one of Buy / Overweight / Hold / Underweight / Sell."""
|
|
return parse_rating(full_signal)
|