lmnr 0.6.2__py3-none-any.whl → 0.6.4__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.
Files changed (36) hide show
  1. lmnr/cli.py +18 -11
  2. lmnr/opentelemetry_lib/__init__.py +6 -7
  3. lmnr/opentelemetry_lib/decorators/__init__.py +23 -8
  4. lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/__init__.py +2 -2
  5. lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/config.py +4 -4
  6. lmnr/opentelemetry_lib/opentelemetry/instrumentation/google_genai/utils.py +19 -19
  7. lmnr/opentelemetry_lib/tracing/__init__.py +6 -7
  8. lmnr/opentelemetry_lib/tracing/_instrument_initializers.py +32 -33
  9. lmnr/opentelemetry_lib/tracing/context_properties.py +2 -3
  10. lmnr/opentelemetry_lib/tracing/exporter.py +4 -5
  11. lmnr/opentelemetry_lib/tracing/instruments.py +5 -6
  12. lmnr/opentelemetry_lib/tracing/processor.py +6 -7
  13. lmnr/sdk/browser/utils.py +4 -3
  14. lmnr/sdk/client/asynchronous/async_client.py +26 -11
  15. lmnr/sdk/client/asynchronous/resources/__init__.py +2 -0
  16. lmnr/sdk/client/asynchronous/resources/agent.py +89 -91
  17. lmnr/sdk/client/asynchronous/resources/evals.py +8 -8
  18. lmnr/sdk/client/asynchronous/resources/tags.py +89 -0
  19. lmnr/sdk/client/synchronous/resources/__init__.py +2 -1
  20. lmnr/sdk/client/synchronous/resources/agent.py +91 -91
  21. lmnr/sdk/client/synchronous/resources/evals.py +8 -8
  22. lmnr/sdk/client/synchronous/resources/tags.py +89 -0
  23. lmnr/sdk/client/synchronous/sync_client.py +28 -13
  24. lmnr/sdk/decorators.py +43 -27
  25. lmnr/sdk/eval_control.py +1 -1
  26. lmnr/sdk/evaluations.py +75 -52
  27. lmnr/sdk/laminar.py +76 -58
  28. lmnr/sdk/types.py +47 -37
  29. lmnr/sdk/utils.py +3 -3
  30. lmnr/version.py +1 -1
  31. {lmnr-0.6.2.dist-info → lmnr-0.6.4.dist-info}/METADATA +1 -1
  32. lmnr-0.6.4.dist-info/RECORD +56 -0
  33. lmnr-0.6.2.dist-info/RECORD +0 -54
  34. {lmnr-0.6.2.dist-info → lmnr-0.6.4.dist-info}/LICENSE +0 -0
  35. {lmnr-0.6.2.dist-info → lmnr-0.6.4.dist-info}/WHEEL +0 -0
  36. {lmnr-0.6.2.dist-info → lmnr-0.6.4.dist-info}/entry_points.txt +0 -0
lmnr/sdk/eval_control.py CHANGED
@@ -2,4 +2,4 @@ from contextvars import ContextVar
2
2
 
3
3
 
4
4
  PREPARE_ONLY: ContextVar[bool] = ContextVar("__lmnr_prepare_only", default=False)
5
- EVALUATION_INSTANCE = ContextVar("__lmnr_evaluation_instance")
5
+ EVALUATION_INSTANCES = ContextVar("__lmnr_evaluation_instances")
lmnr/sdk/evaluations.py CHANGED
@@ -3,7 +3,7 @@ import re
3
3
  import uuid
4
4
 
5
5
  from tqdm import tqdm
6
- from typing import Any, Awaitable, Optional, Set, Union
6
+ from typing import Any, Awaitable
7
7
 
8
8
  from lmnr.opentelemetry_lib.tracing.instruments import Instruments
9
9
  from lmnr.opentelemetry_lib.tracing.attributes import SPAN_TYPE
@@ -11,7 +11,7 @@ from lmnr.opentelemetry_lib.tracing.attributes import SPAN_TYPE
11
11
  from lmnr.sdk.client.asynchronous.async_client import AsyncLaminarClient
