11import inspect
22import logging
33import types
4- from typing import TypeVar , Union , Literal
4+ from typing import TypeVar , Union , Literal , Optional
55from pathlib import Path
66from humanloop .context import (
77 get_decorator_context ,
@@ -117,7 +117,7 @@ def _overload_call(self, **kwargs) -> PromptCallResponse:
117117 logger .info (f"Calling inner overload" )
118118 response = self ._call (** kwargs )
119119 except Exception as e :
120- # Re-raising as HumanloopDecoratorError so the decorators don't catch it
120+ # Re-raising as HumanloopRuntimeError so the decorators don't catch it
121121 raise HumanloopRuntimeError from e
122122
123123 return response
@@ -126,55 +126,75 @@ def _overload_call(self, **kwargs) -> PromptCallResponse:
126126 client .call = types .MethodType (_overload_call , client ) # type: ignore [assignment]
127127 return client
128128
129+ def _get_file_type_from_client (client : Union [PromptsClient , AgentsClient ]) -> FileType :
130+ """Get the file type based on the client type."""
131+ if isinstance (client , PromptsClient ):
132+ return "prompt"
133+ elif isinstance (client , AgentsClient ):
134+ return "agent"
135+ else :
136+ raise ValueError (f"Unsupported client type: { type (client )} " )
137+
138+ def _handle_local_file (path : str , file_type : FileType ) -> Optional [str ]:
139+ """Handle reading from a local file if it exists.
140+
141+ Args:
142+ path: The path to the file
143+ file_type: The type of file ("prompt" or "agent")
144+
145+ Returns:
146+ The file content if found, None otherwise
147+ """
148+ try :
149+ # Construct path to local file
150+ local_path = Path ("humanloop" ) / path # FLAG: ensure that when passing the path back to remote, it's using forward slashes
151+ # Add appropriate extension
152+ local_path = local_path .parent / f"{ local_path .stem } .{ file_type } "
153+
154+ if local_path .exists ():
155+ # Read the file content
156+ with open (local_path ) as f :
157+ file_content = f .read ()
158+ logger .debug (f"Using local file content from { local_path } " )
159+ return file_content
160+ else :
161+ logger .warning (f"Local file not found: { local_path } , falling back to API" )
162+ return None
163+ except Exception as e :
164+ logger .error (f"Error reading local file: { e } , falling back to API" )
165+ return None
166+
129167def overload_with_local_files (
130168 client : Union [PromptsClient , AgentsClient ],
131169 use_local_files : bool ,
132170) -> Union [PromptsClient , AgentsClient ]:
133- """Overload call to handle local files when use_local_files is True.
171+ """Overload call and log methods to handle local files when use_local_files is True.
134172
135173 Args:
136174 client: The client to overload (PromptsClient or AgentsClient)
137175 use_local_files: Whether to use local files
138- file_type: Type of file ("prompt" or "agent")
139176 """
140177 original_call = client ._call if hasattr (client , '_call' ) else client .call
141178 original_log = client ._log if hasattr (client , '_log' ) else client .log
142- # get file type from client type
143- file_type : FileType
144- if isinstance (client , PromptsClient ):
145- file_type = "prompt"
146- elif isinstance (client , AgentsClient ):
147- file_type = "agent"
148- else :
149- raise ValueError (f"Unsupported client type: { type (client )} " )
179+ file_type = _get_file_type_from_client (client )
150180
151181 def _overload (self , function_name : str , ** kwargs ) -> PromptCallResponse :
182+ # Handle local files if enabled
152183 if use_local_files and "path" in kwargs :
153- try :
154- # Construct path to local file
155- local_path = Path ("humanloop" ) / kwargs ["path" ] # FLAG: ensure that when passing the path back to remote, it's using forward slashes
156- # Add appropriate extension
157- local_path = local_path .parent / f"{ local_path .stem } .{ file_type } "
158-
159- if local_path .exists ():
160- # Read the file content
161- with open (local_path ) as f :
162- file_content = f .read ()
163-
164- kwargs [file_type ] = file_content # "prompt" or "agent" # TODO: raise warning if kernel passed in
165-
166- logger .debug (f"Using local file content from { local_path } " )
167- else :
168- logger .warning (f"Local file not found: { local_path } , falling back to API" )
169- except Exception as e :
170- logger .error (f"Error reading local file: { e } , falling back to API" )
184+ file_content = _handle_local_file (kwargs ["path" ], file_type )
185+ if file_content is not None :
186+ kwargs [file_type ] = file_content
171187
172- if function_name == "call" :
173- return original_call (** kwargs )
174- elif function_name == "log" :
175- return original_log (** kwargs )
176- else :
177- raise ValueError (f"Unsupported function name: { function_name } " )
188+ try :
189+ if function_name == "call" :
190+ return original_call (** kwargs )
191+ elif function_name == "log" :
192+ return original_log (** kwargs )
193+ else :
194+ raise ValueError (f"Unsupported function name: { function_name } " )
195+ except Exception as e :
196+ # Re-raising as HumanloopRuntimeError so the decorators don't catch it
197+ raise HumanloopRuntimeError from e
178198
179199 def _overload_call (self , ** kwargs ) -> PromptCallResponse :
180200 return _overload (self , "call" , ** kwargs )
0 commit comments