monocle-apptrace 0.1.0__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/README.md +50 -26
- 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 +18 -1
- monocle_apptrace/instrumentor.py +15 -18
- monocle_apptrace/langchain/__init__.py +6 -3
- monocle_apptrace/llamaindex/__init__.py +8 -7
- monocle_apptrace/metamodel/README.md +47 -0
- monocle_apptrace/metamodel/entities/README.md +77 -0
- 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/{wrapper_config/lang_chain_methods.json → metamodel/maps/langchain_methods.json} +31 -8
- monocle_apptrace/{wrapper_config/llama_index_methods.json → metamodel/maps/llamaindex_methods.json} +12 -8
- monocle_apptrace/metamodel/spans/README.md +121 -0
- monocle_apptrace/metamodel/spans/span_example.json +140 -0
- monocle_apptrace/metamodel/spans/span_format.json +55 -0
- monocle_apptrace/metamodel/spans/span_types.json +16 -0
- monocle_apptrace/utils.py +108 -9
- monocle_apptrace/wrap_common.py +247 -98
- monocle_apptrace/wrapper.py +3 -1
- monocle_apptrace-0.2.0.dist-info/METADATA +115 -0
- monocle_apptrace-0.2.0.dist-info/RECORD +44 -0
- monocle_apptrace-0.1.0.dist-info/METADATA +0 -77
- monocle_apptrace-0.1.0.dist-info/RECORD +0 -22
- /monocle_apptrace/{wrapper_config → metamodel/maps}/haystack_methods.json +0 -0
- {monocle_apptrace-0.1.0.dist-info → monocle_apptrace-0.2.0.dist-info}/WHEEL +0 -0
- {monocle_apptrace-0.1.0.dist-info → monocle_apptrace-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {monocle_apptrace-0.1.0.dist-info → monocle_apptrace-0.2.0.dist-info}/licenses/NOTICE +0 -0
monocle_apptrace/wrap_common.py
CHANGED
|
@@ -1,24 +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
|
|
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
|
-
|
|
18
|
-
CONTEXT_PROPERTIES_KEY = "workflow_context_properties"
|
|
18
|
+
SESSION_PROPERTIES_KEY = "session"
|
|
19
19
|
INFRA_SERVICE_KEY = "infra_service_name"
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
TYPE = "type"
|
|
22
|
+
PROVIDER = "provider_name"
|
|
23
|
+
EMBEDDING_MODEL = "embedding_model"
|
|
24
|
+
VECTOR_STORE = 'vector_store'
|
|
25
|
+
META_DATA = 'metadata'
|
|
22
26
|
|
|
23
27
|
WORKFLOW_TYPE_MAP = {
|
|
24
28
|
"llama_index": "workflow.llamaindex",
|
|
@@ -26,6 +30,53 @@ WORKFLOW_TYPE_MAP = {
|
|
|
26
30
|
"haystack": "workflow.haystack"
|
|
27
31
|
}
|
|
28
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
|
+
|
|
61
|
+
framework_vector_store_mapping = {
|
|
62
|
+
'langchain_core.retrievers': lambda instance: {
|
|
63
|
+
'provider': type(instance.vectorstore).__name__,
|
|
64
|
+
'embedding_model': get_embedding_model_for_vectorstore(instance),
|
|
65
|
+
'type': VECTOR_STORE,
|
|
66
|
+
},
|
|
67
|
+
'llama_index.core.indices.base_retriever': lambda instance: {
|
|
68
|
+
'provider': type(instance._vector_store).__name__,
|
|
69
|
+
'embedding_model': get_embedding_model_for_vectorstore(instance),
|
|
70
|
+
'type': VECTOR_STORE,
|
|
71
|
+
},
|
|
72
|
+
'haystack.components.retrievers.in_memory': lambda instance: {
|
|
73
|
+
'provider': instance.__dict__.get("document_store").__class__.__name__,
|
|
74
|
+
'embedding_model': get_embedding_model(),
|
|
75
|
+
'type': VECTOR_STORE,
|
|
76
|
+
},
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
|
|
29
80
|
@with_tracer_wrapper
|
|
30
81
|
def task_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
31
82
|
"""Instruments and calls every function defined in TO_WRAP."""
|
|
@@ -42,34 +93,82 @@ def task_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
|
42
93
|
name = f"langchain.task.{instance.__class__.__name__}"
|
|
43
94
|
|
|
44
95
|
with tracer.start_as_current_span(name) as span:
|
|
96
|
+
process_span(to_wrap, span, instance, args)
|
|
45
97
|
pre_task_processing(to_wrap, instance, args, span)
|
|
46
98
|
return_value = wrapped(*args, **kwargs)
|
|
47
99
|
post_task_processing(to_wrap, span, return_value)
|
|
48
100
|
|
|
49
101
|
return return_value
|
|
50
102
|
|
|
51
|
-
def post_task_processing(to_wrap, span, return_value):
|
|
52
|
-
update_span_with_context_output(to_wrap=to_wrap, return_value=return_value, span=span)
|
|
53
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
|
|
54
107
|
if is_root_span(span):
|
|
55
|
-
workflow_name = span
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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)
|
|
148
|
+
|
|
149
|
+
else:
|
|
150
|
+
logger.warning("empty or entities json is not in correct format")
|
|
59
151
|
|
|
60
|
-
def pre_task_processing(to_wrap, instance, args, span):
|
|
61
|
-
if is_root_span(span):
|
|
62
|
-
update_span_with_prompt_input(to_wrap=to_wrap, wrapped_args=args, span=span)
|
|
63
152
|
|
|
64
|
-
|
|
153
|
+
def post_task_processing(to_wrap, span, return_value):
|
|
154
|
+
try:
|
|
155
|
+
update_span_with_context_output(to_wrap=to_wrap, return_value=return_value, span=span)
|
|
65
156
|
|
|
66
|
-
|
|
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")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def pre_task_processing(to_wrap, instance, args, span):
|
|
67
164
|
try:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
update_span_with_context_input(to_wrap=to_wrap, wrapped_args=args, span=span)
|
|
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)
|
|
72
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")
|
|
73
172
|
|
|
74
173
|
|
|
75
174
|
@with_tracer_wrapper
|
|
@@ -87,12 +186,14 @@ async def atask_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
|
87
186
|
else:
|
|
88
187
|
name = f"langchain.task.{instance.__class__.__name__}"
|
|
89
188
|
with tracer.start_as_current_span(name) as span:
|
|
189
|
+
process_span(to_wrap, span, instance, args)
|
|
90
190
|
pre_task_processing(to_wrap, instance, args, span)
|
|
91
191
|
return_value = await wrapped(*args, **kwargs)
|
|
92
192
|
post_task_processing(to_wrap, span, return_value)
|
|
93
193
|
|
|
94
194
|
return return_value
|
|
95
195
|
|
|
196
|
+
|
|
96
197
|
@with_tracer_wrapper
|
|
97
198
|
async def allm_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
98
199
|
# Some Langchain objects are wrapped elsewhere, so we ignore them here
|
|
@@ -109,16 +210,24 @@ async def allm_wrapper(tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
|
109
210
|
else:
|
|
110
211
|
name = f"langchain.task.{instance.__class__.__name__}"
|
|
111
212
|
with tracer.start_as_current_span(name) as span:
|
|
112
|
-
|
|
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)
|
|
113
220
|
|
|
114
221
|
return_value = await wrapped(*args, **kwargs)
|
|
115
|
-
|
|
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)
|
|
116
225
|
|
|
117
226
|
return return_value
|
|
118
227
|
|
|
228
|
+
|
|
119
229
|
@with_tracer_wrapper
|
|
120
230
|
def llm_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
121
|
-
|
|
122
231
|
# Some Langchain objects are wrapped elsewhere, so we ignore them here
|
|
123
232
|
if instance.__class__.__name__ in ("AgentExecutor"):
|
|
124
233
|
return wrapped(*args, **kwargs)
|
|
@@ -132,86 +241,121 @@ def llm_wrapper(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
|
|
|
132
241
|
name = to_wrap.get("span_name")
|
|
133
242
|
else:
|
|
134
243
|
name = f"langchain.task.{instance.__class__.__name__}"
|
|
244
|
+
|
|
135
245
|
with tracer.start_as_current_span(name) as span:
|
|
136
|
-
|
|
246
|
+
if 'haystack.components.retrievers' in to_wrap['package'] and 'haystack.retriever' in span.name:
|
|
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)
|
|
137
253
|
|
|
138
254
|
return_value = wrapped(*args, **kwargs)
|
|
139
|
-
|
|
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)
|
|
140
258
|
|
|
141
259
|
return return_value
|
|
142
260
|
|
|
261
|
+
|
|
143
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
|
+
|
|
144
268
|
triton_llm_endpoint = os.environ.get("TRITON_LLM_ENDPOINT")
|
|
145
269
|
if triton_llm_endpoint is not None and len(triton_llm_endpoint) > 0:
|
|
146
270
|
curr_span.set_attribute("server_url", triton_llm_endpoint)
|
|
147
271
|
else:
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
#
|
|
152
|
-
model_name =
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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):
|
|
164
293
|
provider_url = ""
|
|
165
|
-
|
|
166
|
-
try
|
|
167
|
-
if isinstance(instance.client._client.base_url.host, str)
|
|
168
|
-
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)
|
|
169
302
|
except:
|
|
170
303
|
pass
|
|
171
304
|
|
|
172
|
-
try
|
|
305
|
+
try:
|
|
173
306
|
if isinstance(instance.api_base, str):
|
|
174
307
|
provider_url = instance.api_base
|
|
175
308
|
except:
|
|
176
309
|
pass
|
|
177
310
|
|
|
178
|
-
try
|
|
311
|
+
try:
|
|
179
312
|
if len(provider_url) > 0:
|
|
180
313
|
parsed_provider_url = urlparse(provider_url)
|
|
181
|
-
curr_span.set_attribute("provider_name", parsed_provider_url.hostname or provider_url)
|
|
182
314
|
except:
|
|
183
315
|
pass
|
|
316
|
+
return parsed_provider_url.hostname or provider_url,inference_endpoint
|
|
317
|
+
|
|
184
318
|
|
|
185
319
|
def is_root_span(curr_span: Span) -> bool:
|
|
186
320
|
return curr_span.parent is None
|
|
187
321
|
|
|
322
|
+
|
|
188
323
|
def get_input_from_args(chain_args):
|
|
189
324
|
if len(chain_args) > 0 and isinstance(chain_args[0], str):
|
|
190
325
|
return chain_args[0]
|
|
191
326
|
return ""
|
|
192
327
|
|
|
193
|
-
|
|
194
|
-
|
|
328
|
+
|
|
329
|
+
def update_span_from_llm_response(response, span: Span, instance):
|
|
195
330
|
# extract token uasge from langchain openai
|
|
196
331
|
if (response is not None and hasattr(response, "response_metadata")):
|
|
197
332
|
response_metadata = response.response_metadata
|
|
198
333
|
token_usage = response_metadata.get("token_usage")
|
|
334
|
+
meta_dict = {}
|
|
199
335
|
if token_usage is not None:
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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)
|
|
203
342
|
# extract token usage from llamaindex openai
|
|
204
|
-
if(response is not None and hasattr(response, "raw")):
|
|
343
|
+
if (response is not None and hasattr(response, "raw")):
|
|
205
344
|
try:
|
|
345
|
+
meta_dict = {}
|
|
206
346
|
if response.raw is not None:
|
|
207
|
-
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)
|
|
208
349
|
if token_usage is not None:
|
|
350
|
+
temperature = instance.__dict__.get("temperature", None)
|
|
351
|
+
meta_dict.update({"temperature": temperature})
|
|
209
352
|
if getattr(token_usage, "completion_tokens", None):
|
|
210
|
-
|
|
353
|
+
meta_dict.update({"completion_tokens": getattr(token_usage, "completion_tokens")})
|
|
211
354
|
if getattr(token_usage, "prompt_tokens", None):
|
|
212
|
-
|
|
355
|
+
meta_dict.update({"prompt_tokens": getattr(token_usage, "prompt_tokens")})
|
|
213
356
|
if getattr(token_usage, "total_tokens", None):
|
|
214
|
-
|
|
357
|
+
meta_dict.update({"total_tokens": getattr(token_usage, "total_tokens")})
|
|
358
|
+
span.add_event(META_DATA, meta_dict)
|
|
215
359
|
except AttributeError:
|
|
216
360
|
token_usage = None
|
|
217
361
|
|
|
@@ -220,49 +364,54 @@ def update_workflow_type(to_wrap, span: Span):
|
|
|
220
364
|
package_name = to_wrap.get('package')
|
|
221
365
|
|
|
222
366
|
for (package, workflow_type) in WORKFLOW_TYPE_MAP.items():
|
|
223
|
-
if(package_name is not None and package in package_name):
|
|
367
|
+
if (package_name is not None and package in package_name):
|
|
224
368
|
span.set_attribute(WORKFLOW_TYPE_KEY, workflow_type)
|
|
225
369
|
|
|
226
|
-
def update_span_with_context_input(to_wrap, wrapped_args ,span: Span):
|
|
227
|
-
package_name: str = to_wrap.get('package')
|
|
228
|
-
if "langchain_core.retrievers" in package_name:
|
|
229
|
-
input_arg_text = wrapped_args[0]
|
|
230
|
-
span.add_event(CONTEXT_INPUT_KEY, {QUERY:input_arg_text})
|
|
231
|
-
if "llama_index.core.indices.base_retriever" in package_name:
|
|
232
|
-
input_arg_text = wrapped_args[0].query_str
|
|
233
|
-
span.add_event(CONTEXT_INPUT_KEY, {QUERY:input_arg_text})
|
|
234
370
|
|
|
235
|
-
def
|
|
371
|
+
def update_span_with_context_input(to_wrap, wrapped_args, span: Span):
|
|
236
372
|
package_name: str = to_wrap.get('package')
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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):
|
|
242
402
|
input_arg_text = wrapped_args[0]
|
|
243
403
|
|
|
244
404
|
if isinstance(input_arg_text, dict):
|
|
245
|
-
span.add_event(PROMPT_INPUT_KEY,input_arg_text)
|
|
405
|
+
span.add_event(PROMPT_INPUT_KEY, input_arg_text)
|
|
246
406
|
else:
|
|
247
|
-
span.add_event(PROMPT_INPUT_KEY,{QUERY:input_arg_text})
|
|
407
|
+
span.add_event(PROMPT_INPUT_KEY, {QUERY: input_arg_text})
|
|
248
408
|
|
|
249
|
-
|
|
409
|
+
|
|
410
|
+
def update_span_with_prompt_output(to_wrap, wrapped_args, span: Span):
|
|
250
411
|
package_name: str = to_wrap.get('package')
|
|
251
412
|
if isinstance(wrapped_args, str):
|
|
252
|
-
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)
|
|
253
416
|
if "llama_index.core.base.base_query_engine" in package_name:
|
|
254
|
-
span.add_event(PROMPT_OUTPUT_KEY, {RESPONSE:wrapped_args.response})
|
|
255
|
-
|
|
256
|
-
def update_tags(instance, span):
|
|
257
|
-
try:
|
|
258
|
-
# copy tags as is from langchain
|
|
259
|
-
span.set_attribute(TAGS, getattr(instance, TAGS))
|
|
260
|
-
except:
|
|
261
|
-
pass
|
|
262
|
-
try:
|
|
263
|
-
# extract embed model and vector store names for llamaindex
|
|
264
|
-
model_name = instance.retriever._embed_model.model_name
|
|
265
|
-
vector_store_name = type(instance.retriever._vector_store).__name__
|
|
266
|
-
span.set_attribute(TAGS, [model_name, vector_store_name])
|
|
267
|
-
except:
|
|
268
|
-
pass
|
|
417
|
+
span.add_event(PROMPT_OUTPUT_KEY, {RESPONSE: wrapped_args.response})
|
monocle_apptrace/wrapper.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
2
|
from monocle_apptrace.haystack import HAYSTACK_METHODS
|
|
4
3
|
from monocle_apptrace.langchain import LANGCHAIN_METHODS
|
|
5
4
|
from monocle_apptrace.llamaindex import LLAMAINDEX_METHODS
|
|
@@ -13,12 +12,15 @@ class WrapperMethod:
|
|
|
13
12
|
object_name: str,
|
|
14
13
|
method: str,
|
|
15
14
|
span_name: str = None,
|
|
15
|
+
output_processor : list[str] = None,
|
|
16
16
|
wrapper = task_wrapper
|
|
17
17
|
):
|
|
18
18
|
self.package = package
|
|
19
19
|
self.object = object_name
|
|
20
20
|
self.method = method
|
|
21
21
|
self.span_name = span_name
|
|
22
|
+
self.output_processor=output_processor
|
|
23
|
+
|
|
22
24
|
self.wrapper = wrapper
|
|
23
25
|
|
|
24
26
|
INBUILT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: monocle_apptrace
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: package with monocle genAI tracing
|
|
5
|
+
Project-URL: Homepage, https://github.com/monocle2ai/monocle
|
|
6
|
+
Project-URL: Issues, https://github.com/monocle2ai/monocle/issues
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
License-File: NOTICE
|
|
9
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Classifier: Operating System :: OS Independent
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Requires-Python: >=3.8
|
|
13
|
+
Requires-Dist: opentelemetry-api>=1.21.0
|
|
14
|
+
Requires-Dist: opentelemetry-instrumentation
|
|
15
|
+
Requires-Dist: opentelemetry-sdk>=1.21.0
|
|
16
|
+
Requires-Dist: requests
|
|
17
|
+
Requires-Dist: wrapt>=1.14.0
|
|
18
|
+
Provides-Extra: aws
|
|
19
|
+
Requires-Dist: boto3==1.35.19; extra == 'aws'
|
|
20
|
+
Provides-Extra: azure
|
|
21
|
+
Requires-Dist: azure-storage-blob==12.22.0; extra == 'azure'
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: datasets==2.20.0; extra == 'dev'
|
|
24
|
+
Requires-Dist: faiss-cpu==1.8.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: instructorembedding==1.0.1; extra == 'dev'
|
|
26
|
+
Requires-Dist: langchain-chroma==0.1.1; extra == 'dev'
|
|
27
|
+
Requires-Dist: langchain-community==0.2.5; extra == 'dev'
|
|
28
|
+
Requires-Dist: langchain-openai==0.1.8; extra == 'dev'
|
|
29
|
+
Requires-Dist: langchain==0.2.5; extra == 'dev'
|
|
30
|
+
Requires-Dist: llama-index-embeddings-huggingface==0.2.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: llama-index-vector-stores-chroma==0.1.9; extra == 'dev'
|
|
32
|
+
Requires-Dist: llama-index==0.10.30; extra == 'dev'
|
|
33
|
+
Requires-Dist: numpy==1.26.4; extra == 'dev'
|
|
34
|
+
Requires-Dist: parameterized==0.9.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: pytest==8.0.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: sentence-transformers==2.6.1; extra == 'dev'
|
|
37
|
+
Requires-Dist: types-requests==2.31.0.20240106; extra == 'dev'
|
|
38
|
+
Description-Content-Type: text/markdown
|
|
39
|
+
|
|
40
|
+
# Monocle for tracing GenAI app code
|
|
41
|
+
|
|
42
|
+
**Monocle** helps developers and platform engineers building or managing GenAI apps monitor these in prod by making it easy to instrument their code to capture traces that are compliant with open-source cloud-native observability ecosystem.
|
|
43
|
+
|
|
44
|
+
**Monocle** is a community-driven OSS framework for tracing GenAI app code governed as a [Linux Foundation AI & Data project](https://lfaidata.foundation/projects/monocle/).
|
|
45
|
+
|
|
46
|
+
## Why Monocle
|
|
47
|
+
|
|
48
|
+
Monocle is built for:
|
|
49
|
+
- **app developers** to trace their app code in any environment without lots of custom code decoration
|
|
50
|
+
- **platform engineers** to instrument apps in prod through wrapping instead of asking app devs to recode
|
|
51
|
+
- **GenAI component providers** to add observability features to their products
|
|
52
|
+
- **enterprises** to consume traces from GenAI apps in their existing open-source observability stack
|
|
53
|
+
|
|
54
|
+
Benefits:
|
|
55
|
+
- Monocle provides an implementation + package, not just a spec
|
|
56
|
+
- No expertise in OpenTelemetry spec required
|
|
57
|
+
- No bespoke implementation of that spec required
|
|
58
|
+
- No last-mile GenAI domain specific code required to instrument your app
|
|
59
|
+
- Monocle provides consistency
|
|
60
|
+
- Connect traces across app code executions, model inference or data retrievals
|
|
61
|
+
- No cleansing of telemetry data across GenAI component providers required
|
|
62
|
+
- Works the same in personal lab dev or org cloud prod environments
|
|
63
|
+
- Send traces to location that fits your scale, budget and observability stack
|
|
64
|
+
- Monocle is fully open source and community driven
|
|
65
|
+
- No vendor lock-in
|
|
66
|
+
- Implementation is transparent
|
|
67
|
+
- You can freely use or customize it to fit your needs
|
|
68
|
+
|
|
69
|
+
## What Monocle provides
|
|
70
|
+
|
|
71
|
+
- Easy to [use](#use-monocle) code instrumentation
|
|
72
|
+
- OpenTelemetry compatible format for [spans](src/monocle_apptrace/metamodel/spans/span_format.json).
|
|
73
|
+
- Community-curated and extensible [metamodel](src/monocle_apptrace/metamodel/README.md) for consisent tracing of GenAI components.
|
|
74
|
+
- Export to local and cloud storage
|
|
75
|
+
|
|
76
|
+
## Use Monocle
|
|
77
|
+
|
|
78
|
+
- Get the Monocle package
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
pip install monocle_apptrace
|
|
82
|
+
```
|
|
83
|
+
- Instrument your app code
|
|
84
|
+
- Import the Monocle package
|
|
85
|
+
```
|
|
86
|
+
from monocle_apptrace.instrumentor import setup_monocle_telemetry
|
|
87
|
+
```
|
|
88
|
+
- Setup instrumentation in your ```main()``` function
|
|
89
|
+
```
|
|
90
|
+
setup_monocle_telemetry(workflow_name="your-app-name")
|
|
91
|
+
```
|
|
92
|
+
- (Optionally) Modify config to alter where traces are sent
|
|
93
|
+
|
|
94
|
+
See [Monocle user guide](Monocle_User_Guide.md) for more details.
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
## Roadmap
|
|
98
|
+
|
|
99
|
+
Goal of Monocle is to support tracing for apps written in *any language* with *any LLM orchestration or agentic framework* and built using models, vectors, agents or other components served up by *any cloud or model inference provider*.
|
|
100
|
+
|
|
101
|
+
Current version supports:
|
|
102
|
+
- Language: (🟢) Python , (🔜) [Typescript](https://github.com/monocle2ai/monocle-typescript)
|
|
103
|
+
- LLM-frameworks: (🟢) Langchain, (🟢) Llamaindex, (🟢) Haystack, (🔜) Flask
|
|
104
|
+
- LLM inference providers: (🟢) OpenAI, (🟢) Azure OpenAI, (🟢) Nvidia Triton, (🔜) AWS Bedrock, (🔜) Google Vertex, (🔜) Azure ML, (🔜) Hugging Face
|
|
105
|
+
- Vector stores: (🟢) FAISS, (🔜) OpenSearch, (🔜) Milvus
|
|
106
|
+
- Exporter: (🟢) stdout, (🟢) file, (🔜) Azure Blob Storage, (🔜) AWS S3, (🔜) Google Cloud Storage
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
## Get involved
|
|
110
|
+
### Provide feedback
|
|
111
|
+
- Submit issues and enhancements requests via Github issues
|
|
112
|
+
|
|
113
|
+
### Contribute
|
|
114
|
+
- Monocle is community based open source project. We welcome your contributions. Please refer to the CONTRIBUTING and CODE_OF_CONDUCT for guidelines. The [contributor's guide](CONTRIBUTING.md) provides technical details of the project.
|
|
115
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
monocle_apptrace/README.md,sha256=T5NFC01bF8VR0oVnAX_n0bhsEtttwqfTxDNAe5Y_ivE,3765
|
|
2
|
+
monocle_apptrace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
monocle_apptrace/constants.py,sha256=wjObbmMTFL201x-bf3EOXevYygwkFH_1ng5dDrpE3z0,810
|
|
4
|
+
monocle_apptrace/instrumentor.py,sha256=FMQ8yVNYGRBTmyUjC2578G8RzSRpHY5HtVN5WK9ndVE,5291
|
|
5
|
+
monocle_apptrace/utils.py,sha256=l-affXaMX6U_wG1rWRE2GPB6dDGtha7kL0MQK4PEptI,6395
|
|
6
|
+
monocle_apptrace/wrap_common.py,sha256=ux2Ob7g1FYDtt1gF4g8XKQhl6Ed5X_iR4CP4GHX4upM,18178
|
|
7
|
+
monocle_apptrace/wrapper.py,sha256=8bsMaCCCbaEJN8hW5YoYkn2XLJ7vFI1HJk6QHihbkto,830
|
|
8
|
+
monocle_apptrace/exporters/base_exporter.py,sha256=1UvywEiKZ0nilcXiy-mZue012yVvYXgwib1x1qQvpBc,1647
|
|
9
|
+
monocle_apptrace/exporters/exporter_processor.py,sha256=BTcBgMuFLHCdCgVvc9TKIo9y8g1BvShI0L4vX6Q-cmk,393
|
|
10
|
+
monocle_apptrace/exporters/file_exporter.py,sha256=gN9pJ_X5pcstVVsyivgHsjWhr443eRa6Y6Hx1rGLQAM,2280
|
|
11
|
+
monocle_apptrace/exporters/monocle_exporters.py,sha256=mvVoGr4itvUBuakH17zNEuXYpHtqRhq2CBH5JXhleoM,1418
|
|
12
|
+
monocle_apptrace/exporters/aws/s3_exporter.py,sha256=S_w2OBkM8eEYK6bBDbfcRV4LdojU1QygOBqjdhcjAl4,6730
|
|
13
|
+
monocle_apptrace/exporters/azure/blob_exporter.py,sha256=dtL8MPISyP1eKM0gGpSoqTUmFuGEfycqF9OCjFF6H6E,5716
|
|
14
|
+
monocle_apptrace/exporters/okahu/okahu_exporter.py,sha256=p2rjStwo0OMEdHWQt_QvREpUWXbDm5jGx3qXeYai4_M,4407
|
|
15
|
+
monocle_apptrace/haystack/__init__.py,sha256=zMvF6Dh1-vVIAQD__HC1ubT5bs-EupUghg7HNhDv7_c,448
|
|
16
|
+
monocle_apptrace/haystack/wrap_node.py,sha256=IK07Wn3Lk1Os9URsyrmB1HXOH2FNdzK9fNLlR8TZdYc,908
|
|
17
|
+
monocle_apptrace/haystack/wrap_openai.py,sha256=Yp916DhOl0WI6virRi3L43snfsQm7PhI28wlDsg19v8,1536
|
|
18
|
+
monocle_apptrace/haystack/wrap_pipeline.py,sha256=yZAw7Hdv7FXe6rrM7gA2y5SjaZYQZCAi0q-R-uqUEvk,2254
|
|
19
|
+
monocle_apptrace/langchain/__init__.py,sha256=3yhbdw1h9I1nVEfnOOPKz9yD5NqwdLSZsxtXbMplRkw,400
|
|
20
|
+
monocle_apptrace/llamaindex/__init__.py,sha256=TgV1ZM8Cz113pZ2aAgpYwsCJyHA2aHOvNoW1QMBp0mM,709
|
|
21
|
+
monocle_apptrace/metamodel/README.md,sha256=KYuuYqgA9PNbOjG0zYj2nAdvNEpyNN_Bk9M2tNdnZ_s,4598
|
|
22
|
+
monocle_apptrace/metamodel/entities/README.md,sha256=dY7Q8QT_Ju-2ul65J-k0x6beDLvRirlbGufZN1Q0tpk,2068
|
|
23
|
+
monocle_apptrace/metamodel/entities/app_hosting_types.json,sha256=MWeHUe77n4HvO1hm2PX8iWjbRONBuNy2I84vd-lymYk,568
|
|
24
|
+
monocle_apptrace/metamodel/entities/entities.json,sha256=6eDoDm_6aPUgH_ROjI2H9Tk3J5Lj55VkX3Li-TJmaxg,1285
|
|
25
|
+
monocle_apptrace/metamodel/entities/inference_types.json,sha256=Gkq7qNw5Qn91tuq-rBxHJ4BSzT-etCshNjT5_G4Bg8I,651
|
|
26
|
+
monocle_apptrace/metamodel/entities/model_types.json,sha256=Oz9DzuX_ptpLEVTc1Epv4_Y80TrYxR7cyc9kM0Zk780,785
|
|
27
|
+
monocle_apptrace/metamodel/entities/vector_store_types.json,sha256=EA0KayHMOG7wMIkec54en03_3yT1qpdGh5TiDrAoh4g,462
|
|
28
|
+
monocle_apptrace/metamodel/entities/workflow_types.json,sha256=eD0W3_FocKrzrrW0bks0hIJb9Kj7w8c1dpXzxmGLOwk,386
|
|
29
|
+
monocle_apptrace/metamodel/maps/haystack_methods.json,sha256=JmngkaKICAzOyrWNTWEOLYFrp99l5wcERYKE_SQRNxE,698
|
|
30
|
+
monocle_apptrace/metamodel/maps/langchain_methods.json,sha256=lfU8nd6td5qzTI01glM063Vg0UoZM9HGPirwN4OZcKc,4285
|
|
31
|
+
monocle_apptrace/metamodel/maps/llamaindex_methods.json,sha256=pZ4d1DS4D6Wgpz9EBOrEuJUFN58jtu75E8ELyGRBHyM,2636
|
|
32
|
+
monocle_apptrace/metamodel/maps/attributes/inference/langchain_entities.json,sha256=E_K3eHv_e9Va2AcjNLHcXeAgePB1uolTMdRnkxxtNqU,1152
|
|
33
|
+
monocle_apptrace/metamodel/maps/attributes/inference/llamaindex_entities.json,sha256=LuAa9ZKl3mO2VUiXMVGZNEfSHOZ0n5qsVQLqWRHczVM,1122
|
|
34
|
+
monocle_apptrace/metamodel/maps/attributes/retrieval/langchain_entities.json,sha256=vSe2kbeJ7izmj5TBdQpX5pJ3hbjdY4SkcXvWPiam7oo,718
|
|
35
|
+
monocle_apptrace/metamodel/maps/attributes/retrieval/llamaindex_entities.json,sha256=BtWeSXUPzKR9-vuhZUUhn2Gz9Y4SXBZoFC81K1q0XwQ,712
|
|
36
|
+
monocle_apptrace/metamodel/spans/README.md,sha256=_uMkLLaWitQ_rPh7oQbW5Oe7uGSv2h_QA6YwxHRJi74,5433
|
|
37
|
+
monocle_apptrace/metamodel/spans/span_example.json,sha256=7ZAUssL1UYXlbKHre8PfeNlIhHnM28e2CH9EyHDTOt8,4402
|
|
38
|
+
monocle_apptrace/metamodel/spans/span_format.json,sha256=GhfioGgMhG7St0DeYA1fgNtMkbr9wiQ1L2hovekRQ24,1512
|
|
39
|
+
monocle_apptrace/metamodel/spans/span_types.json,sha256=jwVyPbYMhEf2Ea6Egmb3m1Za28ap9dgZIpJDhdE1BlY,361
|
|
40
|
+
monocle_apptrace-0.2.0.dist-info/METADATA,sha256=zaIOrJwa-RLX1bWeBEfTCfeBGC2cuNeXKwPHAYYNRT8,5364
|
|
41
|
+
monocle_apptrace-0.2.0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
42
|
+
monocle_apptrace-0.2.0.dist-info/licenses/LICENSE,sha256=ay9trLiP5I7ZsFXo6AqtkLYdRqe5S9r-DrPOvsNlZrg,9136
|
|
43
|
+
monocle_apptrace-0.2.0.dist-info/licenses/NOTICE,sha256=9jn4xtwM_uUetJMx5WqGnhrR7MIhpoRlpokjSTlyt8c,112
|
|
44
|
+
monocle_apptrace-0.2.0.dist-info/RECORD,,
|