12
12
  from lmnr.sdk.client.synchronous.sync_client import LaminarClient
13
13
  from lmnr.sdk.datasets import EvaluationDataset, LaminarDataset
14
- from lmnr.sdk.eval_control import EVALUATION_INSTANCE, PREPARE_ONLY
14
+ from lmnr.sdk.eval_control import EVALUATION_INSTANCES, PREPARE_ONLY
15
15
  from lmnr.sdk.laminar import Laminar as L
16
16
  from lmnr.sdk.log import get_default_logger
17
17
  from lmnr.sdk.types import (
@@ -33,7 +33,7 @@ MAX_EXPORT_BATCH_SIZE = 64
33
33
 
34
34
 
35
35
  def get_evaluation_url(
36
- project_id: str, evaluation_id: str, base_url: Optional[str] = None
36
+ project_id: str, evaluation_id: str, base_url: str | None = None
37
37
  ):
38
38
  if not base_url or base_url == "https://api.lmnr.ai":
39
39
  base_url = "https://www.lmnr.ai"
@@ -95,32 +95,34 @@ class EvaluationReporter:
95
95
  class Evaluation:
96
96
  def __init__(
97
97
  self,
98
- data: Union[EvaluationDataset, list[Union[Datapoint, dict]]],
98
+ data: EvaluationDataset | list[Datapoint | dict],
99
99
  executor: Any,
100
100
  evaluators: dict[str, EvaluatorFunction],
101
101
  human_evaluators: list[HumanEvaluator] = [],
102
- name: Optional[str] = None,
103
- group_name: Optional[str] = None,
102
+ name: str | None = None,
103
+ group_name: str | None = None,
104
104
  concurrency_limit: int = DEFAULT_BATCH_SIZE,
105
- project_api_key: Optional[str] = None,
106
- base_url: Optional[str] = None,
107
- http_port: Optional[int] = None,
108
- grpc_port: Optional[int] = None,
109
- instruments: Optional[Set[Instruments]] = None,
110
- max_export_batch_size: Optional[int] = MAX_EXPORT_BATCH_SIZE,
111
- trace_export_timeout_seconds: Optional[int] = None,
105
+ project_api_key: str | None = None,
106
+ base_url: str | None = None,
107
+ http_port: int | None = None,
108
+ grpc_port: int | None = None,
109
+ instruments: set[Instruments] | None = None,
110
+ max_export_batch_size: int | None = MAX_EXPORT_BATCH_SIZE,
111
+ trace_export_timeout_seconds: int | None = None,
112
112
  ):
113
113
  """
114
114
  Initializes an instance of the Evaluation class.
115
115
 
116
116
  Parameters:
117
- data (Union[List[EvaluationDatapoint|dict], EvaluationDataset]):\
117
+ data (list[Datapoint|dict] | EvaluationDataset):\
118
118
  List of data points to evaluate or an evaluation dataset.
119
- `data` is the input to the executor function,
120
- `target` is the input to the evaluator function.
119
+ `data` is the input to the executor function.
120
+ `target` is the input to the evaluator function.
121
+ `metadata` is optional metadata to associate with the\
122
+ datapoint.
121
123
  executor (Callable[..., Any]): The executor function.\
122
- Takes the data point + any additional arguments\
123
- and returns the output to evaluate.
124
+ Takes the data point + any additional arguments and returns\
125
+ the output to evaluate.
124
126
  evaluators (dict[str, Callable[..., Any]]): Evaluator functions and\
125
127
  names. Each evaluator function takes the output of the executor\
126
128
  _and_ the target data, and returns a score. The score can be a\
@@ -132,30 +134,31 @@ class Evaluation:
132
134
  [Beta] List of instances of HumanEvaluator. For now, human\
133
135
  evaluator only holds the queue name.
134
136
  Defaults to an empty list.
135
- name (Optional[str], optional): Optional name of the evaluation.\
137
+ name (str | None, optional): Optional name of the evaluation.\
136
138
  Used to identify the evaluation in the group.\
137
139
  If not provided, a random name will be generated.
138
140
  Defaults to None.
139
- group_name (Optional[str], optional): an identifier to group\
141
+ group_name (str | None, optional): an identifier to group\
140
142
  evaluations. Only evaluations within the same group_name can be\
141
143
  visually compared. If not provided, "default" is assigned.
142
144
  Defaults to None
143
- concurrency_limit (int, optional): The concurrency limit for evaluation. This many\
144
- data points will be evaluated in parallel with a pool of workers.
145
+ concurrency_limit (int, optional): The concurrency limit for\
146
+ evaluation. This many data points will be evaluated in parallel\
147
+ with a pool of workers.
145
148
  Defaults to DEFAULT_BATCH_SIZE.
146
- project_api_key (Optional[str], optional): The project API key.\
149
+ project_api_key (str | None, optional): The project API key.\
147
150
  If not provided, LMNR_PROJECT_API_KEY environment variable is\
148
151
  used.
149
152
  Defaults to an empty string.
150
- base_url (Optional[str], optional): The base URL for Laminar API.\
153
+ base_url (str | None, optional): The base URL for Laminar API.\
151
154
  Useful if self-hosted. Do NOT include the port, use `http_port`\
152
155
  and `grpc_port` instead.
153
156
  Defaults to "https://api.lmnr.ai".
154
- http_port (Optional[int], optional): The port for Laminar API\
157
+ http_port (int | None, optional): The port for Laminar API\
155
158
  HTTP service. Defaults to 443 if not specified.
156
- grpc_port (Optional[int], optional): The port for Laminar API\
159
+ grpc_port (int | None, optional): The port for Laminar API\
157
160
  gRPC service. Defaults to 8443 if not specified.
158
- instruments (Optional[Set[Instruments]], optional): Set of modules\
161
+ instruments (set[Instruments] | None, optional): Set of modules\
159
162
  to auto-instrument. If None, all available instruments will be\
160
163
  used.
161
164
  See https://docs.lmnr.ai/tracing/automatic-instrumentation
@@ -204,6 +207,18 @@ class Evaluation:
204
207
  )
205
208
  self.project_api_key = api_key
206
209
 
210
+ if L.is_initialized():
211
+ self.client = AsyncLaminarClient(
212
+ base_url=L.get_base_http_url(),
213
+ project_api_key=L.get_project_api_key(),
214
+ )
215
+ if project_api_key and project_api_key != L.get_project_api_key():
216
+ self._logger.warning(
217
+ "Project API key is different from the one used to initialize"
218
+ " Laminar. Ignoring the project API key passed to the evaluation."
219
+ )
220
+ return
221
+
207
222
  self.client = AsyncLaminarClient(
208
223
  base_url=self.base_http_url,
209
224
  project_api_key=self.project_api_key,
@@ -312,6 +327,7 @@ class Evaluation:
312
327
  index=index,
313
328
  trace_id=trace_id,
314
329
  executor_span_id=executor_span_id,
330
+ metadata=datapoint.metadata,
315
331
  )
316
332
  # First, create datapoint with trace_id so that we can show the dp in the UI
317
333
  await self.client._evals.save_datapoints(
@@ -367,6 +383,7 @@ class Evaluation:
367
383
  human_evaluators=self.human_evaluators,
368
384
  executor_span_id=executor_span_id,
369
385
  index=index,
386
+ metadata=datapoint.metadata,
370
387
  )
371
388
 
372
389
  # Create background upload task without awaiting it
@@ -379,21 +396,21 @@ class Evaluation:
379
396
 
380
397
 
381
398
  def evaluate(
382
- data: Union[EvaluationDataset, list[Union[Datapoint, dict]]],
399
+ data: EvaluationDataset | list[Datapoint | dict],
383
400
  executor: ExecutorFunction,
384
401
  evaluators: dict[str, EvaluatorFunction],
385
402
  human_evaluators: list[HumanEvaluator] = [],
386
- name: Optional[str] = None,
387
- group_name: Optional[str] = None,
403
+ name: str | None = None,
404
+ group_name: str | None = None,
388
405
  concurrency_limit: int = DEFAULT_BATCH_SIZE,
389
- project_api_key: Optional[str] = None,
390
- base_url: Optional[str] = None,
391
- http_port: Optional[int] = None,
392
- grpc_port: Optional[int] = None,
393
- instruments: Optional[Set[Instruments]] = None,
394
- max_export_batch_size: Optional[int] = MAX_EXPORT_BATCH_SIZE,
395
- trace_export_timeout_seconds: Optional[int] = None,
396
- ) -> Optional[Awaitable[None]]:
406
+ project_api_key: str | None = None,
407
+ base_url: str | None = None,
408
+ http_port: int | None = None,
409
+ grpc_port: int | None = None,
410
+ instruments: set[Instruments] | None = None,
411
+ max_export_batch_size: int | None = MAX_EXPORT_BATCH_SIZE,
412
+ trace_export_timeout_seconds: int | None = None,
413
+ ) -> Awaitable[None] | None:
397
414
  """
398
415
  If added to the file which is called through `lmnr eval` command, then
399
416
  registers the evaluation; otherwise, runs the evaluation.
@@ -404,7 +421,7 @@ def evaluate(
404
421
  You must await the call to `evaluate`.
405
422
 
406
423
  Parameters:
407
- data (Union[list[EvaluationDatapoint|dict]], EvaluationDataset]):\
424
+ data (list[EvaluationDatapoint|dict] | EvaluationDataset):\
408
425
  List of data points to evaluate or an evaluation dataset.
409
426
  `data` is the input to the executor function,
410
427
  `target` is the input to the evaluator function.
@@ -423,33 +440,33 @@ def evaluate(
423
440
  [Beta] List of instances of HumanEvaluator. For now, human\
424
441
  evaluator only holds the queue name.
425
442
  Defaults to an empty list.
426
- name (Optional[str], optional): Optional name of the evaluation.\
443
+ name (str | None, optional): Optional name of the evaluation.\
427
444
  Used to identify the evaluation in the group. If not provided, a\
428
445
  random name will be generated.
429
446
  Defaults to None.
430
- group_name (Optional[str], optional): An identifier to group evaluations.\
447
+ group_name (str | None, optional): An identifier to group evaluations.\
431
448
  Only evaluations within the same group_name can be visually compared.\
432
449
  If not provided, set to "default".
433
450
  Defaults to None
434
451
  concurrency_limit (int, optional): The concurrency limit for evaluation.
435
452
  Defaults to DEFAULT_BATCH_SIZE.
436
- project_api_key (Optional[str], optional): The project API key.
453
+ project_api_key (str | None, optional): The project API key.
437
454
  Defaults to None.
438
- base_url (Optional[str], optional): The base URL for Laminar API.\
455
+ base_url (str | None, optional): The base URL for Laminar API.\
439
456
  Useful if self-hosted elsewhere. Do NOT include the\
440
457
  port, use `http_port` and `grpc_port` instead.
441
458
  Defaults to "https://api.lmnr.ai".
442
- http_port (Optional[int], optional): The port for Laminar API's HTTP\
459
+ http_port (int | None, optional): The port for Laminar API's HTTP\
443
460
  service. 443 is used if not specified.
444
461
  Defaults to None.
445
- grpc_port (Optional[int], optional): The port for Laminar API's gRPC\
462
+ grpc_port (int | None, optional): The port for Laminar API's gRPC\
446
463
  service. 8443 is used if not specified.
447
464
  Defaults to None.
448
- instruments (Optional[Set[Instruments]], optional): Set of modules to\
465
+ instruments (set[Instruments] | None, optional): Set of modules to\
449
466
  auto-instrument. If None, all available instruments\
450
467
  will be used.
451
468
  Defaults to None.
452
- trace_export_timeout_seconds (Optional[int], optional): The timeout for\
469
+ trace_export_timeout_seconds (int | None, optional): The timeout for\
453
470
  trace export on OpenTelemetry exporter. Defaults to None.
454
471
  """
455
472
  evaluation = Evaluation(
@@ -470,10 +487,16 @@ def evaluate(
470
487
  )
471
488
 
472
489
  if PREPARE_ONLY.get():
473
- EVALUATION_INSTANCE.set(evaluation)
490
+ existing_evaluations = EVALUATION_INSTANCES.get([])
491
+ new_evaluations = (existing_evaluations or []) + [evaluation]
492
+ EVALUATION_INSTANCES.set(new_evaluations)
493
+ return None
474
494
  else:
475
- loop = asyncio.get_event_loop()
476
- if loop.is_running():
477
- return evaluation.run()
478
- else:
495
+ try:
496
+ loop = asyncio.get_event_loop()
497
+ if loop.is_running():
498
+ return evaluation.run()
499
+ else:
500
+ return asyncio.run(evaluation.run())
501
+ except RuntimeError:
479
502
  return asyncio.run(evaluation.run())
lmnr/sdk/laminar.py CHANGED
@@ -12,10 +12,11 @@ from lmnr.opentelemetry_lib import MAX_MANUAL_SPAN_PAYLOAD_SIZE
12
12
  from lmnr.opentelemetry_lib.decorators import json_dumps
13
13
  from opentelemetry import context as context_api, trace
14
14
  from opentelemetry.context import attach, detach
15
+ from opentelemetry.trace import INVALID_TRACE_ID
15
16
  from opentelemetry.sdk.trace.id_generator import RandomIdGenerator
16
17
  from opentelemetry.util.types import AttributeValue
17
18
 
18
- from typing import Any, Literal, Optional, Set, Union
19
+ from typing import Any, Literal
19
20
 
20
21
  import copy
21
22
  import datetime
@@ -48,21 +49,22 @@ from .types import (
48
49
 
49
50
 
50
51
  class Laminar:
51
- __project_api_key: Optional[str] = None
52
+ __project_api_key: str | None = None
52
53
  __initialized: bool = False
54
+ __base_http_url: str | None = None
53
55
 
54
56
  @classmethod
55
57
  def initialize(
56
58
  cls,
57
- project_api_key: Optional[str] = None,
58
- base_url: Optional[str] = None,
59
- http_port: Optional[int] = None,
60
- grpc_port: Optional[int] = None,
61
- instruments: Optional[Set[Instruments]] = None,
62
- disabled_instruments: Optional[Set[Instruments]] = None,
59
+ project_api_key: str | None = None,
60
+ base_url: str | None = None,
61
+ http_port: int | None = None,
62
+ grpc_port: int | None = None,
63
+ instruments: set[Instruments] | None = None,
64
+ disabled_instruments: set[Instruments] | None = None,
63
65
  disable_batch: bool = False,
64
- max_export_batch_size: Optional[int] = None,
65
- export_timeout_seconds: Optional[int] = None,
66
+ max_export_batch_size: int | None = None,
67
+ export_timeout_seconds: int | None = None,
66
68
  set_global_tracer_provider: bool = True,
67
69
  otel_logger_level: int = logging.ERROR,
68
70
  ):
@@ -71,31 +73,31 @@ class Laminar:
71
73
  decorators.
72
74
 
73
75
  Args:
74
- project_api_key (Optional[str], optional): Laminar project api key.\
76
+ project_api_key (str | None, optional): Laminar project api key.\
75
77
  You can generate one by going to the projects\
76
78
  settings page on the Laminar dashboard.\
77
79
  If not specified, it will try to read from the\
78
80
  LMNR_PROJECT_API_KEY environment variable\
79
81
  in os.environ or in .env file.
80
82
  Defaults to None.
81
- base_url (Optional[str], optional): Laminar API url. Do NOT include\
83
+ base_url (str | None, optional): Laminar API url. Do NOT include\
82
84
  the port number, use `http_port` and `grpc_port`.\
83
85
  If not specified, defaults to https://api.lmnr.ai.
84
- http_port (Optional[int], optional): Laminar API http port.\
86
+ http_port (int | None, optional): Laminar API http port.\
85
87
  If not specified, defaults to 443.
86
- grpc_port (Optional[int], optional): Laminar API grpc port.\
88
+ grpc_port (int | None, optional): Laminar API grpc port.\
87
89
  If not specified, defaults to 8443.
88
- instruments (Optional[Set[Instruments]], optional): Instruments to\
90
+ instruments (set[Instruments] | None, optional): Instruments to\
89
91
  enable. Defaults to all instruments. You can pass\
90
92
  an empty set to disable all instruments. Read more:\
91
93
  https://docs.lmnr.ai/tracing/automatic-instrumentation
92
- disabled_instruments (Optional[Set[Instruments]], optional): Instruments to\
94
+ disabled_instruments (set[Instruments] | None, optional): Instruments to\
93
95
  disable. Defaults to None.
94
96
  disable_batch (bool, optional): If set to True, spans will be sent\
95
97
  immediately to the backend. Useful for debugging, but\
96
98
  may cause performance overhead in production.
97
99
  Defaults to False.
98
- export_timeout_seconds (Optional[int], optional): Timeout for the OTLP\
100
+ export_timeout_seconds (int | None, optional): Timeout for the OTLP\
99
101
  exporter. Defaults to 30 seconds (unlike the\
100
102
  OpenTelemetry default of 10 seconds).
101
103
  Defaults to None.
@@ -133,6 +135,7 @@ class Laminar:
133
135
  cls.__logger.info(f"Using HTTP port passed as an argument: {http_port}")
134
136
 
135
137
  cls.__initialized = True
138
+ cls.__base_http_url = f"{url}:{http_port or 443}"
136
139
 
137
140
  if not os.getenv("OTEL_ATTRIBUTE_COUNT_LIMIT"):
138
141
  # each message is at least 2 attributes: role and content,
@@ -174,8 +177,8 @@ class Laminar:
174
177
  def event(
175
178
  cls,
176
179
  name: str,
177
- value: Optional[AttributeValue] = None,
178
- timestamp: Optional[Union[datetime.datetime, int]] = None,
180
+ value: AttributeValue | None = None,
181
+ timestamp: datetime.datetime | int | None = None,
179
182
  ):
180
183
  """Associate an event with the current span. If using manual\
181
184
  instrumentation, use raw OpenTelemetry `span.add_event()` instead.\
@@ -183,14 +186,13 @@ class Laminar:
183
186
 
184
187
  Args:
185
188
  name (str): event name
186
- value (Optional[AttributeValue]): event value. Must be a primitive\
187
- type. Boolean true is assumed in the backend if\
188
- `value` is None.
189
- Defaults to None.
190
- timestamp (Optional[Union[datetime.datetime, int]], optional):\
191
- If int, must be epoch nanoseconds. If not\
192
- specified, relies on the underlying OpenTelemetry\
193
- implementation. Defaults to None.
189
+ value (AttributeValue | None, optional): event value. Must be a\
190
+ primitive type. Boolean `True` is assumed in the backend if\
191
+ `value` is None.
192
+ Defaults to None.
193
+ timestamp (datetime.datetime | int | None, optional): If int, must\
194
+ be epoch nanoseconds. If not specified, relies on the underlying\
195
+ OpenTelemetry implementation. Defaults to None.
194
196
  """
195
197
  if timestamp and isinstance(timestamp, datetime.datetime):
196
198
  timestamp = int(timestamp.timestamp() * 1e9)
@@ -218,12 +220,10 @@ class Laminar:
218
220
  cls,
219
221
  name: str,
220
222
  input: Any = None,
221
- span_type: Union[
222
- Literal["DEFAULT"], Literal["LLM"], Literal["TOOL"]
223
- ] = "DEFAULT",
224
- context: Optional[Context] = None,
225
- labels: Optional[list[str]] = None,
226
- parent_span_context: Optional[LaminarSpanContext] = None,
223
+ span_type: Literal["DEFAULT", "LLM", "TOOL"] = "DEFAULT",
224
+ context: Context | None = None,
225
+ labels: list[str] | None = None,
226
+ parent_span_context: LaminarSpanContext | None = None,
227
227
  ):
228
228
  """Start a new span as the current span. Useful for manual
229
229
  instrumentation. If `span_type` is set to `"LLM"`, you should report
@@ -241,12 +241,12 @@ class Laminar:
241
241
  name (str): name of the span
242
242
  input (Any, optional): input to the span. Will be sent as an\
243
243
  attribute, so must be json serializable. Defaults to None.
244
- span_type (Union[Literal["DEFAULT"], Literal["LLM"]], optional):\
244
+ span_type (Literal["DEFAULT", "LLM", "TOOL"], optional):\
245
245
  type of the span. If you use `"LLM"`, you should report usage\
246
246
  and response attributes manually. Defaults to "DEFAULT".
247
- context (Optional[Context], optional): raw OpenTelemetry context\
247
+ context (Context | None, optional): raw OpenTelemetry context\
248
248
  to attach the span to. Defaults to None.
249
- parent_span_context (Optional[LaminarSpanContext], optional): parent\
249
+ parent_span_context (LaminarSpanContext | None, optional): parent\
250
250
  span context to use for the span. Useful for continuing traces\
251
251
  across services. If parent_span_context is a\
252
252
  raw OpenTelemetry span context, or if it is a dictionary or string\
@@ -256,7 +256,7 @@ class Laminar:
256
256
  `Laminar.get_span_context`, `Laminar.get_span_context_dict` and\
257
257
  `Laminar.get_span_context_str` for more information.
258
258
  Defaults to None.
259
- labels (Optional[list[str]], optional): labels to set for the\
259
+ labels (list[str] | None, optional): labels to set for the\
260
260
  span. Defaults to None.
261
261
  """
262
262
 
@@ -319,7 +319,7 @@ class Laminar:
319
319
 
320
320
  @classmethod
321
321
  @contextmanager
322
- def with_labels(cls, labels: list[str], context: Optional[Context] = None):
322
+ def with_labels(cls, labels: list[str], context: Context | None = None):
323
323
  """Set labels for spans within this `with` context. This is useful for
324
324
  adding labels to the spans created in the auto-instrumentations.
325
325
 
@@ -361,12 +361,10 @@ class Laminar:
361
361
  cls,
362
362
  name: str,
363
363
  input: Any = None,
364
- span_type: Union[
365
- Literal["DEFAULT"], Literal["LLM"], Literal["TOOL"]
366
- ] = "DEFAULT",
367
- context: Optional[Context] = None,
368
- parent_span_context: Optional[LaminarSpanContext] = None,
369
- labels: Optional[dict[str, str]] = None,
364
+ span_type: Literal["DEFAULT", "LLM", "TOOL"] = "DEFAULT",
365
+ context: Context | None = None,
366
+ parent_span_context: LaminarSpanContext | None = None,
367
+ labels: dict[str, str] | None = None,
370
368
  ):
371
369
  """Start a new span. Useful for manual instrumentation.
372
370
  If `span_type` is set to `"LLM"`, you should report usage and response
@@ -403,12 +401,12 @@ class Laminar:
403
401
  name (str): name of the span
404
402
  input (Any, optional): input to the span. Will be sent as an\
405
403
  attribute, so must be json serializable. Defaults to None.
406
- span_type (Union[Literal["DEFAULT"], Literal["LLM"]], optional):\
404
+ span_type (Literal["DEFAULT", "LLM", "TOOL"], optional):\
407
405
  type of the span. If you use `"LLM"`, you should report usage\
408
406
  and response attributes manually. Defaults to "DEFAULT".
409
- context (Optional[Context], optional): raw OpenTelemetry context\
407
+ context (Context | None, optional): raw OpenTelemetry context\
410
408
  to attach the span to. Defaults to None.
411
- parent_span_context (Optional[LaminarSpanContext], optional): parent\
409
+ parent_span_context (LaminarSpanContext | None, optional): parent\
412
410
  span context to use for the span. Useful for continuing traces\
413
411
  across services. If parent_span_context is a\
414
412
  raw OpenTelemetry span context, or if it is a dictionary or string\
@@ -418,7 +416,7 @@ class Laminar:
418
416
  `Laminar.get_span_context`, `Laminar.get_span_context_dict` and\
419
417
  `Laminar.get_span_context_str` for more information.
420
418
  Defaults to None.
421
- labels (Optional[dict[str, str]], optional): labels to set for the\
419
+ labels (dict[str, str] | None, optional): labels to set for the\
422
420
  span. Defaults to None.
423
421
  """
424
422
  if not cls.is_initialized():
@@ -572,8 +570,8 @@ class Laminar:
572
570
 
573
571
  @classmethod
574
572
  def get_laminar_span_context(
575
- cls, span: Optional[trace.Span] = None
576
- ) -> Optional[LaminarSpanContext]:
573
+ cls, span: trace.Span | None = None
574
+ ) -> LaminarSpanContext | None:
577
575
  """Get the laminar span context for a given span.
578
576
  If no span is provided, the current active span will be used.
579
577
  """
