Skip to content

Commit 83fb88d

Browse files
committed
Improve and make path handling more consistent in call and log overloads
1 parent 44a124e commit 83fb88d

File tree

3 files changed

+29
-19
lines changed

3 files changed

+29
-19
lines changed

src/humanloop/cli/__main__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ def pull(
219219
220220
Currently only supports syncing Prompt and Agent files. Other file types will be skipped."""
221221
client = get_client(api_key, env_file, base_url)
222+
# Although pull() is available on the Humanloop client, we instantiate SyncClient separately as we need to control its log level
222223
sync_client = SyncClient(
223224
client, base_dir=local_files_directory, log_level=logging.DEBUG if verbose else logging.WARNING
224225
)

src/humanloop/overload.py

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -87,45 +87,49 @@ def _handle_tracing_context(kwargs: Dict[str, Any], client: Any) -> Dict[str, An
8787
def _handle_local_files(
8888
kwargs: Dict[str, Any],
8989
client: Any,
90-
sync_client: Optional[SyncClient],
91-
use_local_files: bool,
90+
sync_client: SyncClient,
9291
) -> Dict[str, Any]:
93-
"""Handle local file loading if enabled."""
94-
if not use_local_files or "path" not in kwargs or sync_client is None:
95-
return kwargs
96-
92+
"""Handle local file loading."""
9793
if "id" in kwargs:
9894
raise HumanloopRuntimeError("Can only specify one of `id` or `path`")
95+
96+
path = kwargs["path"]
97+
98+
if sync_client.is_file(path):
99+
file_type = _get_file_type_from_client(client)
100+
raise HumanloopRuntimeError(
101+
f"Path should not include file extension: `{path}`. "
102+
f"Use `{path.rsplit('.', 1)[0]}` instead. "
103+
)
99104

100105
# Check if version_id or environment is specified
101106
use_remote = any(["version_id" in kwargs, "environment" in kwargs])
102-
normalized_path = sync_client._normalize_path(kwargs["path"])
103107

104108
if use_remote:
105109
raise HumanloopRuntimeError(
106-
f"Cannot use local file for `{normalized_path}` as version_id or environment was specified. "
110+
f"Cannot use local file for `{path}` as version_id or environment was specified. "
107111
"Please either remove version_id/environment to use local files, or set use_local_files=False to use remote files."
108112
)
109113

110114
file_type = _get_file_type_from_client(client)
111115
if file_type not in SyncClient.SERIALIZABLE_FILE_TYPES:
112116
raise HumanloopRuntimeError(f"Local files are not supported for `{file_type}` files.")
113117

114-
# If file_type is already specified in kwargs, it means user provided a PromptKernelRequestParams object
118+
# If file_type is already specified in kwargs (prompt or agent), it means user provided a Prompt- or AgentKernelRequestParams object
115119
if file_type in kwargs and not isinstance(kwargs[file_type], str):
116120
logger.warning(
117-
f"Ignoring local file for `{normalized_path}` as {file_type} parameters were directly provided. "
121+
f"Ignoring local file for `{path}` as {file_type} parameters were directly provided. "
118122
"Using provided parameters instead."
119123
)
120124
return kwargs
121125

122126
try:
123-
file_content = sync_client.get_file_content(normalized_path, file_type) # type: ignore[arg-type] # file_type was checked above
127+
file_content = sync_client.get_file_content(path, file_type) # type: ignore[arg-type] # file_type was checked above
124128
kwargs[file_type] = file_content
125-
except HumanloopRuntimeError as e:
126-
raise HumanloopRuntimeError(f"Failed to use local file for `{normalized_path}`: {str(e)}")
127129

128-
return kwargs
130+
return kwargs
131+
except HumanloopRuntimeError as e:
132+
raise HumanloopRuntimeError(f"Failed to use local file for `{path}`: {str(e)}")
129133

130134

131135
def _handle_evaluation_context(kwargs: Dict[str, Any]) -> tuple[Dict[str, Any], Optional[Callable[[str], None]]]:
@@ -151,11 +155,11 @@ def _overload_log(self: Any, sync_client: Optional[SyncClient], use_local_files:
151155
kwargs = _handle_tracing_context(kwargs, self)
152156

153157
# Handle local files for Prompts and Agents clients
154-
if _get_file_type_from_client(self) in ["prompt", "agent"]:
158+
if use_local_files and _get_file_type_from_client(self) in SyncClient.SERIALIZABLE_FILE_TYPES:
155159
if sync_client is None:
156160
logger.error("sync_client is None but client has log method and use_local_files=%s", use_local_files)
157161
raise HumanloopRuntimeError("sync_client is required for clients that support local file operations")
158-
kwargs = _handle_local_files(kwargs, self, sync_client, use_local_files)
162+
kwargs = _handle_local_files(kwargs, self, sync_client)
159163

160164
kwargs, eval_callback = _handle_evaluation_context(kwargs)
161165
response = self._log(**kwargs) # Use stored original method
@@ -173,7 +177,11 @@ def _overload_log(self: Any, sync_client: Optional[SyncClient], use_local_files:
173177
def _overload_call(self: Any, sync_client: Optional[SyncClient], use_local_files: bool, **kwargs) -> CallResponseType:
174178
try:
175179
kwargs = _handle_tracing_context(kwargs, self)
176-
kwargs = _handle_local_files(kwargs, self, sync_client, use_local_files)
180+
if use_local_files and _get_file_type_from_client(self) in SyncClient.SERIALIZABLE_FILE_TYPES:
181+
if sync_client is None:
182+
logger.error("sync_client is None but client has call method and use_local_files=%s", use_local_files)
183+
raise HumanloopRuntimeError("sync_client is required for clients that support call operations")
184+
kwargs = _handle_local_files(kwargs, self, sync_client)
177185
return self._call(**kwargs) # Use stored original method
178186
except HumanloopRuntimeError:
179187
# Re-raise HumanloopRuntimeError without wrapping to preserve the message
@@ -200,7 +208,7 @@ def log_wrapper(self: Any, **kwargs) -> LogResponseType:
200208
client.log = types.MethodType(log_wrapper, client)
201209

202210
# Overload call method for Prompt and Agent clients
203-
if _get_file_type_from_client(client) in ["prompt", "agent"]:
211+
if _get_file_type_from_client(client) in SyncClient.SERIALIZABLE_FILE_TYPES:
204212
if sync_client is None and use_local_files:
205213
logger.error("sync_client is None but client has call method and use_local_files=%s", use_local_files)
206214
raise HumanloopRuntimeError("sync_client is required for clients that support call operations")

src/humanloop/sync/sync_client.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ def _normalize_path(self, path: str, strip_extension: bool = False) -> str:
181181

182182
def is_file(self, path: str) -> bool:
183183
"""Check if the path is a file by checking for .{file_type} extension for serializable file types."""
184-
return path.endswith(tuple(f".{file_type}" for file_type in self.SERIALIZABLE_FILE_TYPES))
184+
clean_path = path.strip()
185+
return any(clean_path.endswith(f".{file_type}") for file_type in self.SERIALIZABLE_FILE_TYPES)
185186

186187
def _save_serialized_file(
187188
self,

0 commit comments

Comments
 (0)