uipath-langchain 0.0.131__py3-none-any.whl → 0.0.132__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of uipath-langchain might be problematic. Click here for more details.

@@ -1,416 +0,0 @@
1
- import functools
2
- import importlib
3
- import inspect
4
- import logging
5
- import sys
6
- import uuid
7
- from typing import Any, Callable, Dict, List, Literal, Optional
8
-
9
- from langchain_core.callbacks import dispatch_custom_event
10
- from uipath.tracing import TracingManager
11
-
12
- from ._events import CustomTraceEvents, FunctionCallEventData
13
-
14
- # Original module and traceable function references
15
- original_langsmith: Any = None
16
- original_traceable: Any = None
17
-
18
- logger = logging.getLogger(__name__)
19
-
20
-
21
- def dispatch_trace_event(
22
- func_name,
23
- inputs: Dict[str, Any],
24
- event_type="call",
25
- call_uuid=None,
26
- result=None,
27
- exception=None,
28
- run_type: Optional[
29
- Literal["tool", "chain", "llm", "retriever", "embedding", "prompt", "parser"]
30
- ] = None,
31
- tags: Optional[List[str]] = None,
32
- metadata: Optional[Dict[str, Any]] = None,
33
- ):
34
- """Dispatch trace event to our server."""
35
- try:
36
- event_data = FunctionCallEventData(
37
- function_name=func_name,
38
- event_type=event_type,
39
- inputs=inputs,
40
- call_uuid=call_uuid,
41
- output=result,
42
- error=str(exception) if exception else None,
43
- run_type=run_type,
44
- tags=tags,
45
- metadata=metadata,
46
- )
47
-
48
- dispatch_custom_event(CustomTraceEvents.UIPATH_TRACE_FUNCTION_CALL, event_data)
49
- except Exception as e:
50
- logger.debug(
51
- f"Error dispatching trace event: {e}. Function name: {func_name} Event type: {event_type}"
52
- )
53
-
54
-
55
- def format_args_for_trace(
56
- signature: inspect.Signature, *args: Any, **kwargs: Any
57
- ) -> Dict[str, Any]:
58
- try:
59
- """Return a dictionary of inputs from the function signature."""
60
- # Create a parameter mapping by partially binding the arguments
61
- parameter_binding = signature.bind_partial(*args, **kwargs)
62
-
63
- # Fill in default values for any unspecified parameters
64
- parameter_binding.apply_defaults()
65
-
66
- # Extract the input parameters, skipping special Python parameters
67
- result = {}
68
- for name, value in parameter_binding.arguments.items():
69
- # Skip class and instance references
70
- if name in ("self", "cls"):
71
- continue
72
-
73
- # Handle **kwargs parameters specially
74
- param_info = signature.parameters.get(name)
75
- if param_info and param_info.kind == inspect.Parameter.VAR_KEYWORD:
76
- # Flatten nested kwargs directly into the result
77
- if isinstance(value, dict):
78
- result.update(value)
79
- else:
80
- # Regular parameter
81
- result[name] = value
82
-
83
- return result
84
- except Exception as e:
85
- logger.warning(
86
- f"Error formatting arguments for trace: {e}. Using args and kwargs directly."
87
- )
88
- return {"args": args, "kwargs": kwargs}
89
-
90
-
91
- def _create_traced_wrapper(
92
- func: Callable[..., Any],
93
- wrapper_func: Optional[Callable[..., Any]] = None,
94
- func_name: Optional[str] = None,
95
- run_type: Optional[str] = None,
96
- span_type: Optional[str] = None,
97
- tags: Optional[List[str]] = None,
98
- metadata: Optional[Dict[str, Any]] = None,
99
- input_processor: Optional[Callable[..., Any]] = None,
100
- output_processor: Optional[Callable[..., Any]] = None,
101
- ):
102
- """Create a traced wrapper based on function type."""
103
- # Use function name if not provided
104
- actual_func_name = func_name or func.__name__
105
- # Function to actually call (the wrapped function or original)
106
- target_func = wrapper_func or func
107
- # Ensure we have metadata
108
- actual_metadata = metadata or {}
109
-
110
- # Define all wrapper functions
111
-
112
- @functools.wraps(target_func)
113
- async def async_gen_wrapper(*args, **kwargs):
114
- try:
115
- call_uuid = str(uuid.uuid4())
116
-
117
- # Get inputs and process them if needed
118
- inputs = format_args_for_trace(inspect.signature(func), *args, **kwargs)
119
- if input_processor is not None:
120
- inputs = input_processor(inputs)
121
-
122
- # Add span_type to metadata if provided
123
- if span_type:
124
- actual_metadata["span_type"] = (
125
- span_type or "function_call_generator_async"
126
- )
127
-
128
- dispatch_trace_event(
129
- actual_func_name,
130
- inputs,
131
- "call",
132
- call_uuid,
133
- run_type=run_type, # type: ignore
134
- tags=tags,
135
- metadata=actual_metadata,
136
- )
137
-
138
- outputs = []
139
- async_gen = target_func(*args, **kwargs)
140
- async for item in async_gen:
141
- outputs.append(item)
142
- yield item
143
-
144
- # Process output if needed
145
- output_to_record = outputs
146
- if output_processor is not None:
147
- output_to_record = output_processor(outputs)
148
-
149
- dispatch_trace_event(
150
- actual_func_name,
151
- inputs,
152
- "completion",
153
- call_uuid,
154
- result=output_to_record,
155
- )
156
- except Exception as e:
157
- dispatch_trace_event(
158
- actual_func_name, inputs, "completion", call_uuid, exception=e
159
- )
160
- raise
161
-
162
- @functools.wraps(target_func)
163
- def gen_wrapper(*args, **kwargs):
164
- try:
165
- call_uuid = str(uuid.uuid4())
166
-
167
- # Get inputs and process them if needed
168
- inputs = format_args_for_trace(inspect.signature(func), *args, **kwargs)
169
- if input_processor is not None:
170
- inputs = input_processor(inputs)
171
-
172
- # Add span_type to metadata if provided
173
- if span_type:
174
- actual_metadata["span_type"] = (
175
- span_type or "function_call_generator_sync"
176
- )
177
-
178
- dispatch_trace_event(
179
- actual_func_name,
180
- inputs,
181
- "call",
182
- call_uuid,
183
- run_type=run_type, # type: ignore
184
- tags=tags,
185
- metadata=actual_metadata,
186
- )
187
-
188
- outputs = []
189
- gen = target_func(*args, **kwargs)
190
- for item in gen:
191
- outputs.append(item)
192
- yield item
193
-
194
- # Process output if needed
195
- output_to_record = outputs
196
- if output_processor is not None:
197
- output_to_record = output_processor(outputs)
198
-
199
- dispatch_trace_event(
200
- actual_func_name,
201
- inputs,
202
- "completion",
203
- call_uuid,
204
- result=output_to_record,
205
- )
206
- except Exception as e:
207
- dispatch_trace_event(
208
- actual_func_name, inputs, "completion", call_uuid, exception=e
209
- )
210
- raise
211
-
212
- @functools.wraps(target_func)
213
- async def async_wrapper(*args, **kwargs):
214
- try:
215
- call_uuid = str(uuid.uuid4())
216
-
217
- # Get inputs and process them if needed
218
- inputs = format_args_for_trace(inspect.signature(func), *args, **kwargs)
219
- if input_processor is not None:
220
- inputs = input_processor(inputs)
221
-
222
- # Add span_type to metadata if provided
223
- if span_type:
224
- actual_metadata["span_type"] = span_type or "function_call_async"
225
-
226
- dispatch_trace_event(
227
- actual_func_name,
228
- inputs,
229
- "call",
230
- call_uuid,
231
- run_type=run_type, # type: ignore
232
- tags=tags,
233
- metadata=actual_metadata,
234
- )
235
-
236
- result = await target_func(*args, **kwargs)
237
-
238
- # Process output if needed
239
- output_to_record = result
240
- if output_processor is not None:
241
- output_to_record = output_processor(result)
242
-
243
- dispatch_trace_event(
244
- actual_func_name,
245
- inputs,
246
- "completion",
247
- call_uuid,
248
- result=output_to_record,
249
- )
250
-
251
- return result
252
- except Exception as e:
253
- dispatch_trace_event(
254
- actual_func_name, inputs, "completion", call_uuid, exception=e
255
- )
256
- raise
257
-
258
- @functools.wraps(target_func)
259
- def sync_wrapper(*args, **kwargs):
260
- try:
261
- call_uuid = str(uuid.uuid4())
262
-
263
- # Get inputs and process them if needed
264
- inputs = format_args_for_trace(inspect.signature(func), *args, **kwargs)
265
- if input_processor is not None:
266
- inputs = input_processor(inputs)
267
-
268
- # Add span_type to metadata if provided
269
- if span_type:
270
- actual_metadata["span_type"] = span_type or "function_call_sync"
271
-
272
- dispatch_trace_event(
273
- actual_func_name,
274
- inputs,
275
- "call",
276
- call_uuid,
277
- run_type=run_type, # type: ignore
278
- tags=tags,
279
- metadata=actual_metadata,
280
- )
281
-
282
- result = target_func(*args, **kwargs)
283
-
284
- # Process output if needed
285
- output_to_record = result
286
- if output_processor is not None:
287
- output_to_record = output_processor(result)
288
-
289
- dispatch_trace_event(
290
- actual_func_name,
291
- inputs,
292
- "completion",
293
- call_uuid,
294
- result=output_to_record,
295
- )
296
-
297
- return result
298
- except Exception as e:
299
- dispatch_trace_event(
300
- actual_func_name, inputs, "completion", call_uuid, exception=e
301
- )
302
- raise
303
-
304
- # Return the appropriate wrapper based on the function type
305
- if inspect.isasyncgenfunction(target_func):
306
- return async_gen_wrapper
307
- elif inspect.isgeneratorfunction(target_func):
308
- return gen_wrapper
309
- elif inspect.iscoroutinefunction(target_func):
310
- return async_wrapper
311
- else:
312
- return sync_wrapper
313
-
314
-
315
- def _create_appropriate_wrapper(
316
- original_func: Any, wrapped_func: Any, decorator_kwargs: Dict[str, Any]
317
- ):
318
- """Create the appropriate wrapper based on function type."""
319
-
320
- # Get the function name and tags from decorator arguments
321
- func_name = decorator_kwargs.get("name", original_func.__name__)
322
- tags = decorator_kwargs.get("tags", None)
323
- metadata = decorator_kwargs.get("metadata", None)
324
- run_type = decorator_kwargs.get("run_type", None)
325
-
326
- return _create_traced_wrapper(
327
- func=original_func,
328
- wrapper_func=wrapped_func,
329
- func_name=func_name,
330
- run_type=run_type,
331
- tags=tags,
332
- metadata=metadata,
333
- )
334
-
335
-
336
- def _uipath_traced(
337
- name: Optional[str] = None,
338
- run_type: Optional[str] = None,
339
- span_type: Optional[str] = None,
340
- input_processor: Optional[Callable[..., Any]] = None,
341
- output_processor: Optional[Callable[..., Any]] = None,
342
- *args: Any,
343
- **kwargs: Any,
344
- ):
345
- """Decorator factory that creates traced functions using dispatch_trace_event."""
346
-
347
- def decorator(func):
348
- return _create_traced_wrapper(
349
- func=func,
350
- func_name=name,
351
- run_type=run_type,
352
- span_type=span_type,
353
- input_processor=input_processor,
354
- output_processor=output_processor,
355
- )
356
-
357
- return decorator
358
-
359
-
360
- # Create patched version of traceable
361
- def patched_traceable(*decorator_args, **decorator_kwargs):
362
- # Handle the case when @traceable is used directly as decorator without arguments
363
- if (
364
- len(decorator_args) == 1
365
- and callable(decorator_args[0])
366
- and not decorator_kwargs
367
- ):
368
- func = decorator_args[0]
369
- return _create_appropriate_wrapper(func, original_traceable(func), {})
370
-
371
- # Handle the case when @traceable(args) is used with parameters
372
- original_decorated = original_traceable(*decorator_args, **decorator_kwargs)
373
-
374
- def uipath_trace_decorator(func):
375
- # Apply the original decorator with its arguments
376
- wrapped_func = original_decorated(func)
377
- return _create_appropriate_wrapper(func, wrapped_func, decorator_kwargs)
378
-
379
- return uipath_trace_decorator
380
-
381
-
382
- # Register the UIPath traced decorator
383
- def register_uipath_tracing():
384
- """Register the UIPath tracing decorator with TracedDecoratorRegistry."""
385
- # Reapply to all previously decorated functions
386
- TracingManager.reapply_traced_decorator(_uipath_traced)
387
-
388
-
389
- # Apply the patch
390
- def _instrument_traceable_attributes():
391
- """Apply the patch to langsmith module at import time."""
392
- global original_langsmith, original_traceable
393
-
394
- # Register our custom tracing decorator
395
- register_uipath_tracing()
396
-
397
- # Import the original module if not already done
398
- if original_langsmith is None:
399
- # Temporarily remove our custom module from sys.modules
400
- if "langsmith" in sys.modules:
401
- original_langsmith = sys.modules["langsmith"]
402
- del sys.modules["langsmith"]
403
-
404
- # Import the original module
405
- original_langsmith = importlib.import_module("langsmith")
406
-
407
- # Store the original traceable
408
- original_traceable = original_langsmith.traceable
409
-
410
- # Replace the traceable function with our patched version
411
- original_langsmith.traceable = patched_traceable
412
-
413
- # Put our modified module back
414
- sys.modules["langsmith"] = original_langsmith
415
-
416
- return original_langsmith