@@ -588,15 +586,15 @@ class Laminar:
588
586
 
589
587
  @classmethod
590
588
  def get_laminar_span_context_dict(
591
- cls, span: Optional[trace.Span] = None
592
- ) -> Optional[dict]:
589
+ cls, span: trace.Span | None = None
590
+ ) -> dict | None:
593
591
  span_context = cls.get_laminar_span_context(span)
594
592
  if span_context is None:
595
593
  return None
596
594
  return span_context.model_dump()
597
595
 
598
596
  @classmethod
599
- def serialize_span_context(cls, span: Optional[trace.Span] = None) -> Optional[str]:
597
+ def serialize_span_context(cls, span: trace.Span | None = None) -> str | None:
600
598
  """Get the laminar span context for a given span as a string.
601
599
  If no span is provided, the current active span will be used.
602
600
 
@@ -630,9 +628,7 @@ class Laminar:
630
628
  return str(span_context)
631
629
 
632
630
  @classmethod
633
- def deserialize_span_context(
634
- cls, span_context: Union[dict, str]
635
- ) -> LaminarSpanContext:
631
+ def deserialize_span_context(cls, span_context: dict | str) -> LaminarSpanContext:
636
632
  return LaminarSpanContext.deserialize(span_context)
637
633
 
638
634
  @classmethod
@@ -655,14 +651,14 @@ class Laminar:
655
651
  @classmethod
656
652
  def set_session(
657
653
  cls,
658
- session_id: Optional[str] = None,
654
+ session_id: str | None = None,
659
655
  ):
660
656
  """Set the session and user id for the current span and the context
