lmnr 0.4.62__py3-none-any.whl → 0.4.63b0__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.
lmnr/__init__.py CHANGED
@@ -10,6 +10,7 @@ from .sdk.types import (
10
10
  TracingLevel,
11
11
  )
12
12
  from .sdk.decorators import observe
13
+ from .sdk.types import LaminarSpanContext
13
14
  from .openllmetry_sdk import Instruments
14
15
  from .openllmetry_sdk.tracing.attributes import Attributes
15
16
  from opentelemetry.trace import use_span
@@ -38,7 +38,7 @@ from opentelemetry.sdk.trace.export import (
38
38
  SimpleSpanProcessor,
39
39
  BatchSpanProcessor,
40
40
  )
41
- from opentelemetry.trace import get_tracer_provider, ProxyTracerProvider
41
+ from opentelemetry.sdk.trace import SpanLimits
42
42
 
43
43
  from typing import Dict, Optional, Set
44
44
 
@@ -69,6 +69,8 @@ EXCLUDED_URLS = """
69
69
  githubusercontent.com,
70
70
  openaipublic.blob.core.windows.net"""
71
71
 
72
+ MAX_EVENTS_OR_ATTRIBUTES_PER_SPAN = 5000
73
+
72
74
 
73
75
  class TracerWrapper(object):
74
76
  resource_attributes: dict = {}
@@ -291,22 +293,21 @@ def init_spans_exporter(api_endpoint: str, headers: Dict[str, str]) -> SpanExpor
291
293
  )
292
294
 
293
295
 
296
+ # TODO: check if it's safer to use the default tracer provider obtained from
297
+ # get_tracer_provider()
294
298
  def init_tracer_provider(resource: Resource) -> TracerProvider:
295
- provider: TracerProvider = None
296
- default_provider: TracerProvider = get_tracer_provider()
297
-
298
- if isinstance(default_provider, ProxyTracerProvider):
299
- provider = TracerProvider(resource=resource)
300
- trace.set_tracer_provider(provider)
301
- elif not hasattr(default_provider, "add_span_processor"):
302
- module_logger.error(
303
- "Cannot add span processor to the default provider since it doesn't support it"
304
- )
305
- return
306
- else:
307
- provider = default_provider
299
+ tracer_provider = TracerProvider(
300
+ resource=resource,
301
+ span_limits=SpanLimits(
302
+ # this defaults to 128, which causes us to drop messages
303
+ max_attributes=MAX_EVENTS_OR_ATTRIBUTES_PER_SPAN,
304
+ max_span_attributes=MAX_EVENTS_OR_ATTRIBUTES_PER_SPAN,
305
+ max_event_attributes=MAX_EVENTS_OR_ATTRIBUTES_PER_SPAN,
306
+ max_events=MAX_EVENTS_OR_ATTRIBUTES_PER_SPAN,
307
+ ),
308
+ )
308
309
 
309
- return provider
310
+ return tracer_provider
310
311
 
311
312
 
