175 lines
5.6 KiB
Python
175 lines
5.6 KiB
Python
import datetime as dt
|
||
from typing import Any, Dict, List, Optional
|
||
|
||
import streamlit as st
|
||
|
||
from tradingagents.llm_clients.model_catalog import MODEL_OPTIONS
|
||
|
||
OUTPUT_LANGS = [
|
||
"English",
|
||
"Chinese",
|
||
"Japanese",
|
||
"Korean",
|
||
"Hindi",
|
||
"Spanish",
|
||
"Portuguese",
|
||
"French",
|
||
"German",
|
||
"Arabic",
|
||
"Russian",
|
||
]
|
||
|
||
PROVIDER_URL = {
|
||
"openai": "https://api.openai.com/v1",
|
||
"siliconflow": "https://api.siliconflow.cn/v1",
|
||
"google": "https://generativelanguage.googleapis.com/v1",
|
||
"anthropic": "https://api.anthropic.com/",
|
||
"xai": "https://api.x.ai/v1",
|
||
"openrouter": "https://openrouter.ai/api/v1",
|
||
"ollama": "http://localhost:11434/v1",
|
||
}
|
||
|
||
|
||
def _models_for(provider: str, mode: str) -> List[str]:
|
||
return [value for _label, value in MODEL_OPTIONS[provider][mode]]
|
||
|
||
|
||
def render_config_form(
|
||
container,
|
||
disabled: bool = False,
|
||
key_prefix: str = "cfg",
|
||
) -> Dict[str, Any]:
|
||
"""在任意 Streamlit 容器中渲染配置表单(主区或侧栏)。"""
|
||
kp = key_prefix
|
||
container.header("分析任务配置")
|
||
ticker = (
|
||
container.text_input("步骤 1:股票代码(Ticker)", value="SPY", disabled=disabled, key=f"{kp}_ticker")
|
||
.strip()
|
||
.upper()
|
||
)
|
||
analysis_date = container.date_input(
|
||
"步骤 2:分析日期",
|
||
value=dt.date.today(),
|
||
disabled=disabled,
|
||
key=f"{kp}_date",
|
||
)
|
||
output_language = container.selectbox(
|
||
"步骤 3:报告输出语言",
|
||
options=OUTPUT_LANGS,
|
||
index=0,
|
||
disabled=disabled,
|
||
key=f"{kp}_lang",
|
||
)
|
||
|
||
container.markdown("步骤 4:分析师团队")
|
||
include_market = container.checkbox("市场", value=True, disabled=disabled, key=f"{kp}_mkt")
|
||
include_social = container.checkbox("舆情", value=True, disabled=disabled, key=f"{kp}_soc")
|
||
include_news = container.checkbox("新闻", value=True, disabled=disabled, key=f"{kp}_news")
|
||
include_fundamentals = container.checkbox(
|
||
"基本面", value=True, disabled=disabled, key=f"{kp}_fund"
|
||
)
|
||
|
||
research_depth = container.radio(
|
||
"步骤 5:研究深度(辩论轮数)",
|
||
options=[1, 3, 5],
|
||
horizontal=True,
|
||
disabled=disabled,
|
||
key=f"{kp}_depth",
|
||
)
|
||
|
||
provider = container.selectbox(
|
||
"步骤 6:LLM 提供商",
|
||
options=list(PROVIDER_URL.keys()),
|
||
index=0,
|
||
disabled=disabled,
|
||
key=f"{kp}_prov",
|
||
)
|
||
|
||
quick_options = _models_for(provider, "quick")
|
||
deep_options = _models_for(provider, "deep")
|
||
quick_model = container.selectbox(
|
||
"步骤 7:快速模型(Quick)", quick_options, disabled=disabled, key=f"{kp}_quick"
|
||
)
|
||
deep_model = container.selectbox(
|
||
"步骤 7:深度模型(Deep)", deep_options, disabled=disabled, key=f"{kp}_deep"
|
||
)
|
||
|
||
google_thinking: Optional[str] = None
|
||
openai_reasoning: Optional[str] = None
|
||
anthropic_effort: Optional[str] = None
|
||
if provider == "google":
|
||
google_thinking = container.selectbox(
|
||
"步骤 8:Google Thinking",
|
||
options=["high", "minimal"],
|
||
index=0,
|
||
disabled=disabled,
|
||
key=f"{kp}_gthink",
|
||
)
|
||
elif provider in ("openai", "siliconflow"):
|
||
openai_reasoning = container.selectbox(
|
||
"步骤 8:推理强度(Reasoning)",
|
||
options=["medium", "high", "low"],
|
||
index=0,
|
||
disabled=disabled,
|
||
key=f"{kp}_reason",
|
||
)
|
||
elif provider == "anthropic":
|
||
anthropic_effort = container.selectbox(
|
||
"步骤 8:Anthropic Effort",
|
||
options=["high", "medium", "low"],
|
||
index=0,
|
||
disabled=disabled,
|
||
key=f"{kp}_anth",
|
||
)
|
||
|
||
selected_analysts = []
|
||
if include_market:
|
||
selected_analysts.append("market")
|
||
if include_social:
|
||
selected_analysts.append("social")
|
||
if include_news:
|
||
selected_analysts.append("news")
|
||
if include_fundamentals:
|
||
selected_analysts.append("fundamentals")
|
||
|
||
return {
|
||
"ticker": ticker,
|
||
"analysis_date": analysis_date.strftime("%Y-%m-%d"),
|
||
"output_language": output_language,
|
||
"selected_analysts": selected_analysts,
|
||
"research_depth": research_depth,
|
||
"llm_provider": provider,
|
||
"backend_url": PROVIDER_URL[provider],
|
||
"quick_model": quick_model,
|
||
"deep_model": deep_model,
|
||
"google_thinking_level": google_thinking,
|
||
"openai_reasoning_effort": openai_reasoning,
|
||
"anthropic_effort": anthropic_effort,
|
||
}
|
||
|
||
|
||
def render_sidebar_form(disabled: bool = False) -> Dict[str, Any]:
|
||
"""向后兼容:在侧栏渲染完整配置。"""
|
||
return render_config_form(st.sidebar, disabled=disabled, key_prefix="sidebar")
|
||
|
||
|
||
def format_params_summary(p: Dict[str, Any]) -> str:
|
||
"""运行结束后折叠摘要卡片用的一段 Markdown。"""
|
||
effort = (
|
||
p.get("google_thinking_level")
|
||
or p.get("openai_reasoning_effort")
|
||
or p.get("anthropic_effort")
|
||
or "—"
|
||
)
|
||
analysts = ", ".join(p.get("selected_analysts") or []) or "—"
|
||
return (
|
||
f"**股票代码:** {p.get('ticker', '—')} \n"
|
||
f"**分析日期:** {p.get('analysis_date', '—')} \n"
|
||
f"**提供商:** {p.get('llm_provider', '—')} \n"
|
||
f"**模型:** quick `{p.get('quick_model', '—')}` · deep `{p.get('deep_model', '—')}` \n"
|
||
f"**研究深度:** {p.get('research_depth', '—')} \n"
|
||
f"**分析师:** {analysts} \n"
|
||
f"**推理 / 思考:** {effort} \n"
|
||
f"**输出语言:** {p.get('output_language', '—')}"
|
||
)
|