lmnr 0.7.4__tar.gz → 0.7.5__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.
Files changed (97) hide show
  1. {lmnr-0.7.4 → lmnr-0.7.5}/PKG-INFO +1 -1
  2. {lmnr-0.7.4 → lmnr-0.7.5}/pyproject.toml +4 -3
  3. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/decorators/__init__.py +15 -4
  4. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/chat_wrappers.py +10 -2
  5. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/responses_wrappers.py +18 -2
  6. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +12 -21
  7. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/attributes.py +1 -0
  8. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/instruments.py +0 -2
  9. lmnr-0.7.5/src/lmnr/sdk/browser/browser_use_cdp_otel.py +95 -0
  10. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/browser_use_otel.py +4 -5
  11. lmnr-0.7.5/src/lmnr/sdk/browser/cdp_utils.py +733 -0
  12. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/pw_utils.py +57 -51
  13. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/decorators.py +6 -0
  14. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/evaluations.py +6 -1
  15. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/types.py +8 -2
  16. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/version.py +1 -1
  17. {lmnr-0.7.4 → lmnr-0.7.5}/README.md +0 -0
  18. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/__init__.py +0 -0
  19. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/cli.py +0 -0
  20. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/.flake8 +0 -0
  21. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/__init__.py +0 -0
  22. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/litellm/__init__.py +0 -0
  23. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/litellm/utils.py +0 -0
  24. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/__init__.py +0 -0
  25. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/config.py +0 -0
  26. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_emitter.py +0 -0
  27. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/event_models.py +0 -0
  28. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/span_utils.py +0 -0
  29. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/streaming.py +0 -0
  30. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/utils.py +0 -0
  31. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/anthropic/version.py +0 -0
  32. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +0 -0
  33. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/config.py +0 -0
  34. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/schema_utils.py +0 -0
  35. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py +0 -0
  36. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/__init__.py +0 -0
  37. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/config.py +0 -0
  38. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_emitter.py +0 -0
  39. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/event_models.py +0 -0
  40. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/span_utils.py +0 -0
  41. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/utils.py +0 -0
  42. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/groq/version.py +0 -0
  43. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/__init__.py +0 -0
  44. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/langgraph/utils.py +0 -0
  45. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/__init__.py +0 -0
  46. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/__init__.py +0 -0
  47. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/completion_wrappers.py +0 -0
  48. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/config.py +0 -0
  49. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/embeddings_wrappers.py +0 -0
  50. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_emitter.py +0 -0
  51. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/event_models.py +0 -0
  52. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/shared/image_gen_wrappers.py +0 -0
  53. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/utils.py +0 -0
  54. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v0/__init__.py +0 -0
  55. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/__init__.py +0 -0
  56. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/assistant_wrappers.py +0 -0
  57. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/v1/event_handler_wrapper.py +0 -0
  58. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/openai/version.py +0 -0
  59. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/opentelemetry/__init__.py +0 -0
  60. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/skyvern/__init__.py +0 -0
  61. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/opentelemetry/instrumentation/threading/__init__.py +0 -0
  62. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/__init__.py +0 -0
  63. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/context.py +0 -0
  64. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/exporter.py +0 -0
  65. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/processor.py +0 -0
  66. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/tracing/tracer.py +0 -0
  67. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/utils/__init__.py +0 -0
  68. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/utils/json_encoder.py +0 -0
  69. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/opentelemetry_lib/utils/package_check.py +0 -0
  70. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/py.typed +0 -0
  71. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/__init__.py +0 -0
  72. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/__init__.py +0 -0
  73. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/patchright_otel.py +0 -0
  74. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/playwright_otel.py +0 -0
  75. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/recorder/record.umd.min.cjs +0 -0
  76. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/browser/utils.py +0 -0
  77. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/async_client.py +0 -0
  78. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/__init__.py +0 -0
  79. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/agent.py +0 -0
  80. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/base.py +0 -0
  81. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/browser_events.py +0 -0
  82. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/evals.py +0 -0
  83. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/evaluators.py +0 -0
  84. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/asynchronous/resources/tags.py +0 -0
  85. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/__init__.py +0 -0
  86. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/agent.py +0 -0
  87. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/base.py +0 -0
  88. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/browser_events.py +0 -0
  89. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/evals.py +0 -0
  90. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/evaluators.py +0 -0
  91. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/resources/tags.py +0 -0
  92. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/client/synchronous/sync_client.py +0 -0
  93. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/datasets.py +0 -0
  94. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/eval_control.py +0 -0
  95. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/laminar.py +0 -0
  96. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/log.py +0 -0
  97. {lmnr-0.7.4 → lmnr-0.7.5}/src/lmnr/sdk/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lmnr
