divi 0.0.1.dev14__py3-none-any.whl → 0.0.1.dev21__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.
divi/__init__.py CHANGED
@@ -12,5 +12,5 @@ _core: Optional[Core] = None
12
12
  _auth: Optional[Auth] = None
13
13
  _datapark: Optional[DataPark] = None
14
14
 
15
- __version__ = "0.0.1.dev14"
15
+ __version__ = "0.0.1.dev21"
16
16
  __all__ = ["proto", "obs_openai", "observable"]
@@ -23,6 +23,7 @@ from divi.proto.trace.v1.trace_pb2 import ScopeSpans
23
23
  from divi.session import SessionExtra
24
24
  from divi.session.setup import setup
25
25
  from divi.signals.trace import Span
26
+ from divi.utils import extract_flattened_inputs
26
27
 
27
28
  R = TypeVar("R", covariant=True)
28
29
  P = ParamSpec("P")
@@ -84,7 +85,8 @@ def observable(
84
85
  # recover parent context
85
86
  _SESSION_EXTRA.reset(token)
86
87
 
87
- # TODO: collect result
88
+ # TODO: collect inputs and outputs
89
+ inputs = extract_flattened_inputs(func, *args, **kwargs)
88
90
  # create the span if it is the root span
89
91
  if divi._datapark and span.trace_id:
90
92
  divi._datapark.create_spans(
@@ -98,9 +100,10 @@ def observable(
98
100
  divi._datapark.upsert_traces(
99
101
  session_id=trace.session_id, traces=[trace.signal]
100
102
  )
103
+ # create the chat completion if it is a chat completion
101
104
  if divi._datapark and isinstance(result, ChatCompletion):
102
105
  divi._datapark.create_chat_completion(
103
- span_id=span.span_id, completion=result
106
+ span_id=span.span_id, inputs=inputs, completion=result
104
107
  )
105
108
  return result
106
109
 
@@ -5,7 +5,7 @@ import warnings
5
5
 
6
6
  from divi.proto.core.health.v1 import health_service_pb2 as divi_dot_proto_dot_core_dot_health_dot_v1_dot_health__service__pb2
7
7
 
8
- GRPC_GENERATED_VERSION = '1.69.0'
8
+ GRPC_GENERATED_VERSION = '1.71.0'
9
9
  GRPC_VERSION = grpc.__version__
10
10
  _version_not_supported = False
11
11
 
@@ -25,7 +25,7 @@ _sym_db = _symbol_database.Default()
25
25
  from divi.proto.common.v1 import common_pb2 as divi_dot_proto_dot_common_dot_v1_dot_common__pb2
26
26
 
27
27
 
28
- DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x64ivi/proto/trace/v1/trace.proto\x12\x13\x64ivi.proto.trace.v1\x1a!divi/proto/common/v1/common.proto\"\x83\x01\n\x05Trace\x12\x10\n\x08trace_id\x18\x01 \x01(\x0c\x12\x1c\n\x14start_time_unix_nano\x18\x02 \x01(\x06\x12\x1a\n\x12\x65nd_time_unix_nano\x18\x03 \x01(\x06\x12.\n\x05spans\x18\x04 \x03(\x0b\x32\x1f.divi.proto.trace.v1.ScopeSpans\"6\n\nScopeSpans\x12(\n\x05spans\x18\x02 \x03(\x0b\x32\x19.divi.proto.trace.v1.Span\"\xa4\x02\n\x04Span\x12\x10\n\x08trace_id\x18\x01 \x01(\x0c\x12\x0f\n\x07span_id\x18\x02 \x01(\x0c\x12\x16\n\x0eparent_span_id\x18\x03 \x01(\x0c\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x30\n\x04kind\x18\x05 \x01(\x0e\x32\".divi.proto.trace.v1.Span.SpanKind\x12\x1c\n\x14start_time_unix_nano\x18\x06 \x01(\x06\x12\x1a\n\x12\x65nd_time_unix_nano\x18\x07 \x01(\x06\x12\x30\n\x08metadata\x18\x08 \x03(\x0b\x32\x1e.divi.proto.common.v1.KeyValue\"5\n\x08SpanKind\x12\x16\n\x12SPAN_KIND_FUNCTION\x10\x00\x12\x11\n\rSPAN_KIND_LLM\x10\x01\x42\rZ\x0bservices/pbb\x06proto3')
28
+ DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1f\x64ivi/proto/trace/v1/trace.proto\x12\x13\x64ivi.proto.trace.v1\x1a!divi/proto/common/v1/common.proto\"6\n\nScopeSpans\x12(\n\x05spans\x18\x02 \x03(\x0b\x32\x19.divi.proto.trace.v1.Span\"\xa4\x02\n\x04Span\x12\x10\n\x08trace_id\x18\x01 \x01(\x0c\x12\x0f\n\x07span_id\x18\x02 \x01(\x0c\x12\x16\n\x0eparent_span_id\x18\x03 \x01(\x0c\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x30\n\x04kind\x18\x05 \x01(\x0e\x32\".divi.proto.trace.v1.Span.SpanKind\x12\x1c\n\x14start_time_unix_nano\x18\x06 \x01(\x06\x12\x1a\n\x12\x65nd_time_unix_nano\x18\x07 \x01(\x06\x12\x30\n\x08metadata\x18\x08 \x03(\x0b\x32\x1e.divi.proto.common.v1.KeyValue\"5\n\x08SpanKind\x12\x16\n\x12SPAN_KIND_FUNCTION\x10\x00\x12\x11\n\rSPAN_KIND_LLM\x10\x01\x42\rZ\x0bservices/pbb\x06proto3')
29
29
 
30
30
  _globals = globals()
31
31
  _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
@@ -33,12 +33,10 @@ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'divi.proto.trace.v1.trace_p
33
33
  if not _descriptor._USE_C_DESCRIPTORS:
34
34
  _globals['DESCRIPTOR']._loaded_options = None
35
35
  _globals['DESCRIPTOR']._serialized_options = b'Z\013services/pb'
36
- _globals['_TRACE']._serialized_start=92
37
- _globals['_TRACE']._serialized_end=223
38
- _globals['_SCOPESPANS']._serialized_start=225
39
- _globals['_SCOPESPANS']._serialized_end=279
40
- _globals['_SPAN']._serialized_start=282
41
- _globals['_SPAN']._serialized_end=574
42
- _globals['_SPAN_SPANKIND']._serialized_start=521
43
- _globals['_SPAN_SPANKIND']._serialized_end=574
36
+ _globals['_SCOPESPANS']._serialized_start=91
37
+ _globals['_SCOPESPANS']._serialized_end=145
38
+ _globals['_SPAN']._serialized_start=148
39
+ _globals['_SPAN']._serialized_end=440
40
+ _globals['_SPAN_SPANKIND']._serialized_start=387
41
+ _globals['_SPAN_SPANKIND']._serialized_end=440
44
42
  # @@protoc_insertion_point(module_scope)
@@ -7,18 +7,6 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map
7
7
 
8
8
  DESCRIPTOR: _descriptor.FileDescriptor
9
9
 
10
- class Trace(_message.Message):
11
- __slots__ = ("trace_id", "start_time_unix_nano", "end_time_unix_nano", "spans")
12
- TRACE_ID_FIELD_NUMBER: _ClassVar[int]
13
- START_TIME_UNIX_NANO_FIELD_NUMBER: _ClassVar[int]
14
- END_TIME_UNIX_NANO_FIELD_NUMBER: _ClassVar[int]
15
- SPANS_FIELD_NUMBER: _ClassVar[int]
16
- trace_id: bytes
17
- start_time_unix_nano: int
18
- end_time_unix_nano: int
19
- spans: _containers.RepeatedCompositeFieldContainer[ScopeSpans]
20
- def __init__(self, trace_id: _Optional[bytes] = ..., start_time_unix_nano: _Optional[int] = ..., end_time_unix_nano: _Optional[int] = ..., spans: _Optional[_Iterable[_Union[ScopeSpans, _Mapping]]] = ...) -> None: ...
21
-
22
10
  class ScopeSpans(_message.Message):
23
11
  __slots__ = ("spans",)
24
12
  SPANS_FIELD_NUMBER: _ClassVar[int]
@@ -1,5 +1,3 @@
1
- import requests
2
-
3
1
  from divi.services.auth.tokman import Token
4
2
  from divi.services.service import Service
5
3
 
@@ -12,10 +10,4 @@ class Auth(Service):
12
10
 
13
11
  def auth_with_api_key(self) -> str:
14
12
  """Get the token with the API key."""
15
- r = requests.post(
16
- f"http://{self.target}/api/auth/api_key",
17
- json={"api_key": self.api_key},
18
- )
19
- if r.status_code == 200:
20
- return r.json()["data"]
21
- raise ValueError(r.json()["message"])
13
+ return self.post("/api/auth/api_key", payload={"api_key": self.api_key})
@@ -1,6 +1,8 @@
1
- import requests
1
+ from typing import Any, Dict, TYPE_CHECKING
2
+ from typing_extensions import Mapping
3
+
2
4
  from google.protobuf.json_format import MessageToDict
3
- from openai.types.chat.chat_completion import ChatCompletion
5
+ from openai.types.chat import ChatCompletion
4
6
  from pydantic import UUID4
5
7
 
6
8
  import divi
@@ -8,6 +10,7 @@ from divi.proto.trace.v1.trace_pb2 import ScopeSpans
8
10
  from divi.services.service import Service
9
11
  from divi.session.session import SessionSignal
10
12
  from divi.signals.trace.trace import TraceSignal
13
+ from openai import NotGiven
11
14
 
12
15
 
13
16
  class DataPark(Service):
@@ -18,46 +21,47 @@ class DataPark(Service):
18
21
  self.token = divi._auth.token
19
22
 
20
23
  @property
21
- def headers(self):
24
+ def headers(self) -> Dict[str, str]:
22
25
  return {"Authorization": f"Bearer {self.token}"}
23
26
 
24
- def create_session(self, session: SessionSignal):
25
- r = requests.post(
26
- f"http://{self.target}/api/session/",
27
- headers=self.headers,
28
- json=session,
29
- )
30
- if r.status_code != 201:
31
- raise ValueError(r.json()["message"])
32
-
33
- def upsert_traces(self, session_id: UUID4, traces: list[TraceSignal]):
34
- r = requests.post(
35
- f"http://{self.target}/api/session/{session_id}/traces",
36
- headers=self.headers,
37
- json=traces,
38
- )
39
- if r.status_code != 201:
40
- raise ValueError(r.json()["message"])
41
-
42
- def create_spans(self, trace_id: UUID4, spans: ScopeSpans):
43
- r = requests.post(
44
- f"http://{self.target}/api/trace/{trace_id}/spans",
45
- headers=self.headers,
46
- json=MessageToDict(spans),
47
- )
48
- if r.status_code != 201:
49
- raise ValueError(r.json()["message"])
27
+ @staticmethod
28
+ def strip_not_given(obj: object | None) -> object:
29
+ """Remove all top-level keys where their values are instances of `NotGiven`"""
30
+ if obj is None:
31
+ return None
32
+
33
+ if not isinstance(obj, Mapping):
34
+ return obj
35
+
36
+ return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)}
37
+
38
+ def create_session(self, session: SessionSignal) -> None:
39
+ self.post("/api/session/", payload=session)
40
+
41
+ def upsert_traces(
42
+ self, session_id: UUID4, traces: list[TraceSignal]
43
+ ) -> None:
44
+ self.post(f"/api/session/{session_id}/traces", payload=traces)
45
+
46
+ def create_spans(self, trace_id: UUID4, spans: ScopeSpans) -> None:
47
+ self.post(f"/api/trace/{trace_id}/spans", payload=MessageToDict(spans))
50
48
 
51
49
  def create_chat_completion(
52
- self, span_id: bytes, completion: ChatCompletion
53
- ):
54
- r = requests.post(
55
- f"http://{self.target}/api/v1/chat/completions",
56
- headers=self.headers,
57
- json={
58
- "span_id": span_id.hex(),
59
- "data": completion.model_dump(),
60
- },
50
+ self,
51
+ span_id: bytes,
52
+ inputs: Dict[str, Any],
53
+ completion: ChatCompletion,
54
+ ) -> None:
55
+ hex_span_id = span_id.hex()
56
+ self.post_concurrent(
57
+ {
58
+ "/api/v1/chat/completions/input": {
59
+ "span_id": hex_span_id,
60
+ "data": self.strip_not_given(inputs),
61
+ },
62
+ "/api/v1/chat/completions": {
63
+ "span_id": hex_span_id,
64
+ "data": completion.model_dump(),
65
+ },
66
+ }
61
67
  )
62
- if r.status_code != 201:
63
- raise ValueError(r.json()["message"])
divi/services/service.py CHANGED
@@ -1,3 +1,9 @@
1
+ from concurrent.futures import ThreadPoolExecutor
2
+ from typing import Any, Dict, Mapping, Sequence
3
+
4
+ import requests
5
+
6
+
1
7
  class Service:
2
8
  """Service management class."""
3
9
 
@@ -9,3 +15,40 @@ class Service:
9
15
  def target(self) -> str:
10
16
  """Return the target string: host:port."""
11
17
  return f"{self.host}:{self.port}"
18
+
19
+ @property
20
+ def headers(self) -> Dict[str, str]:
21
+ """Override to provide default headers."""
22
+ return {}
23
+
24
+ def post(
25
+ self,
26
+ path: str,
27
+ payload: Mapping[str, Any] | Sequence[Mapping[str, Any]],
28
+ ) -> Any:
29
+ response = requests.post(
30
+ f"http://{self.target}{path}",
31
+ json=payload,
32
+ headers=self.headers,
33
+ )
34
+ if not 200 <= response.status_code < 300:
35
+ raise ValueError(
36
+ f"{path} failed: {response.json().get('message', 'Unknown error')}"
37
+ )
38
+ return response.json().get("data", response.json())
39
+
40
+ def post_concurrent(
41
+ self,
42
+ calls: Dict[str, Mapping[str, Any] | Sequence[Mapping[str, Any]]],
43
+ ) -> None:
44
+ with ThreadPoolExecutor(max_workers=len(calls)) as executor:
45
+ futures = [
46
+ executor.submit(
47
+ self.post,
48
+ path,
49
+ payload,
50
+ )
51
+ for path, payload in calls.items()
52
+ ]
53
+ for future in futures:
54
+ future.result()
divi/utils.py CHANGED
@@ -1,6 +1,33 @@
1
1
  import inspect
2
2
  import pathlib
3
- from typing import Callable
3
+ from typing import Any, Callable, Dict
4
+
5
+
6
+ def extract_flattened_inputs(
7
+ func: Callable, *args: Any, **kwargs: Any
8
+ ) -> Dict[str, Any]:
9
+ """
10
+ Extracts a flat dictionary of parameter names and values from a function call.
11
+ Handles default values, removes 'self' and 'cls', and flattens **kwargs.
12
+ """
13
+ signature = inspect.signature(func)
14
+ bound_args = signature.bind_partial(*args, **kwargs)
15
+ bound_args.apply_defaults()
16
+
17
+ arguments = dict(bound_args.arguments)
18
+ arguments.pop("self", None)
19
+ arguments.pop("cls", None)
20
+
21
+ for param_name, param in signature.parameters.items():
22
+ if (
23
+ param.kind == inspect.Parameter.VAR_KEYWORD
24
+ and param_name in arguments
25
+ ):
26
+ kwarg_dict = arguments.pop(param_name)
27
+ if isinstance(kwarg_dict, dict):
28
+ arguments.update(kwarg_dict)
29
+
30
+ return arguments
4
31
 
5
32
 
6
33
  def get_server_path() -> str:
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: divi
3
- Version: 0.0.1.dev14
3
+ Version: 0.0.1.dev21
4
4
  Summary: The Agent Platform for Observability & Evaluation
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
7
- Requires-Dist: grpcio>=1.69.0
8
- Requires-Dist: protobuf>=5.29.3
7
+ Requires-Dist: grpcio>=1.71.0
8
+ Requires-Dist: openai>=1.65.2
9
+ Requires-Dist: protobuf<6.0.dev0,>=5.26.1
9
10
  Requires-Dist: pyjwt>=2.10.1
10
11
  Requires-Dist: requests>=2.32.3
11
12
  Description-Content-Type: text/markdown
@@ -1,28 +1,28 @@
1
- divi/__init__.py,sha256=RnfnWUt0BjmN6ngPQQrUIY6TGJX20NtgbB7fsq2elnQ,396
2
- divi/utils.py,sha256=3iVDogCjqQg0jEjhUKEuQ6vHPFp9w7kXNjSVwXt8KmI,574
1
+ divi/__init__.py,sha256=-XjMcUmlsDSB_v0_rf5owoldPj1mXFoAgUaVZpyfwqk,396
2
+ divi/utils.py,sha256=fXkjoyo_Lh8AZliKICOP460m0czUcNQjcEcceJbaOVA,1439
3
3
  divi/config/config.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  divi/decorators/__init__.py,sha256=HkyWdC1ctTsVFucCWCkj57JB4NmwONus1d2S2dUbvs4,110
5
5
  divi/decorators/obs_openai.py,sha256=4LtQPRgJPPyTOw1318Fi04WYqjIwGX8EP0YiWrTW0dk,1003
6
- divi/decorators/observable.py,sha256=N_MCX_HbMGpD7v_dxnK6ewhgONGxH4XBO-i-6ybGpkc,4038
6
+ divi/decorators/observable.py,sha256=OZ0Ry6Q2sdZPAT_T5j2wSsM7g0nwDroxeSbP3nLMDvA,4250
7
7
  divi/proto/common/v1/common.proto,sha256=Rx8wr0_tOtQ1NseTMnsav4ApD1MDALzQDBA2IvLRTU0,1775
8
8
  divi/proto/common/v1/common_pb2.py,sha256=br61OHQVAi6SI3baFcb5xJv2Xd-AZ04A19xeSjLNMXo,2442
9
9
  divi/proto/common/v1/common_pb2.pyi,sha256=LmTpFFLxHg2a_qPIdNXXwGEMkbiDcTJdarR9eC-6Fq8,2133
10
10
  divi/proto/core/health/v1/health_service.proto,sha256=y39MNiwYiSqxb_JPQI7AmPfNNtP6pbX-WKTseMkjWUM,369
11
11
  divi/proto/core/health/v1/health_service_pb2.py,sha256=PKAxy_z7lU1HrwDNIa7AFHha9i8gICCVrcEhpOM79E0,1999
12
12
  divi/proto/core/health/v1/health_service_pb2.pyi,sha256=VbYHHTcp3epUVg0lTSxo3xpG52ZGhA7KKMRL5LR5e1c,691
13
- divi/proto/core/health/v1/health_service_pb2_grpc.py,sha256=6owvQ41KPqSOQ8WjXDjYsunoDVydJfP93vZ4uVSG59s,3953
13
+ divi/proto/core/health/v1/health_service_pb2_grpc.py,sha256=YmlO94d-G71YBW1XZDSbYkwnHEUe6Z7R3_KAmoDWyA0,3953
14
14
  divi/proto/metric/v1/metric.proto,sha256=YHRMLUW-MtakHuibR3PJ0s2w5KgV12kc4737iHw0DTk,585
15
15
  divi/proto/metric/v1/metric_pb2.py,sha256=uvBhyy8QpaES3Jl82yVfsGazW5654XpRnsdGlpVgIRE,1974
16
16
  divi/proto/metric/v1/metric_pb2.pyi,sha256=S7ipsojkD7QZAYefDE4b3PO99Yzc6mOdtSLxH3-b67A,1304
17
17
  divi/proto/trace/v1/trace.proto,sha256=mh1nzEgufzRTJx3p8NNute-ozEwEYwClWJTdWUGGVA8,1284
18
- divi/proto/trace/v1/trace_pb2.py,sha256=N2sVI20CP2NtJLiNt376bxyYrwiqhQwGHO0hJKciUTo,2650
19
- divi/proto/trace/v1/trace_pb2.pyi,sha256=aTkzh1Txje5ghuAjYpowDLWz8LTNBrJZej5tUHfVIow,2805
18
+ divi/proto/trace/v1/trace_pb2.py,sha256=CuTkSSvhxCa1bk3Ku7tgLqRSovp_Gi52CZ0zLcLP2Ew,2327
19
+ divi/proto/trace/v1/trace_pb2.pyi,sha256=rPo2Oa3NWrINE_dyOVU9HUYHo5LY82Bm5TMenj5dnK8,2136
20
20
  divi/services/__init__.py,sha256=TcVJ_gKxyPIcwhT9GgttqHeyk0icW44uE285KmUiyh4,185
21
21
  divi/services/finish.py,sha256=XKPKGJ5cWd5H95G_VpIOlOZOLrcf9StoTs7ayRic2jY,173
22
22
  divi/services/init.py,sha256=JVzRQ1m1DTHXFVGUMYnsv-vRvzCO8XFdR6MjIwOL_NY,433
23
- divi/services/service.py,sha256=1c7JQ49BSdBipGLfVIxTHaNxTuyvVAgrvxV7lyYv_68,285
23
+ divi/services/service.py,sha256=539MhcYfMvsVGjDdu0UtYSZnL2cloaPeYeOSMl2eUy8,1532
24
24
  divi/services/auth/__init__.py,sha256=PIQ9rQ0jcRqcy03a3BOY7wbzwluIRG_4kI_H4J4mRFk,74
25
- divi/services/auth/auth.py,sha256=dTpFnNhxbEjzOkBLPQvRblFSO5tedJsdObWTXt84MaE,631
25
+ divi/services/auth/auth.py,sha256=eRcE6Kq8jbBr6YL93HCGDIoga90SoZf3ogOAKeza9WY,445
26
26
  divi/services/auth/init.py,sha256=UXsOweatrWnuFO9ivSjEsJnHrK9YtStKYX2Nup64KEU,477
27
27
  divi/services/auth/tokman.py,sha256=V03wcV6TEy1k9o55teDVB9JZ3Ve2cJdwzWstQhWx4BQ,1070
28
28
  divi/services/core/__init__.py,sha256=FWl4ShX0AEnOyL24NEc9mNIVoQOeYO4q0qYX27L_Yyw,111
@@ -30,7 +30,7 @@ divi/services/core/core.py,sha256=PRwPtLgrgmCrejUfKf7HJNrAhGS0paFNZ7JwDToEUAk,12
30
30
  divi/services/core/finish.py,sha256=dIGQpVXcJY4-tKe7A1_VV3yoSHNCDPfOlUltvzvk6VI,231
31
31
  divi/services/core/init.py,sha256=e7-fgpOPglBXyEoPkgOAnpJk2ApdFbo7LPupxOb8N-w,1966
32
32
  divi/services/datapark/__init__.py,sha256=GbV1mwHE07yutgOlCIYHykSEL5KJ-ApgLutGMzu2eUE,86
33
- divi/services/datapark/datapark.py,sha256=J96C-K9RCaLT7oWXnMQOM1g7btfIlU7BMWReF3ePOlc,2055
33
+ divi/services/datapark/datapark.py,sha256=1CoWJI2embzvijCmIIPwTLnliU4kSolUginGuSWHsBw,2182
34
34
  divi/services/datapark/init.py,sha256=C32f9t3eLsxcYNqEyheh6nW455G2oR0YhhdqBcbN3ec,92
35
35
  divi/session/__init__.py,sha256=6lYemv21VQCIHx-xIdi7BxXcPNxVdvE60--8ArReUew,82
36
36
  divi/session/session.py,sha256=ID7bQ4CuRPUcZ9S-DSjndiduslYFJukRLuCtChYm3wk,838
@@ -39,7 +39,7 @@ divi/session/teardown.py,sha256=YiBz_3yCiljMFEofZ60VmRL5sb8WA5GT7EYF8nFznZ4,133
39
39
  divi/signals/__init__.py,sha256=K1PaTAMwyBDsK6jJUg4QWy0xVJ_5MA6dlWiUyJeiSQA,44
40
40
  divi/signals/trace/__init__.py,sha256=K1PaTAMwyBDsK6jJUg4QWy0xVJ_5MA6dlWiUyJeiSQA,44
41
41
  divi/signals/trace/trace.py,sha256=y8Xio8Tp_e1tO8l_DAkUuzMrmENNKfIdklF0iWrjGYk,4547
42
- divi-0.0.1.dev14.dist-info/METADATA,sha256=SPHVAVGgsvGorPvooFCb2MiHdMP2leko0X17Vzvy8xU,457
43
- divi-0.0.1.dev14.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
- divi-0.0.1.dev14.dist-info/licenses/LICENSE,sha256=5OJuZ4wMMEV0DgF0tofhAlS_KLkaUsZwwwDS2U_GwQ0,1063
45
- divi-0.0.1.dev14.dist-info/RECORD,,
42
+ divi-0.0.1.dev21.dist-info/METADATA,sha256=xyAsE6Uj3sA8T7I83LVLtCEyum_hMQT4ULien2Q-0NU,497
43
+ divi-0.0.1.dev21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
44
+ divi-0.0.1.dev21.dist-info/licenses/LICENSE,sha256=5OJuZ4wMMEV0DgF0tofhAlS_KLkaUsZwwwDS2U_GwQ0,1063
45
+ divi-0.0.1.dev21.dist-info/RECORD,,