@@ -741,108 +741,122 @@ def create(self, evaluator_id, display_name=None, description=None, force=False)
741741 # Upload code as tar.gz to GCS
742742 evaluator_name = result .get ("name" ) # e.g., "accounts/pyroworks/evaluators/test-123"
743743
744- if evaluator_name :
745- try :
746- # Create tar.gz of current directory
747- cwd = os .getcwd ()
748- dir_name = os .path .basename (cwd )
749- tar_filename = f"{ dir_name } .tar.gz"
750- tar_path = os .path .join (cwd , tar_filename )
751-
752- tar_size = self ._create_tar_gz_with_ignores (tar_path , cwd )
753-
754- # Call GetEvaluatorUploadEndpoint
755-
756- upload_endpoint_url = f"{ self .api_base } /v1/{ evaluator_name } :getUploadEndpoint"
757- upload_payload = {"name" : evaluator_name , "filename_to_size" : {tar_filename : tar_size }}
758-
759- logger .info (f"Requesting upload endpoint for { tar_filename } " )
760- upload_response = requests .post (upload_endpoint_url , json = upload_payload , headers = headers )
761-
762- upload_response .raise_for_status ()
763-
764- signed_urls = upload_response .json ().get ("filenameToSignedUrls" , {})
765- signed_url = signed_urls .get (tar_filename )
766-
767- if signed_url :
768- logger .info (f"Uploading { tar_filename } to GCS..." )
769-
770- file_size = os .path .getsize (tar_path )
771-
772- # Retry configuration
773- max_retries = 3
774- retry_delay = 2 # seconds
775-
776- for attempt in range (max_retries ):
777- try :
778- with open (tar_path , "rb" ) as f :
779- # Create request exactly like Golang
780- req = requests .Request (
781- "PUT" ,
782- signed_url ,
783- data = f ,
784- headers = {
785- "Content-Type" : "application/octet-stream" ,
786- "X-Goog-Content-Length-Range" : f"{ file_size } ,{ file_size } " ,
787- },
788- )
789- prepared = req .prepare ()
790-
791- # Don't let requests add extra headers
792- session = requests .Session ()
793- gcs_response = session .send (prepared , timeout = 600 )
794- gcs_response .raise_for_status ()
795-
796- logger .info (f"Successfully uploaded { tar_filename } " )
797- break # Success, exit retry loop
798-
799- except (requests .exceptions .RequestException , IOError ) as e :
800- if attempt < max_retries - 1 :
801- # Check if it's a retryable error
802- is_retryable = False
803- if isinstance (e , requests .exceptions .RequestException ):
804- if hasattr (e , "response" ) and e .response is not None :
805- # Retry on 5xx errors or 408 (timeout)
806- is_retryable = (
807- e .response .status_code >= 500 or e .response .status_code == 408
808- )
809- else :
810- # Network errors (no response) are retryable
811- is_retryable = True
812- else :
813- # IOError is retryable
814- is_retryable = True
815-
816- if is_retryable :
817- wait_time = retry_delay * (2 ** attempt ) # Exponential backoff
818- logger .warning (
819- f"Upload attempt { attempt + 1 } /{ max_retries } failed: { e } . "
820- f"Retrying in { wait_time } s..."
821- )
822- time .sleep (wait_time )
823- else :
824- # Non-retryable error, raise immediately
825- raise
744+ if not evaluator_name :
745+ raise ValueError (
746+ "Create evaluator response missing 'name' field. "
747+ f"Cannot proceed with code upload. Response: { result } "
748+ )
749+
750+ try :
751+ # Create tar.gz of current directory
752+ cwd = os .getcwd ()
753+ dir_name = os .path .basename (cwd )
754+ tar_filename = f"{ dir_name } .tar.gz"
755+ tar_path = os .path .join (cwd , tar_filename )
756+
757+ tar_size = self ._create_tar_gz_with_ignores (tar_path , cwd )
758+
759+ # Call GetEvaluatorUploadEndpoint
760+ upload_endpoint_url = f"{ self .api_base } /v1/{ evaluator_name } :getUploadEndpoint"
761+ upload_payload = {"name" : evaluator_name , "filename_to_size" : {tar_filename : tar_size }}
762+
763+ logger .info (f"Requesting upload endpoint for { tar_filename } " )
764+ upload_response = requests .post (upload_endpoint_url , json = upload_payload , headers = headers )
765+ upload_response .raise_for_status ()
766+
767+ # Check for signed URLs
768+ upload_response_data = upload_response .json ()
769+ signed_urls = upload_response_data .get ("filenameToSignedUrls" , {})
770+
771+ if not signed_urls :
772+ raise ValueError (f"GetUploadEndpoint returned no signed URLs. Response: { upload_response_data } " )
773+
774+ signed_url = signed_urls .get (tar_filename )
775+
776+ if not signed_url :
777+ raise ValueError (
778+ f"No signed URL received for { tar_filename } . Available files: { list (signed_urls .keys ())} "
779+ )
780+
781+ # Upload to GCS
782+ logger .info (f"Uploading { tar_filename } to GCS..." )
783+
784+ file_size = os .path .getsize (tar_path )
785+
786+ # Retry configuration
787+ max_retries = 3
788+ retry_delay = 2 # seconds
789+
790+ for attempt in range (max_retries ):
791+ try :
792+ with open (tar_path , "rb" ) as f :
793+ # Create request exactly like Golang
794+ req = requests .Request (
795+ "PUT" ,
796+ signed_url ,
797+ data = f ,
798+ headers = {
799+ "Content-Type" : "application/octet-stream" ,
800+ "X-Goog-Content-Length-Range" : f"{ file_size } ,{ file_size } " ,
801+ },
802+ )
803+ prepared = req .prepare ()
804+
805+ # Don't let requests add extra headers
806+ session = requests .Session ()
807+ gcs_response = session .send (prepared , timeout = 600 )
808+ gcs_response .raise_for_status ()
809+
810+ logger .info (f"Successfully uploaded { tar_filename } " )
811+ break # Success, exit retry loop
812+
813+ except (requests .exceptions .RequestException , IOError ) as e :
814+ if attempt < max_retries - 1 :
815+ # Check if it's a retryable error
816+ is_retryable = False
817+ if isinstance (e , requests .exceptions .RequestException ):
818+ if hasattr (e , "response" ) and e .response is not None :
819+ # Retry on 5xx errors or 408 (timeout)
820+ is_retryable = e .response .status_code >= 500 or e .response .status_code == 408
826821 else :
827- # Last attempt failed
828- logger .error (f"Upload failed after { max_retries } attempts" )
829- raise
822+ # Network errors (no response) are retryable
823+ is_retryable = True
824+ else :
825+ # IOError is retryable
826+ is_retryable = True
827+
828+ if is_retryable :
829+ wait_time = retry_delay * (2 ** attempt ) # Exponential backoff
830+ logger .warning (
831+ f"Upload attempt { attempt + 1 } /{ max_retries } failed: { e } . "
832+ f"Retrying in { wait_time } s..."
833+ )
834+ time .sleep (wait_time )
835+ else :
836+ # Non-retryable error, raise immediately
837+ raise
838+ else :
839+ # Last attempt failed
840+ logger .error (f"Upload failed after { max_retries } attempts" )
841+ raise
842+
843+ # Step 3: Validate upload
844+ validate_url = f"{ self .api_base } /v1/{ evaluator_name } :validateUpload"
845+ validate_payload = {"name" : evaluator_name }
846+ validate_response = requests .post (validate_url , json = validate_payload , headers = headers )
847+ validate_response .raise_for_status ()
830848
831- # Step 3: Validate upload
832- validate_url = f"{ self .api_base } /v1/{ evaluator_name } :validateUpload"
833- validate_payload = {"name" : evaluator_name }
834- validate_response = requests .post (validate_url , json = validate_payload , headers = headers )
835- validate_response .raise_for_status ()
849+ validate_data = validate_response .json ()
836850
837- logger .info ("Upload validated successfully" )
851+ logger .info ("Upload validated successfully" )
838852
839- # Clean up tar file
840- if os .path .exists (tar_path ):
841- os .remove (tar_path )
853+ # Clean up tar file
854+ if os .path .exists (tar_path ):
855+ os .remove (tar_path )
842856
843- except Exception as upload_error :
844- logger .warning (f"Code upload failed (evaluator created but code not uploaded): { upload_error } " )
845- # Don't fail - evaluator is created, just code upload failed
857+ except Exception as upload_error :
858+ logger .warning (f"Code upload failed (evaluator created but code not uploaded): { upload_error } " )
859+ # Don't fail - evaluator is created, just code upload failed
846860
847861 return result # Return after attempting upload
848862 except Exception as e :
0 commit comments