312
313
  def init_instrumentations(
lmnr/sdk/laminar.py CHANGED
@@ -13,7 +13,6 @@ from lmnr.openllmetry_sdk.config import MAX_MANUAL_SPAN_PAYLOAD_SIZE
13
13
  from lmnr.openllmetry_sdk.decorators.base import json_dumps
14
14
  from opentelemetry import context as context_api, trace
15
15
  from opentelemetry.context import attach, detach
16
- from lmnr.version import SDK_VERSION, get_latest_pypi_version, is_latest_version
17
16
  from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
18
17
  OTLPSpanExporter,
19
18
  Compression,
@@ -36,6 +35,7 @@ import requests
36
35
  import re
37
36
  import urllib.parse
38
37
  import uuid
38
+ import warnings
39
39
 
40
40
  from lmnr.openllmetry_sdk.tracing.attributes import (
41
41
  SESSION_ID,
@@ -55,6 +55,7 @@ from .types import (
55
55
  InitEvaluationResponse,
56
56
  EvaluationResultDatapoint,
57
57
  GetDatapointsResponse,
58
+ LaminarSpanContext,
58
59
  PipelineRunError,
59
60
  PipelineRunResponse,
60
61
  NodeInput,
@@ -347,8 +348,10 @@ class Laminar:
347
348
  Literal["DEFAULT"], Literal["LLM"], Literal["TOOL"]
348
349
  ] = "DEFAULT",
349
350
  context: Optional[Context] = None,
350
- trace_id: Optional[uuid.UUID] = None,
351
351
  labels: Optional[dict[str, str]] = None,
352
+ parent_span_context: Optional[LaminarSpanContext] = None,
353
+ # deprecated, use parent_span_context instead
354
+ trace_id: Optional[uuid.UUID] = None,
352
355
  ):
353
356
  """Start a new span as the current span. Useful for manual
354
357
  instrumentation. If `span_type` is set to `"LLM"`, you should report
@@ -371,11 +374,21 @@ class Laminar:
371
374
  and response attributes manually. Defaults to "DEFAULT".
372
375
  context (Optional[Context], optional): raw OpenTelemetry context\
373
376
  to attach the span to. Defaults to None.
374
- trace_id (Optional[uuid.UUID], optional): [EXPERIMENTAL] override\
375
- the trace id for the span. If not provided, use the current\
376
- trace id. Defaults to None.
377
+ parent_span_context (Optional[LaminarSpanContext], optional): parent\
378
+ span context to use for the span. Useful for continuing traces\
379
+ across services. If parent_span_context is a\
380
+ raw OpenTelemetry span context, or if it is a dictionary or string\
381
+ obtained from `Laminar.get_laminar_span_context_dict()` or\
382
+ `Laminar.get_laminar_span_context_str()` respectively, it will be\
383
+ converted to a `LaminarSpanContext` if possible. See also\
384
+ `Laminar.get_span_context`, `Laminar.get_span_context_dict` and\
385
+ `Laminar.get_span_context_str` for more information.
386
+ Defaults to None.
377
387
  labels (Optional[dict[str, str]], optional): labels to set for the\
378
388
  span. Defaults to None.
389
+ trace_id (Optional[uuid.UUID], optional): [Deprecated] override\
390
+ the trace id for the span. If not provided, use the current\
391
+ trace id. Defaults to None.
379
392
  """
380
393
 
381
394
  if not cls.is_initialized():
@@ -385,21 +398,29 @@ class Laminar:
385
398
  with get_tracer() as tracer:
386
399
  ctx = context or context_api.get_current()
387
400
  if trace_id is not None:
388
- if isinstance(trace_id, uuid.UUID):
389
- span_context = trace.SpanContext(
390
- trace_id=int(trace_id),
391
- span_id=random.getrandbits(64),
392
- is_remote=False,
393
- trace_flags=trace.TraceFlags(trace.TraceFlags.SAMPLED),
394
- )
395
- ctx = trace.set_span_in_context(
396
- trace.NonRecordingSpan(span_context), ctx
397
- )
398
- else:
399
- cls.__logger.warning(
400
- "trace_id provided to `Laminar.start_as_current_span`"
401
- " is not a valid UUID"
402
- )
401
+ warnings.warn(
402
+ "trace_id provided to `Laminar.start_as_current_span`"
403
+ " is deprecated, use parent_span_context instead",
404
+ DeprecationWarning,
405
+ stacklevel=2,
406
+ )
407
+ if parent_span_context is not None:
408
+ span_context = LaminarSpanContext.try_to_otel_span_context(
409
+ parent_span_context, cls.__logger
410
+ )
411
+ ctx = trace.set_span_in_context(
412
+ trace.NonRecordingSpan(span_context), ctx
413
+ )
414
+ elif trace_id is not None and isinstance(trace_id, uuid.UUID):
415
+ span_context = trace.SpanContext(
416
+ trace_id=int(trace_id),
417
+ span_id=random.getrandbits(64),
418
+ is_remote=False,
419
+ trace_flags=trace.TraceFlags(trace.TraceFlags.SAMPLED),
420
+ )
421
+ ctx = trace.set_span_in_context(
422
+ trace.NonRecordingSpan(span_context), ctx
423
+ )
403
424
  ctx_token = attach(ctx)
404
425
  label_props = {}
405
426
  try:
@@ -487,8 +508,10 @@ class Laminar:
487
508
  Literal["DEFAULT"], Literal["LLM"], Literal["TOOL"]
488
509
  ] = "DEFAULT",
489
510
  context: Optional[Context] = None,
