9595
9696EvaluatorDict = Union [CodeEvaluatorDict , LLMEvaluatorDict , HumanEvaluatorDict , ExternalEvaluator ]
9797Version = Union [FlowDict , PromptDict , ToolDict , EvaluatorDict ]
98- FileType = Literal ["flow" , "prompt" , "tool" , "evaluator " ]
98+ FileType = Literal ["flow" , "prompt" , "agent " ]
9999
100100
101101# ANSI escape codes for logging colors
@@ -153,7 +153,8 @@ def run_eval(
153153 evaluators_worker_pool = ThreadPoolExecutor (max_workers = workers )
154154
155155 hl_file , function_ = _get_hl_file (client = client , file_config = file )
156- type_ = hl_file .type
156+ # cast is safe, we can only fetch Files allowed by FileType
157+ type_ = typing .cast (FileType , hl_file .type )
157158 try :
158159 hl_dataset = _upsert_dataset (dataset = dataset , client = client )
159160 except Exception as e :
@@ -211,11 +212,11 @@ def handle_exit_signal(signum, frame):
211212 # Generate locally if a file `callable` is provided
212213 if function_ is None :
213214 # TODO: trigger run when updated API is available
214- print_info (f"\n Running '{ hl_file .name } ' { hl_file . type . capitalize ()} over the Dataset '{ hl_dataset .name } '" )
215+ print_info (f"\n Running '{ hl_file .name } ' { type_ . capitalize ()} over the '{ hl_dataset .name } ' Dataset " )
215216 else :
216217 # Running the evaluation locally
217218 print_info (
218- f"\n Running '{ hl_file .name } ' { hl_file . type . capitalize ()} over the Dataset '{ hl_dataset .name } ' using { workers } workers...\n "
219+ f"\n Running '{ hl_file .name } ' { type_ . capitalize ()} over the '{ hl_dataset .name } ' Dataset using { workers } workers...\n "
219220 )
220221
221222 _PROGRESS_BAR = _SimpleProgressBar (len (hl_dataset .datapoints ))
@@ -420,58 +421,71 @@ def _safe_get_default_file_version(client: "BaseHumanloop", file_config: FileEva
420421 raise HumanloopRuntimeError (
421422 f"File in Humanloop workspace at { path } is not of type { type } , but { hl_file .type } ."
422423 )
423- return hl_file
424- else :
424+ # cast is safe, we can only fetch Files allowed by FileType
425+ return typing .cast (EvaluatedFile , hl_file )
426+ elif file_id is not None :
425427 subclient = _get_subclient (client = client , file_config = file_config )
426428 return subclient .get (id = file_id )
429+ else :
430+ raise HumanloopRuntimeError ("You must provide a path or id in your `file` config." )
427431
428432
429433def _resolve_file (client : "BaseHumanloop" , file_config : FileEvalConfig ) -> tuple [EvaluatedFile , Optional [Callable ]]:
430434 """Resolve the File to be evaluated. Will return a FileResponse and an optional callable.
431435
432436 If the callable is null, the File will be evaluated on Humanloop. Otherwise, the File will be evaluated locally.
433437 """
434- hl_file = _safe_get_default_file_version (client = client , file_config = file_config )
435438 file_id = file_config .get ("id" )
436439 path = file_config .get ("path" )
437440 version_id = file_config .get ("version_id" )
438441 environment = file_config .get ("environment" )
439442 callable = _get_file_callable (file_config = file_config )
440443 version = file_config .get ("version" )
441444
442- if version is not None and path is None and file_id :
445+ if callable and path is None and file_id is None :
446+ raise HumanloopRuntimeError (
447+ "You are trying to create a new version of the File by passing the `version` argument. "
448+ "You must pass either the `file.path` or `file.id` argument and provider proper `file.version` for upserting the File."
449+ )
450+ hl_file = _safe_get_default_file_version (client = client , file_config = file_config )
451+
452+ if (version_id or environment ) and (callable or version ):
443453 raise HumanloopRuntimeError (
444- "You are trying to create a new version of the File by passing the ` version` argument. You must pass either the `file.path` or `file.id` argument ."
454+ "You are trying to create a local Evaluation while requesting a specific File version by version ID or environment ."
445455 )
446456
447- if version :
448- # User wants to upsert a version
449- return (_upsert_file (file_config = file_config , client = client ), callable )
457+ if callable :
458+ # User responsibility to provide adequate file.version for upserting the file
459+ print_info (
460+ "Upserting a new File version based on `file.version`. Will use provided callable for generating Logs."
461+ )
462+ try :
463+ return (_upsert_file (file_config = file_config , client = client ), callable )
464+ except Exception as e :
465+ raise HumanloopRuntimeError (f"Error upserting the File. Please ensure `file.version` is valid: { e } " ) from e
450466
451467 if version_id is None and environment is None :
452468 # Return default version of the File
453469 return hl_file , callable
454470
455- if callable :
456- raise HumanloopRuntimeError (
457- "You cannot request local evaluation while requesting a specific File version by version ID or environment"
458- )
459-
460471 if file_id is None and (version_id or environment ):
461472 raise HumanloopRuntimeError (
462473 "You must provide the `file.id` when addressing a file by version ID or environment"
463474 )
475+
464476 # Use version_id or environment to retrieve specific version of the File
465477 subclient = _get_subclient (client = client , file_config = file_config )
466478 # Let backend handle case where both or none of version_id and environment are provided
467479 return subclient .get (
468- version_id = file_config .get ("version_id" ),
469- environment = file_config .get ("environment" ),
480+ # Earlier if checked that file_id is not None
481+ id = file_id , # type: ignore [arg-type]
482+ version_id = version_id ,
483+ environment = environment ,
470484 ), None
471485
472486
473487def _get_hl_file (client : "BaseHumanloop" , file_config : FileEvalConfig ) -> tuple [EvaluatedFile , Optional [Callable ]]:
474- """Check if the config object is valid, and resolve the File to be evaluated
488+ """Check if the config object is valid, and resolve the File to be evaluated.
475489
476490 The callable will be null if the evaluation will happen on Humanloop runtime.
477491 Otherwise, the evaluation will happen locally.
@@ -617,20 +631,19 @@ def _file_or_file_inside_hl_decorator(file_config: FileEvalConfig) -> FileEvalCo
617631 return file_
618632
619633
620- def _check_file_type (file : FileEvalConfig ) -> FileEvalConfig :
634+ def _check_file_type (file_config : FileEvalConfig ) -> FileEvalConfig :
621635 """Check that the file type is provided, or set it to `flow` if not provided."""
622636 try :
623- type_ = typing .cast (FileType , file .pop ("type" )) # type: ignore [arg-type, misc]
637+ type_ = typing .cast (FileType , file_config .pop ("type" )) # type: ignore [arg-type, misc]
624638 print_info (
625- f"Evaluating your { type_ } function corresponding to `{ file .get ('path' ) or file .get ('id' )} ` on Humanloop\n \n "
639+ f"Evaluating your { type_ } function corresponding to `{ file_config .get ('path' ) or file_config .get ('id' )} ` on Humanloop\n \n "
626640 )
627- if type_ is None :
628- file ["type" ] = "flow"
641+ file_config ["type" ] = type_ or "flow"
629642 except KeyError as _ :
630643 type_ = "flow"
631644 print_warning ("No `file` type specified, defaulting to flow." )
632- file ["type" ] = type_
633- return file
645+ file_config ["type" ] = type_
646+ return file_config
634647
635648
636649def _get_file_callable (file_config : FileEvalConfig ) -> Optional [Callable ]:
@@ -653,6 +666,7 @@ def _upsert_file(client: "BaseHumanloop", file_config: FileEvalConfig) -> Evalua
653666 # Get or create the file on Humanloop
654667 version = file_config .pop ("version" , {})
655668 file_dict = {** file_config , ** version }
669+ del file_dict ["type" ]
656670 type_ = file_config .get ("type" )
657671 subclient = _get_subclient (client = client , file_config = file_config )
658672
@@ -672,7 +686,8 @@ def _upsert_file(client: "BaseHumanloop", file_config: FileEvalConfig) -> Evalua
672686 else :
673687 raise NotImplementedError (f"Unsupported File type: { type_ } " )
674688
675- return subclient .upsert (** file_dict )
689+ # mypy complains about the polymorphic subclient
690+ return subclient .upsert (** file_dict ) # type: ignore [arg-type]
676691
677692
678693def _upsert_dataset (dataset : DatasetEvalConfig , client : "BaseHumanloop" ):
0 commit comments