monocle-apptrace 0.0.1__py3-none-any.whl → 0.1.1__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 monocle-apptrace might be problematic. Click here for more details.

Files changed (31) hide show
  1. monocle_apptrace/README.md +52 -28
  2. monocle_apptrace/__init__.py +0 -2
  3. monocle_apptrace/constants.py +22 -0
  4. monocle_apptrace/exporters/file_exporter.py +63 -0
  5. monocle_apptrace/haystack/__init__.py +5 -24
  6. monocle_apptrace/haystack/wrap_node.py +1 -1
  7. monocle_apptrace/haystack/wrap_openai.py +1 -9
  8. monocle_apptrace/haystack/wrap_pipeline.py +22 -9
  9. monocle_apptrace/instrumentor.py +29 -32
  10. monocle_apptrace/langchain/__init__.py +5 -94
  11. monocle_apptrace/llamaindex/__init__.py +7 -63
  12. monocle_apptrace/metamodel/README.md +47 -0
  13. monocle_apptrace/metamodel/entities/README.md +54 -0
  14. monocle_apptrace/metamodel/entities/entity_types.json +157 -0
  15. monocle_apptrace/metamodel/entities/entity_types.py +51 -0
  16. monocle_apptrace/metamodel/maps/haystack_methods.json +25 -0
  17. monocle_apptrace/metamodel/maps/lang_chain_methods.json +106 -0
  18. monocle_apptrace/metamodel/maps/llama_index_methods.json +70 -0
  19. monocle_apptrace/metamodel/spans/README.md +121 -0
  20. monocle_apptrace/metamodel/spans/span_example.json +140 -0
  21. monocle_apptrace/metamodel/spans/span_format.json +55 -0
  22. monocle_apptrace/utils.py +56 -16
  23. monocle_apptrace/wrap_common.py +143 -46
  24. monocle_apptrace/wrapper.py +3 -3
  25. monocle_apptrace-0.1.1.dist-info/METADATA +111 -0
  26. monocle_apptrace-0.1.1.dist-info/RECORD +29 -0
  27. monocle_apptrace-0.0.1.dist-info/METADATA +0 -76
  28. monocle_apptrace-0.0.1.dist-info/RECORD +0 -17
  29. {monocle_apptrace-0.0.1.dist-info → monocle_apptrace-0.1.1.dist-info}/WHEEL +0 -0
  30. {monocle_apptrace-0.0.1.dist-info → monocle_apptrace-0.1.1.dist-info}/licenses/LICENSE +0 -0
  31. {monocle_apptrace-0.0.1.dist-info → monocle_apptrace-0.1.1.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,140 @@
1
+ {
2
+ "name": "llamaindex.retrieve",
3
+ "context": {
4
+ "trace_id": "0x93cd0bf865b3ffcc3cf9c075dc3e3797",
5
+ "span_id": "0x5d3f839e900bda24",
6
+ "trace_state": "[]"
7
+ },
8
+ "kind": "SpanKind.CLIENT",
9
+ "parent_id": "0x7a63d63e42ccac60",
10
+ "start_time": "2024-09-09T14:38:45.237182Z",
11
+ "end_time": "2024-09-09T14:38:45.620112Z",
12
+ "status": {
13
+ "status_code": "OK"
14
+ },
15
+ "attributes": {
16
+ "span.type": "Retrieval",
17
+ "entity.count": 2,
18
+ "entity.1.name": "ChromaVectorStore",
19
+ "entity.1.type": "vectorstore.chroma",
20
+ "entity.1.embedding-model-name": "BAAI/bge-small-en-v1.5",
21
+ "entity.2.name": "BAAI/bge-small-en-v1.5",
22
+ "entity.2.type": "model.embedding",
23
+ "entity.2.model_name": "BAAI/bge-small-en-v1.5"
24
+ },
25
+ "events": [
26
+ {
27
+ "name": "data.input",
28
+ "timestamp": "timestamp",
29
+ "attributes": {
30
+ "context_input": "question: What is an americano?"
31
+ }
32
+ },
33
+ {
34
+ "name": "data.output",
35
+ "timestamp": "timestamp",
36
+ "attributes": {
37
+ "context_output": "Coffee is a hot drink made from the roasted and ground seeds (coffee beans) of a tropical shrub\nA latte consists of one or more shots of espresso, served in a glass (or sometimes a cup), into which hot steamed milk is added\nAmericano is a type of coffee drink prepared by diluting an espresso shot with hot water at a 1:3 to 1:4 ratio, resulting in a drink that retains the complex flavors of espresso, but in a lighter way"
38
+ }
39
+ }
40
+
41
+ ],
42
+ "links": [],
43
+ "resource": {
44
+ "attributes": {
45
+ "service.name": "coffee-bot"
46
+ },
47
+ "schema_url": ""
48
+ }
49
+ },
50
+ {
51
+ "name": "llamaindex.openai",
52
+ "context": {
53
+ "trace_id": "0x93cd0bf865b3ffcc3cf9c075dc3e3797",
54
+ "span_id": "0x8b6363e1937a4d7b",
55
+ "trace_state": "[]"
56
+ },
57
+ "kind": "SpanKind.CLIENT",
58
+ "parent_id": "0x7a63d63e42ccac60",
59
+ "start_time": "2024-09-09T14:38:45.622174Z",
60
+ "end_time": "2024-09-09T14:38:46.514120Z",
61
+ "status": {
62
+ "status_code": "OK"
63
+ },
64
+ "attributes": {
65
+ "span.type": "inference",
66
+ "entity.count": 2,
67
+ "entity.1.name": "AzureOpenAI",
68
+ "entity.1.type": "inference.azure_oai",
69
+ "entity.1.provider_name": "openai.azure.com",
70
+ "entity.1.deployment": "kshitiz-gpt",
71
+ "entity.1.inference_endpoint": "https://okahu-openai-dev.openai.azure.com/",
72
+
73
+ "entity.2.name": "gpt-35-turbo",
74
+ "entity.2.type": "model.llm",
75
+ "entity.2.model_name": "gpt-35-turbo"
76
+ },
77
+ "events": [
78
+ {
79
+ "name": "data.input",
80
+ "timestamp": "timestamp",
81
+ "attributes": {
82
+ "question": "What is an americano?",
83
+ }
84
+ },
85
+ {
86
+ "name": "data.output",
87
+ "timestamp": "timestamp",
88
+ "attributes": {
89
+ "response": "An americano is a type of coffee drink that is made by diluting an espresso shot with hot water at a 1:3 to 1:4 ratio, resulting in a drink that retains the complex flavors of espresso, but in a lighter way.",
90
+ }
91
+ },
92
+ {
93
+ "name": "metadata",
94
+ "timestamp": "timestamp",
95
+ "attributes": {
96
+ "temperature": 0.1,
97
+ "completion_tokens": 52,
98
+ "prompt_tokens": 233,
99
+ "total_tokens": 285
100
+ }
101
+ }
102
+ ],
103
+ "links": [],
104
+ "resource": {
105
+ "attributes": {
106
+ "service.name": "coffee-bot"
107
+ },
108
+ "schema_url": ""
109
+ }
110
+ }
111
+ {
112
+ "name": "llamaindex.query",
113
+ "context": {
114
+ "trace_id": "0x93cd0bf865b3ffcc3cf9c075dc3e3797",
115
+ "span_id": "0x7a63d63e42ccac60",
116
+ "trace_state": "[]"
117
+ },
118
+ "kind": "SpanKind.CLIENT",
119
+ "parent_id": null,
120
+ "start_time": "2024-09-09T14:38:45.236627Z",
121
+ "end_time": "2024-09-09T14:38:46.514442Z",
122
+ "status": {
123
+ "status_code": "OK"
124
+ },
125
+ "attributes": {
126
+ "span.type": "workflow",
127
+ "entity.count": 1,
128
+ "entity.1.name": "coffee-bot",
129
+ "entity.1.type": "workflow.llama_index"
130
+ },
131
+ "events": [
132
+ ],
133
+ "links": [],
134
+ "resource": {
135
+ "attributes": {
136
+ "service.name": "coffee-bot"
137
+ },
138
+ "schema_url": ""
139
+ }
140
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "span-name",
3
+ "context": {
4
+ "trace_id": "trace-id",
5
+ "span_id": "span-id",
6
+ "trace_state": "[]"
7
+ },
8
+ "kind": "SpanKind.CLIENT",
9
+ "parent_id": "parent-id or None (for root span)",
10
+ "start_time": "UTC timestamp",
11
+ "end_time": "UTC timestamp",
12
+ "status": {
13
+ "status_code": "OK or Error"
14
+ },
15
+ "attributes": {
16
+ "description": "List of AI component entities used in this span, eg Model, Inference hosting service. Needs to be one of the supported entity types.",
17
+
18
+ "span.type": "Monocle-span-type",
19
+ "entity.count": "count-of-entities",
20
+
21
+ "entity.<index>.name": "Monocle-Entity-name",
22
+ "entity.<index>.type": "Monocle-Entity-Type",
23
+ "entity.<index>.<attribute>": "Value"
24
+ },
25
+ "events" : [
26
+ {
27
+ "name": "data.input",
28
+ "timestamp": "UTC timestamp",
29
+ "attributes": {
30
+ "input_attribute": "value"
31
+ }
32
+ },
33
+ {
34
+ "name": "data.output",
35
+ "timestamp": "UTC timestamp",
36
+ "attributes": {
37
+ "output_attribute": "value"
38
+ }
39
+ },
40
+ {
41
+ "name": "metadata",
42
+ "timestamp": "UTC timestamp",
43
+ "attributes": {
44
+ "metadata_attribute": "value"
45
+ }
46
+ }
47
+ ],
48
+ "links": [],
49
+ "resource": {
50
+ "attributes": {
51
+ "service.name": "top-workflow-name"
52
+ },
53
+ "schema_url": ""
54
+ }
55
+ }
monocle_apptrace/utils.py CHANGED
@@ -1,17 +1,16 @@
1
-
2
-
3
1
  import logging
4
- logger = logging.getLogger(__name__)
2
+ import json
3
+ from importlib import import_module
4
+ import os
5
+ from opentelemetry.trace import Span
6
+ from monocle_apptrace.constants import azure_service_map, aws_service_map
5
7
 
6
- class Config:
7
- exception_logger = None
8
+ embedding_model_context = {}
8
9
 
9
10
  def set_span_attribute(span, name, value):
10
11
  if value is not None:
11
12
  if value != "":
12
13
  span.set_attribute(name, value)
13
- return
14
-
15
14
 
16
15
  def dont_throw(func):
17
16
  """
@@ -22,15 +21,12 @@ def dont_throw(func):
22
21
  """
23
22
  # Obtain a logger specific to the function's module
24
23
  logger = logging.getLogger(func.__module__)
25
-
24
+ # pylint: disable=inconsistent-return-statements
26
25
  def wrapper(*args, **kwargs):
27
26
  try:
28
27
  return func(*args, **kwargs)
29
- except Exception as e:
30
- logger.warning("Failed to execute %s, error: %s", func.__name__, str(e))
31
- if Config.exception_logger:
32
- Config.exception_logger(e)
33
-
28
+ except Exception as ex:
29
+ logger.warning("Failed to execute %s, error: %s", func.__name__, str(ex))
34
30
  return wrapper
35
31
 
36
32
  def with_tracer_wrapper(func):
@@ -44,10 +40,54 @@ def with_tracer_wrapper(func):
44
40
 
45
41
  return _with_tracer
46
42
 
47
- def resolve_from_alias(map, alias):
43
+ def resolve_from_alias(my_map, alias):
48
44
  """Find a alias that is not none from list of aliases"""
49
45
 
50
46
  for i in alias:
51
- if i in map.keys():
52
- return map[i]
47
+ if i in my_map.keys():
48
+ return my_map[i]
53
49
  return None
50
+
51
+ def load_wrapper_from_config(config_file_path: str, module_name: str = None):
52
+ wrapper_methods = []
53
+ with open(config_file_path, encoding='UTF-8') as config_file:
54
+ json_data = json.load(config_file)
55
+ wrapper_methods = json_data["wrapper_methods"]
56
+ for wrapper_method in wrapper_methods:
57
+ wrapper_method["wrapper"] = get_wrapper_method(
58
+ wrapper_method["wrapper_package"], wrapper_method["wrapper_method"])
59
+ if "span_name_getter_method" in wrapper_method :
60
+ wrapper_method["span_name_getter"] = get_wrapper_method(
61
+ wrapper_method["span_name_getter_package"],
62
+ wrapper_method["span_name_getter_method"])
63
+ return wrapper_methods
64
+
65
+ def get_wrapper_method(package_name: str, method_name: str):
66
+ wrapper_module = import_module("monocle_apptrace." + package_name)
67
+ return getattr(wrapper_module, method_name)
68
+
69
+ def update_span_with_infra_name(span: Span, span_key: str):
70
+ for key,val in azure_service_map.items():
71
+ if key in os.environ:
72
+ span.set_attribute(span_key, val)
73
+ for key,val in aws_service_map.items():
74
+ if key in os.environ:
75
+ span.set_attribute(span_key, val)
76
+
77
+
78
+ def set_embedding_model(model_name: str):
79
+ """
80
+ Sets the embedding model in the global context.
81
+
82
+ @param model_name: The name of the embedding model to set
83
+ """
84
+ embedding_model_context['embedding_model'] = model_name
85
+
86
+
87
+ def get_embedding_model() -> str:
88
+ """
89
+ Retrieves the embedding model from the global context.
90
+
91
+ @return: The name of the embedding model, or 'unknown' if not set
92
+ """
93
+ return embedding_model_context.get('embedding_model', 'unknown')
@@ -1,9 +1,10 @@
1
+ #pylint: disable=protected-access
1
2
  import logging
2
3
  import os
4
+ from urllib.parse import urlparse
3
5
 
4
6
  from opentelemetry.trace import Span, Tracer
5
-
6
- from monocle_apptrace.utils import resolve_from_alias, with_tracer_wrapper
7
+ from monocle_apptrace.utils import resolve_from_alias, update_span_with_infra_name, with_tracer_wrapper, get_embedding_model
7
8
 
8
9
  logger = logging.getLogger(__name__)
9
10
  WORKFLOW_TYPE_KEY = "workflow_type"
@@ -14,7 +15,12 @@ PROMPT_OUTPUT_KEY = "output"
14
15
  QUERY = "question"
15
16
  RESPONSE = "response"
16
17
  TAGS = "tags"
17
- CONTEXT_PROPERTIES_KEY = "workflow_context_properties"
18
+ SESSION_PROPERTIES_KEY = "session"
19
+ INFRA_SERVICE_KEY = "infra_service_name"
20
+ TYPE = "type"
21
+ PROVIDER = "provider_name"
22
+ EMBEDDING_MODEL = "embedding_model"
23
+ VECTOR_STORE = 'vector_store'
18
24
 
19
25
 
20
26
  WORKFLOW_TYPE_MAP = {
@@ -23,6 +29,24 @@ WORKFLOW_TYPE_MAP = {
23
29
  "haystack": "workflow.haystack"
24
30
  }
25
31
 
32
+ framework_vector_store_mapping = {
33
+ 'langchain_core.retrievers': lambda instance: {
34
+ 'provider': instance.tags[0],
35
+ 'embedding_model': instance.tags[1],
36
+ 'type': VECTOR_STORE,
37
+ },
38
+ 'llama_index.core.indices.base_retriever': lambda instance: {
39
+ 'provider': type(instance._vector_store).__name__,
40
+ 'embedding_model': instance._embed_model.model_name,
41
+ 'type': VECTOR_STORE,
42
+ },
43
+ 'haystack.components.retrievers': lambda instance: {
44
+ 'provider': instance.__dict__.get("document_store").__class__.__name__,
45
+ 'embedding_model': get_embedding_model(),
46
+ 'type': VECTOR_STORE,
47
+ },
48
+ }
49
+
26
50
  @with_tracer_wrapper
27
51
  def task_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
28
52
  """Instruments and calls every function defined in TO_WRAP."""
@@ -37,29 +61,39 @@ def task_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
37
61
  name = to_wrap.get("span_name")
38
62
  else:
39
63
  name = f"langchain.task.{instance.__class__.__name__}"
40
- kind = to_wrap.get("kind")
41
64
 
42
65
  with tracer.start_as_current_span(name) as span:
43
- if is_root_span(span):
44
- update_span_with_prompt_input(to_wrap=to_wrap, wrapped_args=args, span=span)
45
-
46
- #capture the tags attribute of the instance if present, else ignore
47
- try:
48
- span.set_attribute(TAGS, getattr(instance, TAGS))
49
- except AttributeError:
50
- pass
51
- update_span_with_context_input(to_wrap=to_wrap, wrapped_args=args, span=span)
66
+ pre_task_processing(to_wrap, instance, args, span)
52
67
  return_value = wrapped(*args, **kwargs)
53
- update_span_with_context_output(to_wrap=to_wrap, return_value=return_value, span=span)
54
-
55
- if is_root_span(span):
56
- workflow_name = span.resource.attributes.get("service.name")
57
- span.set_attribute("workflow_name",workflow_name)
58
- update_span_with_prompt_output(to_wrap=to_wrap, wrapped_args=return_value, span=span)
59
- update_workflow_type(to_wrap, span)
68
+ post_task_processing(to_wrap, span, return_value)
60
69
 
61
70
  return return_value
62
71
 
72
+ def post_task_processing(to_wrap, span, return_value):
73
+ update_span_with_context_output(to_wrap=to_wrap, return_value=return_value, span=span)
74
+
75
+ if is_root_span(span):
76
+ workflow_name = span.resource.attributes.get("service.name")
77
+ span.set_attribute("workflow_name",workflow_name)
78
+ update_span_with_prompt_output(to_wrap=to_wrap, wrapped_args=return_value, span=span)
79
+ update_workflow_type(to_wrap, span)
80
+
81
+ def pre_task_processing(to_wrap, instance, args, span):
82
+ if is_root_span(span):
83
+ update_span_with_prompt_input(to_wrap=to_wrap, wrapped_args=args, span=span)
84
+
85
+ update_span_with_infra_name(span, INFRA_SERVICE_KEY)
86
+
87
+ #capture the tags attribute of the instance if present, else ignore
88
+ try:
89
+ update_tags(instance, span)
90
+ update_vectorstore_attributes(to_wrap, instance, span)
91
+ except AttributeError:
92
+ pass
93
+ update_span_with_context_input(to_wrap=to_wrap, wrapped_args=args, span=span)
94
+
95
+
96
+
63
97
  @with_tracer_wrapper
64
98
  async def atask_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
65
99
  """Instruments and calls every function defined in TO_WRAP."""
@@ -74,9 +108,10 @@ async def atask_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
74
108
  name = to_wrap.get("span_name")
75
109
  else:
76
110
  name = f"langchain.task.{instance.__class__.__name__}"
77
- kind = to_wrap.get("kind")
78
111
  with tracer.start_as_current_span(name) as span:
112
+ pre_task_processing(to_wrap, instance, args, span)
79
113
  return_value = await wrapped(*args, **kwargs)
114
+ post_task_processing(to_wrap, span, return_value)
80
115
 
81
116
  return return_value
82
117
 
@@ -86,7 +121,7 @@ async def allm_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
86
121
  if instance.__class__.__name__ in ("AgentExecutor"):
87
122
  return wrapped(*args, **kwargs)
88
123
 
89
- if to_wrap.get("span_name_getter"):
124
+ if callable(to_wrap.get("span_name_getter")):
90
125
  name = to_wrap.get("span_name_getter")(instance)
91
126
 
92
127
  elif hasattr(instance, "name") and instance.name:
@@ -95,11 +130,11 @@ async def allm_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
95
130
  name = to_wrap.get("span_name")
96
131
  else:
97
132
  name = f"langchain.task.{instance.__class__.__name__}"
98
- kind = to_wrap.get("kind")
99
133
  with tracer.start_as_current_span(name) as span:
100
134
  update_llm_endpoint(curr_span= span, instance=instance)
101
135
 
102
136
  return_value = await wrapped(*args, **kwargs)
137
+ update_span_from_llm_response(response = return_value, span = span)
103
138
 
104
139
  return return_value
105
140
 
@@ -119,8 +154,9 @@ def llm_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
119
154
  name = to_wrap.get("span_name")
120
155
  else:
121
156
  name = f"langchain.task.{instance.__class__.__name__}"
122
- kind = to_wrap.get("kind")
123
157
  with tracer.start_as_current_span(name) as span:
158
+ if 'haystack.components.retrievers' in to_wrap['package'] and 'haystack.retriever' in span.name:
159
+ update_vectorstore_attributes(to_wrap, instance, span)
124
160
  update_llm_endpoint(curr_span= span, instance=instance)
125
161
 
126
162
  return_value = wrapped(*args, **kwargs)
@@ -136,22 +172,45 @@ def update_llm_endpoint(curr_span: Span, instance):
136
172
  if 'temperature' in instance.__dict__:
137
173
  temp_val = instance.__dict__.get("temperature")
138
174
  curr_span.set_attribute("temperature", temp_val)
139
- # handling for model name
140
- model_name = resolve_from_alias(instance.__dict__ , ["model","model_name"])
141
- curr_span.set_attribute("openai_model_name", model_name)
175
+ # handling for model name
176
+ model_name = resolve_from_alias(instance.__dict__ , ["model","model_name"])
177
+ curr_span.set_attribute("model_name", model_name)
178
+ set_provider_name(curr_span, instance)
142
179
  # handling AzureOpenAI deployment
143
- deployment_name = resolve_from_alias(instance.__dict__ , [ "engine", "azure_deployment",
180
+ deployment_name = resolve_from_alias(instance.__dict__ , [ "engine", "azure_deployment",
144
181
  "deployment_name", "deployment_id", "deployment"])
145
182
  curr_span.set_attribute("az_openai_deployment", deployment_name)
146
183
  # handling the inference endpoint
147
184
  inference_ep = resolve_from_alias(instance.__dict__,["azure_endpoint","api_base"])
148
185
  curr_span.set_attribute("inference_endpoint",inference_ep)
149
186
 
187
+ def set_provider_name(curr_span, instance):
188
+ provider_url = ""
189
+
190
+ try :
191
+ if isinstance(instance.client._client.base_url.host, str) :
192
+ provider_url = instance. client._client.base_url.host
193
+ except:
194
+ pass
195
+
196
+ try :
197
+ if isinstance(instance.api_base, str):
198
+ provider_url = instance.api_base
199
+ except:
200
+ pass
201
+
202
+ try :
203
+ if len(provider_url) > 0:
204
+ parsed_provider_url = urlparse(provider_url)
205
+ curr_span.set_attribute("provider_name", parsed_provider_url.hostname or provider_url)
206
+ except:
207
+ pass
208
+
150
209
  def is_root_span(curr_span: Span) -> bool:
151
- return curr_span.parent == None
210
+ return curr_span.parent is None
152
211
 
153
212
  def get_input_from_args(chain_args):
154
- if len(chain_args) > 0 and type(chain_args[0]) == str:
213
+ if len(chain_args) > 0 and isinstance(chain_args[0], str):
155
214
  return chain_args[0]
156
215
  return ""
157
216
 
@@ -166,15 +225,19 @@ def update_span_from_llm_response(response, span: Span):
166
225
  span.set_attribute("total_tokens", token_usage.get("total_tokens"))
167
226
  # extract token usage from llamaindex openai
168
227
  if(response is not None and hasattr(response, "raw")):
169
- if response.raw is not None:
170
- token_usage = response.raw.get("usage")
171
- if token_usage is not None:
172
- if(hasattr(token_usage, "completion_tokens")):
173
- span.set_attribute("completion_tokens", token_usage.completion_tokens)
174
- if(hasattr(token_usage, "prompt_tokens")):
175
- span.set_attribute("prompt_tokens", token_usage.prompt_tokens)
176
- if(hasattr(token_usage, "total_tokens")):
177
- span.set_attribute("total_tokens", token_usage.total_tokens)
228
+ try:
229
+ if response.raw is not None:
230
+ token_usage = response.raw.get("usage") if isinstance(response.raw, dict) else getattr(response.raw, "usage", None)
231
+ if token_usage is not None:
232
+ if getattr(token_usage, "completion_tokens", None):
233
+ span.set_attribute("completion_tokens", getattr(token_usage, "completion_tokens"))
234
+ if getattr(token_usage, "prompt_tokens", None):
235
+ span.set_attribute("prompt_tokens", getattr(token_usage, "prompt_tokens"))
236
+ if getattr(token_usage, "total_tokens", None):
237
+ span.set_attribute("total_tokens", getattr(token_usage, "total_tokens"))
238
+ except AttributeError:
239
+ token_usage = None
240
+
178
241
 
179
242
  def update_workflow_type(to_wrap, span: Span):
180
243
  package_name = to_wrap.get('package')
@@ -185,30 +248,64 @@ def update_workflow_type(to_wrap, span: Span):
185
248
 
186
249
  def update_span_with_context_input(to_wrap, wrapped_args ,span: Span):
187
250
  package_name: str = to_wrap.get('package')
188
- if("langchain_core.retrievers" in package_name):
251
+ if "langchain_core.retrievers" in package_name:
189
252
  input_arg_text = wrapped_args[0]
190
253
  span.add_event(CONTEXT_INPUT_KEY, {QUERY:input_arg_text})
191
- if("llama_index.core.indices.base_retriever" in package_name):
254
+ if "llama_index.core.indices.base_retriever" in package_name:
192
255
  input_arg_text = wrapped_args[0].query_str
193
256
  span.add_event(CONTEXT_INPUT_KEY, {QUERY:input_arg_text})
194
257
 
195
258
  def update_span_with_context_output(to_wrap, return_value ,span: Span):
196
259
  package_name: str = to_wrap.get('package')
197
- if("llama_index.core.indices.base_retriever" in package_name):
260
+ if "llama_index.core.indices.base_retriever" in package_name:
198
261
  output_arg_text = return_value[0].text
199
262
  span.add_event(CONTEXT_OUTPUT_KEY, {RESPONSE:output_arg_text})
200
263
 
201
264
  def update_span_with_prompt_input(to_wrap, wrapped_args ,span: Span):
202
265
  input_arg_text = wrapped_args[0]
203
-
266
+
204
267
  if isinstance(input_arg_text, dict):
205
268
  span.add_event(PROMPT_INPUT_KEY,input_arg_text)
206
- else:
269
+ else:
207
270
  span.add_event(PROMPT_INPUT_KEY,{QUERY:input_arg_text})
208
271
 
209
272
  def update_span_with_prompt_output(to_wrap, wrapped_args ,span: Span):
210
273
  package_name: str = to_wrap.get('package')
211
- if(isinstance(wrapped_args, str)):
274
+ if isinstance(wrapped_args, str):
212
275
  span.add_event(PROMPT_OUTPUT_KEY, {RESPONSE:wrapped_args})
213
- if("llama_index.core.base.base_query_engine" in package_name):
276
+ if "llama_index.core.base.base_query_engine" in package_name:
214
277
  span.add_event(PROMPT_OUTPUT_KEY, {RESPONSE:wrapped_args.response})
278
+
279
+ def update_tags(instance, span):
280
+ try:
281
+ # copy tags as is from langchain
282
+ span.set_attribute(TAGS, getattr(instance, TAGS))
283
+ except:
284
+ pass
285
+ try:
286
+ # extract embed model and vector store names for llamaindex
287
+ model_name = instance.retriever._embed_model.model_name
288
+ vector_store_name = type(instance.retriever._vector_store).__name__
289
+ span.set_attribute(TAGS, [model_name, vector_store_name])
290
+ except:
291
+ pass
292
+
293
+
294
+ def update_vectorstore_attributes(to_wrap, instance, span):
295
+ """
296
+ Updates the telemetry span attributes for vector store retrieval tasks.
297
+ """
298
+ try:
299
+ package = to_wrap.get('package')
300
+ if package in framework_vector_store_mapping:
301
+ attributes = framework_vector_store_mapping[package](instance)
302
+ span._attributes.update({
303
+ TYPE: attributes['type'],
304
+ PROVIDER: attributes['provider'],
305
+ EMBEDDING_MODEL: attributes['embedding_model']
306
+ })
307
+ else:
308
+ logger.warning(f"Package '{package}' not recognized for vector store telemetry.")
309
+
310
+ except Exception as e:
311
+ logger.error(f"Error updating span attributes: {e}")
@@ -5,20 +5,20 @@ from monocle_apptrace.langchain import LANGCHAIN_METHODS
5
5
  from monocle_apptrace.llamaindex import LLAMAINDEX_METHODS
6
6
  from monocle_apptrace.wrap_common import task_wrapper
7
7
 
8
+ # pylint: disable=too-few-public-methods
8
9
  class WrapperMethod:
9
10
  def __init__(
10
11
  self,
11
12
  package: str,
12
- object: str,
13
+ object_name: str,
13
14
  method: str,
14
15
  span_name: str = None,
15
16
  wrapper = task_wrapper
16
17
  ):
17
18
  self.package = package
18
- self.object = object
19
+ self.object = object_name
19
20
  self.method = method
20
21
  self.span_name = span_name
21
22
  self.wrapper = wrapper
22
23
 
23
24
  INBUILT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS
24
-