mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-26 22:42:40 +03:00
- display.py holds the message buffer, layout, status tables and report panels, the analyst wall-time tracker (CLI-only, from graph/analyst_execution) and the one Console - utils.py is renamed prompts.py, which is what it holds; its analyst list is ANALYST_CHOICES, apart from display's ANALYST_ORDER - get_initial_analyst_node, a one-line helper with one caller, is inlined - the wall-time tracker tests sit with the other display tests, and tests import cli.prompts as prompts
92 lines
3.7 KiB
Python
92 lines
3.7 KiB
Python
"""What the last run chose, offered back as the next run's defaults.
|
|
|
|
The interactive flow asks the same questions every time, and only some of them
|
|
have an environment variable to skip them (the analyst set has none). Remembered
|
|
answers prefill the prompts so Enter accepts them; they never skip a step, so a
|
|
run always starts on choices the user has seen.
|
|
|
|
Only answers that are stable between runs are kept. The ticker and the analysis
|
|
date are not: they change every run, and a remembered date would quietly offer a
|
|
stale one.
|
|
|
|
Every value is checked against the current choices on the way out, because
|
|
models and providers are added and retired between versions. A remembered model
|
|
that is no longer offered is dropped rather than shown.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
from cli.models import AnalystType, AssetType
|
|
from cli.prompts import _llm_provider_table, filter_analysts_for_asset_type
|
|
from tradingagents.llm_clients.model_catalog import get_model_options
|
|
|
|
_PREFS_PATH = Path(os.path.expanduser("~")) / ".tradingagents" / "cli_prefs.json"
|
|
|
|
REMEMBERED = (
|
|
"output_language", "analysts", "research_depth", "llm_provider",
|
|
"quick_think_llm", "deep_think_llm", "backend_url",
|
|
)
|
|
|
|
|
|
def load_last_run() -> dict:
|
|
"""The previous run's answers, or an empty dict when there is nothing usable.
|
|
|
|
Convenience state: an unreadable or corrupt file means no defaults, never an
|
|
error in the user's way.
|
|
"""
|
|
try:
|
|
data = json.loads(_PREFS_PATH.read_text(encoding="utf-8"))
|
|
return data if isinstance(data, dict) else {}
|
|
except (OSError, ValueError):
|
|
return {}
|
|
|
|
|
|
def save_last_run(selections: dict) -> None:
|
|
"""Record the answers worth offering next time; failure is never fatal."""
|
|
kept = {k: v for k, v in selections.items() if k in REMEMBERED and v not in (None, "", [])}
|
|
kept["analysts"] = [getattr(a, "value", a) for a in kept.get("analysts", [])] or None
|
|
kept = {k: v for k, v in kept.items() if v is not None}
|
|
try:
|
|
_PREFS_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
temp = _PREFS_PATH.with_suffix(".tmp")
|
|
temp.write_text(json.dumps(kept, indent=2), encoding="utf-8")
|
|
os.replace(temp, _PREFS_PATH) # a concurrent run reads one file or the other
|
|
except OSError:
|
|
return
|
|
|
|
|
|
def sanitize(prefs: dict, asset_type) -> dict:
|
|
"""Keep only the remembered answers that are still choosable now."""
|
|
kept: dict = {}
|
|
if isinstance(prefs.get("output_language"), str):
|
|
kept["output_language"] = prefs["output_language"]
|
|
if prefs.get("research_depth") in (1, 3, 5):
|
|
kept["research_depth"] = prefs["research_depth"]
|
|
|
|
known = {a.value for a in AnalystType}
|
|
analysts = [a for a in prefs.get("analysts") or [] if a in known]
|
|
allowed = filter_analysts_for_asset_type([AnalystType(a) for a in analysts], AssetType(asset_type))
|
|
if allowed:
|
|
kept["analysts"] = [a.value for a in allowed]
|
|
|
|
provider = prefs.get("llm_provider")
|
|
# Region-specific providers (qwen-cn) are picked in a second prompt, so the
|
|
# base key is what the provider menu matches.
|
|
base = (provider or "").split("-cn")[0]
|
|
if base and base in {key for _, key, _ in _llm_provider_table()}:
|
|
kept["llm_provider"] = provider
|
|
if isinstance(prefs.get("backend_url"), str) and prefs["backend_url"]:
|
|
kept["backend_url"] = prefs["backend_url"]
|
|
for field, mode in (("quick_think_llm", "quick"), ("deep_think_llm", "deep")):
|
|
try:
|
|
offered = {model for _, model in get_model_options(base, mode)}
|
|
except KeyError:
|
|
continue
|
|
if prefs.get(field) in offered:
|
|
kept[field] = prefs[field]
|
|
return kept
|