ragaai-catalyst 2.2.4b5__py3-none-any.whl → 2.2.5b2__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.
Files changed (45) hide show
  1. ragaai_catalyst/__init__.py +0 -2
  2. ragaai_catalyst/dataset.py +59 -1
  3. ragaai_catalyst/tracers/agentic_tracing/tracers/main_tracer.py +5 -285
  4. ragaai_catalyst/tracers/agentic_tracing/utils/__init__.py +0 -2
  5. ragaai_catalyst/tracers/agentic_tracing/utils/create_dataset_schema.py +1 -1
  6. ragaai_catalyst/tracers/exporters/__init__.py +1 -2
  7. ragaai_catalyst/tracers/exporters/file_span_exporter.py +0 -1
  8. ragaai_catalyst/tracers/exporters/ragaai_trace_exporter.py +23 -1
  9. ragaai_catalyst/tracers/tracer.py +6 -186
  10. {ragaai_catalyst-2.2.4b5.dist-info → ragaai_catalyst-2.2.5b2.dist-info}/METADATA +1 -1
  11. {ragaai_catalyst-2.2.4b5.dist-info → ragaai_catalyst-2.2.5b2.dist-info}/RECORD +14 -45
  12. ragaai_catalyst/experiment.py +0 -486
  13. ragaai_catalyst/tracers/agentic_tracing/tests/FinancialAnalysisSystem.ipynb +0 -536
  14. ragaai_catalyst/tracers/agentic_tracing/tests/GameActivityEventPlanner.ipynb +0 -134
  15. ragaai_catalyst/tracers/agentic_tracing/tests/TravelPlanner.ipynb +0 -563
  16. ragaai_catalyst/tracers/agentic_tracing/tests/__init__.py +0 -0
  17. ragaai_catalyst/tracers/agentic_tracing/tests/ai_travel_agent.py +0 -197
  18. ragaai_catalyst/tracers/agentic_tracing/tests/unique_decorator_test.py +0 -172
  19. ragaai_catalyst/tracers/agentic_tracing/tracers/agent_tracer.py +0 -687
  20. ragaai_catalyst/tracers/agentic_tracing/tracers/base.py +0 -1319
  21. ragaai_catalyst/tracers/agentic_tracing/tracers/custom_tracer.py +0 -347
  22. ragaai_catalyst/tracers/agentic_tracing/tracers/langgraph_tracer.py +0 -0
  23. ragaai_catalyst/tracers/agentic_tracing/tracers/llm_tracer.py +0 -1182
  24. ragaai_catalyst/tracers/agentic_tracing/tracers/network_tracer.py +0 -288
  25. ragaai_catalyst/tracers/agentic_tracing/tracers/tool_tracer.py +0 -557
  26. ragaai_catalyst/tracers/agentic_tracing/tracers/user_interaction_tracer.py +0 -129
  27. ragaai_catalyst/tracers/agentic_tracing/upload/upload_local_metric.py +0 -74
  28. ragaai_catalyst/tracers/agentic_tracing/utils/api_utils.py +0 -21
  29. ragaai_catalyst/tracers/agentic_tracing/utils/generic.py +0 -32
  30. ragaai_catalyst/tracers/agentic_tracing/utils/get_user_trace_metrics.py +0 -28
  31. ragaai_catalyst/tracers/agentic_tracing/utils/span_attributes.py +0 -133
  32. ragaai_catalyst/tracers/agentic_tracing/utils/supported_llm_provider.toml +0 -34
  33. ragaai_catalyst/tracers/exporters/raga_exporter.py +0 -467
  34. ragaai_catalyst/tracers/langchain_callback.py +0 -821
  35. ragaai_catalyst/tracers/llamaindex_callback.py +0 -361
  36. ragaai_catalyst/tracers/llamaindex_instrumentation.py +0 -424
  37. ragaai_catalyst/tracers/upload_traces.py +0 -170
  38. ragaai_catalyst/tracers/utils/convert_langchain_callbacks_output.py +0 -62
  39. ragaai_catalyst/tracers/utils/convert_llama_instru_callback.py +0 -69
  40. ragaai_catalyst/tracers/utils/extraction_logic_llama_index.py +0 -74
  41. ragaai_catalyst/tracers/utils/langchain_tracer_extraction_logic.py +0 -82
  42. ragaai_catalyst/tracers/utils/rag_trace_json_converter.py +0 -403
  43. {ragaai_catalyst-2.2.4b5.dist-info → ragaai_catalyst-2.2.5b2.dist-info}/WHEEL +0 -0
  44. {ragaai_catalyst-2.2.4b5.dist-info → ragaai_catalyst-2.2.5b2.dist-info}/licenses/LICENSE +0 -0
  45. {ragaai_catalyst-2.2.4b5.dist-info → ragaai_catalyst-2.2.5b2.dist-info}/top_level.txt +0 -0