3
- Version: 0.7.4
3
+ Version: 0.7.5
4
4
  Summary: Python SDK for Laminar
5
5
  Author: lmnr.ai
6
6
  Author-email: lmnr.ai <founders@lmnr.ai>
@@ -6,7 +6,7 @@
6
6
 
7
7
  [project]
8
8
  name = "lmnr"
9
- version = "0.7.4"
9
+ version = "0.7.5"
10
10
  description = "Python SDK for Laminar"
11
11
  authors = [
12
12
  { name = "lmnr.ai", email = "founders@lmnr.ai" }
@@ -124,14 +124,15 @@ dev = [
124
124
  "pytest-asyncio>=0.26.0",
125
125
  "playwright>=1.52.0",
126
126
  "vcrpy>=7.0.0",
127
- "openai>=1.99.7",
127
+ # litellm breaks with openai>=1.100.0 (as of litellm 1.75.8)
128
+ "openai>=1.99.7,<1.100.0",
128
129
  "pytest-recording>=0.13.4",
129
130
  "patchright>=1.52.3",
130
131
  "google-genai>=1.19.0",
131
132
  "langgraph>=0.4.8",
132
133
  "langchain-core>=0.3.64",
133
134
  "langchain>=0.3.25",
134
- "litellm>=1.72.6",
135
+ "litellm>=1.75.8",
135
136
  "groq>=0.30.0",
136
137
  "anthropic>=0.60.0",
137
138
  "langchain-openai>=0.3.28",
@@ -65,14 +65,17 @@ def json_dumps(data: dict) -> str:
65
65
 
66
66
 
67
67
  def _setup_span(
68
- span_name: str, span_type: str, association_properties: dict[str, Any] | None
68
+ span_name: str,
69
+ span_type: str,
70
+ association_properties: dict[str, Any] | None,
71
+ preserve_global_context: bool = False,
69
72
  ):
70
73
  """Set up a span with the given name, type, and association properties."""
71
74
  with get_tracer_with_context() as (tracer, isolated_context):
72
75
  # Create span in isolated context
73
76
  span = tracer.start_span(
74
77
  span_name,
75
- context=isolated_context,
78
+ context=isolated_context if not preserve_global_context else None,
76
79
  attributes={SPAN_TYPE: span_type},
77
80
  )
78
81
 
@@ -167,6 +170,7 @@ def _cleanup_span(span: Span, wrapper: TracerWrapper):
167
170
 
168
171
 
169
172
  def observe_base(
173
+ *,
170
174
  name: str | None = None,
171
175
  ignore_input: bool = False,
172
176
  ignore_inputs: list[str] | None = None,
@@ -175,6 +179,7 @@ def observe_base(
175
179
  association_properties: dict[str, Any] | None = None,
176
180
  input_formatter: Callable[..., str] | None = None,
177
181
  output_formatter: Callable[..., str] | None = None,
182
+ preserve_global_context: bool = False,
178
183
  ):
179
184
  def decorate(fn):
180
185
  @wraps(fn)
@@ -185,7 +190,9 @@ def observe_base(
185
190
  span_name = name or fn.__name__
186
191
  wrapper = TracerWrapper()
187
192
 
188
- span = _setup_span(span_name, span_type, association_properties)
193
+ span = _setup_span(
194
+ span_name, span_type, association_properties, preserve_global_context
195
+ )
189
196
  new_context = wrapper.push_span_context(span)
190
197
  if session_id := association_properties.get("session_id"):
191
198
  new_context = context_api.set_value(
@@ -241,6 +248,7 @@ def observe_base(
241
248
 
242
249
  # Async Decorators
243
250
  def async_observe_base(
251
+ *,
244
252
  name: str | None = None,
245
253
  ignore_input: bool = False,
246
254
  ignore_inputs: list[str] | None = None,
@@ -249,6 +257,7 @@ def async_observe_base(
249
257
  association_properties: dict[str, Any] | None = None,
250
258
  input_formatter: Callable[..., str] | None = None,
251
259
  output_formatter: Callable[..., str] | None = None,
260
+ preserve_global_context: bool = False,
252
261
  ):
253
262
  def decorate(fn):
254
263
  @wraps(fn)
@@ -259,7 +268,9 @@ def async_observe_base(
259
268
  span_name = name or fn.__name__
260
269
  wrapper = TracerWrapper()
261
270
 
262
- span = _setup_span(span_name, span_type, association_properties)
271
+ span = _setup_span(
272
+ span_name, span_type, association_properties, preserve_global_context
273
+ )
263
274
  new_context = wrapper.push_span_context(span)
264
275
  if session_id := association_properties.get("session_id"):
265
276
  new_context = context_api.set_value(
@@ -56,7 +56,6 @@ from opentelemetry.trace import SpanKind, Tracer
56
56
  from opentelemetry.trace.status import Status, StatusCode
57
57
  from wrapt import ObjectProxy
58
58
 
59
- from openai.types.chat.chat_completion_message import FunctionCall
60
59
  import pydantic
61
60
 
62
61
  SPAN_NAME = "openai.chat"
@@ -1014,7 +1013,7 @@ def _parse_tool_calls(
1014
1013
  tool_call_data = copy.deepcopy(tool_call)
1015
1014
  elif _is_tool_call_model(tool_call):
1016
1015
  tool_call_data = tool_call.model_dump()
1017
- elif isinstance(tool_call, FunctionCall):
1016
+ elif _is_function_call(tool_call):
1018
1017
  function_call = tool_call.model_dump()
1019
1018
  tool_call_data = ToolCall(
1020
1019
  id="",
@@ -1040,6 +1039,15 @@ def _is_tool_call_model(tool_call):
1040
1039
  return False
1041
1040
 
1042
1041
 
1042
+ def _is_function_call(model: Union[dict, pydantic.BaseModel]) -> bool:
1043
+ try:
1044
+ from openai.types.chat.chat_completion_message import FunctionCall
1045
+
1046
+ return isinstance(model, FunctionCall)
1047
+ except Exception:
1048
+ return False
1049
+
1050
+
1043
1051
  @singledispatch
1044
1052
  def _parse_choice_event(choice) -> ChoiceEvent:
1045
1053
  has_message = choice.message is not None
@@ -464,7 +464,9 @@ def responses_get_or_create_wrapper(tracer: Tracer, wrapped, instance, args, kwa
464
464
  output_blocks={block.id: block for block in parsed_response.output}
465
465
  | existing_data.get("output_blocks", {}),
466
466
  usage=existing_data.get("usage", parsed_response.usage),
467
- output_text=existing_data.get("output_text", parsed_response.output_text),
467
+ output_text=existing_data.get(
468
+ "output_text", _get_output_text(parsed_response)
469
+ ),
468
470
  request_model=existing_data.get("request_model", kwargs.get("model")),
469
471
  response_model=existing_data.get("response_model", parsed_response.model),
470
472
  )
@@ -560,7 +562,9 @@ async def async_responses_get_or_create_wrapper(
560
562
  output_blocks={block.id: block for block in parsed_response.output}
561
563
  | existing_data.get("output_blocks", {}),
562
564
  usage=existing_data.get("usage", parsed_response.usage),
563
- output_text=existing_data.get("output_text", parsed_response.output_text),
565
+ output_text=existing_data.get(
566
+ "output_text", _get_output_text(parsed_response)
567
+ ),
564
568
  request_model=existing_data.get("request_model", kwargs.get("model")),
565
569
  response_model=existing_data.get("response_model", parsed_response.model),
566
570
  )
@@ -639,4 +643,16 @@ async def async_responses_cancel_wrapper(
639
643
  return response
640
644
 
641
645
 
646
+ def _get_output_text(parsed_response: Response) -> Optional[str]:
647
+ output_text = None
648
+ if hasattr(parsed_response, "output_text"):
649
+ output_text = parsed_response.output_text
650
+ else:
651
+ try:
652
+ output_text = parsed_response.output[0].content[0].text
653
+ except Exception:
654
+ pass
655
+ return output_text
656
+
657
+
642
658
  # TODO: build streaming responses
@@ -51,19 +51,26 @@ class BedrockInstrumentorInitializer(InstrumentorInitializer):
51
51
 
52
52
 
53
53
  class BrowserUseInstrumentorInitializer(InstrumentorInitializer):
54
- def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
54
+ def init_instrumentor(
55
+ self, client, async_client, *args, **kwargs
56
+ ) -> BaseInstrumentor | None:
55
57
  if not is_package_installed("browser-use"):
56
58
  return None
57
59
 
58
60
  version = get_package_version("browser-use")
59
61
  from packaging.version import parse
60
62
 
61
- if version and parse(version) >= parse("0.5.0"):
62
- return None
63
+ if version and parse(version) < parse("0.5.0"):
64
+ from lmnr.sdk.browser.browser_use_otel import BrowserUseLegacyInstrumentor
65
+
66
+ return BrowserUseLegacyInstrumentor()
63
67
 
64
- from lmnr.sdk.browser.browser_use_otel import BrowserUseInstrumentor
68
+ if version and parse(version) >= parse("1.0.0rc1"):
69
+ from lmnr.sdk.browser.browser_use_cdp_otel import BrowserUseInstrumentor
65
70
 
66
- return BrowserUseInstrumentor()
71
+ return BrowserUseInstrumentor(async_client)
72
+
73
+ return None
67
74
 
68
75
 
69
76
  class ChromaInstrumentorInitializer(InstrumentorInitializer):
@@ -102,22 +109,6 @@ class CrewAIInstrumentorInitializer(InstrumentorInitializer):
102
109
  return CrewAiInstrumentor()
103
110
 
104
111
 
105
- class GoogleGenerativeAIInstrumentorInitializer(InstrumentorInitializer):
106
- def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
107
- if not is_package_installed("google-generativeai"):
108
- return None
109
- if not is_package_installed(
110
- "opentelemetry-instrumentation-google-generativeai"
111
- ):
112
- return None
113
-
114
- from opentelemetry.instrumentation.google_generativeai import (
115
- GoogleGenerativeAiInstrumentor,
116
- )
117
-
118
- return GoogleGenerativeAiInstrumentor()
119
-
120
-
121
112
  class GoogleGenAIInstrumentorInitializer(InstrumentorInitializer):
122
113
  def init_instrumentor(self, *args, **kwargs) -> BaseInstrumentor | None:
123
114
  if not is_package_installed("google-genai"):
@@ -19,6 +19,7 @@ PARENT_SPAN_IDS_PATH = "lmnr.span.parent_ids_path"
19
19
  SPAN_INSTRUMENTATION_SOURCE = "lmnr.span.instrumentation_source"
20
20
  SPAN_SDK_VERSION = "lmnr.span.sdk_version"
21
21
  SPAN_LANGUAGE_VERSION = "lmnr.span.language_version"
22
+ HUMAN_EVALUATOR_OPTIONS = "lmnr.span.human_evaluator_options"
22
23
 
23
24
  ASSOCIATION_PROPERTIES = "lmnr.association.properties"
24
25
  SESSION_ID = "session_id"
@@ -20,7 +20,6 @@ class Instruments(Enum):
20
20
  CHROMA = "chroma"
21
21
  COHERE = "cohere"
22
22
  CREWAI = "crewai"
23
- GOOGLE_GENERATIVEAI = "google_generativeai"
24
23
  GOOGLE_GENAI = "google_genai"
25
24
  GROQ = "groq"
26
25
  HAYSTACK = "haystack"
@@ -63,7 +62,6 @@ INSTRUMENTATION_INITIALIZERS: dict[
63
62
  Instruments.CHROMA: initializers.ChromaInstrumentorInitializer(),
64
63
  Instruments.COHERE: initializers.CohereInstrumentorInitializer(),
65
64
  Instruments.CREWAI: initializers.CrewAIInstrumentorInitializer(),
66
- Instruments.GOOGLE_GENERATIVEAI: initializers.GoogleGenerativeAIInstrumentorInitializer(),
67
65
  Instruments.GOOGLE_GENAI: initializers.GoogleGenAIInstrumentorInitializer(),
68
66
  Instruments.GROQ: initializers.GroqInstrumentorInitializer(),
69
67
  Instruments.HAYSTACK: initializers.HaystackInstrumentorInitializer(),
@@ -0,0 +1,95 @@
1
+ from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
2
+ from lmnr.sdk.browser.utils import with_tracer_and_client_wrapper
3
+ from lmnr.version import __version__
4
+ from lmnr.sdk.browser.cdp_utils import (
5
+ is_recorder_present,
6
+ start_recording_events,
7
+ take_full_snapshot,
8
+ )
9
+
10
+ from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
11
+ from opentelemetry.instrumentation.utils import unwrap
12
+ from opentelemetry.trace import get_tracer, Tracer
13
+ from typing import Collection
14
+ from wrapt import wrap_function_wrapper
15
+ import uuid
16
+
17
+ # Stable versions, e.g. 1.0.0, satisfy this condition too
18
+ _instruments = ("browser-use >= 1.0.0rc1",)
19
+
20
+ WRAPPED_METHODS = [
21
+ {
22
+ "package": "browser_use.browser.session",
23
+ "object": "BrowserSession",
24
+ "method": "get_or_create_cdp_session",
25
+ "action": "inject_session_recorder",
26
+ },
27
+ {
28
+ "package": "browser_use.browser.session",
29
+ "object": "BrowserSession",
30
+ "method": "on_SwitchTabEvent",
31
+ "action": "take_full_snapshot",
32
+ },
33
+ ]
34
+
35
+
36
+ @with_tracer_and_client_wrapper
37
+ async def _wrap(
38
+ tracer: Tracer, client: AsyncLaminarClient, to_wrap, wrapped, instance, args, kwargs
39
+ ):
40
+ result = await wrapped(*args, **kwargs)
41
+
42
+ if to_wrap.get("action") == "inject_session_recorder":
43
+ is_registered = await is_recorder_present(result)
44
+ if not is_registered:
45
+ await start_recording_events(result, str(uuid.uuid4()), client)
46
+
47
+ if to_wrap.get("action") == "take_full_snapshot":
48
+ target_id = result
49
+ if target_id:
50
+ cdp_session = await instance.get_or_create_cdp_session(target_id)
51
+ await take_full_snapshot(cdp_session)
52
+
53
+ return result
54
+
55
+
56
+ class BrowserUseInstrumentor(BaseInstrumentor):
57
+ def __init__(self, async_client: AsyncLaminarClient):
58
+ super().__init__()
59
+ self.async_client = async_client
60
+
61
+ def instrumentation_dependencies(self) -> Collection[str]:
62
+ return _instruments
63
+
64
+ def _instrument(self, **kwargs):
65
+ tracer_provider = kwargs.get("tracer_provider")
66
+ tracer = get_tracer(__name__, __version__, tracer_provider)
67
+
68
+ for wrapped_method in WRAPPED_METHODS:
69
+ wrap_package = wrapped_method.get("package")
70
+ wrap_object = wrapped_method.get("object")
71
+ wrap_method = wrapped_method.get("method")
72
+
73
+ try:
74
+ wrap_function_wrapper(
75
+ wrap_package,
76
+ f"{wrap_object}.{wrap_method}",
77
+ _wrap(
78
+ tracer,
79
+ self.async_client,
80
+ wrapped_method,
81
+ ),
82
+ )
83
+ except (ModuleNotFoundError, ImportError):
84
+ pass # that's ok, we're not instrumenting everything
85
+
86
+ def _uninstrument(self, **kwargs):
87
+ for wrapped_method in WRAPPED_METHODS:
88
+ wrap_package = wrapped_method.get("package")
89
+ wrap_object = wrapped_method.get("object")
90
+ wrap_method = wrapped_method.get("method")
91
+
92
+ unwrap(
93
+ f"{wrap_package}.{wrap_object}" if wrap_object else wrap_package,
94
+ wrap_method,
95
+ )
@@ -1,5 +1,5 @@
1
1
  from lmnr.opentelemetry_lib.decorators import json_dumps
2
- from lmnr.sdk.laminar import Laminar
2
+ from lmnr import Laminar
3
3
  from lmnr.sdk.browser.utils import with_tracer_wrapper
4
4
  from lmnr.sdk.utils import get_input_from_func_args
5
5
  from lmnr.version import __version__
@@ -16,11 +16,11 @@ try:
16
16
  except ImportError as e:
17
17
  raise ImportError(
18
18
  f"Attempted to import {__file__}, but it is designed "
19
- "to patch Browser Use, which is not installed. Use `pip install browser-use` "
19
+ "to patch Browser Use < 0.5.0, which is not installed. Use `pip install browser-use` "
20
20
  "to install Browser Use or remove this import."
21
21
  ) from e
22
22
 
23
- _instruments = ("browser-use >= 0.1.0",)
23
+ _instruments = ("browser-use < 0.5.0",)
24
24
 
25
25
  WRAPPED_METHODS = [
26
26
  {
@@ -89,7 +89,6 @@ async def _wrap(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
89
89
  span_name = f"agent.step.{step_info.step_number}"
90
90
 
91
91
  with Laminar.start_as_current_span(span_name) as span:
92
- span.set_attributes(attributes)
93
92
  result = await wrapped(*args, **kwargs)
94
93
  if not to_wrap.get("ignore_output"):
95
94
  to_serialize = result
@@ -104,7 +103,7 @@ async def _wrap(tracer: Tracer, to_wrap, wrapped, instance, args, kwargs):
104
103
  return result
105
104
 
106
105
 
107
- class BrowserUseInstrumentor(BaseInstrumentor):
106
+ class BrowserUseLegacyInstrumentor(BaseInstrumentor):
108
107
  def __init__(self):
109
108
  super().__init__()
110
109