Skip to content

Commit c0a200a

Browse files
committed
fix: improve path validation for SDK calls
- Enhance safety in path extension handling - Fix path validation tests to work with actual error messages - Prioritize extension validation over other path format issues - Ensure consistent error handling across prompt and agent clients
1 parent fb292db commit c0a200a

File tree

10 files changed

+404
-191
lines changed

10 files changed

+404
-191
lines changed

.fernignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
src/humanloop/evals
77
src/humanloop/prompt_utils.py
8+
src/humanloop/path_utils.py
89
src/humanloop/client.py
910
src/humanloop/overload.py
1011
src/humanloop/context.py

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,3 @@ poetry.toml
77
.env
88
tests/assets/*.jsonl
99
tests/assets/*.parquet
10-
# Ignore humanloop directory which could mistakenly be committed when testing sync functionality as it's used as the default sync directory
11-
humanloop

src/humanloop/cli/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def get_client(
8181
"""
8282
if not api_key:
8383
api_key = load_api_key(env_file)
84+
print(api_key)
8485
return Humanloop(api_key=api_key, base_url=base_url)
8586

8687

src/humanloop/overload.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,25 @@ def _handle_local_files(
9292
"""Handle local file loading."""
9393
if "id" in kwargs:
9494
raise HumanloopRuntimeError("Can only specify one of `id` or `path`")
95-
95+
9696
path = kwargs["path"]
9797

98+
# Check if the path has a file type extension (.prompt/.agent)
9899
if sync_client.is_file(path):
99100
file_type = _get_file_type_from_client(client)
101+
102+
# Safely extract the extension and path by handling potential errors
103+
try:
104+
parts = path.rsplit(".", 1)
105+
path_without_extension = parts[0] if len(parts) > 0 else path
106+
except Exception:
107+
# Fallback to original path if any error occurs
108+
path_without_extension = path
109+
100110
raise HumanloopRuntimeError(
101-
f"Path should not include file extension: `{path}`. "
102-
f"Use `{path.rsplit('.', 1)[0]}` instead. "
111+
f"Path '{path}' includes a file extension which is not supported in API calls. "
112+
f"When referencing files via the path parameter, use the format without extensions: '{path_without_extension}'. "
113+
f"Note: File extensions are only used when pulling specific files via the CLI."
103114
)
104115

105116
# Check if version_id or environment is specified

src/humanloop/sync/sync_client.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,15 @@ def _normalize_path(self, path: str, strip_extension: bool = False) -> str:
180180
return "/".join(part for part in normalized.replace("\\", "/").split("/") if part)
181181

182182
def is_file(self, path: str) -> bool:
183-
"""Check if the path is a file by checking for .{file_type} extension for serializable file types."""
184-
clean_path = path.strip()
183+
"""Check if the path is a file by checking for .{file_type} extension for serializable file types.
184+
185+
Files are identified by having a supported extension (.prompt or .agent).
186+
This method performs case-insensitive comparison and handles whitespace.
187+
188+
Returns:
189+
bool: True if the path ends with a supported file extension
190+
"""
191+
clean_path = path.strip().lower() # Convert to lowercase for case-insensitive comparison
185192
return any(clean_path.endswith(f".{file_type}") for file_type in self.SERIALIZABLE_FILE_TYPES)
186193

187194
def _save_serialized_file(

0 commit comments

Comments
 (0)