monocle-apptrace 0.3.0b4__tar.gz → 0.3.0b5__tar.gz

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 (82) hide show
  1. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/CHANGELOG.md +1 -1
  2. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/PKG-INFO +2 -1
  3. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/pyproject.toml +2 -1
  4. monocle_apptrace-0.3.0b5/src/monocle_apptrace/__main__.py +19 -0
  5. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/exporters/monocle_exporters.py +5 -4
  6. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/common/constants.py +5 -0
  7. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/common/instrumentor.py +82 -11
  8. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/common/span_handler.py +25 -10
  9. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/common/utils.py +112 -5
  10. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/common/wrapper.py +94 -0
  11. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/common/wrapper_method.py +68 -0
  12. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/botocore/_helper.py +0 -31
  13. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/botocore/handlers/botocore_span_handler.py +25 -0
  14. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/botocore/methods.py +6 -6
  15. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/flask/_helper.py +29 -0
  16. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/flask/methods.py +13 -0
  17. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/langchain/_helper.py +4 -0
  18. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/langgraph/_helper.py +48 -0
  19. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/langgraph/entities/__init__.py +0 -0
  20. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/langgraph/entities/inference.py +56 -0
  21. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/langgraph/methods.py +14 -0
  22. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/llamaindex/__init__.py +0 -0
  23. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/_helper.py +37 -19
  24. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/__init__.py +0 -0
  25. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/agent.py +47 -0
  26. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/methods.py +9 -0
  27. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/openai/__init__.py +0 -0
  28. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/openai/_helper.py +88 -0
  29. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/openai/entities/__init__.py +0 -0
  30. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/openai/entities/inference.py +71 -0
  31. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/openai/entities/retrieval.py +24 -0
  32. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/openai/methods.py +25 -0
  33. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/requests/__init__.py +2 -0
  34. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/requests/_helper.py +31 -0
  35. monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/requests/methods.py +12 -0
  36. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/common/wrapper.py +0 -69
  37. monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/common/wrapper_method.py +0 -45
  38. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/.gitignore +0 -0
  39. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/CODEOWNERS.md +0 -0
  40. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/CODE_OF_CONDUCT.md +0 -0
  41. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/CONTRIBUTING.md +0 -0
  42. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/COPYRIGHT.template +0 -0
  43. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/LICENSE +0 -0
  44. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/MAINTAINER.md +0 -0
  45. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/Monocle_User_Guide.md +0 -0
  46. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/Monocle_committer_guide.md +0 -0
  47. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/Monocle_contributor_guide.md +0 -0
  48. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/NOTICE +0 -0
  49. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/README.md +0 -0
  50. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/SECURITY.md +0 -0
  51. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/README.md +0 -0
  52. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/__init__.py +0 -0
  53. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/exporters/aws/s3_exporter.py +0 -0
  54. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/exporters/aws/s3_exporter_opendal.py +0 -0
  55. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/exporters/azure/blob_exporter.py +0 -0
  56. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/exporters/azure/blob_exporter_opendal.py +0 -0
  57. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/exporters/base_exporter.py +0 -0
  58. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/exporters/exporter_processor.py +0 -0
  59. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/exporters/file_exporter.py +0 -0
  60. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/exporters/okahu/okahu_exporter.py +0 -0
  61. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/__init__.py +0 -0
  62. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/common/__init__.py +0 -0
  63. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/__init__.py +0 -0
  64. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/botocore/__init__.py +0 -0
  65. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/botocore/entities/__init__.py +0 -0
  66. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/botocore/entities/inference.py +0 -0
  67. {monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/haystack → monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/flask}/__init__.py +0 -0
  68. {monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/haystack/entities → monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/haystack}/__init__.py +0 -0
  69. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/haystack/_helper.py +0 -0
  70. {monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/langchain → monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/haystack/entities}/__init__.py +0 -0
  71. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/inference.py +0 -0
  72. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/haystack/entities/retrieval.py +0 -0
  73. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/haystack/methods.py +0 -0
  74. {monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/langchain/entities → monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/langchain}/__init__.py +0 -0
  75. {monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/llamaindex → monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/langchain/entities}/__init__.py +0 -0
  76. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/inference.py +0 -0
  77. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/langchain/entities/retrieval.py +0 -0
  78. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/langchain/methods.py +0 -0
  79. {monocle_apptrace-0.3.0b4/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities → monocle_apptrace-0.3.0b5/src/monocle_apptrace/instrumentation/metamodel/langgraph}/__init__.py +0 -0
  80. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/inference.py +0 -0
  81. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/src/monocle_apptrace/instrumentation/metamodel/llamaindex/entities/retrieval.py +0 -0
  82. {monocle_apptrace-0.3.0b4 → monocle_apptrace-0.3.0b5}/tox.ini +0 -0
