claude-evolve 1.3.26 → 1.3.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +44 -1
- package/bin/claude-evolve-ideate +30 -79
- package/bin/claude-evolve-worker +44 -1
- package/lib/csv_helper.py +138 -99
- package/package.json +1 -1
- package/templates/evaluator.py +14 -12
package/README.md
CHANGED
|
@@ -203,6 +203,46 @@ your-project/
|
|
|
203
203
|
└── (your main project files)
|
|
204
204
|
```
|
|
205
205
|
|
|
206
|
+
## Evaluator Output Format
|
|
207
|
+
|
|
208
|
+
Your evaluator must output a performance score to stdout. Three formats are supported:
|
|
209
|
+
|
|
210
|
+
### 1. Plain Number (Simplest)
|
|
211
|
+
Just output a single floating-point number:
|
|
212
|
+
```
|
|
213
|
+
1.077506371224117
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### 2. JSON with "score" field
|
|
217
|
+
```json
|
|
218
|
+
{"score": 0.95}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### 3. JSON with "performance" field
|
|
222
|
+
```json
|
|
223
|
+
{"performance": 1.234}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### 4. JSON with additional metrics (Advanced)
|
|
227
|
+
You can include additional metrics that will be automatically added as new columns to the CSV:
|
|
228
|
+
```json
|
|
229
|
+
{
|
|
230
|
+
"score": 0.95,
|
|
231
|
+
"sharpe_ratio": 1.23,
|
|
232
|
+
"max_drawdown": -0.15,
|
|
233
|
+
"total_return": 0.42,
|
|
234
|
+
"win_rate": 0.65
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
**Important notes:**
|
|
239
|
+
- Higher scores indicate better performance
|
|
240
|
+
- A score of 0 indicates complete failure
|
|
241
|
+
- Non-zero exit codes indicate evaluation errors
|
|
242
|
+
- Any additional output (warnings, logs) should go to stderr, not stdout
|
|
243
|
+
- Additional JSON fields will be automatically added as new CSV columns
|
|
244
|
+
- New columns are added after the standard columns (id, basedOnId, description, performance, status)
|
|
245
|
+
|
|
206
246
|
## Environment Variables for Evaluators
|
|
207
247
|
|
|
208
248
|
When your evaluator.py runs, it has access to the `EXPERIMENT_ID` environment variable containing the current experiment's ID (e.g., `gen07-001`). This allows evaluators to:
|
|
@@ -221,7 +261,10 @@ experiment_id = os.environ.get('EXPERIMENT_ID', 'unknown')
|
|
|
221
261
|
|
|
222
262
|
# Use it for logging or file naming
|
|
223
263
|
output_file = f"results_{experiment_id}.json"
|
|
224
|
-
print(f"Evaluating experiment: {experiment_id}")
|
|
264
|
+
print(f"Evaluating experiment: {experiment_id}", file=sys.stderr) # Use stderr for logs!
|
|
265
|
+
|
|
266
|
+
# Output just the score
|
|
267
|
+
print(score) # Simple number to stdout
|
|
225
268
|
```
|
|
226
269
|
|
|
227
270
|
## Configuration
|
package/bin/claude-evolve-ideate
CHANGED
|
@@ -78,7 +78,6 @@ call_claude_with_limit_check() {
|
|
|
78
78
|
|
|
79
79
|
# Parse arguments
|
|
80
80
|
use_strategies=true
|
|
81
|
-
no_ai=false
|
|
82
81
|
|
|
83
82
|
while [[ $# -gt 0 ]]; do
|
|
84
83
|
case $1 in
|
|
@@ -87,11 +86,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
87
86
|
claude-evolve ideate - Generate new algorithm ideas using evolutionary strategies
|
|
88
87
|
|
|
89
88
|
USAGE:
|
|
90
|
-
claude-evolve ideate [--legacy N]
|
|
89
|
+
claude-evolve ideate [--legacy N]
|
|
91
90
|
|
|
92
91
|
OPTIONS:
|
|
93
92
|
--legacy N Use legacy mode with N ideas (ignores strategy config)
|
|
94
|
-
--no-ai Use manual entry mode instead of AI generation
|
|
95
93
|
--help Show this help message
|
|
96
94
|
|
|
97
95
|
DESCRIPTION:
|
|
@@ -116,10 +114,6 @@ EOF
|
|
|
116
114
|
exit 1
|
|
117
115
|
fi
|
|
118
116
|
;;
|
|
119
|
-
--no-ai)
|
|
120
|
-
no_ai=true
|
|
121
|
-
shift
|
|
122
|
-
;;
|
|
123
117
|
*)
|
|
124
118
|
echo "[ERROR] Unknown option: $1" >&2
|
|
125
119
|
exit 1
|
|
@@ -195,21 +189,6 @@ get_next_id() {
|
|
|
195
189
|
printf "gen%s-%03d" "$generation" $((max_id + 1))
|
|
196
190
|
}
|
|
197
191
|
|
|
198
|
-
# Add idea to CSV manually (fallback for manual mode)
|
|
199
|
-
add_idea_manual() {
|
|
200
|
-
local description="$1"
|
|
201
|
-
local based_on_id="$2"
|
|
202
|
-
local generation="$3"
|
|
203
|
-
local id
|
|
204
|
-
id=$(get_next_id "$generation")
|
|
205
|
-
|
|
206
|
-
# Escape quotes in description
|
|
207
|
-
local escaped_desc="${description//\"/\"\"}"
|
|
208
|
-
|
|
209
|
-
# Append to CSV
|
|
210
|
-
echo "${id},${based_on_id},\"${escaped_desc}\",," >>"$FULL_CSV_PATH"
|
|
211
|
-
echo "[INFO] Added idea: $description"
|
|
212
|
-
}
|
|
213
192
|
|
|
214
193
|
# Get top performers for parent selection
|
|
215
194
|
get_top_performers() {
|
|
@@ -246,47 +225,12 @@ with open('$FULL_CSV_PATH', 'r') as f:
|
|
|
246
225
|
"
|
|
247
226
|
}
|
|
248
227
|
|
|
249
|
-
# Manual entry mode
|
|
250
|
-
ideate_manual() {
|
|
251
|
-
local ideas_added=0
|
|
252
|
-
|
|
253
|
-
for ((i = 1; i <= TOTAL_IDEAS; i++)); do
|
|
254
|
-
if [[ $TOTAL_IDEAS -eq 1 ]]; then
|
|
255
|
-
read -r -p "Enter algorithm idea (or empty to skip): " description
|
|
256
|
-
else
|
|
257
|
-
read -r -p "Enter algorithm idea $i/$TOTAL_IDEAS (or empty to skip): " description
|
|
258
|
-
fi
|
|
259
|
-
|
|
260
|
-
if [[ -z $description ]]; then
|
|
261
|
-
echo "[INFO] Empty description, skipping idea"
|
|
262
|
-
continue
|
|
263
|
-
fi
|
|
264
|
-
|
|
265
|
-
add_idea_manual "$description" "" "$CURRENT_GENERATION"
|
|
266
|
-
((ideas_added++))
|
|
267
|
-
|
|
268
|
-
if [[ $i -lt $TOTAL_IDEAS ]]; then
|
|
269
|
-
read -r -p "Add another idea? (y/N) " continue_adding
|
|
270
|
-
if [[ $continue_adding != "y" && $continue_adding != "Y" ]]; then
|
|
271
|
-
break
|
|
272
|
-
fi
|
|
273
|
-
fi
|
|
274
|
-
done
|
|
275
|
-
|
|
276
|
-
echo "[INFO] Added $ideas_added idea(s) to $EVOLUTION_CSV"
|
|
277
|
-
}
|
|
278
228
|
|
|
279
229
|
# Generate ideas using AI with multi-strategy approach
|
|
280
230
|
ideate_ai_strategies() {
|
|
281
|
-
# Check for AI CLI (codex or claude)
|
|
282
|
-
if ! command -v codex >/dev/null 2>&1 && ! command -v claude >/dev/null 2>&1; then
|
|
283
|
-
echo "[WARN] No AI CLI found (codex or claude). Falling back to manual entry."
|
|
284
|
-
return 1
|
|
285
|
-
fi
|
|
286
|
-
|
|
287
231
|
if [[ ! -f "$FULL_BRIEF_PATH" ]]; then
|
|
288
|
-
echo "[
|
|
289
|
-
|
|
232
|
+
echo "[ERROR] $BRIEF_FILE not found. Run 'claude-evolve setup' first." >&2
|
|
233
|
+
exit 1
|
|
290
234
|
fi
|
|
291
235
|
|
|
292
236
|
# Get top performers
|
|
@@ -531,6 +475,16 @@ Requirements for new CSV rows:
|
|
|
531
475
|
- Each description should be one clear sentence combining elements from different algorithms
|
|
532
476
|
- Be specific about what elements to merge
|
|
533
477
|
- All new rows should have empty performance and status fields
|
|
478
|
+
|
|
479
|
+
CRITICAL CSV FORMAT RULES:
|
|
480
|
+
- DO NOT modify the CSV header row
|
|
481
|
+
- DO NOT change the column order
|
|
482
|
+
- DO NOT add extra columns or fields
|
|
483
|
+
- DO NOT modify existing rows - only append new ones
|
|
484
|
+
- DO NOT add extra blank lines or formatting
|
|
485
|
+
- Maintain exact CSV format: id,basedOnId,description,performance,status
|
|
486
|
+
- Leave performance and status fields completely empty (just commas)
|
|
487
|
+
- Use proper CSV quoting only when descriptions contain commas
|
|
534
488
|
- CRITICAL: You must read the relevant algorithm files to:
|
|
535
489
|
* Identify the specific improvements that made each algorithm successful
|
|
536
490
|
* Understand which components are compatible for merging
|
|
@@ -553,15 +507,9 @@ Add exactly $count hybrid combination rows to the CSV file now."
|
|
|
553
507
|
|
|
554
508
|
# Legacy AI generation mode (for backward compatibility)
|
|
555
509
|
ideate_ai_legacy() {
|
|
556
|
-
# Check for AI CLI (codex or claude)
|
|
557
|
-
if ! command -v codex >/dev/null 2>&1 && ! command -v claude >/dev/null 2>&1; then
|
|
558
|
-
echo "[WARN] No AI CLI found (codex or claude). Falling back to manual entry."
|
|
559
|
-
return 1
|
|
560
|
-
fi
|
|
561
|
-
|
|
562
510
|
if [[ ! -f "$FULL_BRIEF_PATH" ]]; then
|
|
563
|
-
echo "[
|
|
564
|
-
|
|
511
|
+
echo "[ERROR] $BRIEF_FILE not found. Run 'claude-evolve setup' first." >&2
|
|
512
|
+
exit 1
|
|
565
513
|
fi
|
|
566
514
|
|
|
567
515
|
# Get top performers (pure shell)
|
|
@@ -606,6 +554,16 @@ Requirements for new CSV rows:
|
|
|
606
554
|
- Each description should be one clear sentence describing an algorithmic approach
|
|
607
555
|
- All new rows should have empty performance and status fields
|
|
608
556
|
|
|
557
|
+
CRITICAL CSV FORMAT RULES:
|
|
558
|
+
- DO NOT modify the CSV header row
|
|
559
|
+
- DO NOT change the column order
|
|
560
|
+
- DO NOT add extra columns or fields
|
|
561
|
+
- DO NOT modify existing rows - only append new ones
|
|
562
|
+
- DO NOT add extra blank lines or formatting
|
|
563
|
+
- Maintain exact CSV format: id,basedOnId,description,performance,status
|
|
564
|
+
- Leave performance and status fields completely empty (just commas)
|
|
565
|
+
- Use proper CSV quoting only when descriptions contain commas
|
|
566
|
+
|
|
609
567
|
Add exactly $TOTAL_IDEAS algorithm variation rows to the CSV file now."
|
|
610
568
|
|
|
611
569
|
echo "[INFO] Generating $TOTAL_IDEAS ideas (legacy mode)..."
|
|
@@ -621,19 +579,12 @@ CURRENT_GENERATION=$(get_next_generation)
|
|
|
621
579
|
echo "[INFO] Starting ideation for generation $CURRENT_GENERATION"
|
|
622
580
|
|
|
623
581
|
# Main execution
|
|
624
|
-
if [[ $
|
|
625
|
-
echo "[INFO] Manual entry mode"
|
|
626
|
-
ideate_manual
|
|
627
|
-
elif [[ $use_strategies == true ]]; then
|
|
582
|
+
if [[ $use_strategies == true ]]; then
|
|
628
583
|
echo "[INFO] Multi-strategy AI generation mode"
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
ideate_manual
|
|
632
|
-
fi
|
|
584
|
+
ideate_ai_strategies
|
|
585
|
+
echo "[INFO] Ideation complete! Check $EVOLUTION_CSV for new ideas."
|
|
633
586
|
else
|
|
634
587
|
echo "[INFO] Legacy AI generation mode"
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
ideate_manual
|
|
638
|
-
fi
|
|
588
|
+
ideate_ai_legacy
|
|
589
|
+
echo "[INFO] Ideation complete! Check $EVOLUTION_CSV for new ideas."
|
|
639
590
|
fi
|
package/bin/claude-evolve-worker
CHANGED
|
@@ -239,7 +239,49 @@ fi
|
|
|
239
239
|
|
|
240
240
|
# Process results
|
|
241
241
|
if [[ $eval_exit_code -eq 0 ]]; then
|
|
242
|
-
#
|
|
242
|
+
# First, check if output is just a plain number
|
|
243
|
+
if [[ $eval_output =~ ^[[:space:]]*-?[0-9]+\.?[0-9]*[[:space:]]*$ ]]; then
|
|
244
|
+
score=$(echo "$eval_output" | tr -d ' ')
|
|
245
|
+
if (( $(echo "$score == 0" | bc -l) )); then
|
|
246
|
+
update_csv_row_with_lock "$candidate_id" "status" "failed"
|
|
247
|
+
update_csv_row_with_lock "$candidate_id" "performance" "$score"
|
|
248
|
+
echo "[WORKER-$$] ✗ Evaluation failed with score 0"
|
|
249
|
+
exit 1
|
|
250
|
+
else
|
|
251
|
+
update_csv_row_with_lock "$candidate_id" "performance" "$score"
|
|
252
|
+
update_csv_row_with_lock "$candidate_id" "status" "complete"
|
|
253
|
+
echo "[WORKER-$$] ✓ Evaluation complete, score: $score"
|
|
254
|
+
exit 0
|
|
255
|
+
fi
|
|
256
|
+
fi
|
|
257
|
+
|
|
258
|
+
# Try to parse as JSON and extract all fields
|
|
259
|
+
if echo "$eval_output" | jq . >/dev/null 2>&1; then
|
|
260
|
+
# Valid JSON - use CSV helper to update with all fields
|
|
261
|
+
if ! acquire_csv_lock; then
|
|
262
|
+
echo "[ERROR] Failed to acquire CSV lock" >&2
|
|
263
|
+
exit 1
|
|
264
|
+
fi
|
|
265
|
+
|
|
266
|
+
score=$("$PYTHON_CMD" "$SCRIPT_DIR/../lib/csv_helper.py" update_with_json "$FULL_CSV_PATH" "$candidate_id" "$eval_output")
|
|
267
|
+
release_csv_lock
|
|
268
|
+
|
|
269
|
+
if [[ -n $score ]] && [[ $score != "0" ]]; then
|
|
270
|
+
echo "[WORKER-$$] ✓ Evaluation complete, score: $score"
|
|
271
|
+
# Extract and display additional fields if present
|
|
272
|
+
if additional_fields=$(echo "$eval_output" | jq -r 'to_entries | map(select(.key != "score" and .key != "performance")) | map("\(.key): \(.value)") | join(", ")' 2>/dev/null); then
|
|
273
|
+
if [[ -n $additional_fields ]]; then
|
|
274
|
+
echo "[WORKER-$$] Additional metrics: $additional_fields"
|
|
275
|
+
fi
|
|
276
|
+
fi
|
|
277
|
+
exit 0
|
|
278
|
+
else
|
|
279
|
+
echo "[WORKER-$$] ✗ Evaluation failed with score 0"
|
|
280
|
+
exit 1
|
|
281
|
+
fi
|
|
282
|
+
fi
|
|
283
|
+
|
|
284
|
+
# Fallback: Try simple grep for score/performance fields
|
|
243
285
|
if score=$(echo "$eval_output" | grep -o '"score"[[:space:]]*:[[:space:]]*[0-9.]*' | cut -d: -f2 | tr -d ' '); then
|
|
244
286
|
if [[ -n $score ]]; then
|
|
245
287
|
if (( $(echo "$score == 0" | bc -l) )); then
|
|
@@ -274,6 +316,7 @@ if [[ $eval_exit_code -eq 0 ]]; then
|
|
|
274
316
|
fi
|
|
275
317
|
|
|
276
318
|
echo "[ERROR] No score found in evaluator output" >&2
|
|
319
|
+
echo "[ERROR] Expected: plain number (e.g., 1.23) or JSON with 'score' or 'performance' field" >&2
|
|
277
320
|
update_csv_row_with_lock "$candidate_id" "status" "failed"
|
|
278
321
|
exit 1
|
|
279
322
|
else
|
package/lib/csv_helper.py
CHANGED
|
@@ -1,120 +1,159 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
CSV helper for
|
|
3
|
+
CSV helper for dynamic column management in claude-evolve.
|
|
4
|
+
Handles adding new columns and updating rows with arbitrary fields.
|
|
4
5
|
"""
|
|
6
|
+
|
|
5
7
|
import csv
|
|
6
|
-
import sys
|
|
7
8
|
import json
|
|
9
|
+
import sys
|
|
10
|
+
import os
|
|
11
|
+
from typing import Dict, List, Any
|
|
8
12
|
|
|
9
|
-
def find_pending_row(csv_path):
|
|
10
|
-
"""Find the first pending row in the CSV."""
|
|
11
|
-
with open(csv_path, 'r') as f:
|
|
12
|
-
reader = csv.reader(f)
|
|
13
|
-
next(reader) # Skip header
|
|
14
|
-
for row_num, row in enumerate(reader, start=2):
|
|
15
|
-
# If row has fewer than 5 fields, it's pending
|
|
16
|
-
if len(row) < 5:
|
|
17
|
-
return row_num
|
|
18
|
-
|
|
19
|
-
# Ensure row has at least 5 fields for status check
|
|
20
|
-
while len(row) < 5:
|
|
21
|
-
row.append('')
|
|
22
|
-
|
|
23
|
-
status = row[4].strip()
|
|
24
|
-
# Check if status is pending or empty
|
|
25
|
-
if status == 'pending' or status == '':
|
|
26
|
-
return row_num
|
|
27
|
-
return None
|
|
28
|
-
|
|
29
|
-
def get_row_data(csv_path, row_num):
|
|
30
|
-
"""Get data from a specific row."""
|
|
31
|
-
with open(csv_path, 'r') as f:
|
|
32
|
-
reader = csv.reader(f)
|
|
33
|
-
for i, row in enumerate(reader, start=1):
|
|
34
|
-
if i == row_num:
|
|
35
|
-
# Ensure row has at least 5 fields
|
|
36
|
-
while len(row) < 5:
|
|
37
|
-
row.append('')
|
|
38
|
-
return {
|
|
39
|
-
'id': row[0],
|
|
40
|
-
'basedOnId': row[1],
|
|
41
|
-
'description': row[2],
|
|
42
|
-
'performance': row[3],
|
|
43
|
-
'status': row[4]
|
|
44
|
-
}
|
|
45
|
-
return None
|
|
46
13
|
|
|
47
|
-
def
|
|
48
|
-
"""
|
|
49
|
-
|
|
50
|
-
with open(csv_path, 'r') as f:
|
|
14
|
+
def read_csv(filepath: str) -> tuple[list[str], list[list[str]]]:
|
|
15
|
+
"""Read CSV and return headers and rows."""
|
|
16
|
+
with open(filepath, 'r') as f:
|
|
51
17
|
reader = csv.reader(f)
|
|
18
|
+
headers = next(reader, [])
|
|
52
19
|
rows = list(reader)
|
|
20
|
+
return headers, rows
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def write_csv(filepath: str, headers: list[str], rows: list[list[str]]):
|
|
24
|
+
"""Write CSV with headers and rows."""
|
|
25
|
+
with open(filepath, 'w', newline='') as f:
|
|
26
|
+
writer = csv.writer(f)
|
|
27
|
+
writer.writerow(headers)
|
|
28
|
+
writer.writerows(rows)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def ensure_columns(headers: list[str], rows: list[list[str]], new_fields: dict) -> tuple[list[str], list[list[str]]]:
|
|
32
|
+
"""Add new columns if they don't exist and ensure all rows have correct length."""
|
|
33
|
+
# Find which fields need to be added as new columns
|
|
34
|
+
existing_columns = set(headers)
|
|
35
|
+
new_columns = []
|
|
36
|
+
|
|
37
|
+
for field in new_fields:
|
|
38
|
+
if field not in existing_columns and field not in ['id', 'basedOnId', 'description', 'performance', 'status']:
|
|
39
|
+
new_columns.append(field)
|
|
53
40
|
|
|
54
|
-
#
|
|
55
|
-
if
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
41
|
+
# Add new columns to headers (after status column)
|
|
42
|
+
if new_columns:
|
|
43
|
+
headers = headers + new_columns
|
|
44
|
+
|
|
45
|
+
# Ensure all rows have the correct number of columns
|
|
46
|
+
for row in rows:
|
|
47
|
+
while len(row) < len(headers):
|
|
59
48
|
row.append('')
|
|
60
|
-
row[3] = performance # performance field
|
|
61
|
-
row[4] = status # status field
|
|
62
49
|
|
|
63
|
-
|
|
64
|
-
with open(csv_path, 'w', newline='') as f:
|
|
65
|
-
writer = csv.writer(f)
|
|
66
|
-
writer.writerows(rows)
|
|
50
|
+
return headers, rows
|
|
67
51
|
|
|
68
|
-
|
|
52
|
+
|
|
53
|
+
def update_row_with_fields(headers: list[str], rows: list[list[str]], target_id: str, fields: dict):
|
|
54
|
+
"""Update a specific row with multiple fields."""
|
|
55
|
+
# Find column indices
|
|
56
|
+
col_indices = {header: i for i, header in enumerate(headers)}
|
|
57
|
+
|
|
58
|
+
# Find and update the target row
|
|
59
|
+
for row in rows:
|
|
60
|
+
if row[0] == target_id:
|
|
61
|
+
for field, value in fields.items():
|
|
62
|
+
if field in col_indices:
|
|
63
|
+
row[col_indices[field]] = str(value)
|
|
64
|
+
break
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main():
|
|
68
|
+
"""Main entry point for CSV operations."""
|
|
69
69
|
if len(sys.argv) < 3:
|
|
70
|
-
print("Usage: csv_helper.py <
|
|
70
|
+
print("Usage: csv_helper.py <operation> <args...>", file=sys.stderr)
|
|
71
71
|
sys.exit(1)
|
|
72
72
|
|
|
73
|
-
|
|
74
|
-
csv_path = sys.argv[2]
|
|
73
|
+
operation = sys.argv[1]
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
sys.exit(0)
|
|
82
|
-
else:
|
|
83
|
-
sys.exit(1)
|
|
84
|
-
|
|
85
|
-
elif command == 'get_row':
|
|
86
|
-
if len(sys.argv) < 4:
|
|
87
|
-
print("Usage: csv_helper.py get_row <csv_path> <row_num>", file=sys.stderr)
|
|
88
|
-
sys.exit(1)
|
|
89
|
-
row_num = int(sys.argv[3])
|
|
90
|
-
data = get_row_data(csv_path, row_num)
|
|
91
|
-
if data:
|
|
92
|
-
# Output as shell variable assignments
|
|
93
|
-
for key, value in data.items():
|
|
94
|
-
# Escape special characters for shell
|
|
95
|
-
value = value.replace('\\', '\\\\')
|
|
96
|
-
value = value.replace('"', '\\"')
|
|
97
|
-
value = value.replace('$', '\\$')
|
|
98
|
-
value = value.replace('`', '\\`')
|
|
99
|
-
print(f'{key}="{value}"')
|
|
100
|
-
sys.exit(0)
|
|
101
|
-
else:
|
|
102
|
-
sys.exit(1)
|
|
75
|
+
if operation == "update_with_json":
|
|
76
|
+
# Args: csv_file, target_id, json_output
|
|
77
|
+
if len(sys.argv) != 5:
|
|
78
|
+
print("Usage: csv_helper.py update_with_json <csv_file> <target_id> <json_output>", file=sys.stderr)
|
|
79
|
+
sys.exit(1)
|
|
103
80
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
sys.exit(1)
|
|
108
|
-
row_num = int(sys.argv[3])
|
|
109
|
-
performance = sys.argv[4]
|
|
110
|
-
status = sys.argv[5]
|
|
111
|
-
update_row(csv_path, row_num, performance, status)
|
|
112
|
-
sys.exit(0)
|
|
81
|
+
csv_file = sys.argv[2]
|
|
82
|
+
target_id = sys.argv[3]
|
|
83
|
+
json_output = sys.argv[4]
|
|
113
84
|
|
|
114
|
-
|
|
115
|
-
|
|
85
|
+
try:
|
|
86
|
+
# Parse JSON output
|
|
87
|
+
data = json.loads(json_output)
|
|
88
|
+
|
|
89
|
+
# Extract performance/score
|
|
90
|
+
performance = data.get('performance') or data.get('score', 0)
|
|
91
|
+
|
|
92
|
+
# Build fields to update
|
|
93
|
+
fields = {'performance': performance, 'status': 'complete' if performance > 0 else 'failed'}
|
|
94
|
+
|
|
95
|
+
# Add all other fields from the JSON
|
|
96
|
+
for key, value in data.items():
|
|
97
|
+
if key not in ['performance', 'score', 'status']:
|
|
98
|
+
fields[key] = value
|
|
99
|
+
|
|
100
|
+
# Read CSV
|
|
101
|
+
headers, rows = read_csv(csv_file)
|
|
102
|
+
|
|
103
|
+
# Ensure columns exist for all fields
|
|
104
|
+
headers, rows = ensure_columns(headers, rows, fields)
|
|
105
|
+
|
|
106
|
+
# Update the row
|
|
107
|
+
update_row_with_fields(headers, rows, target_id, fields)
|
|
108
|
+
|
|
109
|
+
# Write back
|
|
110
|
+
write_csv(csv_file + '.tmp', headers, rows)
|
|
111
|
+
os.rename(csv_file + '.tmp', csv_file)
|
|
112
|
+
|
|
113
|
+
# Return the performance score
|
|
114
|
+
print(performance)
|
|
115
|
+
|
|
116
|
+
except json.JSONDecodeError:
|
|
117
|
+
print("0") # Invalid JSON means failed
|
|
118
|
+
sys.exit(1)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
121
|
+
print("0")
|
|
116
122
|
sys.exit(1)
|
|
123
|
+
|
|
124
|
+
elif operation == "update_field":
|
|
125
|
+
# Args: csv_file, target_id, field, value
|
|
126
|
+
if len(sys.argv) != 6:
|
|
127
|
+
print("Usage: csv_helper.py update_field <csv_file> <target_id> <field> <value>", file=sys.stderr)
|
|
128
|
+
sys.exit(1)
|
|
129
|
+
|
|
130
|
+
csv_file = sys.argv[2]
|
|
131
|
+
target_id = sys.argv[3]
|
|
132
|
+
field = sys.argv[4]
|
|
133
|
+
value = sys.argv[5]
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
# Read CSV
|
|
137
|
+
headers, rows = read_csv(csv_file)
|
|
138
|
+
|
|
139
|
+
# Ensure column exists
|
|
140
|
+
headers, rows = ensure_columns(headers, rows, {field: value})
|
|
141
|
+
|
|
142
|
+
# Update the row
|
|
143
|
+
update_row_with_fields(headers, rows, target_id, {field: value})
|
|
144
|
+
|
|
145
|
+
# Write back
|
|
146
|
+
write_csv(csv_file + '.tmp', headers, rows)
|
|
147
|
+
os.rename(csv_file + '.tmp', csv_file)
|
|
117
148
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
149
|
+
except Exception as e:
|
|
150
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
151
|
+
sys.exit(1)
|
|
152
|
+
|
|
153
|
+
else:
|
|
154
|
+
print(f"Unknown operation: {operation}", file=sys.stderr)
|
|
155
|
+
sys.exit(1)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if __name__ == "__main__":
|
|
159
|
+
main()
|
package/package.json
CHANGED
package/templates/evaluator.py
CHANGED
|
@@ -40,11 +40,10 @@ def evaluate_performance(algorithm_module):
|
|
|
40
40
|
end_time = time.time()
|
|
41
41
|
execution_time = end_time - start_time
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
43
|
+
# Calculate a performance score (higher is better)
|
|
44
|
+
score = 1.0 / execution_time if execution_time > 0 else 0
|
|
45
|
+
|
|
46
|
+
return score # Simple: just return the number
|
|
48
47
|
|
|
49
48
|
|
|
50
49
|
def main():
|
|
@@ -60,15 +59,18 @@ def main():
|
|
|
60
59
|
|
|
61
60
|
try:
|
|
62
61
|
algorithm_module = load_algorithm(algorithm_file)
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
score = evaluate_performance(algorithm_module)
|
|
63
|
+
|
|
64
|
+
# Option 1: Just print the number (simplest)
|
|
65
|
+
print(score)
|
|
66
|
+
|
|
67
|
+
# Option 2: Print as JSON (if you need more structure)
|
|
68
|
+
# print(json.dumps({"score": score}))
|
|
69
|
+
|
|
65
70
|
sys.exit(0)
|
|
66
71
|
except Exception as e:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"status": "failed"
|
|
70
|
-
}
|
|
71
|
-
print(json.dumps(error_result))
|
|
72
|
+
# Log errors to stderr, not stdout
|
|
73
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
72
74
|
sys.exit(1)
|
|
73
75
|
|
|
74
76
|
|