Skip to content

Commit cb295f3

Browse files
author
Andrei Bratu
committed
eval run quality of life changes
1 parent aed862b commit cb295f3

File tree

1 file changed

+53
-49
lines changed

1 file changed

+53
-49
lines changed

src/humanloop/evals/run.py

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ def run_eval(
152152
"""
153153
evaluators_worker_pool = ThreadPoolExecutor(max_workers=workers)
154154

155-
hl_file, function_ = _get_hl_file(client=client, file=file)
155+
hl_file, function_ = _get_hl_file(client=client, file_config=file)
156156
type_ = hl_file.type
157157
try:
158158
hl_dataset = _upsert_dataset(dataset=dataset, client=client)
@@ -415,48 +415,52 @@ def _safe_get_default_file_version(client: "BaseHumanloop", file_config: FileEva
415415
raise HumanloopRuntimeError("You must provide a path or id in your `file`.")
416416

417417
if path is not None:
418-
try:
419-
hl_file = client.files.retrieve_by_path(path=path)
420-
if hl_file.type != type:
421-
raise HumanloopRuntimeError(
422-
f"File in Humanloop workspace at {path} is not of type {type}, but {hl_file.type}."
423-
)
424-
return hl_file
425-
except ApiError as e:
426-
if e.status_code == 404:
427-
raise HumanloopRuntimeError(f"File with path {path} not found. Please check your path and try again.")
428-
raise e
418+
hl_file = client.files.retrieve_by_path(path=path)
419+
if hl_file.type != type:
420+
raise HumanloopRuntimeError(
421+
f"File in Humanloop workspace at {path} is not of type {type}, but {hl_file.type}."
422+
)
423+
return hl_file
429424
else:
430-
subclient = _get_subclient(client, file_config)
431-
try:
432-
return subclient.get(id=file_id)
433-
except ApiError as e:
434-
if e.status_code == 404:
435-
raise HumanloopRuntimeError(f"File with id {file_id} not found. Please check your id and try again.")
436-
raise e
425+
subclient = _get_subclient(client=client, file_config=file_config)
426+
return subclient.get(id=file_id)
437427

438428

439429
def _resolve_file(client: "BaseHumanloop", file_config: FileEvalConfig) -> tuple[EvaluatedFile, Optional[Callable]]:
440430
"""Resolve the File to be evaluated. Will return a FileResponse and an optional callable.
441431
442432
If the callable is null, the File will be evaluated on Humanloop. Otherwise, the File will be evaluated locally.
443433
"""
444-
if file_config.get("version") is not None and file_config.get("path") is None:
445-
raise HumanloopRuntimeError(
446-
"You are trying to create a new version of the File by passing the `version` argument. You must also pass the `path` argument."
447-
)
448434
hl_file = _safe_get_default_file_version(client=client, file_config=file_config)
449-
if "version" in file_config:
450-
# User wants to upsert a version
451-
return (
452-
_upsert_file(file_config=file_config, client=client),
453-
_get_file_callable(file_config),
454-
)
435+
file_id = file_config.get("id")
436+
path = file_config.get("path")
455437
version_id = file_config.get("version_id")
456438
environment = file_config.get("environment")
439+
callable = _get_file_callable(file_config=file_config)
440+
version = file_config.get("version")
441+
442+
if version is not None and path is None and file_id:
443+
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."
445+
)
446+
447+
if version:
448+
# User wants to upsert a version
449+
return (_upsert_file(file_config=file_config, client=client), callable)
450+
457451
if version_id is None and environment is None:
458452
# Return default version of the File
459-
return hl_file, None
453+
return hl_file, callable
454+
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+
460+
if file_id is None and (version_id or environment):
461+
raise HumanloopRuntimeError(
462+
"You must provide the `file.id` when addressing a file by version ID or environment"
463+
)
460464
# Use version_id or environment to retrieve specific version of the File
461465
subclient = _get_subclient(client=client, file_config=file_config)
462466
# Let backend handle case where both or none of version_id and environment are provided
@@ -466,21 +470,21 @@ def _resolve_file(client: "BaseHumanloop", file_config: FileEvalConfig) -> tuple
466470
), None
467471

468472

469-
def _get_hl_file(client: "BaseHumanloop", file: FileEvalConfig) -> tuple[EvaluatedFile, Optional[Callable]]:
473+
def _get_hl_file(client: "BaseHumanloop", file_config: FileEvalConfig) -> tuple[EvaluatedFile, Optional[Callable]]:
470474
"""Check if the config object is valid, and resolve the File to be evaluated
471475
472476
The callable will be null if the evaluation will happen on Humanloop runtime.
473477
Otherwise, the evaluation will happen locally.
474478
"""
475-
file_ = _file_or_file_inside_hl_decorator(file)
479+
file_ = _file_or_file_inside_hl_decorator(file_config)
476480
file_ = _check_file_type(file_)
477481

478482
return _resolve_file(client=client, file_config=file_)
479483

480484

481-
def _callable_is_hl_utility(file: FileEvalConfig) -> bool:
485+
def _callable_is_hl_utility(file_config: FileEvalConfig) -> bool:
482486
"""Check if a File is a decorated function."""
483-
return hasattr(file.get("callable", {}), "file")
487+
return hasattr(file_config.get("callable", {}), "file")
484488

485489

486490
def _wait_for_evaluation_to_complete(
@@ -557,37 +561,37 @@ def _get_checks(
557561
return checks
558562

559563

560-
def _file_or_file_inside_hl_decorator(file: FileEvalConfig) -> FileEvalConfig:
561-
if _callable_is_hl_utility(file):
562-
inner_file: FileEvalConfig = file["callable"].file # type: ignore [misc, attr-defined]
563-
function_ = file["callable"]
564-
type_ = file["type"]
564+
def _file_or_file_inside_hl_decorator(file_config: FileEvalConfig) -> FileEvalConfig:
565+
if _callable_is_hl_utility(file_config):
566+
inner_file: FileEvalConfig = file_config["callable"].file # type: ignore [misc, attr-defined]
567+
function_ = file_config["callable"]
568+
type_ = file_config["type"]
565569
decorator_type = function_.file["type"] # type: ignore [attr-defined, union-attr]
566570
if decorator_type != type_:
567571
raise HumanloopRuntimeError(
568572
"The type of the decorated function does not match the type of the file. Expected `%s`, got `%s`."
569573
% (type_.capitalize(), decorator_type.capitalize())
570574
)
571-
if "path" in file and inner_file["path"] != file["path"]:
575+
if "path" in file_config and inner_file["path"] != file_config["path"]:
572576
raise HumanloopRuntimeError(
573577
"`path` attribute specified in the `file` does not match the path of the decorated function. "
574-
f"Expected `{inner_file['path']}`, got `{file['path']}`."
578+
f"Expected `{inner_file['path']}`, got `{file_config['path']}`."
575579
)
576-
if "id" in file:
580+
if "id" in file_config:
577581
raise HumanloopRuntimeError(
578582
"Do not specify an `id` attribute in `file` argument when using a decorated function."
579583
)
580-
if "version" in file:
584+
if "version" in file_config:
581585
if inner_file["type"] != "prompt":
582586
raise HumanloopRuntimeError(
583587
f"Do not specify a `version` attribute in `file` argument when using a {inner_file['type'].capitalize()} decorated function."
584588
)
585-
if "type" in file and inner_file["type"] != file["type"]:
589+
if "type" in file_config and inner_file["type"] != file_config["type"]:
586590
raise HumanloopRuntimeError(
587591
"Attribute `type` of `file` argument does not match the file type of the decorated function. "
588-
f"Expected `{inner_file['type']}`, got `{file['type']}`."
592+
f"Expected `{inner_file['type']}`, got `{file_config['type']}`."
589593
)
590-
if "id" in file:
594+
if "id" in file_config:
591595
raise HumanloopRuntimeError(
592596
"Do not specify an `id` attribute in `file` argument when using a decorated function."
593597
)
@@ -602,9 +606,9 @@ def _file_or_file_inside_hl_decorator(file: FileEvalConfig) -> FileEvalConfig:
602606
f"{RESET}"
603607
)
604608
# TODO: document this
605-
file_["version"] = file["version"]
609+
file_["version"] = file_config["version"]
606610
else:
607-
file_ = copy.deepcopy(file)
611+
file_ = copy.deepcopy(file_config)
608612

609613
# Raise error if neither path nor id is provided
610614
if not file_.get("path") and not file_.get("id"):
@@ -807,7 +811,7 @@ def _call_function(
807811
try:
808812
output = json.dumps(output)
809813
except Exception:
810-
# throw error if it fails to serialize
814+
# throw error if output fails to serialize
811815
raise ValueError(f"Your {type}'s `callable` must return a string or a JSON serializable object.")
812816
return output
813817

0 commit comments

Comments
 (0)