490
- trace_id: Optional[uuid.UUID] = None,
511
+ parent_span_context: Optional[LaminarSpanContext] = None,
491
512
  labels: Optional[dict[str, str]] = None,
513
+ # deprecated, use parent_span_context instead
514
+ trace_id: Optional[uuid.UUID] = None,
492
515
  ):
493
516
  """Start a new span. Useful for manual instrumentation.
494
517
  If `span_type` is set to `"LLM"`, you should report usage and response
@@ -530,30 +553,48 @@ class Laminar:
530
553
  and response attributes manually. Defaults to "DEFAULT".
531
554
  context (Optional[Context], optional): raw OpenTelemetry context\
532
555
  to attach the span to. Defaults to None.
533
- trace_id (Optional[uuid.UUID], optional): [EXPERIMENTAL] override\
534
- the trace id for the span. If not provided, use the current\
535
- trace id. Defaults to None.
556
+ parent_span_context (Optional[LaminarSpanContext], optional): parent\
557
+ span context to use for the span. Useful for continuing traces\
558
+ across services. If parent_span_context is a\
559
+ raw OpenTelemetry span context, or if it is a dictionary or string\
560
+ obtained from `Laminar.get_laminar_span_context_dict()` or\
561
+ `Laminar.get_laminar_span_context_str()` respectively, it will be\
562
+ converted to a `LaminarSpanContext` if possible. See also\
563
+ `Laminar.get_span_context`, `Laminar.get_span_context_dict` and\
564
+ `Laminar.get_span_context_str` for more information.
565
+ Defaults to None.
536
566
  labels (Optional[dict[str, str]], optional): labels to set for the\
537
567
  span. Defaults to None.
568
+ trace_id (Optional[uuid.UUID], optional): Deprecated, use\
569
+ `parent_span_context` instead. If provided, it will be used to\
570
+ set the trace id for the span.
538
571
  """
539
572
  with get_tracer() as tracer:
540
573
  ctx = context or context_api.get_current()
541
574
  if trace_id is not None:
542
- if isinstance(trace_id, uuid.UUID):
543
- span_context = trace.SpanContext(
544
- trace_id=int(trace_id),
545
- span_id=random.getrandbits(64),
546
- is_remote=False,
547
- trace_flags=trace.TraceFlags(trace.TraceFlags.SAMPLED),
548
- )
549
- ctx = trace.set_span_in_context(
550
- trace.NonRecordingSpan(span_context), ctx
551
- )
552
- else:
553
- cls.__logger.warning(
554
- "trace_id provided to `Laminar.start_span`"
555
- " is not a valid UUID"
556
- )
575
+ warnings.warn(
576
+ "trace_id provided to `Laminar.start_span`"
577
+ " is deprecated, use parent_span_context instead",
578
+ DeprecationWarning,
579
+ stacklevel=2,
580
+ )
581
+ if parent_span_context is not None:
582
+ span_context = LaminarSpanContext.try_to_otel_span_context(
583
+ parent_span_context, cls.__logger
584
+ )
585
+ ctx = trace.set_span_in_context(
586
+ trace.NonRecordingSpan(span_context), ctx
587
+ )
588
+ elif trace_id is not None and isinstance(trace_id, uuid.UUID):
589
+ span_context = trace.SpanContext(
590
+ trace_id=int(trace_id),
591
+ span_id=random.getrandbits(64),
592
+ is_remote=False,
593
+ trace_flags=trace.TraceFlags(trace.TraceFlags.SAMPLED),
594
+ )
595
+ ctx = trace.set_span_in_context(
596
+ trace.NonRecordingSpan(span_context), ctx
597
+ )
557
598
  label_props = {}
558
599
  try:
559
600
  if labels:
@@ -688,6 +729,73 @@ class Laminar:
688
729
  else:
689
730
  span.set_attribute(key.value, value)
690
731
 
