Skip to content

Commit 94606a0

Browse files
committed
feat(evals): implement local File support in _resolve_file
1 parent e2adacf commit 94606a0

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

src/humanloop/evals/run.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,11 +453,79 @@ def _resolve_file(
453453
"""
454454
file_id = file_config.get("id")
455455
path = file_config.get("path")
456+
type_ = file_config.get("type")
456457
version_id = file_config.get("version_id")
457458
environment = file_config.get("environment")
458459
callable = _get_file_callable(file_config=file_config)
459460
version = file_config.get("version")
460461

462+
# Early validation for local Files
463+
if (
464+
use_local_files and
465+
path
466+
):
467+
if not file_syncer:
468+
raise HumanloopRuntimeError(
469+
"Internal error: FileSyncer is required when `use_local_files=True`. "
470+
"This may indicate improper SDK usage. Please use the `client.evaluations.run()` method."
471+
)
472+
473+
# Check 1: Error if trying to use specific version with local Files
474+
if version_id or environment:
475+
raise HumanloopRuntimeError(
476+
f"Cannot use local File for path '{path}' when `version_id` or `environment` is specified. "
477+
"Local Files always use the content from your local filesystem, not a specific version from Humanloop. "
478+
"To use a specific version: either provide `id` instead of `path` with `version_id`/`environment`, "
479+
"or set `use_local_files=False` to use remote Files."
480+
)
481+
482+
# Check 2: Version takes precedence over local Files (warn)
483+
if version:
484+
print_warning(
485+
f"Using provided `version` configuration instead of local file for '{path}'."
486+
)
487+
# Continue with normal flow — don't load local File
488+
489+
# Check 3: Callable takes precedence for prompts
490+
elif callable and type_ == "prompt":
491+
print_warning(
492+
"Both local File and callable provided for Prompt. "
493+
"Using callable instead of local File."
494+
)
495+
# Continue with normal flow — don't load local File
496+
497+
# Check 4: Unsupported File type
498+
elif type_ not in ["prompt", "agent"]:
499+
raise HumanloopRuntimeError(
500+
f"Local files are not supported for '{type_}' files. "
501+
"Only 'prompt' and 'agent' files can be used locally."
502+
)
503+
504+
# Load local File
505+
else:
506+
file_content = file_syncer.get_file_content(path, type_)
507+
subclient: PromptsClient | AgentsClient = _get_subclient(client=client, file_config=file_config)
508+
if isinstance(subclient, PromptsClient):
509+
kernel_request = subclient.deserialize(prompt=file_content)
510+
elif isinstance(subclient, AgentsClient):
511+
kernel_request = subclient.deserialize(agent=file_content)
512+
else:
513+
raise ValueError(f"Unsupported subclient type: {type(subclient)}")
514+
515+
if hasattr(kernel_request, "model_dump"):
516+
kernel_request_dict = kernel_request.model_dump(exclude_none=True) # Pydantic v2
517+
elif hasattr(kernel_request, "dict"): # Pydantic v1
518+
kernel_request_dict = kernel_request.dict(exclude_none=True)
519+
520+
upsert_config: FileEvalConfig = {
521+
"path": path,
522+
"type": type_,
523+
"version": kernel_request_dict
524+
}
525+
hl_file = _upsert_file(client=client, file_config=upsert_config)
526+
print_info(f"Using local {type_} file: {path}")
527+
return hl_file, callable
528+
461529
if callable and path is None and file_id is None:
462530
raise HumanloopRuntimeError(
463531
"You are trying to create a new version of the File by passing the `version` argument. "

0 commit comments

Comments
 (0)