@@ -1,4 +1,4 @@
1
- ## Version 0.3.0b4 (2024-12-10)
1
+ ## Version 0.3.0b5 (2024-12-10)
2
2
 
3
3
  - Add dev dependency for Mistral AI integration ([#81](https://github.com/monocle2ai/monocle/pull/81))
4
4
  - Add VectorStore deployment URL capture support ([#80](https://github.com/monocle2ai/monocle/pull/80))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: monocle_apptrace
3
- Version: 0.3.0b4
3
+ Version: 0.3.0b5
4
4
  Summary: package with monocle genAI tracing
5
5
  Project-URL: Homepage, https://github.com/monocle2ai/monocle
6
6
  Project-URL: Issues, https://github.com/monocle2ai/monocle/issues
@@ -35,6 +35,7 @@ Requires-Dist: langchain-mistralai==0.1.13; extra == 'dev'
35
35
  Requires-Dist: langchain-openai==0.1.8; extra == 'dev'
36
36
  Requires-Dist: langchain==0.2.5; extra == 'dev'
37
37
  Requires-Dist: langchainhub==0.1.21; extra == 'dev'
38
+ Requires-Dist: langgraph==0.2.68; extra == 'dev'
38
39
  Requires-Dist: llama-index-embeddings-huggingface==0.2.0; extra == 'dev'
39
40
  Requires-Dist: llama-index-llms-azure-openai==0.1.9; extra == 'dev'
40
41
  Requires-Dist: llama-index-llms-mistralai==0.1.20; extra == 'dev'
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "monocle_apptrace"
7
- version = "0.3.0b4"
7
+ version = "0.3.0b5"
8
8
  authors = []
9
9
  description = "package with monocle genAI tracing"
10
10
  readme = "README.md"
@@ -71,6 +71,7 @@ dev = [
71
71
  'chromadb==0.4.22',
72
72
  'flask',
73
73
  'opentelemetry-instrumentation-flask',
74
+ 'langgraph==0.2.68',
74
75
  'opendal==0.45.14'
75
76
  ]
76
77
 
@@ -0,0 +1,19 @@
1
+ import sys, os
2
+ import runpy
3
+ from monocle_apptrace.instrumentation.common.instrumentor import setup_monocle_telemetry
4
+
5
+ def main():
6
+ if len(sys.argv) < 2 or not sys.argv[1].endswith(".py"):
7
+ print("Usage: python -m monocle_apptrace <your-main-module-file> <args>")
8
+ sys.exit(1)
9
+ file_name = os.path.basename(sys.argv[1])
10
+ workflow_name = file_name[:-3]
11
+ setup_monocle_telemetry(workflow_name=workflow_name)
12
+ sys.argv.pop(0)
13
+
14
+ try:
15
+ runpy.run_path(path_name=sys.argv[0], run_name="__main__")
16
+ except Exception as e:
17
+ print(e)
18
+ if __name__ == "__main__":
19
+ main()
@@ -1,9 +1,10 @@
1
1
  from typing import Dict, Any, List
2
2
  import os
3
- import warnings
3
+ import logging
4
4
  from importlib import import_module
5
5
  from opentelemetry.sdk.trace.export import SpanExporter, ConsoleSpanExporter
6
6
  from monocle_apptrace.exporters.file_exporter import FileSpanExporter
7
+ logger = logging.getLogger(__name__)
7
8
 
8
9
  monocle_exporters: Dict[str, Any] = {
9
10
  "s3": {"module": "monocle_apptrace.exporters.aws.s3_exporter", "class": "S3SpanExporter"},
@@ -25,21 +26,21 @@ def get_monocle_exporter() -> List[SpanExporter]:
25
26
  try:
26
27
  exporter_class_path = monocle_exporters[exporter_name]
27
28
  except KeyError:
28
- warnings.warn(f"Unsupported Monocle span exporter '{exporter_name}', skipping.")
29
+ logger.debug(f"Unsupported Monocle span exporter '{exporter_name}', skipping.")
29
30
  continue
30
31
  try:
31
32
  exporter_module = import_module(exporter_class_path["module"])
32
33
  exporter_class = getattr(exporter_module, exporter_class_path["class"])
33
34
  exporters.append(exporter_class())
34
35
  except Exception as ex:
35
- warnings.warn(
36
+ logger.debug(
36
37
  f"Unable to initialize Monocle span exporter '{exporter_name}', error: {ex}. Using ConsoleSpanExporter as a fallback.")
37
38
  exporters.append(ConsoleSpanExporter())
38
39
  continue
39
40
 
40
41
  # If no exporters were created, default to FileSpanExporter
41
42
  if not exporters:
42
- warnings.warn("No valid Monocle span exporters configured. Defaulting to FileSpanExporter.")
43
+ logger.debug("No valid Monocle span exporters configured. Defaulting to FileSpanExporter.")
43
44
  exporters.append(FileSpanExporter())
44
45
 
45
46
  return exporters
@@ -37,6 +37,7 @@ service_name_map = {
37
37
  GITHUB_CODESPACE_SERVICE_NAME: GITHUB_CODESPACE_IDENTIFIER_ENV_NAME
38
38
  }
39
39
 
40
+ MONOCLE_INSTRUMENTOR = "monocle_apptrace"
40
41
  WORKFLOW_TYPE_KEY = "workflow_type"
41
42
  DATA_INPUT_KEY = "data.input"
42
43
  DATA_OUTPUT_KEY = "data.output"
@@ -47,3 +48,7 @@ RESPONSE = "response"
47
48
  SESSION_PROPERTIES_KEY = "session"
48
49
  INFRA_SERVICE_KEY = "infra_service_name"
49
50
  META_DATA = 'metadata'
51
+ MONOCLE_SCOPE_NAME_PREFIX = "monocle.scope."
52
+ SCOPE_METHOD_LIST = 'MONOCLE_SCOPE_METHODS'
53
+ SCOPE_METHOD_FILE = 'monocle_scopes.json'
54
+ SCOPE_CONFIG_PATH = 'MONOCLE_SCOPE_CONFIG_PATH'
@@ -3,6 +3,7 @@ from typing import Collection, Dict, List, Union
3
3
  import random
4
4
  import uuid
5
5
  from opentelemetry import trace
6
+ from contextlib import contextmanager
6
7
  from opentelemetry.context import attach, get_value, set_value, get_current, detach
7
8
  from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
8
9
  from opentelemetry.instrumentation.utils import unwrap
@@ -19,8 +20,14 @@ from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
19
20
  from monocle_apptrace.instrumentation.common.wrapper_method import (
20
21
  DEFAULT_METHODS_LIST,
21
22
  WrapperMethod,
23
+ MONOCLE_SPAN_HANDLERS
22
24
  )
23
-
25
+ from monocle_apptrace.instrumentation.common.wrapper import scope_wrapper
26
+ from monocle_apptrace.instrumentation.common.utils import (
27
+ set_scope, remove_scope, http_route_handler, load_scopes
28
+ )
29
+ from monocle_apptrace.instrumentation.common.constants import MONOCLE_INSTRUMENTOR
30
+ from functools import wraps
24
31
  logger = logging.getLogger(__name__)
25
32
 
26
33
  SESSION_PROPERTIES_KEY = "session"
@@ -29,8 +36,6 @@ _instruments = ()
29
36
 
30
37
  monocle_tracer_provider: TracerProvider = None
31
38
 
32
- MONOCLE_INSTRUMENTOR = "monocle_apptrace"
33
-
34
39
  class MonocleInstrumentor(BaseInstrumentor):
35
40
  workflow_name: str = ""
36
41
  user_wrapper_methods: list[Union[dict,WrapperMethod]] = []
@@ -45,17 +50,36 @@ class MonocleInstrumentor(BaseInstrumentor):
45
50
  union_with_default_methods: bool = True
46
51
  ) -> None:
47
52
  self.user_wrapper_methods = user_wrapper_methods or []
48
- self.handlers = handlers or {'default':SpanHandler()}
53
+ self.handlers = handlers
54
+ if self.handlers is not None:
55
+ for key, val in MONOCLE_SPAN_HANDLERS.items():
56
+ if key not in self.handlers:
57
+ self.handlers[key] = val
58
+ else:
59
+ self.handlers = MONOCLE_SPAN_HANDLERS
49
60
  self.union_with_default_methods = union_with_default_methods
50
61
  super().__init__()
51
62
 
63
+ def get_instrumentor(self, tracer):
64
+ def instrumented_endpoint_invoke(to_wrap,wrapped, span_name, instance,fn):
65
+ @wraps(fn)
66
+ def with_instrumentation(*args, **kwargs):
67
+ handler = SpanHandler()
68
+ with tracer.start_as_current_span(span_name) as span:
69
+ response = fn(*args, **kwargs)
70
+ handler.hydrate_span(to_wrap, span=span, wrapped=wrapped, instance=instance, args=args, kwargs=kwargs,
71
+ result=response)
72
+ return response
73
+
74
+ return with_instrumentation
75
+ return instrumented_endpoint_invoke
76
+
52
77
  def instrumentation_dependencies(self) -> Collection[str]:
53
78
  return _instruments
54
79
 
55
80
  def _instrument(self, **kwargs):
56
81
  tracer_provider: TracerProvider = kwargs.get("tracer_provider")
57
- global monocle_tracer_provider
58
- monocle_tracer_provider = tracer_provider
82
+ set_tracer_provider(tracer_provider)
59
83
  tracer = get_tracer(instrumenting_module_name=MONOCLE_INSTRUMENTOR, tracer_provider=tracer_provider)
60
84
 
61
85
  final_method_list = []
@@ -67,6 +91,10 @@ class MonocleInstrumentor(BaseInstrumentor):
67
91
  final_method_list.append(method)
68
92
  elif isinstance(method, WrapperMethod):
69
93
  final_method_list.append(method.to_dict())
94
+
95
+ for method in load_scopes():
96
+ method['wrapper_method'] = scope_wrapper
97
+ final_method_list.append(method)
70
98
 
71
99
  for method_config in final_method_list:
72
100
  target_package = method_config.get("package", None)
@@ -77,6 +105,10 @@ class MonocleInstrumentor(BaseInstrumentor):
77
105
  handler_key = method_config.get("span_handler",'default')
78
106
  try:
79
107
  handler = self.handlers.get(handler_key)
108
+ if not handler:
109
+ logger.warning("incorrect or empty handler falling back to default handler")
110
+ handler = self.handlers.get('default')
111
+ handler.set_instrumentor(self.get_instrumentor(tracer))
80
112
  wrap_function_wrapper(
81
113
  target_package,
82
114
  f"{target_object}.{target_method}" if target_object else target_method,
@@ -108,6 +140,14 @@ class MonocleInstrumentor(BaseInstrumentor):
108
140
  object:{wrap_object},
109
141
  method:{wrap_method}""")
110
142
 
143
+ def set_tracer_provider(tracer_provider: TracerProvider):
144
+ global monocle_tracer_provider
145
+ monocle_tracer_provider = tracer_provider
146
+
147
+ def get_tracer_provider() -> TracerProvider:
148
+ global monocle_tracer_provider
149
+ return monocle_tracer_provider
150
+
111
151
  def setup_monocle_telemetry(
112
152
  workflow_name: str,
113
153
  span_processors: List[SpanProcessor] = None,
@@ -119,7 +159,7 @@ def setup_monocle_telemetry(
119
159
  })
120
160
  exporters = get_monocle_exporter()
121
161
  span_processors = span_processors or [BatchSpanProcessor(exporter) for exporter in exporters]
122
- trace_provider = TracerProvider(resource=resource)
162
+ set_tracer_provider(TracerProvider(resource=resource))
123
163
  attach(set_value("workflow_name", workflow_name))
124
164
  tracer_provider_default = trace.get_tracer_provider()
125
165
  provider_type = type(tracer_provider_default).__name__
@@ -129,14 +169,14 @@ def setup_monocle_telemetry(
129
169
  if not is_proxy_provider:
130
170
  tracer_provider_default.add_span_processor(processor)
131
171
  else:
132
- trace_provider.add_span_processor(processor)
172
+ get_tracer_provider().add_span_processor(processor)
133
173
  if is_proxy_provider:
134
- trace.set_tracer_provider(trace_provider)
174
+ trace.set_tracer_provider(get_tracer_provider())
135
175
  instrumentor = MonocleInstrumentor(user_wrapper_methods=wrapper_methods or [],
136
176
  handlers=span_handlers, union_with_default_methods = union_with_default_methods)
137
177
  # instrumentor.app_name = workflow_name
138
178
  if not instrumentor.is_instrumented_by_opentelemetry:
139
- instrumentor.instrument(trace_provider=trace_provider)
179
+ instrumentor.instrument(trace_provider=get_tracer_provider())
140
180
 
141
181
  return instrumentor
142
182
 
@@ -156,7 +196,7 @@ def propagate_trace_id(traceId = "", use_trace_context = False):
156
196
  try:
157
197
  if traceId.startswith("0x"):
158
198
  traceId = traceId.lstrip("0x")
159
- tracer = get_tracer(instrumenting_module_name= MONOCLE_INSTRUMENTOR, tracer_provider= monocle_tracer_provider)
199
+ tracer = get_tracer(instrumenting_module_name= MONOCLE_INSTRUMENTOR, tracer_provider= get_tracer_provider())
160
200
  initial_id_generator = tracer.id_generator
161
201
  _parent_span_context = get_current() if use_trace_context else None
162
202
  if traceId and is_valid_trace_id_uuid(traceId):
@@ -194,6 +234,37 @@ def is_valid_trace_id_uuid(traceId: str) -> bool:
194
234
  pass
195
235
  return False
196
236
 
237
+ def start_scope(scope_name: str, scope_value:str = None) -> object:
238
+ return set_scope(scope_name, scope_value)
239
+
240
+ def stop_scope(token:object) -> None:
241
+ remove_scope(token)
242
+ return
243
+
244
+ @contextmanager
245
+ def monocle_trace_scope(scope_name: str, scope_value:str = None):
246
+ token = start_scope(scope_name, scope_value)
247
+ try:
248
+ yield
249
+ finally:
250
+ stop_scope(token)
251
+
252
+ def monocle_trace_scope_method(scope_name: str):
253
+ def decorator(func):
254
+ def wrapper(*args, **kwargs):
255
+ token = start_scope(scope_name)
256
+ try:
257
+ result = func(*args, **kwargs)
258
+ return result
259
+ finally:
260
+ stop_scope(token)
261
+ return wrapper
262
+ return decorator
263
+
264
+ def monocle_trace_http_route(func):
265
+ def wrapper(req):
266
+ return http_route_handler(req.headers, func, req)
267
+ return wrapper
197
268
 
198
269
  class FixedIdGenerator(id_generator.IdGenerator):
199
270
  def __init__(
@@ -10,7 +10,7 @@ from monocle_apptrace.instrumentation.common.constants import (
10
10
  service_name_map,
11
11
  service_type_map,
12
12
  )
13
- from monocle_apptrace.instrumentation.common.utils import set_attribute
13
+ from monocle_apptrace.instrumentation.common.utils import set_attribute, get_scopes
14
14
 
15
15
  logger = logging.getLogger(__name__)
16
16
 
@@ -24,10 +24,25 @@ WORKFLOW_TYPE_MAP = {
24
24
 
25
25
  class SpanHandler:
26
26
 
27
+ def __init__(self,instrumentor=None):
28
+ self.instrumentor=instrumentor
29
+
30
+ def set_instrumentor(self, instrumentor):
31
+ self.instrumentor = instrumentor
32
+
27
33
  def validate(self, to_wrap, wrapped, instance, args, kwargs):
28
34
  pass
29
35
 
30
- def pre_task_processing(self, to_wrap, wrapped, instance, args, span):
36
+ def pre_tracing(self, to_wrap, wrapped, instance, args, kwargs):
37
+ pass
38
+
39
+ def post_tracing(self, to_wrap, wrapped, instance, args, kwargs, return_value):
40
+ pass
41
+
42
+ def skip_span(self, to_wrap, wrapped, instance, args, kwargs) -> bool:
43
+ return False
44
+
45
+ def pre_task_processing(self, to_wrap, wrapped, instance, args,kwargs, span):
31
46
  if self.__is_root_span(span):
32
47
  try:
33
48
  sdk_version = version("monocle_apptrace")
@@ -37,14 +52,9 @@ class SpanHandler:
37
52
  if "pipeline" in to_wrap['package']:
38
53
  set_attribute(QUERY, args[0]['prompt_builder']['question'])
39
54
 
40
-
41
-
42
55
  def post_task_processing(self, to_wrap, wrapped, instance, args, kwargs, result, span):
43
56
  pass
44
57
 
45
- def set_context_properties(self, to_wrap, wrapped, instance, args, kwargs):
46
- pass
47
-
48
58
  def hydrate_span(self, to_wrap, wrapped, instance, args, kwargs, result, span):
49
59
  self.hydrate_attributes(to_wrap, wrapped, instance, args, kwargs, result, span)
50
60
  self.hydrate_events(to_wrap, wrapped, instance, args, kwargs, result, span)
@@ -72,16 +82,21 @@ class SpanHandler:
72
82
  try:
73
83
  arguments = {"instance":instance, "args":args, "kwargs":kwargs, "result":result}
74
84
  result = accessor(arguments)
75
- if result and isinstance(result, str):
85
+ if result and isinstance(result, (str, list)):
76
86
  span.set_attribute(attribute_name, result)
77
87
  except Exception as e:
78
88
  logger.debug(f"Error processing accessor: {e}")
79
89
  else:
80
- logger.warning(f"{' and '.join([key for key in ['attribute', 'accessor'] if not processor.get(key)])} not found or incorrect in entity JSON")
90
+ logger.debug(f"{' and '.join([key for key in ['attribute', 'accessor'] if not processor.get(key)])} not found or incorrect in entity JSON")
81
91
  span_index += 1
82
92
  else:
83
- logger.warning("attributes not found or incorrect written in entity json")
93
+ logger.debug("attributes not found or incorrect written in entity json")
84
94
 
95
+ # set scopes as attributes by calling get_scopes()
96
+ # scopes is a Mapping[str:object], iterate directly with .items()
97
+ for scope_key, scope_value in get_scopes().items():
98
+ span.set_attribute(f"scope.{scope_key}", scope_value)
99
+
85
100
  if span_index > 0:
86
101
  span.set_attribute("entity.count", span_index)
87
102
 
@@ -1,16 +1,33 @@
1
- import logging
2
- from typing import Callable, Generic, Optional, TypeVar
1
+ import logging, json
2
+ import os
3
+ from typing import Callable, Generic, Optional, TypeVar, Mapping
4
+ from threading import local
3
5
 
4
- from opentelemetry.context import attach, detach, get_current, get_value, set_value
5
- from opentelemetry.trace import NonRecordingSpan, Span
6
+ from opentelemetry.context import attach, detach, get_current, get_value, set_value, Context
7
+ from opentelemetry.trace import NonRecordingSpan, Span, get_tracer
6
8
  from opentelemetry.trace.propagation import _SPAN_KEY
9
+ from opentelemetry.sdk.trace import id_generator, TracerProvider
10
+ from opentelemetry.propagate import inject, extract
11
+ from opentelemetry import baggage
12
+ from monocle_apptrace.instrumentation.common.constants import MONOCLE_SCOPE_NAME_PREFIX, SCOPE_METHOD_FILE, SCOPE_CONFIG_PATH
7
13
 
8
14
  T = TypeVar('T')
9
15
  U = TypeVar('U')
10
16
 
11
17
  logger = logging.getLogger(__name__)
12
18
 
19
+ monocle_tracer_provider: TracerProvider = None
13
20
  embedding_model_context = {}
21
+ scope_id_generator = id_generator.RandomIdGenerator()
22
+ http_scopes:dict[str:str] = {}
23
+
24
+ def set_tracer_provider(tracer_provider: TracerProvider):
25
+ global monocle_tracer_provider
26
+ monocle_tracer_provider = tracer_provider
27
+
28
+ def get_tracer_provider() -> TracerProvider:
29
+ global monocle_tracer_provider
30
+ return monocle_tracer_provider
14
31
 
15
32
  def set_span_attribute(span, name, value):
16
33
  if value is not None:
@@ -139,6 +156,89 @@ def get_nested_value(data, keys):
139
156
  def get_keys_as_tuple(dictionary, *keys):
140
157
  return tuple(next((value for key, value in dictionary.items() if key.endswith(k) and value is not None), None) for k in keys)
141
158
 
159
+ def load_scopes() -> dict:
160
+ methods_data = []
161
+ scope_methods = []
162
+ try:
163
+ scope_config_file_path = os.environ.get(SCOPE_CONFIG_PATH,
164
+ os.path.join(os.getcwd(), SCOPE_METHOD_FILE))
165
+ with open(scope_config_file_path) as f:
166
+ methods_data = json.load(f)
167
+ for method in methods_data:
168
+ if method.get('http_header'):
169
+ http_scopes[method.get('http_header')] = method.get('scope_name')
170
+ else:
171
+ scope_methods.append(method)
172
+ except Exception as e:
173
+ logger.debug(f"Error loading scope methods from file: {e}")
174
+ return scope_methods
175
+
176
+ def __generate_scope_id() -> str:
177
+ global scope_id_generator
178
+ return f"{hex(scope_id_generator.generate_trace_id())}"
179
+
180
+ def set_scope(scope_name: str, scope_value:str = None) -> object:
181
+ return set_scopes({scope_name: scope_value})
182
+
183
+ def set_scopes(scopes:dict[str, object], baggage_context:Context = None) -> object:
184
+ if baggage_context is None:
185
+ baggage_context:Context = get_current()
186
+ for scope_name, scope_value in scopes.items():
187
+ if scope_value is None:
188
+ scope_value = __generate_scope_id()
189
+ baggage_context = baggage.set_baggage(f"{MONOCLE_SCOPE_NAME_PREFIX}{scope_name}", scope_value, baggage_context)
190
+
191
+ token:object = attach(baggage_context)
192
+ return token
193
+
194
+ def remove_scope(token:object) -> None:
195
+ remove_scopes(token)
196
+
197
+ def remove_scopes(token:object) -> None:
198
+ if token is not None:
199
+ detach(token)
200
+
201
+ def get_scopes() -> dict[str, object]:
202
+ monocle_scopes:dict[str, object] = {}
203
+ for key, val in baggage.get_all().items():
204
+ if key.startswith(MONOCLE_SCOPE_NAME_PREFIX):
205
+ monocle_scopes[key[len(MONOCLE_SCOPE_NAME_PREFIX):]] = val
206
+ return monocle_scopes
207
+
208
+ def get_baggage_for_scopes():
209
+ baggage_context:Context = None
210
+ for scope_key, scope_value in get_scopes():
211
+ monocle_scope_name = f"{MONOCLE_SCOPE_NAME_PREFIX}{scope_key}"
212
+ baggage_context = baggage.set_baggage(monocle_scope_name, scope_value, context=baggage_context)
213
+ return baggage_context
214
+
215
+ def set_scopes_from_baggage(baggage_context:Context):
216
+ for scope_key, scope_value in baggage.get_all(baggage_context):
217
+ if scope_key.startswith(MONOCLE_SCOPE_NAME_PREFIX):
218
+ scope_name = scope_key[len(MONOCLE_SCOPE_NAME_PREFIX):]
219
+ set_scope(scope_name, scope_value)
220
+
221
+ def extract_http_headers(headers) -> object:
222
+ global http_scopes
223
+ trace_context:Context = extract(headers, context=get_current())
224
+ imported_scope:dict[str, object] = {}
225
+ for http_header, http_scope in http_scopes.items():
226
+ if http_header in headers:
227
+ imported_scope[http_scope] = f"{http_header}: {headers[http_header]}"
228
+ token = set_scopes(imported_scope, trace_context)
229
+ return token
230
+
231
+ def clear_http_scopes(token:object) -> None:
232
+ global http_scopes
233
+ remove_scopes(token)
234
+
235
+ def http_route_handler(headers, func, req):
236
+ token = extract_http_headers(headers)
237
+ try:
238
+ result = func(req)
239
+ return result
240
+ finally:
241
+ clear_http_scopes(token)
142
242
 
143
243
  class Option(Generic[T]):
144
244
  def __init__(self, value: Optional[T]):
@@ -168,4 +268,11 @@ def try_option(func: Callable[..., T], *args, **kwargs) -> Option[T]:
168
268
  try:
169
269
  return Option(func(*args, **kwargs))
170
270
  except Exception:
171
- return Option(None)
271
+ return Option(None)
272
+
273
+ def resolve_from_alias(my_map, alias):
274
+ """Find a alias that is not none from list of aliases"""
275
+ for i in alias:
276
+ if i in my_map.keys():
277
+ return my_map[i]
278
+ return None
@@ -0,0 +1,94 @@
1
+ # pylint: disable=protected-access
2
+ import logging
3
+
4
+ from opentelemetry.trace import Tracer
5
+
6
+ from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
7
+ from monocle_apptrace.instrumentation.common.utils import (
8
+ get_fully_qualified_class_name,
9
+ with_tracer_wrapper,
10
+ set_scope,
11
+ remove_scope
12
+ )
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ @with_tracer_wrapper
17
+ def task_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
18
+
19
+ # Some Langchain objects are wrapped elsewhere, so we ignore them here
20
+ if instance.__class__.__name__ in ("AgentExecutor"):
21
+ return wrapped(*args, **kwargs)
22
+
23
+ if hasattr(instance, "name") and instance.name:
24
+ name = f"{to_wrap.get('span_name')}.{instance.name.lower()}"
25
+ elif to_wrap.get("span_name"):
26
+ name = to_wrap.get("span_name")
27
+ else:
28
+ name = get_fully_qualified_class_name(instance)
29
+
30
+ return_value = None
31
+ try:
32
+ handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
33
+ if to_wrap.get('skip_span') or handler.skip_span(to_wrap, wrapped, instance, args, kwargs):
34
+ return_value = wrapped(*args, **kwargs)
35
+ else:
36
+ with tracer.start_as_current_span(name) as span:
37
+ handler.pre_task_processing(to_wrap, wrapped, instance, args, kwargs, span)
38
+ return_value = wrapped(*args, **kwargs)
39
+ handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span)
40
+ handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
41
+ return return_value
42
+ finally:
43
+ handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
44
+
45
+ @with_tracer_wrapper
46
+ async def atask_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
47
+ """Instruments and calls every function defined in TO_WRAP."""
48
+
49
+ # Some Langchain objects are wrapped elsewhere, so we ignore them here
50
+ if instance.__class__.__name__ in ("AgentExecutor"):
51
+ return wrapped(*args, **kwargs)
52
+
53
+ if hasattr(instance, "name") and instance.name:
54
+ name = f"{to_wrap.get('span_name')}.{instance.name.lower()}"
55
+ elif to_wrap.get("span_name"):
56
+ name = to_wrap.get("span_name")
57
+ else:
58
+ name = get_fully_qualified_class_name(instance)
59
+
60
+ return_value = None
61
+ try:
62
+ handler.pre_tracing(to_wrap, wrapped, instance, args, kwargs)
63
+ if to_wrap.get('skip_span') or handler.skip_span(to_wrap, wrapped, instance, args, kwargs):
64
+ return_value = wrapped(*args, **kwargs)
65
+ else:
66
+ with tracer.start_as_current_span(name) as span:
67
+ handler.pre_task_processing(to_wrap, wrapped, instance, args, kwargs, span)
68
+ return_value = wrapped(*args, **kwargs)
69
+ handler.hydrate_span(to_wrap, wrapped, instance, args, kwargs, return_value, span)
70
+ handler.post_task_processing(to_wrap, wrapped, instance, args, kwargs, return_value, span)
71
+ return return_value
72
+ finally:
73
+ handler.post_tracing(to_wrap, wrapped, instance, args, kwargs, return_value)
74
+
75
+ @with_tracer_wrapper
76
+ def scope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
77
+ scope_name = to_wrap.get('scope_name', None)
78
+ if scope_name:
79
+ token = set_scope(scope_name)
80
+ return_value = wrapped(*args, **kwargs)
81
+ if scope_name:
82
+ remove_scope(token)
83
+ return return_value
84
+
85
+
86
+ @with_tracer_wrapper
87
+ async def ascope_wrapper(tracer: Tracer, handler: SpanHandler, to_wrap, wrapped, instance, args, kwargs):
88
+ scope_name = to_wrap.get('scope_name', None)
89
+ if scope_name:
90
+ token = set_scope(scope_name)
91
+ return_value = wrapped(*args, **kwargs)
92
+ if scope_name:
93
+ remove_scope(token)
94
+ return return_value
@@ -0,0 +1,68 @@
1
+ # pylint: disable=too-few-public-methods
2
+ from typing import Any, Dict
3
+ from monocle_apptrace.instrumentation.common.wrapper import task_wrapper, scope_wrapper
4
+ from monocle_apptrace.instrumentation.common.span_handler import SpanHandler
5
+ from monocle_apptrace.instrumentation.metamodel.botocore.methods import BOTOCORE_METHODS
6
+ from monocle_apptrace.instrumentation.metamodel.botocore.handlers.botocore_span_handler import BotoCoreSpanHandler
7
+ from monocle_apptrace.instrumentation.metamodel.langchain.methods import (
8
+ LANGCHAIN_METHODS,
9
+ )
10
+ from monocle_apptrace.instrumentation.metamodel.llamaindex.methods import (LLAMAINDEX_METHODS, )
11
+ from monocle_apptrace.instrumentation.metamodel.haystack.methods import (HAYSTACK_METHODS, )
12
+ from monocle_apptrace.instrumentation.metamodel.openai.methods import (OPENAI_METHODS,)
13
+ from monocle_apptrace.instrumentation.metamodel.langgraph.methods import LANGGRAPH_METHODS
14
+ from monocle_apptrace.instrumentation.metamodel.flask.methods import (FLASK_METHODS, )
15
+ from monocle_apptrace.instrumentation.metamodel.flask._helper import FlaskSpanHandler
16
+ from monocle_apptrace.instrumentation.metamodel.requests.methods import (REQUESTS_METHODS, )
17
+ from monocle_apptrace.instrumentation.metamodel.requests._helper import RequestSpanHandler
18
+
19
+ class WrapperMethod:
20
+ def __init__(
21
+ self,
22
+ package: str,
23
+ object_name: str,
24
+ method: str,
25
+ span_name: str = None,
26
+ output_processor : str = None,
27
+ wrapper_method = task_wrapper,
28
+ span_handler = 'default',
29
+ scope_name: str = None
30
+ ):
31
+ self.package = package
32
+ self.object = object_name
33
+ self.method = method
34
+ self.span_name = span_name
35
+ self.output_processor=output_processor
36
+
37
+ self.span_handler:SpanHandler.__class__ = span_handler
38
+ self.scope_name = scope_name
39
+ if scope_name:
40
+ self.wrapper_method = scope_wrapper
41
+ else:
42
+ self.wrapper_method = wrapper_method
43
+
44
+ def to_dict(self) -> dict:
45
+ # Create a dictionary representation of the instance
46
+ instance_dict = {
47
+ 'package': self.package,
48
+ 'object': self.object,
49
+ 'method': self.method,
50
+ 'span_name': self.span_name,
51
+ 'output_processor': self.output_processor,
52
+ 'wrapper_method': self.wrapper_method,
53
+ 'span_handler': self.span_handler,
54
+ 'scope_name': self.scope_name
55
+ }
56
+ return instance_dict
57
+
58
+ def get_span_handler(self) -> SpanHandler:
59
+ return self.span_handler()
60
+
61
+ DEFAULT_METHODS_LIST = LANGCHAIN_METHODS + LLAMAINDEX_METHODS + HAYSTACK_METHODS + BOTOCORE_METHODS + FLASK_METHODS + REQUESTS_METHODS + LANGGRAPH_METHODS + OPENAI_METHODS
62
+
63
+ MONOCLE_SPAN_HANDLERS: Dict[str, SpanHandler] = {
64
+ "default": SpanHandler(),
65
+ "botocore_handler": BotoCoreSpanHandler(),
66
+ "flask_handler": FlaskSpanHandler(),
67
+ "request_handler": RequestSpanHandler()
68
+ }