|
5 | 5 | import argparse |
6 | 6 | from typing import Any, Dict, Optional |
7 | 7 |
|
| 8 | +import requests |
| 9 | + |
8 | 10 | from ..auth import ( |
9 | 11 | get_fireworks_account_id, |
10 | 12 | get_fireworks_api_base, |
11 | 13 | get_fireworks_api_key, |
12 | 14 | verify_api_key_and_get_account_id, |
13 | 15 | ) |
| 16 | +from ..common_utils import get_user_agent |
14 | 17 | from ..fireworks_rft import ( |
15 | 18 | _map_api_host_to_app_host, |
16 | 19 | build_default_output_model, |
@@ -263,6 +266,67 @@ def _auto_select_evaluator_id(cwd: str) -> Optional[str]: |
263 | 266 | return None |
264 | 267 |
|
265 | 268 |
|
| 269 | +def _poll_evaluator_status( |
| 270 | + evaluator_resource_name: str, api_key: str, api_base: str, timeout_minutes: int = 5 |
| 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 | + |
266 | 330 | def create_rft_command(args) -> int: |
267 | 331 | evaluator_id: Optional[str] = getattr(args, "evaluator_id", None) |
268 | 332 | non_interactive: bool = bool(getattr(args, "yes", False)) |
@@ -328,10 +392,28 @@ def create_rft_command(args) -> int: |
328 | 392 | description=None, |
329 | 393 | force=False, |
330 | 394 | yes=True, |
| 395 | + env_file=None, # Add the new env_file parameter |
331 | 396 | ) |
332 | 397 | rc = upload_command(upload_args) |
333 | 398 | if rc == 0: |
334 | 399 | print(f"✓ Uploaded/ensured evaluator: {evaluator_id}") |
| 400 | + |
| 401 | + # Poll for evaluator status |
| 402 | + print(f"Waiting for evaluator '{evaluator_id}' to become ACTIVE...") |
| 403 | + is_active = _poll_evaluator_status( |
| 404 | + evaluator_resource_name=evaluator_resource_name, api_key=api_key, api_base=api_base, timeout_minutes=5 |
| 405 | + ) |
| 406 | + |
| 407 | + if not is_active: |
| 408 | + # Print helpful message with dashboard link |
| 409 | + app_base = _map_api_host_to_app_host(api_base) |
| 410 | + evaluator_slug = _extract_terminal_segment(evaluator_id) |
| 411 | + dashboard_url = f"{app_base}/dashboard/evaluators/{evaluator_slug}" |
| 412 | + |
| 413 | + print("\n❌ Evaluator is not ready within the timeout period.") |
| 414 | + print(f"📊 Please check the evaluator status at: {dashboard_url}") |
| 415 | + print(" Wait for it to become ACTIVE, then run 'eval-protocol create rft' again.") |
| 416 | + return 1 |
335 | 417 | else: |
336 | 418 | print("Warning: Evaluator upload did not complete successfully; proceeding to RFT creation.") |
337 | 419 | except Exception as e: |
|
0 commit comments