661
657
  (i.e. any children spans created from the current span in the current
662
658
  thread).
663
659
 
664
660
  Args:
665
- session_id (Optional[str], optional): Custom session id.\
661
+ session_id (str | None, optional): Custom session id.\
666
662
  Useful to debug and group long-running\
667
663
  sessions/conversations.
668
664
  Defaults to None.
@@ -700,6 +696,28 @@ class Laminar:
700
696
  props.pop("user_id", None)
701
697
  set_association_properties(props)
702
698
 
699
+ @classmethod
700
+ def get_base_http_url(cls):
701
+ return cls.__base_http_url
702
+
703
+ @classmethod
704
+ def get_project_api_key(cls):
705
+ return cls.__project_api_key
706
+
707
+ @classmethod
708
+ def get_trace_id(cls) -> uuid.UUID | None:
709
+ """Get the trace id for the current active span represented as a UUID.
710
+ Returns None if there is no active span.
711
+
712
+ Returns:
713
+ uuid.UUID | None: The trace id for the current span, or None if\
714
+ there is no active span.
715
+ """
716
+ trace_id = trace.get_current_span().get_span_context().trace_id
717
+ if trace_id == INVALID_TRACE_ID:
718
+ return None
719
+ return uuid.UUID(int=trace_id)
720
+
703
721
  @classmethod
704
722
  def _headers(cls):
705
723
  assert cls.__project_api_key is not None, "Project API key is not set"