1515
1616import requests
1717from aignx .codegen .api .public_api import PublicApi
18- from aignx .codegen .exceptions import NotFoundException , ServiceException
18+ from aignx .codegen .exceptions import ApiException , NotFoundException , ServiceException
1919from aignx .codegen .models import (
2020 CustomMetadataUpdateRequest ,
2121 ItemCreationRequest ,
@@ -351,7 +351,7 @@ def download_to_folder( # noqa: C901
351351 def get_artifact_download_url (self , artifact_id : str ) -> str :
352352 """Get a presigned download URL for an artifact via the file endpoint.
353353
354- Calls GET /v1/runs/{run_id}/artifacts/{artifact_id}/file with
354+ Calls GET /api/ v1/runs/{run_id}/artifacts/{artifact_id}/file with
355355 allow_redirects=False and extracts the presigned URL from the Location
356356 header of the 307 redirect response. Using the codegen method is not
357357 viable here because urllib3 follows the redirect automatically, fetching
@@ -365,19 +365,36 @@ def get_artifact_download_url(self, artifact_id: str) -> str:
365365
366366 Raises:
367367 NotFoundException: If the artifact is not found (404).
368- ServiceException: On 5xx server errors (retried automatically).
368+ ServiceException: On 5xx or other unexpected HTTP errors (retried automatically where transient ).
369369 RuntimeError: If the redirect Location header is missing.
370370 """
371- endpoint_url = f"{ self ._api .api_client .configuration .host } /v1/runs/{ self .run_id } /artifacts/{ artifact_id } /file"
371+ configuration = self ._api .api_client .configuration
372+ host = configuration .host .rstrip ("/" )
373+ endpoint_url = f"{ host } /api/v1/runs/{ self .run_id } /artifacts/{ artifact_id } /file"
374+ token_provider = getattr (configuration , "token_provider" , None ) or get_token
375+ proxy = getattr (configuration , "proxy" , None )
376+ ssl_ca_cert = getattr (configuration , "ssl_ca_cert" , None )
377+ verify_ssl = getattr (configuration , "verify_ssl" , True )
378+ ssl_verify : bool | str = ssl_ca_cert or verify_ssl
379+ logger .trace ("Resolving download URL for artifact {} via {}" , artifact_id , endpoint_url )
372380
373381 def _resolve () -> str :
374- token = get_token ()
375- response = requests .get (
376- endpoint_url ,
377- headers = {"Authorization" : f"Bearer { token } " , "User-Agent" : user_agent ()},
378- allow_redirects = False ,
379- timeout = settings ().run_timeout ,
380- )
382+ token = token_provider ()
383+ try :
384+ response = requests .get (
385+ endpoint_url ,
386+ headers = {"Authorization" : f"Bearer { token } " , "User-Agent" : user_agent ()},
387+ allow_redirects = False ,
388+ timeout = settings ().run_timeout ,
389+ proxies = {"http" : proxy , "https" : proxy } if proxy else None ,
390+ verify = ssl_verify ,
391+ )
392+ except requests .Timeout as e :
393+ raise ServiceException (status = HTTPStatus .SERVICE_UNAVAILABLE , reason = "Request timed out" ) from e
394+ except requests .ConnectionError as e :
395+ raise ServiceException (status = HTTPStatus .SERVICE_UNAVAILABLE , reason = "Connection failed" ) from e
396+ except requests .RequestException as e :
397+ raise ServiceException (status = HTTPStatus .SERVICE_UNAVAILABLE , reason = f"Request failed: { e } " ) from e
381398 if response .status_code in {
382399 HTTPStatus .MOVED_PERMANENTLY ,
383400 HTTPStatus .FOUND ,
@@ -393,17 +410,20 @@ def _resolve() -> str:
393410 raise NotFoundException (status = HTTPStatus .NOT_FOUND , reason = "Artifact not found" )
394411 if response .status_code >= HTTPStatus .INTERNAL_SERVER_ERROR :
395412 raise ServiceException (status = response .status_code , reason = response .reason )
396- response .raise_for_status ()
413+ if response .status_code >= HTTPStatus .BAD_REQUEST :
414+ raise ApiException (status = response .status_code , reason = response .reason )
397415 msg = f"Unexpected status { response .status_code } from artifact file endpoint for artifact { artifact_id } "
398416 raise RuntimeError (msg )
399417
400- return Retrying (
418+ url = Retrying (
401419 retry = retry_if_exception_type (exception_types = RETRYABLE_EXCEPTIONS ),
402420 stop = stop_after_attempt (settings ().run_retry_attempts ),
403421 wait = wait_exponential_jitter (initial = settings ().run_retry_wait_min , max = settings ().run_retry_wait_max ),
404422 before_sleep = _log_retry_attempt ,
405423 reraise = True ,
406424 )(_resolve )
425+ logger .trace ("Resolved download URL for artifact {}" , artifact_id )
426+ return url
407427
408428 def ensure_artifacts_downloaded (
409429 self ,
@@ -431,6 +451,13 @@ def ensure_artifacts_downloaded(
431451 downloaded_at_least_one_artifact = False
432452 for artifact in item .output_artifacts :
433453 if not artifact .output_artifact_id :
454+ logger .warning (
455+ "Skipping artifact {} for item {}: missing output_artifact_id" , artifact .name , item .external_id
456+ )
457+ if print_status :
458+ print (
459+ f"> Skipping artifact { artifact .name } for item { item .external_id } : missing output_artifact_id"
460+ )
434461 continue
435462 item_dir .mkdir (exist_ok = True , parents = True )
436463 file_ending = mime_type_to_file_ending (get_mime_type_for_artifact (artifact ))
0 commit comments