2024-08-02 18:37:15 +08:00
import argparse
import textwrap
2024-07-30 17:23:05 +08:00
from collections import defaultdict
from datetime import datetime , timezone
2024-08-09 17:23:45 +08:00
from importlib.resources import files as rfiles
2024-08-02 18:37:15 +08:00
from pathlib import Path
2024-07-30 17:23:05 +08:00
from typing import Callable , Type
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import streamlit as st
from plotly.subplots import make_subplots
from streamlit import session_state as state
2024-08-09 20:40:16 +08:00
from streamlit_theme import st_theme
2024-07-30 17:23:05 +08:00
from rdagent.components.coder.factor_coder.CoSTEER.evaluators import (
FactorSingleFeedback ,
)
from rdagent.components.coder.factor_coder.factor import FactorFBWorkspace , FactorTask
from rdagent.components.coder.model_coder.CoSTEER.evaluators import ModelCoderFeedback
from rdagent.components.coder.model_coder.model import ModelFBWorkspace , ModelTask
from rdagent.core.proposal import Hypothesis , HypothesisFeedback
2024-08-09 20:40:16 +08:00
from rdagent.core.scenario import Scenario
2024-07-30 17:23:05 +08:00
from rdagent.log.base import Message
from rdagent.log.storage import FileStorage
from rdagent.log.ui.qlib_report_figure import report_figure
2024-08-02 18:37:15 +08:00
from rdagent.scenarios.data_mining.experiment.model_experiment import DMModelScenario
2024-08-05 18:11:23 +08:00
from rdagent.scenarios.general_model.scenario import GeneralModelScenario
2024-08-09 20:40:16 +08:00
from rdagent.scenarios.qlib.experiment.factor_experiment import QlibFactorScenario
from rdagent.scenarios.qlib.experiment.factor_from_report_experiment import (
QlibFactorFromReportScenario ,
2024-08-02 18:37:15 +08:00
)
2024-07-30 17:23:05 +08:00
from rdagent.scenarios.qlib.experiment.model_experiment import (
QlibModelExperiment ,
QlibModelScenario ,
2024-07-26 12:12:16 +08:00
)
2024-07-23 14:48:14 +08:00
2024-08-02 18:37:15 +08:00
st . set_page_config ( layout = "wide" , page_title = "RD-Agent" , page_icon = "🎓" , initial_sidebar_state = "expanded" )
2024-07-30 17:23:05 +08:00
2024-08-02 18:37:15 +08:00
# 获取log_path参数
parser = argparse . ArgumentParser ( description = "RD-Agent Streamlit App" )
parser . add_argument ( "--log_dir" , type = str , help = "Path to the log directory" )
2024-08-09 20:40:16 +08:00
parser . add_argument ( "--debug" , action = "store_true" , help = "Enable debug mode" )
2024-08-02 18:37:15 +08:00
args = parser . parse_args ()
if args . log_dir :
main_log_path = Path ( args . log_dir )
if not main_log_path . exists ():
st . error ( f "Log dir ` { main_log_path } ` does not exist!" )
st . stop ()
else :
main_log_path = None
SELECTED_METRICS = [
"IC" ,
"1day.excess_return_without_cost.annualized_return" ,
"1day.excess_return_without_cost.information_ratio" ,
"1day.excess_return_without_cost.max_drawdown" ,
]
2024-07-30 17:23:05 +08:00
2024-08-02 18:37:15 +08:00
if "log_path" not in state :
if main_log_path :
state . log_path = next ( main_log_path . iterdir ()) . relative_to ( main_log_path )
else :
2024-08-09 20:40:16 +08:00
state . log_path = None
st . toast ( ":red[**Please Set Log Path!**]" , icon = "⚠️" )
if "scenario" not in state :
state . scenario = None
2024-07-30 17:23:05 +08:00
if "fs" not in state :
state . fs = None
if "msgs" not in state :
state . msgs = defaultdict ( lambda : defaultdict ( list ))
if "last_msg" not in state :
state . last_msg = None
if "current_tags" not in state :
state . current_tags = []
if "lround" not in state :
state . lround = 0 # RD Loop Round
if "erounds" not in state :
state . erounds = defaultdict ( int ) # Evolving Rounds in each RD Loop
2024-08-02 18:37:15 +08:00
if "e_decisions" not in state :
state . e_decisions = defaultdict ( lambda : defaultdict ( tuple ))
2024-07-30 17:23:05 +08:00
# Summary Info
if "hypotheses" not in state :
# Hypotheses in each RD Loop
state . hypotheses = defaultdict ( None )
if "h_decisions" not in state :
state . h_decisions = defaultdict ( bool )
if "metric_series" not in state :
state . metric_series = []
2024-08-02 18:37:15 +08:00
# Factor Task Baseline
if "alpha158_metrics" not in state :
state . alpha158_metrics = None
2024-07-30 17:23:05 +08:00
def should_display ( msg : Message ):
for t in state . excluded_tags :
if t in msg . tag . split ( "." ):
return False
if type ( msg . content ) . __name__ in state . excluded_types :
return False
return True
def get_msgs_until ( end_func : Callable [[ Message ], bool ] = lambda _ : True ):
if state . fs :
while True :
try :
msg = next ( state . fs )
if should_display ( msg ):
tags = msg . tag . split ( "." )
if "r" not in state . current_tags and "r" in tags :
state . lround += 1
if "evolving code" not in state . current_tags and "evolving code" in tags :
state . erounds [ state . lround ] += 1
state . current_tags = tags
state . last_msg = msg
# Update Summary Info
if "model runner result" in tags or "factor runner result" in tags or "runner result" in tags :
2024-08-02 18:37:15 +08:00
# factor baseline exp metrics
2024-08-09 20:40:16 +08:00
if isinstance ( state . scenario , QlibFactorScenario ) and state . alpha158_metrics is None :
2024-08-02 18:37:15 +08:00
sms = msg . content . based_experiments [ 0 ] . result . loc [ SELECTED_METRICS ]
sms . name = "alpha158"
state . alpha158_metrics = sms
# common metrics
2024-07-30 17:23:05 +08:00
if msg . content . result is None :
2024-08-02 18:37:15 +08:00
state . metric_series . append ( pd . Series ([ None ], index = [ "AUROC" ], name = f "Round { state . lround } " ))
2024-07-30 17:23:05 +08:00
else :
2024-08-02 18:37:15 +08:00
if len ( msg . content . result ) < 4 :
2024-07-30 17:23:05 +08:00
ps = msg . content . result
ps . index = [ "AUROC" ]
2024-08-02 18:37:15 +08:00
ps . name = f "Round { state . lround } "
2024-07-30 17:23:05 +08:00
state . metric_series . append ( ps )
else :
2024-08-02 18:37:15 +08:00
sms = msg . content . result . loc [ SELECTED_METRICS ]
sms . name = f "Round { state . lround } "
state . metric_series . append ( sms )
2024-07-30 17:23:05 +08:00
elif "hypothesis generation" in tags :
state . hypotheses [ state . lround ] = msg . content
elif "ef" in tags and "feedback" in tags :
state . h_decisions [ state . lround ] = msg . content . decision
2024-08-02 18:37:15 +08:00
elif "d" in tags :
if "evolving code" in tags :
msg . content = [ i for i in msg . content if i ]
if "evolving feedback" in tags :
2024-08-09 20:40:16 +08:00
total_len = len ( msg . content )
2024-08-02 18:37:15 +08:00
msg . content = [ i for i in msg . content if i ]
2024-08-09 20:40:16 +08:00
none_num = total_len - len ( msg . content )
2024-08-02 18:37:15 +08:00
if len ( msg . content ) != len ( state . msgs [ state . lround ][ "d.evolving code" ][ - 1 ] . content ):
st . toast ( ":red[**Evolving Feedback Length Error!**]" , icon = "‼️" )
right_num = 0
for wsf in msg . content :
if wsf . final_decision :
right_num += 1
wrong_num = len ( msg . content ) - right_num
2024-08-09 20:40:16 +08:00
state . e_decisions [ state . lround ][ state . erounds [ state . lround ]] = (
right_num ,
wrong_num ,
none_num ,
)
2024-07-30 17:23:05 +08:00
2024-08-02 18:37:15 +08:00
state . msgs [ state . lround ][ msg . tag ] . append ( msg )
2024-07-30 17:23:05 +08:00
# Stop Getting Logs
if end_func ( msg ):
break
except StopIteration :
2024-08-02 18:37:15 +08:00
st . toast ( ":red[**No More Logs to Show!**]" , icon = "🛑" )
2024-07-30 17:23:05 +08:00
break
2024-08-09 20:40:16 +08:00
def refresh ( same_trace : bool = False ):
if state . log_path is None :
st . toast ( ":red[**Please Set Log Path!**]" , icon = "⚠️" )
return
if main_log_path :
state . fs = FileStorage ( main_log_path / state . log_path ) . iter_msg ()
else :
state . fs = FileStorage ( state . log_path ) . iter_msg ()
# detect scenario
if not same_trace :
get_msgs_until ( lambda m : not isinstance ( m . content , str ))
if state . last_msg is None or not isinstance ( state . last_msg . content , Scenario ):
st . toast ( ":red[**No Scenario Info detected**]" , icon = "❗" )
state . scenario = None
else :
state . scenario = state . last_msg . content
st . toast ( f ":green[**Scenario Info detected**] * { type ( state . scenario ) . __name__ } *" , icon = "✅" )
state . msgs = defaultdict ( lambda : defaultdict ( list ))
state . lround = 0
state . erounds = defaultdict ( int )
state . e_decisions = defaultdict ( lambda : defaultdict ( tuple ))
state . hypotheses = defaultdict ( None )
state . h_decisions = defaultdict ( bool )
state . metric_series = []
state . last_msg = None
state . current_tags = []
state . alpha158_metrics = None
2024-08-02 18:37:15 +08:00
def evolving_feedback_window ( wsf : FactorSingleFeedback | ModelCoderFeedback ):
if isinstance ( wsf , FactorSingleFeedback ):
ffc , efc , cfc , vfc = st . tabs (
[ "**Final Feedback🏁**" , "Execution Feedback🖥️" , "Code Feedback📄" , "Value Feedback🔢" ]
)
with ffc :
st . markdown ( wsf . final_feedback )
with efc :
st . code ( wsf . execution_feedback , language = "log" )
with cfc :
st . markdown ( wsf . code_feedback )
with vfc :
st . markdown ( wsf . factor_value_feedback )
elif isinstance ( wsf , ModelCoderFeedback ):
ffc , efc , cfc , msfc , vfc = st . tabs (
[
"**Final Feedback🏁**" ,
"Execution Feedback🖥️" ,
"Code Feedback📄" ,
"Model Shape Feedback📐" ,
"Value Feedback🔢" ,
]
)
with ffc :
st . markdown ( wsf . final_feedback )
with efc :
st . code ( wsf . execution_feedback , language = "log" )
with cfc :
st . markdown ( wsf . code_feedback )
with msfc :
st . markdown ( wsf . shape_feedback )
with vfc :
st . markdown ( wsf . value_feedback )
def display_hypotheses ( hypotheses : dict [ int , Hypothesis ], decisions : dict [ int , bool ], success_only : bool = False ):
2024-08-09 20:40:16 +08:00
name_dict = {
"hypothesis" : "RD-Agent proposes the hypothesis⬇️" ,
"concise_justification" : "because the reason⬇️" ,
"concise_observation" : "based on the observation⬇️" ,
"concise_knowledge" : "Knowledge⬇️ gained after practice" ,
}
2024-08-02 18:37:15 +08:00
if success_only :
shd = { k : v . __dict__ for k , v in hypotheses . items () if decisions [ k ]}
else :
shd = { k : v . __dict__ for k , v in hypotheses . items ()}
df = pd . DataFrame ( shd ) . T
2024-08-09 20:40:16 +08:00
if "concise_observation" in df . columns and "concise_justification" in df . columns :
df [ "concise_observation" ], df [ "concise_justification" ] = df [ "concise_justification" ], df [ "concise_observation" ]
df . rename (
columns = { "concise_observation" : "concise_justification" , "concise_justification" : "concise_observation" },
inplace = True ,
)
2024-08-02 18:37:15 +08:00
if "reason" in df . columns :
df . drop ([ "reason" ], axis = 1 , inplace = True )
2024-08-09 20:40:16 +08:00
if "concise_reason" in df . columns :
df . drop ([ "concise_reason" ], axis = 1 , inplace = True )
df . columns = df . columns . map ( lambda x : name_dict . get ( x , x ))
2024-08-02 18:37:15 +08:00
def style_rows ( row ):
if decisions [ row . name ]:
return [ "color: green;" ] * len ( row )
return [ "" ] * len ( row )
def style_columns ( col ):
2024-08-09 20:40:16 +08:00
if col . name != name_dict . get ( "hypothesis" , "hypothesis" ):
2024-08-02 18:37:15 +08:00
return [ "font-style: italic;" ] * len ( col )
return [ "font-weight: bold;" ] * len ( col )
# st.dataframe(df.style.apply(style_rows, axis=1).apply(style_columns, axis=0))
st . markdown ( df . style . apply ( style_rows , axis = 1 ) . apply ( style_columns , axis = 0 ) . to_html (), unsafe_allow_html = True )
def metrics_window ( df : pd . DataFrame , R : int , C : int , * , height : int = 300 , colors : list [ str ] = None ):
fig = make_subplots ( rows = R , cols = C , subplot_titles = df . columns )
def hypothesis_hover_text ( h : Hypothesis , d : bool = False ):
color = "green" if d else "black"
text = h . hypothesis
lines = textwrap . wrap ( text , width = 60 )
return f "<span style='color: { color } ;'> { '<br>' . join ( lines ) } </span>"
hover_texts = [
hypothesis_hover_text ( state . hypotheses [ int ( i [ 6 :])], state . h_decisions [ int ( i [ 6 :])])
for i in df . index
if i != "alpha158"
]
if state . alpha158_metrics is not None :
hover_texts = [ "Baseline: alpha158" ] + hover_texts
for ci , col in enumerate ( df . columns ):
row = ci // C + 1
col_num = ci % C + 1
fig . add_trace (
go . Scatter (
x = df . index ,
y = df [ col ],
name = col ,
mode = "lines+markers" ,
connectgaps = True ,
marker = dict ( size = 10 , color = colors [ ci ]) if colors else dict ( size = 10 ),
hovertext = hover_texts ,
hovertemplate = "% {hovertext} <br><br><span style='color: black'>% {x} Value:</span> <span style='color: blue'>% {y} </span><extra></extra>" ,
),
row = row ,
col = col_num ,
)
fig . update_layout ( showlegend = False , height = height )
if state . alpha158_metrics is not None :
for i in range ( 1 , R + 1 ): # 行
for j in range ( 1 , C + 1 ): # 列
fig . update_xaxes (
tickvals = [ df . index [ 0 ]] + list ( df . index [ 1 :]),
ticktext = [ f '<span style="color:blue; font-weight:bold"> { df . index [ 0 ] } </span>' ] + list ( df . index [ 1 :]),
row = i ,
col = j ,
)
st . plotly_chart ( fig )
def summary_window ():
2024-08-09 20:40:16 +08:00
if isinstance (
state . scenario , ( QlibModelScenario , DMModelScenario , QlibFactorScenario , QlibFactorFromReportScenario )
):
2024-08-02 18:37:15 +08:00
st . header ( "Summary📊" , divider = "rainbow" , anchor = "_summary" )
2024-08-09 20:40:16 +08:00
if state . lround == 0 :
return
2024-08-02 18:37:15 +08:00
with st . container ():
# TODO: not fixed height
with st . container ():
bc , cc = st . columns ([ 2 , 2 ], vertical_alignment = "center" )
with bc :
st . subheader ( "Metrics📈" , anchor = "_metrics" )
with cc :
show_true_only = st . toggle ( "successful hypotheses" , value = False )
# hypotheses_c, chart_c = st.columns([2, 3])
chart_c = st . container ()
hypotheses_c = st . container ()
with hypotheses_c :
st . subheader ( "Hypotheses🏅" , anchor = "_hypotheses" )
display_hypotheses ( state . hypotheses , state . h_decisions , show_true_only )
with chart_c :
2024-08-09 20:40:16 +08:00
if isinstance ( state . scenario , QlibFactorScenario ) and state . alpha158_metrics is not None :
2024-08-02 18:37:15 +08:00
df = pd . DataFrame ([ state . alpha158_metrics ] + state . metric_series )
else :
df = pd . DataFrame ( state . metric_series )
if show_true_only and len ( state . hypotheses ) >= len ( state . metric_series ):
if state . alpha158_metrics is not None :
selected = [ "alpha158" ] + [ i for i in df . index if state . h_decisions [ int ( i [ 6 :])]]
else :
selected = [ i for i in df . index if state . h_decisions [ int ( i [ 6 :])]]
df = df . loc [ selected ]
if df . shape [ 0 ] == 1 :
st . table ( df . iloc [ 0 ])
elif df . shape [ 0 ] > 1 :
if df . shape [ 1 ] == 1 :
# suhan's scenario
fig = px . line ( df , x = df . index , y = df . columns , markers = True )
fig . update_layout ( xaxis_title = "Loop Round" , yaxis_title = None )
st . plotly_chart ( fig )
else :
metrics_window ( df , 1 , 4 , height = 300 , colors = [ "red" , "blue" , "orange" , "green" ])
2024-08-09 20:40:16 +08:00
elif isinstance ( state . scenario , GeneralModelScenario ):
2024-08-02 18:37:15 +08:00
with st . container ( border = True ):
st . subheader ( "Summary📊" , divider = "rainbow" , anchor = "_summary" )
2024-08-09 20:40:16 +08:00
if len ( state . msgs [ state . lround ][ "d.evolving code" ]) > 0 :
# pass
ws : list [ FactorFBWorkspace | ModelFBWorkspace ] = state . msgs [ state . lround ][ "d.evolving code" ][ - 1 ] . content
# All Tasks
tab_names = [
w . target_task . factor_name if isinstance ( w . target_task , FactorTask ) else w . target_task . name
for w in ws
]
for j in range ( len ( ws )):
if state . msgs [ state . lround ][ "d.evolving feedback" ][ - 1 ] . content [ j ] . final_decision :
tab_names [ j ] += "✔️"
else :
tab_names [ j ] += "❌"
2024-08-02 18:37:15 +08:00
2024-08-09 20:40:16 +08:00
wtabs = st . tabs ( tab_names )
for j , w in enumerate ( ws ):
with wtabs [ j ]:
# Evolving Code
for k , v in w . code_dict . items ():
with st . expander ( f ":green[` { k } `]" , expanded = False ):
st . code ( v , language = "python" )
2024-08-02 18:37:15 +08:00
2024-08-09 20:40:16 +08:00
# Evolving Feedback
evolving_feedback_window ( state . msgs [ state . lround ][ "d.evolving feedback" ][ - 1 ] . content [ j ])
2024-08-02 18:37:15 +08:00
def tabs_hint ():
st . markdown (
"<p style='font-size: small; color: #888888;'>You can navigate through the tabs using ⬅️ ➡️ or by holding Shift and scrolling with the mouse wheel🖱️.</p>" ,
unsafe_allow_html = True ,
)
def tasks_window ( tasks : list [ FactorTask | ModelTask ]):
if isinstance ( tasks [ 0 ], FactorTask ):
st . markdown ( "**Factor Tasks🚩**" )
tnames = [ f . factor_name for f in tasks ]
if sum ( len ( tn ) for tn in tnames ) > 100 :
tabs_hint ()
tabs = st . tabs ( tnames )
for i , ft in enumerate ( tasks ):
with tabs [ i ]:
# st.markdown(f"**Factor Name**: {ft.factor_name}")
st . markdown ( f "**Description**: { ft . factor_description } " )
st . latex ( "Formulation" )
2024-08-09 20:40:16 +08:00
st . latex ( ft . factor_formulation )
2024-08-02 18:37:15 +08:00
mks = "| Variable | Description | \n | --- | --- | \n "
for v , d in ft . variables . items ():
mks += f "| $ { v } $ | { d } | \n "
st . markdown ( mks )
elif isinstance ( tasks [ 0 ], ModelTask ):
st . markdown ( "**Model Tasks🚩**" )
tnames = [ m . name for m in tasks ]
if sum ( len ( tn ) for tn in tnames ) > 100 :
tabs_hint ()
tabs = st . tabs ( tnames )
for i , mt in enumerate ( tasks ):
with tabs [ i ]:
# st.markdown(f"**Model Name**: {mt.name}")
st . markdown ( f "**Model Type**: { mt . model_type } " )
st . markdown ( f "**Description**: { mt . description } " )
st . latex ( "Formulation" )
2024-08-09 20:40:16 +08:00
st . latex ( mt . formulation )
2024-08-02 18:37:15 +08:00
mks = "| Variable | Description | \n | --- | --- | \n "
for v , d in mt . variables . items ():
mks += f "| $ { v } $ | { d } | \n "
st . markdown ( mks )
def research_window ():
with st . container ( border = True ):
2024-08-09 20:40:16 +08:00
title = (
"Research🔍"
if isinstance (
state . scenario , ( QlibModelScenario , DMModelScenario , QlibFactorScenario , QlibFactorFromReportScenario )
)
else "Research🔍 (reader)"
)
2024-08-02 18:37:15 +08:00
st . subheader ( title , divider = "blue" , anchor = "_research" )
2024-08-09 20:40:16 +08:00
if isinstance (
state . scenario , ( QlibModelScenario , DMModelScenario , QlibFactorScenario , QlibFactorFromReportScenario )
):
2024-07-30 17:23:05 +08:00
# pdf image
if pim := state . msgs [ round ][ "r.extract_factors_and_implement.load_pdf_screenshot" ]:
for i in range ( min ( 2 , len ( pim ))):
2024-08-02 18:37:15 +08:00
st . image ( pim [ i ] . content , use_column_width = True )
2024-07-30 17:23:05 +08:00
# Hypothesis
if hg := state . msgs [ round ][ "r.hypothesis generation" ]:
st . markdown ( "**Hypothesis💡**" ) # 🧠
h : Hypothesis = hg [ 0 ] . content
st . markdown (
f """
- **Hypothesis**: { h . hypothesis }
- **Reason**: { h . reason } """
)
if eg := state . msgs [ round ][ "r.experiment generation" ]:
2024-08-02 18:37:15 +08:00
tasks_window ( eg [ 0 ] . content )
2024-08-09 20:40:16 +08:00
elif isinstance ( state . scenario , GeneralModelScenario ):
2024-08-02 18:37:15 +08:00
# pdf image
c1 , c2 = st . columns ([ 2 , 3 ])
with c1 :
if pim := state . msgs [ round ][ "r.pdf_image" ]:
for i in range ( len ( pim )):
st . image ( pim [ i ] . content , use_column_width = True )
# loaded model exp
with c2 :
if mem := state . msgs [ round ][ "d.load_experiment" ]:
me : QlibModelExperiment = mem [ 0 ] . content
tasks_window ( me . sub_tasks )
def feedback_window ():
2024-08-09 20:40:16 +08:00
if isinstance (
state . scenario , ( QlibModelScenario , DMModelScenario , QlibFactorScenario , QlibFactorFromReportScenario )
):
2024-07-30 17:23:05 +08:00
with st . container ( border = True ):
2024-08-02 18:37:15 +08:00
st . subheader ( "Feedback📝" , divider = "orange" , anchor = "_feedback" )
2024-08-09 20:40:16 +08:00
if state . lround > 0 and isinstance (
state . scenario , ( QlibModelScenario , QlibFactorScenario , QlibFactorFromReportScenario )
):
with st . expander ( "**Config⚙️**" , expanded = True ):
st . markdown ( state . scenario . experiment_setting , unsafe_allow_html = True )
2024-07-30 17:23:05 +08:00
if fbr := state . msgs [ round ][ "ef.Quantitative Backtesting Chart" ]:
st . markdown ( "**Returns📈**" )
fig = report_figure ( fbr [ 0 ] . content )
st . plotly_chart ( fig )
if fb := state . msgs [ round ][ "ef.feedback" ]:
st . markdown ( "**Hypothesis Feedback🔍**" )
h : HypothesisFeedback = fb [ 0 ] . content
st . markdown (
f """
- **Observations**: { h . observations }
- **Hypothesis Evaluation**: { h . hypothesis_evaluation }
- **New Hypothesis**: { h . new_hypothesis }
- **Decision**: { h . decision }
- **Reason**: { h . reason } """
)
2024-08-09 20:40:16 +08:00
@st.fragment
def evolving_window ():
2024-08-02 18:37:15 +08:00
title = (
"Development🛠️"
2024-08-09 20:40:16 +08:00
if isinstance (
state . scenario , ( QlibModelScenario , DMModelScenario , QlibFactorScenario , QlibFactorFromReportScenario )
)
2024-08-02 18:37:15 +08:00
else "Development🛠️ (evolving coder)"
)
st . subheader ( title , divider = "green" , anchor = "_development" )
# Evolving Status
2024-07-30 17:23:05 +08:00
if state . erounds [ round ] > 0 :
2024-08-02 18:37:15 +08:00
st . markdown ( "**☑️ Evolving Status**" )
es = state . e_decisions [ round ]
e_status_mks = "" . join ( f "| { ei } " for ei in range ( 1 , state . erounds [ round ] + 1 )) + "| \n "
e_status_mks += "|--" * state . erounds [ round ] + "| \n "
for ei , estatus in es . items ():
if not estatus :
2024-08-09 20:40:16 +08:00
estatus = ( 0 , 0 , 0 )
e_status_mks += "| " + "🕙<br>" * estatus [ 2 ] + "✔️<br>" * estatus [ 0 ] + "❌<br>" * estatus [ 1 ] + " "
2024-08-02 18:37:15 +08:00
e_status_mks += "| \n "
st . markdown ( e_status_mks , unsafe_allow_html = True )
2024-07-23 14:48:14 +08:00
2024-08-02 18:37:15 +08:00
# Evolving Tabs
if state . erounds [ round ] > 0 :
if state . erounds [ round ] > 1 :
2024-08-09 20:40:16 +08:00
evolving_round = st . radio (
"**🔄️Evolving Rounds**" ,
horizontal = True ,
options = range ( 1 , state . erounds [ round ] + 1 ),
index = state . erounds [ round ] - 1 ,
key = "show_eround" ,
2024-08-02 18:37:15 +08:00
)
else :
evolving_round = 1
ws : list [ FactorFBWorkspace | ModelFBWorkspace ] = state . msgs [ round ][ "d.evolving code" ][
evolving_round - 1
] . content
# All Tasks
tab_names = [
w . target_task . factor_name if isinstance ( w . target_task , FactorTask ) else w . target_task . name for w in ws
]
if len ( state . msgs [ round ][ "d.evolving feedback" ]) >= evolving_round :
for j in range ( len ( ws )):
if state . msgs [ round ][ "d.evolving feedback" ][ evolving_round - 1 ] . content [ j ] . final_decision :
tab_names [ j ] += "✔️"
else :
tab_names [ j ] += "❌"
if sum ( len ( tn ) for tn in tab_names ) > 100 :
tabs_hint ()
wtabs = st . tabs ( tab_names )
for j , w in enumerate ( ws ):
with wtabs [ j ]:
# Evolving Code
for k , v in w . code_dict . items ():
with st . expander ( f ":green[` { k } `]" , expanded = True ):
st . code ( v , language = "python" )
# Evolving Feedback
if len ( state . msgs [ round ][ "d.evolving feedback" ]) >= evolving_round :
evolving_feedback_window ( state . msgs [ round ][ "d.evolving feedback" ][ evolving_round - 1 ] . content [ j ])
2024-07-23 14:48:14 +08:00
2024-08-09 20:40:16 +08:00
toc = """
## [Scenario Description📖](#_scenario)
## [Summary📊](#_summary)
- [**Metrics📈**](#_metrics)
- [**Hypotheses🏅**](#_hypotheses)
## [RD-Loops♾️](#_rdloops)
- [**Research🔍**](#_research)
- [**Development🛠️**](#_development)
- [**Feedback📝**](#_feedback)
"""
if isinstance ( state . scenario , GeneralModelScenario ):
toc = """
## [Scenario Description📖](#_scenario)
### [Summary📊](#_summary)
### [Research🔍](#_research)
### [Development🛠️](#_development)
"""
# Config Sidebar
with st . sidebar :
st . markdown ( "# RD-Agent🤖 [:grey[@GitHub]](https://github.com/microsoft/RD-Agent)" )
st . subheader ( ":blue[Table of Content]" , divider = "blue" )
st . markdown ( toc )
st . subheader ( ":orange[Control Panel]" , divider = "red" )
with st . container ( border = True ):
if main_log_path :
lc1 , lc2 = st . columns ([ 1 , 2 ], vertical_alignment = "center" )
with lc1 :
st . markdown ( ":blue[**Log Path**]" )
with lc2 :
manually = st . toggle ( "Manual Input" )
if manually :
st . text_input ( "log path" , key = "log_path" , on_change = refresh , label_visibility = "collapsed" )
else :
folders = [ folder . relative_to ( main_log_path ) for folder in main_log_path . iterdir () if folder . is_dir ()]
st . selectbox ( f "**Select from ` { main_log_path } `**" , folders , key = "log_path" , on_change = refresh )
else :
st . text_input ( ":blue[**log path**]" , key = "log_path" , on_change = refresh )
c1 , c2 = st . columns ([ 1 , 1 ], vertical_alignment = "center" )
with c1 :
if st . button ( ":green[**All Loops**]" , use_container_width = True ):
if not state . fs :
refresh ()
get_msgs_until ( lambda m : False )
if st . button ( "**Reset**" , use_container_width = True ):
refresh ( same_trace = True )
with c2 :
if st . button ( ":green[Next Loop]" , use_container_width = True ):
if not state . fs :
refresh ()
get_msgs_until ( lambda m : "ef.feedback" in m . tag )
if st . button ( "Next Step" , use_container_width = True ):
if not state . fs :
refresh ()
get_msgs_until ( lambda m : "d.evolving feedback" in m . tag )
with st . popover ( ":orange[**Config⚙️**]" , use_container_width = True ):
st . multiselect ( "excluded log tags" , [ "llm_messages" ], [ "llm_messages" ], key = "excluded_tags" )
st . multiselect ( "excluded log types" , [ "str" , "dict" , "list" ], [ "str" ], key = "excluded_types" )
if args . debug :
debug = st . toggle ( "debug" , value = False )
if debug :
if st . button ( "Single Step Run" , use_container_width = True ):
get_msgs_until ()
else :
debug = False
# Debug Info Window
if debug :
with st . expander ( ":red[**Debug Info**]" , expanded = True ):
dcol1 , dcol2 = st . columns ([ 1 , 3 ])
with dcol1 :
st . markdown (
f "**log path**: { state . log_path } \n\n "
f "**excluded tags**: { state . excluded_tags } \n\n "
f "**excluded types**: { state . excluded_types } \n\n "
f ":blue[**message id**]: { sum ( sum ( len ( tmsgs ) for tmsgs in rmsgs . values ()) for rmsgs in state . msgs . values ()) } \n\n "
f ":blue[**round**]: { state . lround } \n\n "
f ":blue[**evolving round**]: { state . erounds [ state . lround ] } \n\n "
)
with dcol2 :
if state . last_msg :
st . write ( state . last_msg )
if isinstance ( state . last_msg . content , list ):
st . write ( state . last_msg . content [ 0 ])
elif not isinstance ( state . last_msg . content , str ):
st . write ( state . last_msg . content . __dict__ )
if state . log_path and state . fs is None :
refresh ()
# Main Window
header_c1 , header_c3 = st . columns ([ 1 , 6 ], vertical_alignment = "center" )
with st . container ():
with header_c1 :
st . image ( "https://img-prod-cms-rt-microsoft-com.akamaized.net/cms/api/am/imageFileData/RE1Mu3b?ver=5c31" )
with header_c3 :
st . markdown (
"""
<h1>
RD-Agent:<br>LLM-based autonomous evolving agents for industrial data-driven R&D
</h1>
""" ,
unsafe_allow_html = True ,
)
# Project Info
with st . container ():
image_c , scen_c = st . columns ([ 3 , 3 ], vertical_alignment = "center" )
with image_c :
img_path = rfiles ( "rdagent.log.ui" ) . joinpath ( "flow.png" )
st . image ( str ( img_path ), use_column_width = True )
with scen_c :
st . header ( "Scenario Description📖" , divider = "violet" , anchor = "_scenario" )
if state . scenario is not None :
theme = st_theme ()
if theme :
theme = theme . get ( "base" , "light" )
css = f """
<style>
a[href="#_rdloops"], a[href="#_research"], a[href="#_development"], a[href="#_feedback"], a[href="#_scenario"], a[href="#_summary"], a[href="#_hypotheses"], a[href="#_metrics"] {{
color: { "black" if theme == "light" else "white" } ;
}}
</style>
"""
st . markdown ( state . scenario . rich_style_description + css , unsafe_allow_html = True )
if state . scenario is not None :
summary_window ()
# R&D Loops Window
if isinstance (
state . scenario , ( QlibModelScenario , DMModelScenario , QlibFactorScenario , QlibFactorFromReportScenario )
):
st . header ( "R&D Loops♾️" , divider = "rainbow" , anchor = "_rdloops" )
if len ( state . msgs ) > 1 :
r_options = list ( state . msgs . keys ())
if 0 in r_options :
r_options . remove ( 0 )
round = st . radio ( "**Loops**" , horizontal = True , options = r_options , index = state . lround - 1 )
else :
round = 1
rf_c , d_c = st . columns ([ 2 , 2 ])
elif isinstance ( state . scenario , GeneralModelScenario ):
rf_c = st . container ()
d_c = st . container ()
round = 1
else :
st . error ( "Unknown Scenario!" )
st . stop ()
with rf_c :
research_window ()
feedback_window ()
with d_c . container ( border = True ):
evolving_window ()
st . markdown ( "<br><br><br>" , unsafe_allow_html = True )
st . markdown ( "#### Disclaimer" )
st . markdown (
"*This content is AI-generated and may not be fully accurate or up-to-date; please verify with a professional for critical matters.*" ,
unsafe_allow_html = True ,
)