@@ -1,821 +0,0 @@
1
- from typing import Any, Dict, List, Optional, Union, Sequence
2
-
3
- import attr
4
- from langchain.callbacks.base import BaseCallbackHandler
5
- from langchain.schema import LLMResult, AgentAction, AgentFinish, BaseMessage
6
- from datetime import datetime
7
- import json
8
- import os
9
- from uuid import UUID
10
- from functools import wraps
11
- import asyncio
12
- from langchain_core.documents import Document
13
- import logging
14
- import tempfile
15
- import sys
16
- import importlib
17
- from importlib.util import find_spec
18
-
19
- logging.basicConfig(level=logging.INFO)
20
- logger = logging.getLogger(__name__)
21
-
22
-
23
- class LangchainTracer(BaseCallbackHandler):
24
- """
25
- An enhanced callback handler for LangChain that traces all actions and saves them to a JSON file.
26
- Includes improved error handling, async support, and configuration options.
27
- """
28
-
29
- def __init__(
30
- self,
31
- output_path: str = tempfile.gettempdir(),
32
- trace_all: bool = True,
33
- save_interval: Optional[int] = None,
34
- log_level: int = logging.INFO,
35
- ):
36
- """
37
- Initialize the tracer with enhanced configuration options.
38
-
39
- Args:
40
- output_path (str): Directory where trace files will be saved
41
- trace_all (bool): Whether to trace all components or only specific ones
42
- save_interval (Optional[int]): Interval in seconds to auto-save traces
43
- log_level (int): Logging level for the tracer
44
- """
45
- super().__init__()
46
- self.output_path = output_path
47
- self.trace_all = trace_all
48
- self.save_interval = save_interval
49
- self._active = False
50
- self._original_inits = {}
51
- self._original_methods = {}
52
- self.additional_metadata = {}
53
- self._save_task = None
54
- self._current_query = None
55
- self.filepath = None
56
- self.model_names = {} # Store model names by component instance
57
- logger.setLevel(log_level)
58
-
59
- if not os.path.exists(output_path):
60
- os.makedirs(output_path)
61
-
62
- self.reset_trace()
63
-
64
-
65
- def __enter__(self):
66
- """Context manager entry"""
67
- self.start()
68
- return self
69
-
70
- def __exit__(self, exc_type, exc_val, exc_tb):
71
- """Context manager exit"""
72
-
73
- self.stop()
74
- if exc_type:
75
- logger.error(f"Error in context manager: {exc_val}")
76
- return False
77
- return True
78
-
79
- def reset_trace(self):
80
- """Reset the current trace to initial state with enhanced structure"""
81
- self.current_trace: Dict[str, Any] = {
82
- "start_time": None,
83
- "end_time": None,
84
- "actions": [],
85
- "llm_calls": [],
86
- "chain_starts": [],
87
- "chain_ends": [],
88
- "agent_actions": [],
89
- "chat_model_calls": [],
90
- "retriever_actions": [],
91
- "tokens": [],
92
- "errors": [],
93
- "query": self._current_query,
94
- "metadata": {
95
- "version": "2.0",
96
- "trace_all": self.trace_all,
97
- "save_interval": self.save_interval,
98
- },
99
- }
100
-
101
- async def _periodic_save(self):
102
- """Periodically save traces if save_interval is set"""
103
- while self._active and self.save_interval:
104
- await asyncio.sleep(self.save_interval)
105
- await self._async_save_trace()
106
-
107
- async def _async_save_trace(self, force: bool = False):
108
- """Asynchronously save the current trace to a JSON file"""
109
- if not self.current_trace["start_time"] and not force:
110
- return
111
-
112
- try:
113
- self.current_trace["end_time"] = datetime.now()
114
-
115
- # Use the query from the trace or fallback to a default
116
- safe_query = self._current_query or "unknown"
117
-
118
- # Sanitize the query for filename
119
- safe_query = ''.join(c for c in safe_query if c.isalnum() or c.isspace())[:50].strip()
120
-
121
- # Add a timestamp to ensure unique filenames
122
- timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
123
- filename = f"langchain_callback_traces.json"
124
- filepath = os.path.join(self.output_path, filename)
125
- self.filepath = filepath
126
-
127
- trace_to_save = self.current_trace.copy()
128
- trace_to_save["start_time"] = str(trace_to_save["start_time"])
129
- trace_to_save["end_time"] = str(trace_to_save["end_time"])
130
-
131
- # Save if there are meaningful events or if force is True
132
- if (
133
- len(trace_to_save["llm_calls"]) > 0
134
- or len(trace_to_save["chain_starts"]) > 0
135
- or len(trace_to_save["chain_ends"]) > 0
136
- or len(trace_to_save["errors"]) > 0
137
- or force
138
- ):
139
- async with asyncio.Lock():
140
- with open(filepath, "w", encoding="utf-8") as f:
141
- json.dump(trace_to_save, f, indent=2, default=str)
142
-
143
- logger.info(f"Trace saved to: {filepath}")
144
-
145
- # Reset the current query after saving
146
- self._current_query = None
147
-
148
- # Reset the trace
149
- self.reset_trace()
150
-
151
- except Exception as e:
152
- logger.error(f"Error saving trace: {e}")
153
- self.on_error(e, context="save_trace")
154
-
155
- def _save_trace(self, force: bool = False):
156
- """Synchronous version of trace saving"""
157
- if asyncio.get_event_loop().is_running():
158
- asyncio.create_task(self._async_save_trace(force))
159
- else:
160
- asyncio.run(self._async_save_trace(force))
161
-
162
- def _create_safe_wrapper(self, original_func, component_name, method_name):
163
- """Create a safely wrapped version of an original function with enhanced error handling"""
164
-
165
- @wraps(original_func)
166
- def wrapped(*args, **kwargs):
167
- if not self._active:
168
- return original_func(*args, **kwargs)
169
-
170
- try:
171
- # Deep copy kwargs to avoid modifying the original
172
- kwargs_copy = kwargs.copy() if kwargs is not None else {}
173
-
174
- # Handle different calling conventions
175
- if 'callbacks' not in kwargs_copy:
176
- kwargs_copy['callbacks'] = [self]
177
- elif self not in kwargs_copy['callbacks']:
178
- kwargs_copy['callbacks'].append(self)
179
-
180
- # Store model name if available
181
- if component_name in ["OpenAI", "ChatOpenAI_LangchainOpenAI", "ChatOpenAI_ChatModels",
182
- "ChatVertexAI", "VertexAI", "ChatGoogleGenerativeAI", "ChatAnthropic",
183
- "ChatLiteLLM", "ChatBedrock", "AzureChatOpenAI", "ChatAnthropicVertex"]:
184
- instance = args[0] if args else None
185
- model_name = kwargs.get('model_name') or kwargs.get('model') or kwargs.get('model_id')
186
-
187
- if instance and model_name:
188
- self.model_names[id(instance)] = model_name
189
-
190
- # Try different method signatures
191
- try:
192
- # First, try calling with modified kwargs
193
- return original_func(*args, **kwargs_copy)
194
- except TypeError:
195
- # If that fails, try without kwargs
196
- try:
197
- return original_func(*args)
198
- except Exception as e:
199
- # If all else fails, use original call
200
- logger.error(f"Failed to invoke {component_name} with modified callbacks: {e}")
201
- return original_func(*args, **kwargs)
202
-
203
- except Exception as e:
204
- # Log any errors that occur during the function call
205
- logger.error(f"Error in {component_name} wrapper: {e}")
206
-
207
- # Record the error using the tracer's error handling method
208
- self.on_error(e, context=f"wrapper_{component_name}")
209
-
210
- # Fallback to calling the original function without modifications
211
- return original_func(*args, **kwargs)
212
-
213
- @wraps(original_func)
214
- def wrapped_invoke(*args, **kwargs):
215
- if not self._active:
216
- return original_func(*args, **kwargs)
217
-
218
- try:
219
- # Deep copy kwargs to avoid modifying the original
220
- kwargs_copy = kwargs.copy() if kwargs is not None else {}
221
-
222
- # Handle different calling conventions
223
- if 'config' not in kwargs_copy:
224
- kwargs_copy['config'] = {'callbacks': [self]}
225
- elif 'callbacks' not in kwargs_copy['config']:
226
- kwargs_copy['config']['callbacks'] = [self]
227
- elif self not in kwargs_copy['config']['callbacks']:
228
- kwargs_copy['config']['callbacks'].append(self)
229
-
230
- # Store model name if available
231
- if component_name in ["OpenAI", "ChatOpenAI_LangchainOpenAI", "ChatOpenAI_ChatModels",
232
- "ChatVertexAI", "VertexAI", "ChatGoogleGenerativeAI", "ChatAnthropic",
233
- "ChatLiteLLM", "ChatBedrock", "AzureChatOpenAI", "ChatAnthropicVertex"]:
234
- instance = args[0] if args else None
235
- model_name = kwargs.get('model_name') or kwargs.get('model') or kwargs.get('model_id')
236
-
237
- if instance and model_name:
238
- self.model_names[id(instance)] = model_name
239
-
240
- # Try different method signatures
241
- try:
242
- # First, try calling with modified kwargs
243
- return original_func(*args, **kwargs_copy)
244
- except TypeError:
245
- # If that fails, try without kwargs
246
- try:
247
- return original_func(*args)
248
- except Exception as e:
249
- # If all else fails, use original call
250
- logger.error(f"Failed to invoke {component_name} with modified callbacks: {e}")
251
- return original_func(*args, **kwargs)
252
-
253
- except Exception as e:
254
- # Log any errors that occur during the function call
255
- logger.error(f"Error in {component_name} wrapper: {e}")
256
-
257
- # Record the error using the tracer's error handling method
258
- self.on_error(e, context=f"wrapper_{component_name}")
259
-
260
- # Fallback to calling the original function without modifications
261
- return original_func(*args, **kwargs)
262
-
263
- if method_name == 'invoke':
264
- return wrapped_invoke
265
- return wrapped
266
-
267
-
268
- def _monkey_patch(self):
269
- """Enhanced monkey-patching with comprehensive component support"""
270
- components_to_patch = {}
271
-
272
- try:
273
- from langchain.llms import OpenAI
274
- components_to_patch["OpenAI"] = (OpenAI, "__init__")
275
- except ImportError:
276
- logger.debug("OpenAI not available for patching")
277
-
278
- try:
279
- from langchain_aws import ChatBedrock
280
- components_to_patch["ChatBedrock"] = (ChatBedrock, "__init__")
281
- except ImportError:
282
- logger.debug("ChatBedrock not available for patching")
283
-
284
- try:
285
- from langchain_google_vertexai import ChatVertexAI
286
- components_to_patch["ChatVertexAI"] = (ChatVertexAI, "__init__")
287
- except ImportError:
288
- logger.debug("ChatVertexAI not available for patching")
289
-
290
- try:
291
- from langchain_google_vertexai import VertexAI
292
- components_to_patch["VertexAI"] = (VertexAI, "__init__")
293
- except ImportError:
294
- logger.debug("VertexAI not available for patching")
295
-
296
- try:
297
- from langchain_google_vertexai.model_garden import ChatAnthropicVertex
298
- components_to_patch["ChatAnthropicVertex"] = (ChatAnthropicVertex, "__init__")
299
- except ImportError:
300
- logger.debug("ChatAnthropicVertex not available for patching")
301
-
302
- try:
303
- from langchain_google_genai import ChatGoogleGenerativeAI
304
- components_to_patch["ChatGoogleGenerativeAI"] = (ChatGoogleGenerativeAI, "__init__")
305
- except ImportError:
306
- logger.debug("ChatGoogleGenerativeAI not available for patching")
307
-
308
- try:
309
- from langchain_anthropic import ChatAnthropic
310
- components_to_patch["ChatAnthropic"] = (ChatAnthropic, "__init__")
311
- except ImportError:
312
- logger.debug("ChatAnthropic not available for patching")
313
-
314
- try:
315
- from langchain_community.chat_models import ChatLiteLLM
316
- components_to_patch["ChatLiteLLM"] = (ChatLiteLLM, "__init__")
317
- except ImportError:
318
- logger.debug("ChatLiteLLM not available for patching")
319
-
320
- try:
321
- from langchain_openai import ChatOpenAI as ChatOpenAI_LangchainOpenAI
322
- components_to_patch["ChatOpenAI_LangchainOpenAI"] = (ChatOpenAI_LangchainOpenAI, "__init__")
323
- except ImportError:
324
- logger.debug("ChatOpenAI (from langchain_openai) not available for patching")
325
-
326
- try:
327
- from langchain_openai import AzureChatOpenAI
328
- components_to_patch["AzureChatOpenAI"] = (AzureChatOpenAI, "__init__")
329
- except ImportError:
330
- logger.debug("AzureChatOpenAI (from langchain_openai) not available for patching")
331
-
332
- try:
333
- from langchain.chat_models import ChatOpenAI as ChatOpenAI_ChatModels
334
- components_to_patch["ChatOpenAI_ChatModels"] = (ChatOpenAI_ChatModels, "__init__")
335
- except ImportError:
336
- logger.debug("ChatOpenAI (from langchain.chat_models) not available for patching")
337
-
338
- try:
339
- from langchain.chains import create_retrieval_chain, RetrievalQA
340
- from langchain_core.runnables import RunnableBinding
341
- from langchain_core.runnables import RunnableSequence
342
- from langchain.chains import ConversationalRetrievalChain
343
- components_to_patch["RetrievalQA"] = (RetrievalQA, "from_chain_type")
344
- components_to_patch["create_retrieval_chain"] = (create_retrieval_chain, None)
345
- components_to_patch['RetrievalQA.invoke'] = (RetrievalQA, 'invoke')
346
- components_to_patch["RunnableBinding"] = (RunnableBinding, "invoke")
347
- components_to_patch["RunnableSequence"] = (RunnableSequence, "invoke")
348
- components_to_patch["ConversationalRetrievalChain"] = (ConversationalRetrievalChain, "invoke")
349
- except ImportError:
350
- logger.debug("Langchain chains not available for patching")
351
-
352
- for name, (component, method_name) in components_to_patch.items():
353
- try:
354
- if method_name == "__init__":
355
- original = component.__init__
356
- self._original_inits[name] = original
357
- component.__init__ = self._create_safe_wrapper(original, name, method_name)
358
- elif method_name:
359
- original = getattr(component, method_name)
360
- self._original_methods[name] = original
361
- if isinstance(original, classmethod):
362
- wrapped = classmethod(
363
- self._create_safe_wrapper(original.__func__, name, method_name)
364
- )
365
- else:
366
- wrapped = self._create_safe_wrapper(original, name, method_name)
367
- setattr(component, method_name, wrapped)
368
- else:
369
- self._original_methods[name] = component
370
- globals()[name] = self._create_safe_wrapper(component, name, method_name)
371
- except Exception as e:
372
- logger.error(f"Error patching {name}: {e}")
373
- self.on_error(e, context=f"patch_{name}")
374
-
375
- def _restore_original_methods(self):
376
- """Restore all original methods and functions with enhanced error handling"""
377
- # Dynamically import only what we need based on what was patched
378
- imported_components = {}
379
-
380
- if self._original_inits or self._original_methods:
381
- for name in list(self._original_inits.keys()) + list(self._original_methods.keys()):
382
- try:
383
- if name == "OpenAI":
384
- from langchain.llms import OpenAI
385
- imported_components[name] = OpenAI
386
- elif name == "ChatVertexAI":
387
- from langchain_google_vertexai import ChatVertexAI
388
- imported_components[name] = ChatVertexAI
389
- elif name == "VertexAI":
390
- from langchain_google_vertexai import VertexAI
391
- imported_components[name] = VertexAI
392
- elif name == "ChatGoogleGenerativeAI":
393
- from langchain_google_genai import ChatGoogleGenerativeAI
394
- imported_components[name] = ChatGoogleGenerativeAI
395
- elif name == "ChatAnthropic":
396
- from langchain_anthropic import ChatAnthropic
397
- imported_components[name] = ChatAnthropic
398
- elif name == "ChatBedrock":
399
- from langchain_aws import ChatBedrock
400
- imported_components[name] = ChatBedrock
401
- elif name == "AzureChatOpenAI":
402
- from langchain_openai import AzureChatOpenAI
403
- imported_components[name] = AzureChatOpenAI
404
- elif name == "ChatAnthropicVertex":
405
- from langchain_google_vertexai.model_garden import ChatAnthropicVertex
406
- imported_components[name] = ChatAnthropicVertex
407
- elif name == "ChatLiteLLM":
408
- from langchain_community.chat_models import ChatLiteLLM
409
- imported_components[name] = ChatLiteLLM
410
- elif name == "ChatOpenAI_LangchainOpenAI":
411
- from langchain_openai import ChatOpenAI as ChatOpenAI_LangchainOpenAI
412
- imported_components[name] = ChatOpenAI_LangchainOpenAI
413
- elif name == "ChatOpenAI_ChatModels":
414
- from langchain.chat_models import ChatOpenAI as ChatOpenAI_ChatModels
415
- imported_components[name] = ChatOpenAI_ChatModels
416
- elif name in ["RetrievalQA", "create_retrieval_chain", 'RetrievalQA.invoke', "RunnableBinding", "RunnableSequence","ConversationalRetrievalChain"]:
417
- from langchain.chains import create_retrieval_chain, RetrievalQA
418
- from langchain_core.runnables import RunnableBinding
419
- from langchain_core.runnables import RunnableSequence
420
- from langchain.chains import ConversationalRetrievalChain
421
- imported_components["RetrievalQA"] = RetrievalQA
422
- imported_components["create_retrieval_chain"] = create_retrieval_chain
423
- imported_components["RunnableBinding"] = RunnableBinding
424
- imported_components["RunnableSequence"] = RunnableSequence
425
- imported_components["ConversationalRetrievalChain"] = ConversationalRetrievalChain
426
- except ImportError:
427
- logger.debug(f"{name} not available for restoration")
428
-
429
- for name, original in self._original_inits.items():
430
- try:
431
- if name in imported_components:
432
- component = imported_components[name]
433
- component.__init__ = original
434
- except Exception as e:
435
- logger.error(f"Error restoring {name}: {e}")
436
- self.on_error(e, context=f"restore_{name}")
437
-
438
- # Restore original methods and functions
439
- for name, original in self._original_methods.items():
440
- try:
441
- if "." in name:
442
- module_name, method_name = name.rsplit(".", 1)
443
- if module_name in imported_components:
444
- module = imported_components[module_name]
445
- setattr(module, method_name, original)
446
- else:
447
- if name in imported_components:
448
- globals()[name] = original
449
- except Exception as e:
450
- logger.error(f"Error restoring {name}: {e}")
451
- self.on_error(e, context=f"restore_{name}")
452
-
453
- def start(self):
454
- """Start tracing with enhanced error handling and async support"""
455
- try:
456
- self.reset_trace()
457
- self.current_trace["start_time"] = datetime.now()
458
- self._active = True
459
- self._monkey_patch()
460
-
461
- if self.save_interval:
462
- loop = asyncio.get_event_loop()
463
- self._save_task = loop.create_task(self._periodic_save())
464
-
465
- logger.info("Tracing started")
466
- except Exception as e:
467
- logger.error(f"Error starting tracer: {e}")
468
- self.on_error(e, context="start")
469
- raise
470
-
471
- def stop(self):
472
- """Stop tracing with enhanced cleanup"""
473
- try:
474
- self._active = False
475
- if self._save_task:
476
- self._save_task.cancel()
477
- self._restore_original_methods()
478
- # self._save_trace(force=True)
479
-
480
- return self.current_trace.copy(), self.additional_metadata
481
-
482
- logger.info("Tracing stopped")
483
- except Exception as e:
484
- logger.error(f"Error stopping tracer: {e}")
485
- self.on_error(e, context="stop")
486
- raise
487
- finally:
488
- self._original_inits.clear()
489
- self._original_methods.clear()
490
-
491
- def force_save(self):
492
- """Force save the current trace"""
493
- self._save_trace(force=True)
494
-
495
- # Callback methods with enhanced error handling and logging
496
- def on_llm_start(
497
- self,
498
- serialized: Dict[str, Any],
499
- prompts: List[str],
500
- run_id: UUID,
501
- **kwargs: Any,
502
- ) -> None:
503
- try:
504
- if not self.current_trace["start_time"]:
505
- self.current_trace["start_time"] = datetime.now()
506
-
507
- self.current_trace["llm_calls"].append(
508
- {
509
- "timestamp": datetime.now(),
510
- "event": "llm_start",
511
- "serialized": serialized,
512
- "prompts": prompts,
513
- "run_id": str(run_id),
514
- "additional_kwargs": kwargs,
515
- }
516
- )
517
- except Exception as e:
518
- self.on_error(e, context="llm_start")
519
-
520
- def on_llm_end(self, response: LLMResult, *, run_id: UUID, **kwargs: Any) -> None:
521
- try:
522
- self.current_trace["llm_calls"].append(
523
- {
524
- "timestamp": datetime.now(),
525
- "event": "llm_end",
526
- "response": response.dict(),
527
- "run_id": str(run_id),
528
- "additional_kwargs": kwargs,
529
- }
530
- )
531
-
532
- # Calculate latency
533
- end_time = datetime.now()
534
- latency = (end_time - self.current_trace["start_time"]).total_seconds()
535
-
536
- # Check if values are there in llm_output
537
- model = ""
538
- prompt_tokens = 0
539
- completion_tokens = 0
540
- total_tokens = 0
541
-
542
- # Try to get model name from llm_output first
543
- if response and response.llm_output:
544
- try:
545
- model = response.llm_output.get("model_name")
546
- if not model:
547
- model = response.llm_output.get("model", "")
548
- except Exception as e:
549
- # logger.debug(f"Error getting model name: {e}")
550
- model = ""
551
-
552
- # Add model name
553
- if not model:
554
- try:
555
- model = response.llm_output.get("model_name")
556
- if not model:
557
- model = response.llm_output.get("model", "")
558
- except Exception as e:
559
- # logger.debug(f"Error getting model name: {e}")
560
- model = ""
561
-
562
-
563
- # Add token usage
564
- try:
565
- token_usage = response.llm_output.get("token_usage", {})
566
- if token_usage=={}:
567
- try:
568
- token_usage = response.llm_output.get("usage")
569
- except Exception as e:
570
- # logger.debug(f"Error getting token usage: {e}")
571
- token_usage = {}
572
-
573
- if token_usage !={}:
574
- prompt_tokens = token_usage.get("prompt_tokens", 0)
575
- if prompt_tokens==0:
576
- prompt_tokens = token_usage.get("input_tokens", 0)
577
- completion_tokens = token_usage.get("completion_tokens", 0)
578
- if completion_tokens==0:
579
- completion_tokens = token_usage.get("output_tokens", 0)
580
-
581
- total_tokens = prompt_tokens + completion_tokens
582
- except Exception as e:
583
- # logger.debug(f"Error getting token usage: {e}")
584
- prompt_tokens = 0
585
- completion_tokens = 0
586
- total_tokens = 0
587
-
588
- # Check if values are there in
589
- if prompt_tokens == 0 and completion_tokens == 0:
590
- try:
591
- usage_data = response.generations[0][0].message.usage_metadata
592
- prompt_tokens = usage_data.get("input_tokens", 0)
593
- completion_tokens = usage_data.get("output_tokens", 0)
594
- total_tokens = prompt_tokens + completion_tokens
595
- except Exception as e:
596
- # logger.debug(f"Error getting usage data: {e}")
597
- try:
598
- usage_data = response.generations[0][0].generation_info['usage_metadata']
599
- prompt_tokens = usage_data.get("prompt_token_count", 0)
600
- completion_tokens = usage_data.get("candidates_token_count", 0)
601
- total_tokens = prompt_tokens + completion_tokens
602
- except Exception as e:
603
- # logger.debug(f"Error getting token usage: {e}")
604
- prompt_tokens = 0
605
- completion_tokens = 0
606
- total_tokens = 0
607
-
608
- # If no model name in llm_output, try to get it from stored model names
609
- try:
610
- if model == "":
611
- model = list(self.model_names.values())[0]
612
- except Exception as e:
613
- model=""
614
-
615
- self.additional_metadata = {
616
- 'latency': latency,
617
- 'model_name': model,
618
- 'tokens': {
619
- 'prompt': prompt_tokens,
620
- 'completion': completion_tokens,
621
- 'total': total_tokens
622
- }
623
- }
624
-
625
- except Exception as e:
626
- self.on_error(e, context="llm_end")
627
-
628
- def on_chat_model_start(
629
- self,
630
- serialized: Dict[str, Any],
631
- messages: List[List[BaseMessage]],
632
- *,
633
- run_id: UUID,
634
- **kwargs: Any,
635
- ) -> None:
636
- try:
637
- messages_dict = [
638
- [
639
- {
640
- "type": msg.type,
641
- "content": msg.content,
642
- "additional_kwargs": msg.additional_kwargs,
643
- }
644
- for msg in batch
645
- ]
646
- for batch in messages
647
- ]
648
-
649
- self.current_trace["chat_model_calls"].append(
650
- {
651
- "timestamp": datetime.now(),
652
- "event": "chat_model_start",
653
- "serialized": serialized,
654
- "messages": messages_dict,
655
- "run_id": str(run_id),
656
- "additional_kwargs": kwargs,
657
- }
658
- )
659
- except Exception as e:
660
- self.on_error(e, context="chat_model_start")
661
-
662
- def on_chain_start(
663
- self,
664
- serialized: Dict[str, Any],
665
- inputs: Dict[str, Any],
666
- *,
667
- run_id: UUID,
668
- **kwargs: Any,
669
- ) -> None:
670
- try:
671
- context = ""
672
- query = ""
673
- if isinstance(inputs, dict):
674
- if "context" in inputs:
675
- if isinstance(inputs["context"], Document):
676
- context = inputs["context"].page_content
677
- elif isinstance(inputs["context"], list):
678
- context = "\n".join(
679
- doc.page_content if isinstance(doc, Document) else str(doc)
680
- for doc in inputs["context"]
681
- )
682
- elif isinstance(inputs["context"], str):
683
- context = inputs["context"]
684
-
685
- query = inputs.get("question", inputs.get("input", ""))
686
-
687
- # Set the current query
688
- self._current_query = query
689
-
690
- chain_event = {
691
- "timestamp": datetime.now(),
692
- "serialized": serialized,
693
- "context": context,
694
- "query": inputs.get("question", inputs.get("input", "")),
695
- "run_id": str(run_id),
696
- "additional_kwargs": kwargs,
697
- }
698
-
699
- self.current_trace["chain_starts"].append(chain_event)
700
- except Exception as e:
701
- self.on_error(e, context="chain_start")
702
-
703
- def on_chain_end(
704
- self, outputs: Dict[str, Any], *, run_id: UUID, **kwargs: Any
705
- ) -> None:
706
- try:
707
- self.current_trace["chain_ends"].append(
708
- {
709
- "timestamp": datetime.now(),
710
- "outputs": outputs,
711
- "run_id": str(run_id),
712
- "additional_kwargs": kwargs,
713
- }
714
- )
715
- except Exception as e:
716
- self.on_error(e, context="chain_end")
717
-
718
- def on_agent_action(self, action: AgentAction, run_id: UUID, **kwargs: Any) -> None:
719
- try:
720
- self.current_trace["agent_actions"].append(
721
- {
722
- "timestamp": datetime.now(),
723
- "action": action.dict(),
724
- "run_id": str(run_id),
725
- "additional_kwargs": kwargs,
726
- }
727
- )
728
- except Exception as e:
729
- self.on_error(e, context="agent_action")
730
-
731
- def on_agent_finish(self, finish: AgentFinish, run_id: UUID, **kwargs: Any) -> None:
732
- try:
733
- self.current_trace["agent_actions"].append(
734
- {
735
- "timestamp": datetime.now(),
736
- "event": "agent_finish",
737
- "finish": finish.dict(),
738
- "run_id": str(run_id),
739
- "additional_kwargs": kwargs,
740
- }
741
- )
742
- except Exception as e:
743
- self.on_error(e, context="agent_finish")
744
-
745
- def on_retriever_start(
746
- self, serialized: Dict[str, Any], query: str, *, run_id: UUID, **kwargs: Any
747
- ) -> None:
748
- try:
749
- retriever_event = {
750
- "timestamp": datetime.now(),
751
- "event": "retriever_start",
752
- "serialized": serialized,
753
- "query": query,
754
- "run_id": str(run_id),
755
- "additional_kwargs": kwargs,
756
- }
757
-
758
- self.current_trace["retriever_actions"].append(retriever_event)
759
- except Exception as e:
760
- self.on_error(e, context="retriever_start")
761
-
762
- def on_retriever_end(
763
- self, documents: Sequence[Document], *, run_id: UUID, **kwargs: Any
764
- ) -> None:
765
- try:
766
- processed_documents = [
767
- {"page_content": doc.page_content, "metadata": doc.metadata}
768
- for doc in documents
769
- ]
770
-
771
- retriever_event = {
772
- "timestamp": datetime.now(),
773
- "event": "retriever_end",
774
- "documents": processed_documents,
775
- "run_id": str(run_id),
776
- "additional_kwargs": kwargs,
777
- }
778
-
779
- self.current_trace["retriever_actions"].append(retriever_event)
780
- except Exception as e:
781
- self.on_error(e, context="retriever_end")
782
-
783
- def on_llm_new_token(self, token: str, **kwargs: Any) -> None:
784
- try:
785
- self.current_trace["tokens"].append(
786
- {
787
- "timestamp": datetime.now(),
788
- "event": "new_token",
789
- "token": token,
790
- "additional_kwargs": kwargs,
791
- }
792
- )
793
- except Exception as e:
794
- self.on_error(e, context="llm_new_token")
795
-
796
- def on_error(self, error: Exception, context: str = "", **kwargs: Any) -> None:
797
- """Enhanced error handling with context"""
798
- try:
799
- error_event = {
800
- "timestamp": datetime.now(),
801
- "error": str(error),
802
- "error_type": type(error).__name__,
803
- "context": context,
804
- "additional_kwargs": kwargs,
805
- }
806
- self.current_trace["errors"].append(error_event)
807
- logger.error(f"Error in {context}: {error}")
808
- except Exception as e:
809
- logger.critical(f"Error in error handler: {e}")
810
-
811
- def on_chain_error(self, error: Exception, **kwargs: Any) -> None:
812
- self.on_error(error, context="chain", **kwargs)
813
-
814
- def on_llm_error(self, error: Exception, **kwargs: Any) -> None:
815
- self.on_error(error, context="llm", **kwargs)
816
-
817
- def on_tool_error(self, error: Exception, **kwargs: Any) -> None:
818
- self.on_error(error, context="tool", **kwargs)
819
-
820
- def on_retriever_error(self, error: Exception, **kwargs: Any) -> None:
821
- self.on_error(error, context="retriever", **kwargs)