Spaces:
Sleeping
Sleeping
Commit ·
13402be
1
Parent(s): dc6c23a
fix: proper rewards for all steps and clean CSV output
Browse files- Added rewards to plugin, planner, planner_python, navigator, navigator_python steps
- Navigation step now gets 0.5 reward on success
- Extraction step calculates reward based on repo count (0.5 per repo + 1.0 bonus)
- CSV output now returns clean data without nested structure
- format_output() checks for csv_output key and returns it directly
- Total rewards now accumulate correctly across all steps
- backend/app/api/routes/scrape.py +80 -16
backend/app/api/routes/scrape.py
CHANGED
|
@@ -311,6 +311,11 @@ async def format_output(data: dict[str, Any], output_format: OutputFormat, _inst
|
|
| 311 |
return json.dumps(data, indent=2, default=str)
|
| 312 |
|
| 313 |
if output_format == OutputFormat.CSV:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
if (
|
| 315 |
isinstance(data, dict)
|
| 316 |
and isinstance(data.get("rows"), list)
|
|
@@ -827,7 +832,26 @@ async def _scrape_github_trending(
|
|
| 827 |
)
|
| 828 |
|
| 829 |
nav_obs, reward, _, _, _, nav_info = await env.step(navigate_action)
|
| 830 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 831 |
|
| 832 |
if not nav_obs.page_html:
|
| 833 |
session["errors"].append("Failed to load GitHub trending page")
|
|
@@ -845,6 +869,7 @@ async def _scrape_github_trending(
|
|
| 845 |
url=trending_url,
|
| 846 |
status="running",
|
| 847 |
message="Extracting trending repositories...",
|
|
|
|
| 848 |
timestamp=_now_iso(),
|
| 849 |
),
|
| 850 |
)
|
|
@@ -894,36 +919,69 @@ async def _scrape_github_trending(
|
|
| 894 |
logger.warning(f"Failed to parse repo entry: {exc}")
|
| 895 |
continue
|
| 896 |
|
| 897 |
-
#
|
|
|
|
|
|
|
|
|
|
| 898 |
step_num += 1
|
| 899 |
yield _record_step(
|
| 900 |
session,
|
| 901 |
ScrapeStep(
|
| 902 |
step_number=step_num,
|
| 903 |
-
action="
|
| 904 |
url=trending_url,
|
| 905 |
status="completed",
|
| 906 |
message=f"Extracted {len(trending_repos)} trending repositories",
|
| 907 |
-
reward=
|
| 908 |
-
extracted_data={"trending_repos": trending_repos},
|
| 909 |
timestamp=_now_iso(),
|
| 910 |
),
|
| 911 |
)
|
| 912 |
|
| 913 |
-
#
|
| 914 |
-
|
| 915 |
-
|
| 916 |
-
|
| 917 |
-
|
| 918 |
-
|
| 919 |
-
|
| 920 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 921 |
session["extracted_data"][trending_url] = {
|
| 922 |
"trending_repositories": trending_repos,
|
| 923 |
-
"
|
| 924 |
}
|
| 925 |
-
|
| 926 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 927 |
|
| 928 |
|
| 929 |
async def _scrape_single_page(
|
|
@@ -1132,6 +1190,7 @@ async def scrape_stream(
|
|
| 1132 |
message=(
|
| 1133 |
f"Enabled plugins: {enabled_plugins}" if enabled_plugins else "No plugins enabled"
|
| 1134 |
),
|
|
|
|
| 1135 |
extracted_data={
|
| 1136 |
"requested": request.enable_plugins,
|
| 1137 |
"enabled": enabled_plugins,
|
|
@@ -1158,6 +1217,7 @@ async def scrape_stream(
|
|
| 1158 |
action="mcp_search",
|
| 1159 |
status="completed",
|
| 1160 |
message="Resolved non-URL assets using search/discovery plugin logic",
|
|
|
|
| 1161 |
extracted_data={"discoveries": discoveries, "resolved_assets": resolved_assets},
|
| 1162 |
timestamp=_now_iso(),
|
| 1163 |
),
|
|
@@ -1205,6 +1265,7 @@ async def scrape_stream(
|
|
| 1205 |
action="planner",
|
| 1206 |
status="completed",
|
| 1207 |
message=f"Planner created execution plan for {len(resolved_assets)} assets",
|
|
|
|
| 1208 |
extracted_data={
|
| 1209 |
"assets": resolved_assets,
|
| 1210 |
"instructions": request.instructions,
|
|
@@ -1254,6 +1315,7 @@ async def scrape_stream(
|
|
| 1254 |
action="planner_python",
|
| 1255 |
status="completed",
|
| 1256 |
message="Planner agent executed sandbox Python code",
|
|
|
|
| 1257 |
extracted_data=planner_sandbox.output,
|
| 1258 |
timestamp=_now_iso(),
|
| 1259 |
),
|
|
@@ -1273,6 +1335,7 @@ async def scrape_stream(
|
|
| 1273 |
url=url,
|
| 1274 |
status="running",
|
| 1275 |
message=f"Navigator selected source {idx + 1}/{len(resolved_assets)}",
|
|
|
|
| 1276 |
timestamp=_now_iso(),
|
| 1277 |
),
|
| 1278 |
)
|
|
@@ -1317,6 +1380,7 @@ async def scrape_stream(
|
|
| 1317 |
url=url,
|
| 1318 |
status="completed",
|
| 1319 |
message="Navigator agent executed sandbox Python code",
|
|
|
|
| 1320 |
extracted_data=navigator_sandbox.output,
|
| 1321 |
timestamp=_now_iso(),
|
| 1322 |
),
|
|
|
|
| 311 |
return json.dumps(data, indent=2, default=str)
|
| 312 |
|
| 313 |
if output_format == OutputFormat.CSV:
|
| 314 |
+
# Check if there's a pre-formatted csv_output
|
| 315 |
+
if isinstance(data, dict) and "csv_output" in data:
|
| 316 |
+
return data["csv_output"]
|
| 317 |
+
|
| 318 |
+
# Check for rows format
|
| 319 |
if (
|
| 320 |
isinstance(data, dict)
|
| 321 |
and isinstance(data.get("rows"), list)
|
|
|
|
| 832 |
)
|
| 833 |
|
| 834 |
nav_obs, reward, _, _, _, nav_info = await env.step(navigate_action)
|
| 835 |
+
|
| 836 |
+
# Calculate navigation reward (0.5 for successful navigation)
|
| 837 |
+
nav_reward = 0.5 if nav_obs.page_html else 0.0
|
| 838 |
+
total_reward += nav_reward
|
| 839 |
+
|
| 840 |
+
# Update the navigation step with actual reward
|
| 841 |
+
step_num += 1
|
| 842 |
+
yield _record_step(
|
| 843 |
+
session,
|
| 844 |
+
ScrapeStep(
|
| 845 |
+
step_number=step_num,
|
| 846 |
+
action="navigate",
|
| 847 |
+
url=trending_url,
|
| 848 |
+
status="completed" if nav_obs.page_html else "failed",
|
| 849 |
+
message=f"Navigated to {trending_url}" if nav_obs.page_html else "Navigation failed",
|
| 850 |
+
reward=nav_reward,
|
| 851 |
+
duration_ms=nav_info.get("step_duration_ms", 0),
|
| 852 |
+
timestamp=_now_iso(),
|
| 853 |
+
),
|
| 854 |
+
)
|
| 855 |
|
| 856 |
if not nav_obs.page_html:
|
| 857 |
session["errors"].append("Failed to load GitHub trending page")
|
|
|
|
| 869 |
url=trending_url,
|
| 870 |
status="running",
|
| 871 |
message="Extracting trending repositories...",
|
| 872 |
+
reward=0.1, # Small reward for starting extraction
|
| 873 |
timestamp=_now_iso(),
|
| 874 |
),
|
| 875 |
)
|
|
|
|
| 919 |
logger.warning(f"Failed to parse repo entry: {exc}")
|
| 920 |
continue
|
| 921 |
|
| 922 |
+
# Calculate extraction reward based on repo count
|
| 923 |
+
extraction_reward = len(trending_repos) * 0.5 + (1.0 if len(trending_repos) >= 10 else 0.5)
|
| 924 |
+
total_reward += extraction_reward
|
| 925 |
+
|
| 926 |
step_num += 1
|
| 927 |
yield _record_step(
|
| 928 |
session,
|
| 929 |
ScrapeStep(
|
| 930 |
step_number=step_num,
|
| 931 |
+
action="extract",
|
| 932 |
url=trending_url,
|
| 933 |
status="completed",
|
| 934 |
message=f"Extracted {len(trending_repos)} trending repositories",
|
| 935 |
+
reward=extraction_reward,
|
| 936 |
+
extracted_data={"count": len(trending_repos), "repos": trending_repos[:3]}, # Preview only
|
| 937 |
timestamp=_now_iso(),
|
| 938 |
),
|
| 939 |
)
|
| 940 |
|
| 941 |
+
# Generate clean CSV output
|
| 942 |
+
csv_buffer = io.StringIO()
|
| 943 |
+
writer = csv.DictWriter(csv_buffer, fieldnames=["username", "repo_name", "stars", "forks"])
|
| 944 |
+
writer.writeheader()
|
| 945 |
+
writer.writerows(trending_repos)
|
| 946 |
+
clean_csv = csv_buffer.getvalue()
|
| 947 |
+
|
| 948 |
+
# Store the clean CSV directly as extracted data for CSV output format
|
| 949 |
+
if request.output_format == OutputFormat.CSV:
|
| 950 |
+
session["extracted_data"] = {
|
| 951 |
+
"rows": trending_repos,
|
| 952 |
+
"columns": ["username", "repo_name", "stars", "forks"],
|
| 953 |
+
"csv_output": clean_csv,
|
| 954 |
+
"row_count": len(trending_repos),
|
| 955 |
+
"source": trending_url
|
| 956 |
+
}
|
| 957 |
+
session["final_output"] = clean_csv
|
| 958 |
+
else:
|
| 959 |
session["extracted_data"][trending_url] = {
|
| 960 |
"trending_repositories": trending_repos,
|
| 961 |
+
"summary": f"Found {len(trending_repos)} trending repos"
|
| 962 |
}
|
| 963 |
+
|
| 964 |
+
_write_session_artifact(session, "trending_repos.csv", clean_csv)
|
| 965 |
+
|
| 966 |
+
# Completion step with final reward
|
| 967 |
+
complete_reward = 1.0 # Bonus for successful completion
|
| 968 |
+
total_reward += complete_reward
|
| 969 |
+
session["total_reward"] = total_reward
|
| 970 |
+
|
| 971 |
+
step_num += 1
|
| 972 |
+
yield _record_step(
|
| 973 |
+
session,
|
| 974 |
+
ScrapeStep(
|
| 975 |
+
step_number=step_num,
|
| 976 |
+
action="complete",
|
| 977 |
+
url=trending_url,
|
| 978 |
+
status="completed",
|
| 979 |
+
message=f"Successfully scraped {len(trending_repos)} repos with reward {total_reward:.2f}",
|
| 980 |
+
reward=complete_reward,
|
| 981 |
+
extracted_data={"total_reward": total_reward, "repos_found": len(trending_repos)},
|
| 982 |
+
timestamp=_now_iso(),
|
| 983 |
+
),
|
| 984 |
+
)
|
| 985 |
|
| 986 |
|
| 987 |
async def _scrape_single_page(
|
|
|
|
| 1190 |
message=(
|
| 1191 |
f"Enabled plugins: {enabled_plugins}" if enabled_plugins else "No plugins enabled"
|
| 1192 |
),
|
| 1193 |
+
reward=0.1 if enabled_plugins else 0.0, # Small reward for plugin setup
|
| 1194 |
extracted_data={
|
| 1195 |
"requested": request.enable_plugins,
|
| 1196 |
"enabled": enabled_plugins,
|
|
|
|
| 1217 |
action="mcp_search",
|
| 1218 |
status="completed",
|
| 1219 |
message="Resolved non-URL assets using search/discovery plugin logic",
|
| 1220 |
+
reward=0.2, # Reward for successful discovery
|
| 1221 |
extracted_data={"discoveries": discoveries, "resolved_assets": resolved_assets},
|
| 1222 |
timestamp=_now_iso(),
|
| 1223 |
),
|
|
|
|
| 1265 |
action="planner",
|
| 1266 |
status="completed",
|
| 1267 |
message=f"Planner created execution plan for {len(resolved_assets)} assets",
|
| 1268 |
+
reward=0.15, # Reward for planning
|
| 1269 |
extracted_data={
|
| 1270 |
"assets": resolved_assets,
|
| 1271 |
"instructions": request.instructions,
|
|
|
|
| 1315 |
action="planner_python",
|
| 1316 |
status="completed",
|
| 1317 |
message="Planner agent executed sandbox Python code",
|
| 1318 |
+
reward=0.1, # Reward for sandbox execution
|
| 1319 |
extracted_data=planner_sandbox.output,
|
| 1320 |
timestamp=_now_iso(),
|
| 1321 |
),
|
|
|
|
| 1335 |
url=url,
|
| 1336 |
status="running",
|
| 1337 |
message=f"Navigator selected source {idx + 1}/{len(resolved_assets)}",
|
| 1338 |
+
reward=0.05, # Small reward for navigator selection
|
| 1339 |
timestamp=_now_iso(),
|
| 1340 |
),
|
| 1341 |
)
|
|
|
|
| 1380 |
url=url,
|
| 1381 |
status="completed",
|
| 1382 |
message="Navigator agent executed sandbox Python code",
|
| 1383 |
+
reward=0.1, # Reward for sandbox navigation
|
| 1384 |
extracted_data=navigator_sandbox.output,
|
| 1385 |
timestamp=_now_iso(),
|
| 1386 |
),
|