Skip to content

Commit 8d72105

Browse files
authored
One command (#315)
* one command feature * added --force * 10 min instead
1 parent 23fae2c commit 8d72105

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

eval_protocol/cli.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,7 @@ def parse_args(args=None):
425425
rft_parser.add_argument("--rft-job-id", help="Specify an explicit RFT job id")
426426
rft_parser.add_argument("--yes", "-y", action="store_true", help="Non-interactive mode")
427427
rft_parser.add_argument("--dry-run", action="store_true", help="Print planned REST calls without sending")
428+
rft_parser.add_argument("--force", action="store_true", help="Overwrite existing evaluator with the same ID")
428429

429430
# Run command (for Hydra-based evaluations)
430431
# This subparser intentionally defines no arguments itself.

eval_protocol/cli_commands/create_rft.py

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
import argparse
66
from typing import Any, Dict, Optional
77

8+
import requests
9+
810
from ..auth import (
911
get_fireworks_account_id,
1012
get_fireworks_api_base,
1113
get_fireworks_api_key,
1214
verify_api_key_and_get_account_id,
1315
)
16+
from ..common_utils import get_user_agent
1417
from ..fireworks_rft import (
1518
_map_api_host_to_app_host,
1619
build_default_output_model,
@@ -263,10 +266,72 @@ def _auto_select_evaluator_id(cwd: str) -> Optional[str]:
263266
return None
264267

265268

269+
def _poll_evaluator_status(
270+
evaluator_resource_name: str, api_key: str, api_base: str, timeout_minutes: int = 10
271+
) -> bool:
272+
"""
273+
Poll evaluator status until it becomes ACTIVE or times out.
274+
275+
Args:
276+
evaluator_resource_name: Full evaluator resource name (e.g., accounts/xxx/evaluators/yyy)
277+
api_key: Fireworks API key
278+
api_base: Fireworks API base URL
279+
timeout_minutes: Maximum time to wait in minutes
280+
281+
Returns:
282+
True if evaluator becomes ACTIVE, False if timeout or BUILD_FAILED
283+
"""
284+
headers = {
285+
"Authorization": f"Bearer {api_key}",
286+
"Content-Type": "application/json",
287+
"User-Agent": get_user_agent(),
288+
}
289+
290+
check_url = f"{api_base}/v1/{evaluator_resource_name}"
291+
timeout_seconds = timeout_minutes * 60
292+
poll_interval = 10 # seconds
293+
start_time = time.time()
294+
295+
print(f"Polling evaluator status (timeout: {timeout_minutes}m, interval: {poll_interval}s)...")
296+
297+
while time.time() - start_time < timeout_seconds:
298+
try:
299+
response = requests.get(check_url, headers=headers, timeout=30)
300+
response.raise_for_status()
301+
302+
evaluator_data = response.json()
303+
state = evaluator_data.get("state", "STATE_UNSPECIFIED")
304+
status = evaluator_data.get("status", "")
305+
306+
if state == "ACTIVE":
307+
print("✅ Evaluator is ACTIVE and ready!")
308+
return True
309+
elif state == "BUILD_FAILED":
310+
print(f"❌ Evaluator build failed. Status: {status}")
311+
return False
312+
elif state == "BUILDING":
313+
elapsed_minutes = (time.time() - start_time) / 60
314+
print(f"⏳ Evaluator is still building... ({elapsed_minutes:.1f}m elapsed)")
315+
else:
316+
print(f"⏳ Evaluator state: {state}, status: {status}")
317+
318+
except requests.exceptions.RequestException as e:
319+
print(f"Warning: Failed to check evaluator status: {e}")
320+
321+
# Wait before next poll
322+
time.sleep(poll_interval)
323+
324+
# Timeout reached
325+
elapsed_minutes = (time.time() - start_time) / 60
326+
print(f"⏰ Timeout after {elapsed_minutes:.1f}m - evaluator is not yet ACTIVE")
327+
return False
328+
329+
266330
def create_rft_command(args) -> int:
267331
evaluator_id: Optional[str] = getattr(args, "evaluator_id", None)
268332
non_interactive: bool = bool(getattr(args, "yes", False))
269333
dry_run: bool = bool(getattr(args, "dry_run", False))
334+
force: bool = bool(getattr(args, "force", False))
270335

271336
api_key = get_fireworks_api_key()
272337
if not api_key:
@@ -326,12 +391,34 @@ def create_rft_command(args) -> int:
326391
id=evaluator_id,
327392
display_name=None,
328393
description=None,
329-
force=False,
394+
force=force, # Pass through the --force flag
330395
yes=True,
396+
env_file=None, # Add the new env_file parameter
331397
)
398+
399+
if force:
400+
print(f"🔄 Force flag enabled - will overwrite existing evaluator '{evaluator_id}'")
401+
332402
rc = upload_command(upload_args)
333403
if rc == 0:
334404
print(f"✓ Uploaded/ensured evaluator: {evaluator_id}")
405+
406+
# Poll for evaluator status
407+
print(f"Waiting for evaluator '{evaluator_id}' to become ACTIVE...")
408+
is_active = _poll_evaluator_status(
409+
evaluator_resource_name=evaluator_resource_name, api_key=api_key, api_base=api_base, timeout_minutes=10
410+
)
411+
412+
if not is_active:
413+
# Print helpful message with dashboard link
414+
app_base = _map_api_host_to_app_host(api_base)
415+
evaluator_slug = _extract_terminal_segment(evaluator_id)
416+
dashboard_url = f"{app_base}/dashboard/evaluators/{evaluator_slug}"
417+
418+
print("\n❌ Evaluator is not ready within the timeout period.")
419+
print(f"📊 Please check the evaluator status at: {dashboard_url}")
420+
print(" Wait for it to become ACTIVE, then run 'eval-protocol create rft' again.")
421+
return 1
335422
else:
336423
print("Warning: Evaluator upload did not complete successfully; proceeding to RFT creation.")
337424
except Exception as e:

0 commit comments

Comments
 (0)