Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions rdagent/core/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ def __reduce__(self) -> NoReturn:


def parse_json(response: str) -> Any:
import re

# Some LLM providers (e.g. Volcengine) return Python-style booleans
# (True/False) instead of JSON-standard (true/false). Normalize them
# before parsing to avoid JSONDecodeError.
response = re.sub(r"\bTrue\b", "true", response)
response = re.sub(r"\bFalse\b", "false", response)
response = re.sub(r"\bNone\b", "null", response)
try:
return json.loads(response)
except json.decoder.JSONDecodeError:
Expand Down
3 changes: 3 additions & 0 deletions rdagent/log/ui/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ def get_msgs_until(end_func: Callable[[Message], bool] = lambda _: True):
state.hypotheses[state.lround] = msg.content
elif "evolving code" in tags:
msg.content = [i for i in msg.content if i]
# Also store under the short key so downstream consumers
# that read state.msgs[round]["evolving code"] can find it.
state.msgs[state.lround]["evolving code"].append(msg)
elif "evolving feedback" in tags:
total_len = len(msg.content)
none_num = total_len - len(msg.content)
Expand Down
7 changes: 6 additions & 1 deletion rdagent/log/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,12 @@ def extract_evoid(tag: str) -> str | None:
def extract_json(log_content: str) -> dict | None:
match = re.search(r"\{.*\}", log_content, re.DOTALL)
if match:
return cast(dict, json.loads(match.group(0)))
raw = match.group(0)
# Normalize Python-style booleans/null that some LLM providers return
raw = re.sub(r"\bTrue\b", "true", raw)
raw = re.sub(r"\bFalse\b", "false", raw)
raw = re.sub(r"\bNone\b", "null", raw)
return cast(dict, json.loads(raw))
return None


Expand Down
34 changes: 34 additions & 0 deletions rdagent/scenarios/qlib/developer/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,40 @@ def process_factor_data(exp_or_list: List[QlibFactorExperiment] | QlibFactorExpe

# Combine all successful factor data
if factor_dfs:
# Normalize MultiIndex levels: some factors may produce 3-level
# MultiIndex (e.g. instrument, datetime, instrument) while others
# use the standard 2-level (datetime, instrument). Detect and fix
# mismatched indices before concat to prevent AssertionError.
target_nlevels = None
for df in factor_dfs:
if target_nlevels is None:
target_nlevels = df.index.nlevels
elif df.index.nlevels != target_nlevels:
# Reindex to the most common level count (usually 2)
level_counts = {}
for d in factor_dfs:
n = d.index.nlevels
level_counts[n] = level_counts.get(n, 0) + 1
target_nlevels = max(level_counts, key=level_counts.get)
break

if target_nlevels is not None:
normalized = []
for df in factor_dfs:
if df.index.nlevels != target_nlevels:
# Try to swap/reorder levels or reset extra levels
if df.index.nlevels == 3 and target_nlevels == 2:
# 3-level index with duplicated instrument: drop the extra level
df.index = df.index.droplevel(0)
elif df.index.nlevels == 2 and target_nlevels == 3:
# Re-construct 3-level from 2-level is not possible without context
# Just try swapping as fallback
df = df.swaplevel(0, 1, axis=0)
normalized.append(df)
else:
normalized.append(df)
factor_dfs = normalized

try:
return pd.concat(factor_dfs, axis=1)
except Exception as concat_error:
Expand Down
Loading