732
+ @classmethod
733
+ def get_laminar_span_context(
734
+ cls, span: Optional[trace.Span] = None
735
+ ) -> Optional[LaminarSpanContext]:
736
+ """Get the laminar span context for a given span.
737
+ If no span is provided, the current active span will be used.
738
+ """
739
+ span = span or trace.get_current_span()
740
+ if span == trace.INVALID_SPAN:
741
+ return None
742
+ return LaminarSpanContext(
743
+ trace_id=uuid.UUID(int=span.get_span_context().trace_id),
744
+ span_id=uuid.UUID(int=span.get_span_context().span_id),
745
+ is_remote=span.get_span_context().is_remote,
746
+ )
747
+
748
+ @classmethod
749
+ def get_laminar_span_context_dict(
750
+ cls, span: Optional[trace.Span] = None
751
+ ) -> Optional[dict]:
752
+ """Get the laminar span context for a given span as a dictionary.
753
+ If no span is provided, the current active span will be used.
754
+
755
+ This is useful for continuing a trace across services.
756
+
757
+ Example:
758
+ ```python
759
+ # service A:
760
+ with Laminar.start_as_current_span("service_a"):
761
+ span_context = Laminar.get_laminar_span_context_dict()
762
+ # send span_context to service B
763
+ call_service_b(request, headers={"laminar-span-context": span_context})
764
+
765
+ # service B:
766
+ def call_service_b(request, headers):
767
+ span_context = LaminarSpanContext.from_dict(headers["laminar-span-context"])
768
+ with Laminar.start_as_current_span("service_b", parent_span_context=span_context):
769
+ # rest of the function
770
+ pass
771
+ ```
772
+
773
+ This will result in a trace like:
774
+ ```
775
+ service_a
776
+ service_b
777
+ ```
778
+ """
779
+ span_context = cls.get_laminar_span_context(span)
780
+ if span_context is None:
781
+ return None
782
+ return span_context.to_dict()
783
+
784
+ @classmethod
785
+ def get_laminar_span_context_str(
786
+ cls, span: Optional[trace.Span] = None
787
+ ) -> Optional[str]:
788
+ span_context = cls.get_laminar_span_context(span)
789
+ if span_context is None:
790
+ return None
791
+ return json.dumps(span_context.to_dict())
792
+
793
+ @classmethod
794
+ def deserialize_laminar_span_context(
795
+ cls, span_context: Union[dict, str]
796
+ ) -> LaminarSpanContext:
797
+ return LaminarSpanContext.deserialize(span_context)
798
+
691
799
  @classmethod
