|
64 | 64 | from kiro.http_client import KiroHttpClient |
65 | 65 |
|
66 | 66 |
|
| 67 | +def _is_runtime_endpoint(auth_manager: KiroAuthManager) -> bool: |
| 68 | + """ |
| 69 | + Check if auth manager uses runtime endpoint that doesn't provide /ListAvailableModels. |
| 70 | + |
| 71 | + Runtime endpoint pattern: https://runtime.{region}.kiro.dev |
| 72 | + Old endpoint pattern: https://q.{region}.amazonaws.com |
| 73 | + |
| 74 | + Runtime endpoint does not provide /ListAvailableModels API (AWS limitation). |
| 75 | + |
| 76 | + Args: |
| 77 | + auth_manager: KiroAuthManager instance |
| 78 | + |
| 79 | + Returns: |
| 80 | + True if using runtime endpoint, False otherwise |
| 81 | + |
| 82 | + Examples: |
| 83 | + >>> auth_manager.api_host = "https://runtime.us-east-1.kiro.dev" |
| 84 | + >>> _is_runtime_endpoint(auth_manager) |
| 85 | + True |
| 86 | + >>> auth_manager.api_host = "https://runtime.eu-central-1.kiro.dev" |
| 87 | + >>> _is_runtime_endpoint(auth_manager) |
| 88 | + True |
| 89 | + >>> auth_manager.api_host = "https://q.us-east-1.amazonaws.com" |
| 90 | + >>> _is_runtime_endpoint(auth_manager) |
| 91 | + False |
| 92 | + """ |
| 93 | + return "://runtime." in auth_manager.api_host |
| 94 | + |
| 95 | + |
67 | 96 | def _format_duration(seconds: float) -> str: |
68 | 97 | """ |
69 | 98 | Format duration in human-readable format. |
@@ -468,40 +497,48 @@ async def _initialize_account(self, account_id: str) -> bool: |
468 | 497 | # Get token to verify credentials |
469 | 498 | token = await auth_manager.get_access_token() |
470 | 499 |
|
471 | | - # Fetch models list with retry + fallback |
472 | | - params = {"origin": "AI_EDITOR"} |
473 | | - if auth_manager.auth_type == AuthType.KIRO_DESKTOP and auth_manager.profile_arn: |
474 | | - params["profileArn"] = auth_manager.profile_arn |
475 | | - |
476 | | - list_models_url = f"{auth_manager.q_host}/ListAvailableModels" |
477 | | - |
478 | | - # Use KiroHttpClient for retry logic (3 attempts with exponential backoff) |
479 | | - http_client = KiroHttpClient(auth_manager, shared_client=None) |
480 | | - |
481 | | - try: |
482 | | - response = await http_client.request_with_retry( |
483 | | - method="GET", |
484 | | - url=list_models_url, |
485 | | - json_data=None, |
486 | | - params=params, |
487 | | - stream=False |
488 | | - ) |
489 | | - |
490 | | - if response.status_code == 200: |
491 | | - data = response.json() |
492 | | - models_list = data.get("models", []) |
493 | | - else: |
494 | | - # Shouldn't happen (retry handles non-200), but keep for safety |
495 | | - raise Exception(f"HTTP {response.status_code}") |
496 | | - |
497 | | - except Exception as e: |
498 | | - # All retries exhausted - use fallback |
499 | | - logger.error(f"Failed to fetch models for {account_id} after retries: {e}") |
500 | | - logger.warning("Using pre-configured fallback models. Models will be refreshed on next TTL cycle when network recovers.") |
| 500 | + # Determine if we should fetch models or use static list |
| 501 | + if _is_runtime_endpoint(auth_manager): |
| 502 | + # New runtime endpoint does not provide /ListAvailableModels (AWS limitation) |
| 503 | + # Use static list without attempting request |
| 504 | + logger.debug(f"Account {account_id}: Using static model list for runtime.kiro.dev endpoint") |
501 | 505 | models_list = FALLBACK_MODELS |
502 | | - |
503 | | - finally: |
504 | | - await http_client.close() |
| 506 | + else: |
| 507 | + # Old endpoint - attempt to fetch dynamic model list |
| 508 | + # Fetch models list with retry + fallback |
| 509 | + params = {"origin": "AI_EDITOR"} |
| 510 | + if auth_manager.auth_type == AuthType.KIRO_DESKTOP and auth_manager.profile_arn: |
| 511 | + params["profileArn"] = auth_manager.profile_arn |
| 512 | + |
| 513 | + list_models_url = f"{auth_manager.q_host}/ListAvailableModels" |
| 514 | + |
| 515 | + # Use KiroHttpClient for retry logic (3 attempts with exponential backoff) |
| 516 | + http_client = KiroHttpClient(auth_manager, shared_client=None) |
| 517 | + |
| 518 | + try: |
| 519 | + response = await http_client.request_with_retry( |
| 520 | + method="GET", |
| 521 | + url=list_models_url, |
| 522 | + json_data=None, |
| 523 | + params=params, |
| 524 | + stream=False |
| 525 | + ) |
| 526 | + |
| 527 | + if response.status_code == 200: |
| 528 | + data = response.json() |
| 529 | + models_list = data.get("models", []) |
| 530 | + else: |
| 531 | + # Shouldn't happen (retry handles non-200), but keep for safety |
| 532 | + raise Exception(f"HTTP {response.status_code}") |
| 533 | + |
| 534 | + except Exception as e: |
| 535 | + # All retries exhausted - use fallback |
| 536 | + logger.error(f"Failed to fetch models for {account_id} after retries: {e}") |
| 537 | + logger.warning("Using pre-configured fallback models. Models will be refreshed on next TTL cycle when network recovers.") |
| 538 | + models_list = FALLBACK_MODELS |
| 539 | + |
| 540 | + finally: |
| 541 | + await http_client.close() |
505 | 542 |
|
506 | 543 | # Create model cache and update |
507 | 544 | model_cache = ModelInfoCache() |
@@ -552,6 +589,17 @@ async def _refresh_account_models(self, account_id: str) -> None: |
552 | 589 | if not account or not account.auth_manager: |
553 | 590 | return |
554 | 591 |
|
| 592 | + # Check if using runtime endpoint (no dynamic model list available) |
| 593 | + if _is_runtime_endpoint(account.auth_manager): |
| 594 | + # Runtime endpoint does not provide /ListAvailableModels |
| 595 | + # Use static list and update cache timestamp |
| 596 | + logger.debug(f"Account {account_id}: Skipping model refresh for runtime.kiro.dev endpoint (using static list)") |
| 597 | + await account.model_cache.update(FALLBACK_MODELS) |
| 598 | + account.models_cached_at = time.time() |
| 599 | + self._dirty = True |
| 600 | + return |
| 601 | + |
| 602 | + # Old endpoint - attempt to fetch dynamic model list |
555 | 603 | # Use KiroHttpClient for retry logic |
556 | 604 | http_client = KiroHttpClient(account.auth_manager, shared_client=None) |
557 | 605 |
|
|
0 commit comments