monocle-apptrace 0.1.1__py3-none-any.whl → 0.2.0__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.
- monocle_apptrace/exporters/aws/s3_exporter.py +158 -0
- monocle_apptrace/exporters/azure/blob_exporter.py +128 -0
- monocle_apptrace/exporters/base_exporter.py +47 -0
- monocle_apptrace/exporters/exporter_processor.py +19 -0
- monocle_apptrace/exporters/monocle_exporters.py +27 -0
- monocle_apptrace/exporters/okahu/okahu_exporter.py +115 -0
- monocle_apptrace/haystack/__init__.py +4 -4
- monocle_apptrace/haystack/wrap_pipeline.py +3 -2
- monocle_apptrace/instrumentor.py +12 -15
- monocle_apptrace/langchain/__init__.py +6 -3
- monocle_apptrace/llamaindex/__init__.py +8 -7
- monocle_apptrace/metamodel/entities/README.md +33 -10
- monocle_apptrace/metamodel/entities/app_hosting_types.json +29 -0
- monocle_apptrace/metamodel/entities/entities.json +49 -0
- monocle_apptrace/metamodel/entities/inference_types.json +33 -0
- monocle_apptrace/metamodel/entities/model_types.json +41 -0
- monocle_apptrace/metamodel/entities/vector_store_types.json +25 -0
- monocle_apptrace/metamodel/entities/workflow_types.json +22 -0
- monocle_apptrace/metamodel/maps/attributes/inference/langchain_entities.json +35 -0
- monocle_apptrace/metamodel/maps/attributes/inference/llamaindex_entities.json +35 -0
- monocle_apptrace/metamodel/maps/attributes/retrieval/langchain_entities.json +27 -0
- monocle_apptrace/metamodel/maps/attributes/retrieval/llamaindex_entities.json +27 -0
- monocle_apptrace/metamodel/maps/{lang_chain_methods.json → langchain_methods.json} +31 -8
- monocle_apptrace/metamodel/maps/{llama_index_methods.json → llamaindex_methods.json} +12 -8
- monocle_apptrace/metamodel/spans/span_example.json +1 -1
- monocle_apptrace/metamodel/spans/span_types.json +16 -0
- monocle_apptrace/utils.py +90 -11
- monocle_apptrace/wrap_common.py +228 -122
- monocle_apptrace/wrapper.py +3 -1
- {monocle_apptrace-0.1.1.dist-info → monocle_apptrace-0.2.0.dist-info}/METADATA +5 -1
- monocle_apptrace-0.2.0.dist-info/RECORD +44 -0
- monocle_apptrace/metamodel/entities/entity_types.json +0 -157
- monocle_apptrace/metamodel/entities/entity_types.py +0 -51
- monocle_apptrace-0.1.1.dist-info/RECORD +0 -29
- {monocle_apptrace-0.1.1.dist-info → monocle_apptrace-0.2.0.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.1.1.dist-info → monocle_apptrace-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {monocle_apptrace-0.1.1.dist-info → monocle_apptrace-0.2.0.dist-info}/licenses/NOTICE +0 -0
monocle_apptrace/utils.py
CHANGED
|
@@ -3,7 +3,10 @@ import json
|
|
|
3
3
|
from importlib import import_module
|
|
4
4
|
import os
|
|
5
5
|
from opentelemetry.trace import Span
|
|
6
|
+
from opentelemetry.context import attach, set_value, get_value
|
|
6
7
|
from monocle_apptrace.constants import azure_service_map, aws_service_map
|
|
8
|
+
from json.decoder import JSONDecodeError
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
7
10
|
|
|
8
11
|
embedding_model_context = {}
|
|
9
12
|
|
|
@@ -21,12 +24,14 @@ def dont_throw(func):
|
|
|
21
24
|
"""
|
|
22
25
|
# Obtain a logger specific to the function's module
|
|
23
26
|
logger = logging.getLogger(func.__module__)
|
|
27
|
+
|
|
24
28
|
# pylint: disable=inconsistent-return-statements
|
|
25
29
|
def wrapper(*args, **kwargs):
|
|
26
30
|
try:
|
|
27
31
|
return func(*args, **kwargs)
|
|
28
32
|
except Exception as ex:
|
|
29
33
|
logger.warning("Failed to execute %s, error: %s", func.__name__, str(ex))
|
|
34
|
+
|
|
30
35
|
return wrapper
|
|
31
36
|
|
|
32
37
|
def with_tracer_wrapper(func):
|
|
@@ -48,33 +53,79 @@ def resolve_from_alias(my_map, alias):
|
|
|
48
53
|
return my_map[i]
|
|
49
54
|
return None
|
|
50
55
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
def load_output_processor(wrapper_method, attributes_config_base_path):
|
|
57
|
+
"""Load the output processor from a file if the file path is provided and valid."""
|
|
58
|
+
logger = logging.getLogger()
|
|
59
|
+
output_processor_file_path = wrapper_method["output_processor"][0]
|
|
60
|
+
logger.info(f'Output processor file path is: {output_processor_file_path}')
|
|
61
|
+
|
|
62
|
+
if isinstance(output_processor_file_path, str) and output_processor_file_path: # Combined condition
|
|
63
|
+
if not attributes_config_base_path:
|
|
64
|
+
absolute_file_path = os.path.abspath(output_processor_file_path)
|
|
65
|
+
else:
|
|
66
|
+
absolute_file_path = os.path.join(attributes_config_base_path, output_processor_file_path)
|
|
67
|
+
|
|
68
|
+
logger.info(f'Absolute file path is: {absolute_file_path}')
|
|
69
|
+
try:
|
|
70
|
+
with open(absolute_file_path, encoding='UTF-8') as op_file:
|
|
71
|
+
wrapper_method["output_processor"] = json.load(op_file)
|
|
72
|
+
logger.info('Output processor loaded successfully.')
|
|
73
|
+
except FileNotFoundError:
|
|
74
|
+
logger.error(f"Error: File not found at {absolute_file_path}.")
|
|
75
|
+
except JSONDecodeError:
|
|
76
|
+
logger.error(f"Error: Invalid JSON content in the file {absolute_file_path}.")
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logger.error(f"Error: An unexpected error occurred: {e}")
|
|
79
|
+
else:
|
|
80
|
+
logger.error("Invalid or missing output processor file path.")
|
|
81
|
+
|
|
82
|
+
def get_wrapper_methods_config(
|
|
83
|
+
wrapper_methods_config_path: str,
|
|
84
|
+
attributes_config_base_path: str = None
|
|
85
|
+
):
|
|
86
|
+
parent_dir = os.path.dirname(os.path.join(os.path.dirname(__file__), '..'))
|
|
87
|
+
wrapper_methods_config = load_wrapper_methods_config_from_file(
|
|
88
|
+
wrapper_methods_config_path=os.path.join(parent_dir, wrapper_methods_config_path))
|
|
89
|
+
process_wrapper_method_config(
|
|
90
|
+
wrapper_methods_config=wrapper_methods_config,
|
|
91
|
+
attributes_config_base_path=attributes_config_base_path)
|
|
92
|
+
return wrapper_methods_config
|
|
93
|
+
|
|
94
|
+
def load_wrapper_methods_config_from_file(
|
|
95
|
+
wrapper_methods_config_path: str):
|
|
96
|
+
json_data = {}
|
|
97
|
+
|
|
98
|
+
with open(wrapper_methods_config_path, encoding='UTF-8') as config_file:
|
|
54
99
|
json_data = json.load(config_file)
|
|
55
|
-
|
|
56
|
-
|
|
100
|
+
|
|
101
|
+
return json_data["wrapper_methods"]
|
|
102
|
+
|
|
103
|
+
def process_wrapper_method_config(
|
|
104
|
+
wrapper_methods_config: str,
|
|
105
|
+
attributes_config_base_path: str = ""):
|
|
106
|
+
for wrapper_method in wrapper_methods_config:
|
|
107
|
+
if "wrapper_package" in wrapper_method and "wrapper_method" in wrapper_method:
|
|
57
108
|
wrapper_method["wrapper"] = get_wrapper_method(
|
|
58
109
|
wrapper_method["wrapper_package"], wrapper_method["wrapper_method"])
|
|
59
|
-
if "span_name_getter_method" in wrapper_method
|
|
110
|
+
if "span_name_getter_method" in wrapper_method:
|
|
60
111
|
wrapper_method["span_name_getter"] = get_wrapper_method(
|
|
61
112
|
wrapper_method["span_name_getter_package"],
|
|
62
113
|
wrapper_method["span_name_getter_method"])
|
|
63
|
-
|
|
114
|
+
if "output_processor" in wrapper_method and wrapper_method["output_processor"]:
|
|
115
|
+
load_output_processor(wrapper_method, attributes_config_base_path)
|
|
64
116
|
|
|
65
117
|
def get_wrapper_method(package_name: str, method_name: str):
|
|
66
118
|
wrapper_module = import_module("monocle_apptrace." + package_name)
|
|
67
119
|
return getattr(wrapper_module, method_name)
|
|
68
120
|
|
|
69
121
|
def update_span_with_infra_name(span: Span, span_key: str):
|
|
70
|
-
for key,val
|
|
122
|
+
for key, val in azure_service_map.items():
|
|
71
123
|
if key in os.environ:
|
|
72
124
|
span.set_attribute(span_key, val)
|
|
73
|
-
for key,val
|
|
125
|
+
for key, val in aws_service_map.items():
|
|
74
126
|
if key in os.environ:
|
|
75
127
|
span.set_attribute(span_key, val)
|
|
76
128
|
|
|
77
|
-
|
|
78
129
|
def set_embedding_model(model_name: str):
|
|
79
130
|
"""
|
|
80
131
|
Sets the embedding model in the global context.
|
|
@@ -83,7 +134,6 @@ def set_embedding_model(model_name: str):
|
|
|
83
134
|
"""
|
|
84
135
|
embedding_model_context['embedding_model'] = model_name
|
|
85
136
|
|
|
86
|
-
|
|
87
137
|
def get_embedding_model() -> str:
|
|
88
138
|
"""
|
|
89
139
|
Retrieves the embedding model from the global context.
|
|
@@ -91,3 +141,32 @@ def get_embedding_model() -> str:
|
|
|
91
141
|
@return: The name of the embedding model, or 'unknown' if not set
|
|
92
142
|
"""
|
|
93
143
|
return embedding_model_context.get('embedding_model', 'unknown')
|
|
144
|
+
|
|
145
|
+
def set_attribute(key: str, value: str):
|
|
146
|
+
"""
|
|
147
|
+
Set a value in the global context for a given key.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
key: The key for the context value to set.
|
|
151
|
+
value: The value to set for the given key.
|
|
152
|
+
"""
|
|
153
|
+
attach(set_value(key, value))
|
|
154
|
+
|
|
155
|
+
def get_attribute(key: str) -> str:
|
|
156
|
+
"""
|
|
157
|
+
Retrieve a value from the global context for a given key.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
key: The key for the context value to retrieve.
|
|
161
|
+
|
|
162
|
+
Returns:
|
|
163
|
+
The value associated with the given key.
|
|
164
|
+
"""
|
|
165
|
+
return get_value(key)
|
|
166
|
+
|
|
167
|
+
def get_workflow_name(span: Span) -> str:
|
|
168
|
+
try:
|
|
169
|
+
return get_value("workflow_name") or span.resource.attributes.get("service.name")
|
|
170
|
+
except Exception as e:
|
|
171
|
+
logger.exception(f"Error getting workflow name: {e}")
|
|
172
|
+
return None
|
monocle_apptrace/wrap_common.py
CHANGED
|
@@ -1,27 +1,28 @@
|
|
|
1
|
-
#pylint: disable=protected-access
|
|
1
|
+
# pylint: disable=protected-access
|
|
2
2
|
import logging
|
|
3
3
|
import os
|
|
4
|
+
import inspect
|
|
4
5
|
from urllib.parse import urlparse
|
|
5
|
-
|
|
6
6
|
from opentelemetry.trace import Span, Tracer
|
|
7
|
-
from monocle_apptrace.utils import resolve_from_alias, update_span_with_infra_name, with_tracer_wrapper, get_embedding_model
|
|
8
|
-
|
|
7
|
+
from monocle_apptrace.utils import resolve_from_alias, update_span_with_infra_name, with_tracer_wrapper, get_embedding_model, get_attribute, get_workflow_name
|
|
8
|
+
from monocle_apptrace.utils import set_attribute
|
|
9
|
+
from opentelemetry.context import get_value, attach, set_value
|
|
9
10
|
logger = logging.getLogger(__name__)
|
|
10
11
|
WORKFLOW_TYPE_KEY = "workflow_type"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
PROMPT_INPUT_KEY = "input"
|
|
14
|
-
PROMPT_OUTPUT_KEY = "output"
|
|
12
|
+
DATA_INPUT_KEY = "data.input"
|
|
13
|
+
DATA_OUTPUT_KEY = "data.output"
|
|
14
|
+
PROMPT_INPUT_KEY = "data.input"
|
|
15
|
+
PROMPT_OUTPUT_KEY = "data.output"
|
|
15
16
|
QUERY = "question"
|
|
16
17
|
RESPONSE = "response"
|
|
17
|
-
TAGS = "tags"
|
|
18
18
|
SESSION_PROPERTIES_KEY = "session"
|
|
19
19
|
INFRA_SERVICE_KEY = "infra_service_name"
|
|
20
|
+
|
|
20
21
|
TYPE = "type"
|
|
21
22
|
PROVIDER = "provider_name"
|
|
22
23
|
EMBEDDING_MODEL = "embedding_model"
|
|
23
24
|
VECTOR_STORE = 'vector_store'
|
|
24
|
-
|
|
25
|
+
META_DATA = 'metadata'
|
|
25
26
|
|
|
26
27
|
WORKFLOW_TYPE_MAP = {
|
|
27
28
|
"llama_index": "workflow.llamaindex",
|
|
@@ -29,24 +30,53 @@ WORKFLOW_TYPE_MAP = {
|
|
|
29
30
|
"haystack": "workflow.haystack"
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
def get_embedding_model_for_vectorstore(instance):
|
|
34
|
+
# Handle Langchain or other frameworks where vectorstore exists
|
|
35
|
+
if hasattr(instance, 'vectorstore'):
|
|
36
|
+
vectorstore_dict = instance.vectorstore.__dict__
|
|
37
|
+
|
|
38
|
+
# Use inspect to check if the embedding function is from Sagemaker
|
|
39
|
+
if 'embedding_func' in vectorstore_dict:
|
|
40
|
+
embedding_func = vectorstore_dict['embedding_func']
|
|
41
|
+
class_name = embedding_func.__class__.__name__
|
|
42
|
+
file_location = inspect.getfile(embedding_func.__class__)
|
|
43
|
+
|
|
44
|
+
# Check if the class is SagemakerEndpointEmbeddings
|
|
45
|
+
if class_name == 'SagemakerEndpointEmbeddings' and 'langchain_community' in file_location:
|
|
46
|
+
# Set embedding_model as endpoint_name if it's Sagemaker
|
|
47
|
+
if hasattr(embedding_func, 'endpoint_name'):
|
|
48
|
+
return embedding_func.endpoint_name
|
|
49
|
+
|
|
50
|
+
# Default to the regular embedding model if not Sagemaker
|
|
51
|
+
return instance.vectorstore.embeddings.model
|
|
52
|
+
|
|
53
|
+
# Handle llama_index where _embed_model is present
|
|
54
|
+
if hasattr(instance, '_embed_model') and hasattr(instance._embed_model, 'model_name'):
|
|
55
|
+
return instance._embed_model.model_name
|
|
56
|
+
|
|
57
|
+
# Fallback if no specific model is found
|
|
58
|
+
return "Unknown Embedding Model"
|
|
59
|
+
|
|
60
|
+
|
|
32
61
|
framework_vector_store_mapping = {
|
|
33
62
|
'langchain_core.retrievers': lambda instance: {
|
|
34
|
-
'provider': instance.
|
|
35
|
-
'embedding_model': instance
|
|
63
|
+
'provider': type(instance.vectorstore).__name__,
|
|
64
|
+
'embedding_model': get_embedding_model_for_vectorstore(instance),
|
|
36
65
|
'type': VECTOR_STORE,
|
|
37
66
|
},
|
|
38
67
|
'llama_index.core.indices.base_retriever': lambda instance: {
|
|
39
68
|
'provider': type(instance._vector_store).__name__,
|
|
40
|
-
'embedding_model': instance
|
|
69
|
+
'embedding_model': get_embedding_model_for_vectorstore(instance),
|
|
41
70
|
'type': VECTOR_STORE,
|
|
42
71
|
},
|
|
43
|
-
'haystack.components.retrievers': lambda instance: {
|
|
72
|
+
'haystack.components.retrievers.in_memory': lambda instance: {
|
|
44
73
|
'provider': instance.__dict__.get("document_store").__class__.__name__,
|
|
45
74
|
'embedding_model': get_embedding_model(),
|
|
46
75
|
'type': VECTOR_STORE,
|
|
47
76
|
},
|
|
48
77
|
}
|
|
49
78
|
|
|
79
|
+
|
|
50
80
|
@with_tracer_wrapper
|
|
51
81
|
def task_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
52
82
|
"""Instruments and calls every function defined in TO_WRAP."""
|
|
@@ -63,36 +93,83 @@ def task_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
|
63
93
|
name = f"langchain.task.{instance.__class__.__name__}"
|
|
64
94
|
|
|
65
95
|
with tracer.start_as_current_span(name) as span:
|
|
96
|
+
process_span(to_wrap, span, instance, args)
|
|
66
97
|
pre_task_processing(to_wrap, instance, args, span)
|
|
67
98
|
return_value = wrapped(*args, **kwargs)
|
|
68
99
|
post_task_processing(to_wrap, span, return_value)
|
|
69
100
|
|
|
70
101
|
return return_value
|
|
71
102
|
|
|
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
103
|
|
|
104
|
+
def process_span(to_wrap, span, instance, args):
|
|
105
|
+
# Check if the output_processor is a valid JSON (in Python, that means it's a dictionary)
|
|
106
|
+
span_index = 1
|
|
75
107
|
if is_root_span(span):
|
|
76
|
-
workflow_name = span
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
108
|
+
workflow_name = get_workflow_name(span)
|
|
109
|
+
if workflow_name:
|
|
110
|
+
span.set_attribute(f"entity.{span_index}.name", workflow_name)
|
|
111
|
+
# workflow type
|
|
112
|
+
package_name = to_wrap.get('package')
|
|
113
|
+
for (package, workflow_type) in WORKFLOW_TYPE_MAP.items():
|
|
114
|
+
if (package_name is not None and package in package_name):
|
|
115
|
+
span.set_attribute(f"entity.{span_index}.type", workflow_type)
|
|
116
|
+
span_index += 1
|
|
117
|
+
if 'output_processor' in to_wrap:
|
|
118
|
+
output_processor=to_wrap['output_processor']
|
|
119
|
+
if isinstance(output_processor, dict) and len(output_processor) > 0:
|
|
120
|
+
if 'type' in output_processor:
|
|
121
|
+
span.set_attribute("span.type", output_processor['type'])
|
|
122
|
+
else:
|
|
123
|
+
logger.warning("type of span not found or incorrect written in entity json")
|
|
124
|
+
count = 0
|
|
125
|
+
if 'attributes' in output_processor:
|
|
126
|
+
count = len(output_processor["attributes"])
|
|
127
|
+
span.set_attribute("entity.count", count)
|
|
128
|
+
span_index = 1
|
|
129
|
+
for processors in output_processor["attributes"]:
|
|
130
|
+
for processor in processors:
|
|
131
|
+
attribute = processor.get('attribute')
|
|
132
|
+
accessor = processor.get('accessor')
|
|
133
|
+
|
|
134
|
+
if attribute and accessor:
|
|
135
|
+
attribute_name = f"entity.{span_index}.{attribute}"
|
|
136
|
+
try:
|
|
137
|
+
result = eval(accessor)(instance, args)
|
|
138
|
+
if result and isinstance(result, str):
|
|
139
|
+
span.set_attribute(attribute_name, result)
|
|
140
|
+
except Exception as e:
|
|
141
|
+
logger.error(f"Error processing accessor: {e}")
|
|
142
|
+
else:
|
|
143
|
+
logger.warning(f"{' and '.join([key for key in ['attribute', 'accessor'] if not processor.get(key)])} not found or incorrect in entity JSON")
|
|
144
|
+
span_index += 1
|
|
145
|
+
else:
|
|
146
|
+
logger.warning("attributes not found or incorrect written in entity json")
|
|
147
|
+
span.set_attribute("span.count", count)
|
|
80
148
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
update_span_with_prompt_input(to_wrap=to_wrap, wrapped_args=args, span=span)
|
|
149
|
+
else:
|
|
150
|
+
logger.warning("empty or entities json is not in correct format")
|
|
84
151
|
|
|
85
|
-
update_span_with_infra_name(span, INFRA_SERVICE_KEY)
|
|
86
152
|
|
|
87
|
-
|
|
153
|
+
def post_task_processing(to_wrap, span, return_value):
|
|
88
154
|
try:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
155
|
+
update_span_with_context_output(to_wrap=to_wrap, return_value=return_value, span=span)
|
|
156
|
+
|
|
157
|
+
if is_root_span(span):
|
|
158
|
+
update_span_with_prompt_output(to_wrap=to_wrap, wrapped_args=return_value, span=span)
|
|
159
|
+
except:
|
|
160
|
+
logger.exception("exception in post_task_processing")
|
|
94
161
|
|
|
95
162
|
|
|
163
|
+
def pre_task_processing(to_wrap, instance, args, span):
|
|
164
|
+
try:
|
|
165
|
+
if is_root_span(span):
|
|
166
|
+
update_span_with_prompt_input(to_wrap=to_wrap, wrapped_args=args, span=span)
|
|
167
|
+
update_span_with_infra_name(span, INFRA_SERVICE_KEY)
|
|
168
|
+
|
|
169
|
+
update_span_with_context_input(to_wrap=to_wrap, wrapped_args=args, span=span)
|
|
170
|
+
except:
|
|
171
|
+
logger.exception("exception in pre_task_processing")
|
|
172
|
+
|
|
96
173
|
|
|
97
174
|
@with_tracer_wrapper
|
|
98
175
|
async def atask_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
@@ -109,12 +186,14 @@ async def atask_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
|
109
186
|
else:
|
|
110
187
|
name = f"langchain.task.{instance.__class__.__name__}"
|
|
111
188
|
with tracer.start_as_current_span(name) as span:
|
|
189
|
+
process_span(to_wrap, span, instance, args)
|
|
112
190
|
pre_task_processing(to_wrap, instance, args, span)
|
|
113
191
|
return_value = await wrapped(*args, **kwargs)
|
|
114
192
|
post_task_processing(to_wrap, span, return_value)
|
|
115
193
|
|
|
116
194
|
return return_value
|
|
117
195
|
|
|
196
|
+
|
|
118
197
|
@with_tracer_wrapper
|
|
119
198
|
async def allm_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
120
199
|
# Some Langchain objects are wrapped elsewhere, so we ignore them here
|
|
@@ -131,16 +210,24 @@ async def allm_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
|
131
210
|
else:
|
|
132
211
|
name = f"langchain.task.{instance.__class__.__name__}"
|
|
133
212
|
with tracer.start_as_current_span(name) as span:
|
|
134
|
-
|
|
213
|
+
if 'haystack.components.retrievers' in to_wrap['package'] and 'haystack.retriever' in span.name:
|
|
214
|
+
input_arg_text = get_attribute(DATA_INPUT_KEY)
|
|
215
|
+
span.add_event(DATA_INPUT_KEY, {QUERY: input_arg_text})
|
|
216
|
+
provider_name, inference_endpoint = get_provider_name(instance)
|
|
217
|
+
instance_args = {"provider_name": provider_name, "inference_endpoint": inference_endpoint}
|
|
218
|
+
|
|
219
|
+
process_span(to_wrap, span, instance, instance_args)
|
|
135
220
|
|
|
136
221
|
return_value = await wrapped(*args, **kwargs)
|
|
137
|
-
|
|
222
|
+
if 'haystack.components.retrievers' in to_wrap['package'] and 'haystack.retriever' in span.name:
|
|
223
|
+
update_span_with_context_output(to_wrap=to_wrap, return_value=return_value, span=span)
|
|
224
|
+
update_span_from_llm_response(response=return_value, span=span, instance=instance)
|
|
138
225
|
|
|
139
226
|
return return_value
|
|
140
227
|
|
|
228
|
+
|
|
141
229
|
@with_tracer_wrapper
|
|
142
230
|
def llm_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
143
|
-
|
|
144
231
|
# Some Langchain objects are wrapped elsewhere, so we ignore them here
|
|
145
232
|
if instance.__class__.__name__ in ("AgentExecutor"):
|
|
146
233
|
return wrapped(*args, **kwargs)
|
|
@@ -154,87 +241,121 @@ def llm_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
|
154
241
|
name = to_wrap.get("span_name")
|
|
155
242
|
else:
|
|
156
243
|
name = f"langchain.task.{instance.__class__.__name__}"
|
|
244
|
+
|
|
157
245
|
with tracer.start_as_current_span(name) as span:
|
|
158
246
|
if 'haystack.components.retrievers' in to_wrap['package'] and 'haystack.retriever' in span.name:
|
|
159
|
-
|
|
160
|
-
|
|
247
|
+
input_arg_text = get_attribute(DATA_INPUT_KEY)
|
|
248
|
+
span.add_event(DATA_INPUT_KEY, {QUERY: input_arg_text})
|
|
249
|
+
provider_name, inference_endpoint = get_provider_name(instance)
|
|
250
|
+
instance_args = {"provider_name": provider_name, "inference_endpoint": inference_endpoint}
|
|
251
|
+
|
|
252
|
+
process_span(to_wrap, span, instance, instance_args)
|
|
161
253
|
|
|
162
254
|
return_value = wrapped(*args, **kwargs)
|
|
163
|
-
|
|
255
|
+
if 'haystack.components.retrievers' in to_wrap['package'] and 'haystack.retriever' in span.name:
|
|
256
|
+
update_span_with_context_output(to_wrap=to_wrap, return_value=return_value, span=span)
|
|
257
|
+
update_span_from_llm_response(response=return_value, span=span, instance=instance)
|
|
164
258
|
|
|
165
259
|
return return_value
|
|
166
260
|
|
|
261
|
+
|
|
167
262
|
def update_llm_endpoint(curr_span: Span, instance):
|
|
263
|
+
# Lambda to set attributes if values are not None
|
|
264
|
+
__set_span_attribute_if_not_none = lambda span, **kwargs: [
|
|
265
|
+
span.set_attribute(k, v) for k, v in kwargs.items() if v is not None
|
|
266
|
+
]
|
|
267
|
+
|
|
168
268
|
triton_llm_endpoint = os.environ.get("TRITON_LLM_ENDPOINT")
|
|
169
269
|
if triton_llm_endpoint is not None and len(triton_llm_endpoint) > 0:
|
|
170
270
|
curr_span.set_attribute("server_url", triton_llm_endpoint)
|
|
171
271
|
else:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
model_name = resolve_from_alias(instance.__dict__
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
272
|
+
# Get temperature if present
|
|
273
|
+
temp_val = instance.__dict__.get("temperature")
|
|
274
|
+
|
|
275
|
+
# Resolve values for model name, deployment, and inference endpoint
|
|
276
|
+
model_name = resolve_from_alias(instance.__dict__, ["model", "model_name"])
|
|
277
|
+
deployment_name = resolve_from_alias(instance.__dict__,
|
|
278
|
+
["engine", "azure_deployment", "deployment_name", "deployment_id",
|
|
279
|
+
"deployment"])
|
|
280
|
+
inference_ep = resolve_from_alias(instance.__dict__, ["azure_endpoint", "api_base"])
|
|
281
|
+
|
|
282
|
+
# Use the lambda to set attributes conditionally
|
|
283
|
+
__set_span_attribute_if_not_none(
|
|
284
|
+
curr_span,
|
|
285
|
+
temperature=temp_val,
|
|
286
|
+
model_name=model_name,
|
|
287
|
+
az_openai_deployment=deployment_name,
|
|
288
|
+
inference_endpoint=inference_ep
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def get_provider_name(instance):
|
|
188
293
|
provider_url = ""
|
|
189
|
-
|
|
190
|
-
try
|
|
191
|
-
if isinstance(instance.client._client.base_url.host, str)
|
|
192
|
-
provider_url = instance.
|
|
294
|
+
inference_endpoint = ""
|
|
295
|
+
try:
|
|
296
|
+
if isinstance(instance.client._client.base_url.host, str):
|
|
297
|
+
provider_url = instance.client._client.base_url.host
|
|
298
|
+
if isinstance(instance.client._client.base_url, str):
|
|
299
|
+
inference_endpoint = instance.client._client.base_url
|
|
300
|
+
else:
|
|
301
|
+
inference_endpoint = str(instance.client._client.base_url)
|
|
193
302
|
except:
|
|
194
303
|
pass
|
|
195
304
|
|
|
196
|
-
try
|
|
305
|
+
try:
|
|
197
306
|
if isinstance(instance.api_base, str):
|
|
198
307
|
provider_url = instance.api_base
|
|
199
308
|
except:
|
|
200
309
|
pass
|
|
201
310
|
|
|
202
|
-
try
|
|
311
|
+
try:
|
|
203
312
|
if len(provider_url) > 0:
|
|
204
313
|
parsed_provider_url = urlparse(provider_url)
|
|
205
|
-
curr_span.set_attribute("provider_name", parsed_provider_url.hostname or provider_url)
|
|
206
314
|
except:
|
|
207
315
|
pass
|
|
316
|
+
return parsed_provider_url.hostname or provider_url,inference_endpoint
|
|
317
|
+
|
|
208
318
|
|
|
209
319
|
def is_root_span(curr_span: Span) -> bool:
|
|
210
320
|
return curr_span.parent is None
|
|
211
321
|
|
|
322
|
+
|
|
212
323
|
def get_input_from_args(chain_args):
|
|
213
324
|
if len(chain_args) > 0 and isinstance(chain_args[0], str):
|
|
214
325
|
return chain_args[0]
|
|
215
326
|
return ""
|
|
216
327
|
|
|
217
|
-
|
|
328
|
+
|
|
329
|
+
def update_span_from_llm_response(response, span: Span, instance):
|
|
218
330
|
# extract token uasge from langchain openai
|
|
219
331
|
if (response is not None and hasattr(response, "response_metadata")):
|
|
220
332
|
response_metadata = response.response_metadata
|
|
221
333
|
token_usage = response_metadata.get("token_usage")
|
|
334
|
+
meta_dict = {}
|
|
222
335
|
if token_usage is not None:
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
336
|
+
temperature = instance.__dict__.get("temperature", None)
|
|
337
|
+
meta_dict.update({"temperature": temperature})
|
|
338
|
+
meta_dict.update({"completion_tokens": token_usage.get("completion_tokens")})
|
|
339
|
+
meta_dict.update({"prompt_tokens": token_usage.get("prompt_tokens")})
|
|
340
|
+
meta_dict.update({"total_tokens": token_usage.get("total_tokens")})
|
|
341
|
+
span.add_event(META_DATA, meta_dict)
|
|
226
342
|
# extract token usage from llamaindex openai
|
|
227
|
-
if(response is not None and hasattr(response, "raw")):
|
|
343
|
+
if (response is not None and hasattr(response, "raw")):
|
|
228
344
|
try:
|
|
345
|
+
meta_dict = {}
|
|
229
346
|
if response.raw is not None:
|
|
230
|
-
token_usage = response.raw.get("usage") if isinstance(response.raw, dict) else getattr(response.raw,
|
|
347
|
+
token_usage = response.raw.get("usage") if isinstance(response.raw, dict) else getattr(response.raw,
|
|
348
|
+
"usage", None)
|
|
231
349
|
if token_usage is not None:
|
|
350
|
+
temperature = instance.__dict__.get("temperature", None)
|
|
351
|
+
meta_dict.update({"temperature": temperature})
|
|
232
352
|
if getattr(token_usage, "completion_tokens", None):
|
|
233
|
-
|
|
353
|
+
meta_dict.update({"completion_tokens": getattr(token_usage, "completion_tokens")})
|
|
234
354
|
if getattr(token_usage, "prompt_tokens", None):
|
|
235
|
-
|
|
355
|
+
meta_dict.update({"prompt_tokens": getattr(token_usage, "prompt_tokens")})
|
|
236
356
|
if getattr(token_usage, "total_tokens", None):
|
|
237
|
-
|
|
357
|
+
meta_dict.update({"total_tokens": getattr(token_usage, "total_tokens")})
|
|
358
|
+
span.add_event(META_DATA, meta_dict)
|
|
238
359
|
except AttributeError:
|
|
239
360
|
token_usage = None
|
|
240
361
|
|
|
@@ -243,69 +364,54 @@ def update_workflow_type(to_wrap, span: Span):
|
|
|
243
364
|
package_name = to_wrap.get('package')
|
|
244
365
|
|
|
245
366
|
for (package, workflow_type) in WORKFLOW_TYPE_MAP.items():
|
|
246
|
-
if(package_name is not None and package in package_name):
|
|
367
|
+
if (package_name is not None and package in package_name):
|
|
247
368
|
span.set_attribute(WORKFLOW_TYPE_KEY, workflow_type)
|
|
248
369
|
|
|
249
|
-
def update_span_with_context_input(to_wrap, wrapped_args ,span: Span):
|
|
250
|
-
package_name: str = to_wrap.get('package')
|
|
251
|
-
if "langchain_core.retrievers" in package_name:
|
|
252
|
-
input_arg_text = wrapped_args[0]
|
|
253
|
-
span.add_event(CONTEXT_INPUT_KEY, {QUERY:input_arg_text})
|
|
254
|
-
if "llama_index.core.indices.base_retriever" in package_name:
|
|
255
|
-
input_arg_text = wrapped_args[0].query_str
|
|
256
|
-
span.add_event(CONTEXT_INPUT_KEY, {QUERY:input_arg_text})
|
|
257
370
|
|
|
258
|
-
def
|
|
371
|
+
def update_span_with_context_input(to_wrap, wrapped_args, span: Span):
|
|
259
372
|
package_name: str = to_wrap.get('package')
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
373
|
+
input_arg_text = ""
|
|
374
|
+
if "langchain_core.retrievers" in package_name and len(wrapped_args) > 0:
|
|
375
|
+
input_arg_text += wrapped_args[0]
|
|
376
|
+
if "llama_index.core.indices.base_retriever" in package_name and len(wrapped_args) > 0:
|
|
377
|
+
input_arg_text += wrapped_args[0].query_str
|
|
378
|
+
if "haystack.components.retrievers.in_memory" in package_name:
|
|
379
|
+
input_arg_text += get_attribute(DATA_INPUT_KEY)
|
|
380
|
+
if input_arg_text:
|
|
381
|
+
span.add_event(DATA_INPUT_KEY, {QUERY: input_arg_text})
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def update_span_with_context_output(to_wrap, return_value, span: Span):
|
|
385
|
+
package_name: str = to_wrap.get('package')
|
|
386
|
+
output_arg_text = ""
|
|
387
|
+
if "langchain_core.retrievers" in package_name:
|
|
388
|
+
output_arg_text += " ".join([doc.page_content for doc in return_value if hasattr(doc, 'page_content')])
|
|
389
|
+
if len(output_arg_text) > 100:
|
|
390
|
+
output_arg_text = output_arg_text[:100] + "..."
|
|
391
|
+
if "llama_index.core.indices.base_retriever" in package_name and len(return_value) > 0:
|
|
392
|
+
output_arg_text += return_value[0].text
|
|
393
|
+
if "haystack.components.retrievers.in_memory" in package_name:
|
|
394
|
+
output_arg_text += " ".join([doc.content for doc in return_value['documents']])
|
|
395
|
+
if len(output_arg_text) > 100:
|
|
396
|
+
output_arg_text = output_arg_text[:100] + "..."
|
|
397
|
+
if output_arg_text:
|
|
398
|
+
span.add_event(DATA_OUTPUT_KEY, {RESPONSE: output_arg_text})
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def update_span_with_prompt_input(to_wrap, wrapped_args, span: Span):
|
|
265
402
|
input_arg_text = wrapped_args[0]
|
|
266
403
|
|
|
267
404
|
if isinstance(input_arg_text, dict):
|
|
268
|
-
span.add_event(PROMPT_INPUT_KEY,input_arg_text)
|
|
405
|
+
span.add_event(PROMPT_INPUT_KEY, input_arg_text)
|
|
269
406
|
else:
|
|
270
|
-
span.add_event(PROMPT_INPUT_KEY,{QUERY:input_arg_text})
|
|
407
|
+
span.add_event(PROMPT_INPUT_KEY, {QUERY: input_arg_text})
|
|
271
408
|
|
|
272
|
-
|
|
409
|
+
|
|
410
|
+
def update_span_with_prompt_output(to_wrap, wrapped_args, span: Span):
|
|
273
411
|
package_name: str = to_wrap.get('package')
|
|
274
412
|
if isinstance(wrapped_args, str):
|
|
275
|
-
span.add_event(PROMPT_OUTPUT_KEY, {RESPONSE:wrapped_args})
|
|
413
|
+
span.add_event(PROMPT_OUTPUT_KEY, {RESPONSE: wrapped_args})
|
|
414
|
+
if isinstance(wrapped_args, dict):
|
|
415
|
+
span.add_event(PROMPT_OUTPUT_KEY, wrapped_args)
|
|
276
416
|
if "llama_index.core.base.base_query_engine" in package_name:
|
|
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}")
|
|
417
|
+
span.add_event(PROMPT_OUTPUT_KEY, {RESPONSE: wrapped_args.response})
|