692
800
  def set_session(
693
801
  cls,
lmnr/sdk/types.py CHANGED
@@ -1,6 +1,9 @@
1
+ import logging
1
2
  import aiohttp
2
3
  import datetime
3
4
  from enum import Enum
5
+ import json
6
+ from opentelemetry.trace import SpanContext, TraceFlags
4
7
  import pydantic
5
8
  from typing import Any, Awaitable, Callable, Optional, Union
6
9
  import uuid
@@ -210,3 +213,86 @@ class TracingLevel(Enum):
210
213
  OFF = 0
211
214
  META_ONLY = 1
212
215
  ALL = 2
216
+
217
+
218
+ class LaminarSpanContext(pydantic.BaseModel):
219
+ """
220
+ A span context that can be used to continue a trace across services. This
221
+ is a slightly modified version of the OpenTelemetry span context. For
222
+ usage examples, see `Laminar.get_laminar_span_context_dict`,
223
+ `Laminar.get_laminar_span_context_str`, `Laminar.get_span_context`, and
224
+ `Laminar.deserialize_laminar_span_context`.
225
+
226
+ The difference between this and the OpenTelemetry span context is that
227
+ the `trace_id` and `span_id` are stored as UUIDs instead of integers for
228
+ easier debugging, and the separate trace flags are not currently stored.
229
+ """
230
+
231
+ trace_id: uuid.UUID
232
+ span_id: uuid.UUID
233
+ is_remote: bool = pydantic.Field(default=False)
234
+
235
+ # uuid is not serializable by default, so we need to convert it to a string
236
+ def to_dict(self):
237
+ return {
238
+ "traceId": str(self.trace_id),
239
+ "spanId": str(self.span_id),
240
+ "isRemote": self.is_remote,
241
+ }
242
+
243
+ @classmethod
244
+ def from_dict(cls, data: dict[str, Any]) -> "LaminarSpanContext":
245
+ return cls(
246
+ trace_id=uuid.UUID(data["traceId"]),
247
+ span_id=uuid.UUID(data["spanId"]),
248
+ is_remote=data["isRemote"],
249
+ )
250
+
251
+ @classmethod
252
+ def try_to_otel_span_context(
253
+ cls,
254
+ span_context: Union["LaminarSpanContext", dict[str, Any], str, SpanContext],
255
+ logger: Optional[logging.Logger] = None,
256
+ ) -> SpanContext:
257
+ if logger is None:
258
+ logger = logging.getLogger(__name__)
259
+
260
+ if isinstance(span_context, LaminarSpanContext):
261
+ return SpanContext(
262
+ trace_id=span_context.trace_id.int,
263
+ span_id=span_context.span_id.int,
264
+ is_remote=span_context.is_remote,
265
+ trace_flags=TraceFlags(TraceFlags.SAMPLED),
266
+ )
267
+ elif isinstance(span_context, SpanContext) or (
268
+ isinstance(getattr(span_context, "trace_id", None), int)
269
+ and isinstance(getattr(span_context, "span_id", None), int)
270
+ ):
271
+ logger.warning(
272
+ "span_context provided"
273
+ " is likely a raw OpenTelemetry span context. Will try to use it. "
274
+ "Please use `LaminarSpanContext` instead."
275
+ )
276
+ return span_context
277
+ elif isinstance(span_context, dict) or isinstance(span_context, str):
278
+ try:
279
+ laminar_span_context = cls.deserialize(span_context)
280
+ return SpanContext(
281
+ trace_id=laminar_span_context.trace_id.int,
282
+ span_id=laminar_span_context.span_id.int,
283
+ is_remote=laminar_span_context.is_remote,
284
+ trace_flags=TraceFlags(TraceFlags.SAMPLED),
285
+ )
286
+ except Exception:
287
+ raise ValueError("Invalid span_context provided")
288
+ else:
289
+ raise ValueError("Invalid span_context provided")
290
+
291
+ @classmethod
292
+ def deserialize(cls, data: Union[dict[str, Any], str]) -> "LaminarSpanContext":
293
+ if isinstance(data, dict):
294
+ return cls.from_dict(data)
295
+ elif isinstance(data, str):
296
+ return cls.from_dict(json.loads(data))
297
+ else:
298
+ raise ValueError("Invalid span_context provided")
lmnr/version.py CHANGED
@@ -3,7 +3,7 @@ import requests
3
3
  from packaging import version
4
4
 
5
5
 
6
- SDK_VERSION = "0.4.62"
6
+ SDK_VERSION = "0.4.63b0"
7
7
  PYTHON_VERSION = f"{sys.version_info.major}.{sys.version_info.minor}"
8
8
 
9
9
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: lmnr
3
- Version: 0.4.62
3
+ Version: 0.4.63b0
4
4
  Summary: Python SDK for Laminar
5
5
  License: Apache-2.0
6
6
  Author: lmnr.ai
@@ -1,4 +1,4 @@
1
- lmnr/__init__.py,sha256=Bqxs-8Mh4h69pOHURgBCgo9EW1GwChebxP6wUX2-bsU,452
1
+ lmnr/__init__.py,sha256=UiFNMZL5IqflaxgAv5FXcbvFmw4xCQbF6RwXlNYmAAU,494
2
2
  lmnr/cli.py,sha256=4J2RZQhHM3jJcjFvBC4PChQTS-ukxykVvI0X6lTkK-o,2918
3
3
  lmnr/openllmetry_sdk/.flake8,sha256=bCxuDlGx3YQ55QHKPiGJkncHanh9qGjQJUujcFa3lAU,150
4
4
  lmnr/openllmetry_sdk/__init__.py,sha256=TpFNPrRosz-BUpWdfT9ROiZPTGA_JshNwqOfiXlR0MU,2643
@@ -10,7 +10,7 @@ lmnr/openllmetry_sdk/tracing/__init__.py,sha256=xT73L1t2si2CM6QmMiTZ7zn-dKKYBLNr
10
10
  lmnr/openllmetry_sdk/tracing/attributes.py,sha256=cLBmSp4AMv9E91Ck3yD5Z1Qx1L5ZRV-80VJxFA-sO0Q,1426
11
11
  lmnr/openllmetry_sdk/tracing/content_allow_list.py,sha256=3feztm6PBWNelc8pAZUcQyEGyeSpNiVKjOaDk65l2ps,846
12
12
  lmnr/openllmetry_sdk/tracing/context_manager.py,sha256=rdSus-p-TaevQ8hIAhfbnZr5dTqRvACDkzXGDpflncY,306
13
- lmnr/openllmetry_sdk/tracing/tracing.py,sha256=aQsQRI4NKl3K3e0W7TZvbNqtdiWngbmj7lnY9VOak1I,34026
13
+ lmnr/openllmetry_sdk/tracing/tracing.py,sha256=W6Aps6CfkecscSqvcmMPL-C9Ee3J0jMKIMfK27ce5NI,34081
14
14
  lmnr/openllmetry_sdk/utils/__init__.py,sha256=pNhf0G3vTd5ccoc03i1MXDbricSaiqCbi1DLWhSekK8,604
15
15
  lmnr/openllmetry_sdk/utils/in_memory_span_exporter.py,sha256=H_4TRaThMO1H6vUQ0OpQvzJk_fZH0OOsRAM1iZQXsR8,2112
16
16
  lmnr/openllmetry_sdk/utils/json_encoder.py,sha256=dK6b_axr70IYL7Vv-bu4wntvDDuyntoqsHaddqX7P58,463
@@ -24,13 +24,13 @@ lmnr/sdk/datasets.py,sha256=hJcQcwTJbtA4COoVG3god4xll9TBSDMfvrhKmMfanjg,1567
24
24
  lmnr/sdk/decorators.py,sha256=g0VBqUEMCPRbgjgGHauVuKK1wHEd9rkiGzlYUYrcml4,2336
25
25
  lmnr/sdk/eval_control.py,sha256=G6Fg3Xx_KWv72iBaWlNMdyRTF2bZFQnwJ68sJNSpIcY,177
26
26
  lmnr/sdk/evaluations.py,sha256=WmPAgQVm2C83xsG-qMdPrMuvCMFMoVdO37LqZtLc8xw,18702
27
- lmnr/sdk/laminar.py,sha256=0t6hSryO8tNALe9gf32O56K2l2e7j1LoGZv6twkitk0,33910
27
+ lmnr/sdk/laminar.py,sha256=HeE8iJJawnmzUjyAq6krWLBBBcNYe4T8MLcJ2B4oaq0,38650
28
28
  lmnr/sdk/log.py,sha256=nt_YMmPw1IRbGy0b7q4rTtP4Yo3pQfNxqJPXK3nDSNQ,2213
29
- lmnr/sdk/types.py,sha256=Y4msdSM_IvQ5LOfV2jvk4R0-6skW5Ilml466a6swul4,6506
29
+ lmnr/sdk/types.py,sha256=NUVAIZAjjyWB6Ze7naa3Vx9g14Em6OtIXsSNSCJWISE,9892
30
30
  lmnr/sdk/utils.py,sha256=sD1YEqhdPaHweY2VGmjMF9MC-X7Ikdc49E01D-HF77E,3377
31
- lmnr/version.py,sha256=g4oOxngHz-K-Zl-f8U2Q90d4OC8L6Kb9cwsj7eCX60A,1328
32
- lmnr-0.4.62.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
33
- lmnr-0.4.62.dist-info/METADATA,sha256=S5FLOzyb86OdZw1Z9AQ_5gPpR-Lwl_RYCe-RqbhocXc,13827
34
- lmnr-0.4.62.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
35
- lmnr-0.4.62.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
36
- lmnr-0.4.62.dist-info/RECORD,,
31
+ lmnr/version.py,sha256=eqAhz5cq3mTJ-_1A7p7qsBwVH5RjlzNXIKhET20mupY,1330
32
+ lmnr-0.4.63b0.dist-info/LICENSE,sha256=67b_wJHVV1CBaWkrKFWU1wyqTPSdzH77Ls-59631COg,10411
33
+ lmnr-0.4.63b0.dist-info/METADATA,sha256=_PyzYkAcA3j9eRZxvWtwikQpW3mxFnCba7BjZlebkHM,13829
34
+ lmnr-0.4.63b0.dist-info/WHEEL,sha256=XbeZDeTWKc1w7CSIyre5aMDU_-PohRwTQceYnisIYYY,88
35
+ lmnr-0.4.63b0.dist-info/entry_points.txt,sha256=K1jE20ww4jzHNZLnsfWBvU3YKDGBgbOiYG5Y7ivQcq4,37
36
+ lmnr-0.4.63b0.dist-info/RECORD,,
File without changes