divi 0.0.1b0__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 (51) hide show
  1. divi/__init__.py +18 -0
  2. divi/decorators/__init__.py +4 -0
  3. divi/decorators/collect.py +34 -0
  4. divi/decorators/obs_openai.py +48 -0
  5. divi/decorators/observable.py +91 -0
  6. divi/decorators/observe.py +47 -0
  7. divi/evaluation/__init__.py +4 -0
  8. divi/evaluation/evaluate.py +61 -0
  9. divi/evaluation/evaluator.py +174 -0
  10. divi/evaluation/prompts.py +19 -0
  11. divi/evaluation/scores.py +8 -0
  12. divi/proto/common/v1/common.proto +49 -0
  13. divi/proto/common/v1/common_pb2.py +43 -0
  14. divi/proto/common/v1/common_pb2.pyi +44 -0
  15. divi/proto/core/health/v1/health_service.proto +17 -0
  16. divi/proto/core/health/v1/health_service_pb2.py +41 -0
  17. divi/proto/core/health/v1/health_service_pb2.pyi +19 -0
  18. divi/proto/core/health/v1/health_service_pb2_grpc.py +100 -0
  19. divi/proto/metric/v1/metric.proto +26 -0
  20. divi/proto/metric/v1/metric_pb2.py +40 -0
  21. divi/proto/metric/v1/metric_pb2.pyi +25 -0
  22. divi/proto/trace/v1/trace.proto +50 -0
  23. divi/proto/trace/v1/trace_pb2.py +42 -0
  24. divi/proto/trace/v1/trace_pb2.pyi +42 -0
  25. divi/services/__init__.py +7 -0
  26. divi/services/auth/__init__.py +4 -0
  27. divi/services/auth/auth.py +13 -0
  28. divi/services/auth/init.py +22 -0
  29. divi/services/auth/tokman.py +42 -0
  30. divi/services/core/__init__.py +5 -0
  31. divi/services/core/core.py +35 -0
  32. divi/services/core/finish.py +15 -0
  33. divi/services/core/init.py +70 -0
  34. divi/services/datapark/__init__.py +4 -0
  35. divi/services/datapark/datapark.py +91 -0
  36. divi/services/datapark/init.py +21 -0
  37. divi/services/finish.py +9 -0
  38. divi/services/init.py +10 -0
  39. divi/services/service.py +54 -0
  40. divi/session/__init__.py +3 -0
  41. divi/session/session.py +40 -0
  42. divi/session/setup.py +48 -0
  43. divi/session/teardown.py +7 -0
  44. divi/signals/__init__.py +3 -0
  45. divi/signals/span.py +83 -0
  46. divi/signals/trace.py +79 -0
  47. divi/utils.py +49 -0
  48. divi-0.0.1b0.dist-info/METADATA +18 -0
  49. divi-0.0.1b0.dist-info/RECORD +51 -0
  50. divi-0.0.1b0.dist-info/WHEEL +4 -0
  51. divi-0.0.1b0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,91 @@
1
+ from typing import Any, Dict
2
+
3
+ from google.protobuf.json_format import MessageToDict
4
+ from openai import NotGiven
5
+ from openai.types.chat import ChatCompletion
6
+ from pydantic import UUID4
7
+ from typing_extensions import List, Mapping
8
+
9
+ import divi
10
+ from divi.evaluation.evaluator import EvaluationScore
11
+ from divi.proto.trace.v1.trace_pb2 import ScopeSpans
12
+ from divi.services.service import Service
13
+ from divi.session.session import SessionSignal
14
+ from divi.signals.trace import TraceSignal
15
+
16
+
17
+ class DataPark(Service):
18
+ def __init__(self, host="localhost", port=3001):
19
+ super().__init__(host, port)
20
+ if not divi._auth:
21
+ raise ValueError("No auth service")
22
+ self.token = divi._auth.token
23
+
24
+ @property
25
+ def headers(self) -> Dict[str, str]:
26
+ return {"Authorization": f"Bearer {self.token}"}
27
+
28
+ @staticmethod
29
+ def strip_not_given(obj: object | None) -> object:
30
+ """Remove all top-level keys where their values are instances of `NotGiven`"""
31
+ if obj is None:
32
+ return None
33
+
34
+ if not isinstance(obj, Mapping):
35
+ return obj
36
+
37
+ return {
38
+ key: value
39
+ for key, value in obj.items()
40
+ if not isinstance(value, NotGiven)
41
+ }
42
+
43
+ def create_session(self, session: SessionSignal) -> None:
44
+ self.post("/api/session/", payload=session)
45
+
46
+ def upsert_traces(
47
+ self, session_id: UUID4, traces: list[TraceSignal]
48
+ ) -> None:
49
+ self.post(f"/api/session/{session_id}/traces", payload=traces)
50
+
51
+ def create_spans(self, trace_id: UUID4, spans: ScopeSpans) -> None:
52
+ self.post(f"/api/trace/{trace_id}/spans", payload=MessageToDict(spans))
53
+
54
+ def create_chat_completion(
55
+ self,
56
+ span_id: bytes,
57
+ trace_id: UUID4,
58
+ inputs: Dict[str, Any],
59
+ completion: ChatCompletion,
60
+ ) -> None:
61
+ hex_span_id = span_id.hex()
62
+ str_trace_id = str(trace_id)
63
+
64
+ self.post_concurrent(
65
+ {
66
+ "/api/v1/chat/completions/input": {
67
+ "span_id": hex_span_id,
68
+ "data": self.strip_not_given(inputs),
69
+ },
70
+ "/api/v1/chat/completions": {
71
+ "span_id": hex_span_id,
72
+ "trace_id": str_trace_id,
73
+ "data": completion.model_dump(),
74
+ },
75
+ }
76
+ )
77
+
78
+ def create_scores(
79
+ self,
80
+ span_id: bytes,
81
+ trace_id: UUID4,
82
+ scores: List[EvaluationScore],
83
+ ) -> None:
84
+ self.post(
85
+ "/api/v1/chat/completions/scores",
86
+ payload={
87
+ "span_id": span_id.hex(),
88
+ "trace_id": str(trace_id),
89
+ "data": [score.model_dump() for score in scores],
90
+ },
91
+ )
@@ -0,0 +1,21 @@
1
+ import os
2
+
3
+ from typing_extensions import Optional
4
+
5
+ from divi.services.datapark import DataPark
6
+
7
+ DIVI_DATAPARK_HOST = "DIVI_DATAPARK_HOST"
8
+ DIVI_DATAPARK_PORT = "DIVI_DATAPARK_PORT"
9
+
10
+
11
+ def init_datapark(
12
+ host: Optional[str] = None, port: Optional[int] = None
13
+ ) -> DataPark:
14
+ host = (
15
+ host
16
+ if host
17
+ else os.getenv(DIVI_DATAPARK_HOST, "datapark.divine-agent.com")
18
+ )
19
+ port = port if port else int(os.getenv(DIVI_DATAPARK_PORT, 80))
20
+
21
+ return DataPark(host=host, port=port)
@@ -0,0 +1,9 @@
1
+ import divi
2
+ from divi.services.core import finish as clean_up_core
3
+
4
+
5
+ def finish():
6
+ clean_up_core()
7
+ divi._auth = None
8
+ divi._core = None
9
+ divi._datapark = None
divi/services/init.py ADDED
@@ -0,0 +1,10 @@
1
+ import divi
2
+ from divi.services.auth import init_auth
3
+ from divi.services.datapark import init_datapark
4
+
5
+
6
+ def init_services():
7
+ if not divi._auth:
8
+ divi._auth = init_auth()
9
+ if not divi._datapark:
10
+ divi._datapark = init_datapark()
@@ -0,0 +1,54 @@
1
+ from concurrent.futures import ThreadPoolExecutor
2
+ from typing import Any, Dict, Mapping, Sequence
3
+
4
+ import requests
5
+
6
+
7
+ class Service:
8
+ """Service management class."""
9
+
10
+ def __init__(self, host: str, port: int):
11
+ self.host = host
12
+ self.port = port
13
+
14
+ @property
15
+ def target(self) -> str:
16
+ """Return the target string: host:port."""
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()
@@ -0,0 +1,3 @@
1
+ from .session import Session, SessionExtra
2
+
3
+ __all__ = ["Session", "SessionExtra"]
@@ -0,0 +1,40 @@
1
+ from typing import Optional, TypedDict
2
+ from uuid import uuid4
3
+
4
+ from divi.signals.trace import Trace
5
+
6
+
7
+ class SessionExtra(TypedDict, total=False):
8
+ """Extra information for Session"""
9
+
10
+ session_name: Optional[str]
11
+ """Name of the session"""
12
+ trace: Trace
13
+ """Trace in session"""
14
+ parent_span_id: Optional[bytes]
15
+ """Parent Span ID fixed string(8)"""
16
+
17
+
18
+ class SessionSignal(TypedDict):
19
+ """Session request"""
20
+
21
+ id: str
22
+ """Session ID UUID4"""
23
+ name: Optional[str]
24
+ """Session name"""
25
+
26
+
27
+ class Session:
28
+ def __init__(
29
+ self,
30
+ name: Optional[str] = None,
31
+ ):
32
+ self.id = uuid4()
33
+ self.name = name
34
+
35
+ @property
36
+ def signal(self) -> SessionSignal:
37
+ return SessionSignal(
38
+ id=str(self.id),
39
+ name=self.name,
40
+ )
divi/session/setup.py ADDED
@@ -0,0 +1,48 @@
1
+ from typing_extensions import Optional
2
+
3
+ import divi
4
+ from divi.services import init_services
5
+ from divi.session import Session, SessionExtra
6
+ from divi.signals.span import Span
7
+ from divi.signals.trace import Trace
8
+
9
+
10
+ def init_session(name: Optional[str] = None) -> Session:
11
+ """init initializes the services and the Run"""
12
+ init_services()
13
+ session = Session(name=name)
14
+ if divi._datapark:
15
+ divi._datapark.create_session(session.signal)
16
+ return session
17
+
18
+
19
+ def setup(
20
+ span: Span,
21
+ session_extra: SessionExtra | None,
22
+ ):
23
+ """setup trace
24
+
25
+ Args:
26
+ span (Span): Span instance
27
+ session_extra (SessionExtra | None): Extra information from user input
28
+ """
29
+ session_extra = session_extra or SessionExtra()
30
+
31
+ # init the session if not already initialized
32
+ if not divi._session:
33
+ divi._session = init_session(
34
+ name=session_extra.get("session_name") or span.name
35
+ )
36
+
37
+ # setup trace
38
+ trace = session_extra.get("trace") or Trace(divi._session.id, span.name)
39
+ parent_span_id = session_extra.get("parent_span_id")
40
+ span._add_node(trace.trace_id, parent_span_id)
41
+
42
+ # update the session_extra with the current trace and span
43
+ return SessionExtra(
44
+ session_name=divi._session.name,
45
+ trace=trace,
46
+ # set the parent_span_id to the current span_id
47
+ parent_span_id=span.span_id,
48
+ )
@@ -0,0 +1,7 @@
1
+ import divi
2
+ from divi.services import finish as clean_up_services
3
+
4
+
5
+ def teardown():
6
+ clean_up_services()
7
+ divi._session = None
@@ -0,0 +1,3 @@
1
+ from .span import Kind
2
+
3
+ __all__ = ["Kind"]
divi/signals/span.py ADDED
@@ -0,0 +1,83 @@
1
+ import os
2
+ import time
3
+ from enum import Enum
4
+ from typing import Any, Mapping, Optional
5
+
6
+ from pydantic import UUID4
7
+
8
+ import divi
9
+ from divi.proto.common.v1.common_pb2 import KeyValue
10
+ from divi.proto.trace.v1.trace_pb2 import ScopeSpans
11
+ from divi.proto.trace.v1.trace_pb2 import Span as SpanProto
12
+
13
+
14
+ class Kind(int, Enum):
15
+ """Enum for the kind of span."""
16
+
17
+ function = SpanProto.SpanKind.SPAN_KIND_FUNCTION
18
+ llm = SpanProto.SpanKind.SPAN_KIND_LLM
19
+ evaluation = SpanProto.SpanKind.SPAN_KIND_EVALUATION
20
+
21
+
22
+ class Span:
23
+ def __init__(
24
+ self,
25
+ kind: Kind = Kind.function,
26
+ name: Optional[str] = None,
27
+ metadata: Optional[Mapping[str, Any]] = None,
28
+ ):
29
+ # span_id is a FixedString(8)
30
+ self.span_id: bytes = self._generate_span_id()
31
+ self.name = name
32
+ self.kind = kind
33
+ self.metadata = metadata
34
+ self.start_time_unix_nano: int | None = None
35
+ self.end_time_unix_nano: int | None = None
36
+
37
+ self.trace_id: UUID4 | None = None
38
+ self.parent_span_id: bytes | None = None
39
+
40
+ @property
41
+ def signal(self) -> SpanProto:
42
+ signal: SpanProto = SpanProto(
43
+ name=self.name,
44
+ span_id=self.span_id,
45
+ kind=SpanProto.SpanKind.Name(self.kind),
46
+ start_time_unix_nano=self.start_time_unix_nano,
47
+ end_time_unix_nano=self.end_time_unix_nano,
48
+ trace_id=self.trace_id.bytes if self.trace_id else None,
49
+ parent_span_id=self.parent_span_id,
50
+ )
51
+ signal.metadata.extend(
52
+ KeyValue(key=k, value=v)
53
+ for k, v in (self.metadata or dict()).items()
54
+ )
55
+ return signal
56
+
57
+ @classmethod
58
+ def _generate_span_id(cls) -> bytes:
59
+ return os.urandom(8)
60
+
61
+ def start(self):
62
+ """Start the span by recording the current time in nanoseconds."""
63
+ self.start_time_unix_nano = time.time_ns()
64
+ self.upsert_span()
65
+
66
+ def end(self):
67
+ """End the span by recording the end time in nanoseconds."""
68
+ if self.start_time_unix_nano is None:
69
+ raise ValueError("Span must be started before ending.")
70
+ self.end_time_unix_nano = time.time_ns()
71
+ self.upsert_span()
72
+
73
+ def _add_node(self, trace_id: UUID4, parent_id: Optional[bytes] = None):
74
+ """Add node for obs tree."""
75
+ self.trace_id = trace_id
76
+ self.parent_span_id = parent_id
77
+
78
+ def upsert_span(self):
79
+ """Upsert span with datapark."""
80
+ if divi._datapark and self.trace_id:
81
+ divi._datapark.create_spans(
82
+ self.trace_id, ScopeSpans(spans=[self.signal])
83
+ )
divi/signals/trace.py ADDED
@@ -0,0 +1,79 @@
1
+ from datetime import UTC, datetime
2
+ from typing import Optional
3
+ from uuid import uuid4
4
+
5
+ from pydantic import UUID4
6
+ from typing_extensions import TypedDict
7
+
8
+ import divi
9
+
10
+
11
+ class NullTime(TypedDict, total=False):
12
+ """Null time"""
13
+
14
+ Time: str
15
+ """Time in iso format"""
16
+ Valid: bool
17
+ """Valid"""
18
+
19
+
20
+ class TraceSignal(TypedDict, total=False):
21
+ """Trace request"""
22
+
23
+ id: str
24
+ """Trace ID UUID4"""
25
+ start_time: str
26
+ """Start time in iso format"""
27
+ end_time: NullTime
28
+ """End time in iso format"""
29
+ name: Optional[str]
30
+
31
+
32
+ class Trace:
33
+ def __init__(self, session_id: UUID4, name: Optional[str] = None):
34
+ self.trace_id: UUID4 = uuid4()
35
+ self.start_time: str | None = None
36
+ self.end_time: str | None = None
37
+ self.name: Optional[str] = name
38
+ self.session_id: UUID4 = session_id
39
+
40
+ self.start()
41
+
42
+ @property
43
+ def signal(self) -> TraceSignal:
44
+ if self.start_time is None:
45
+ raise ValueError("Trace must be started.")
46
+ signal = TraceSignal(
47
+ id=str(self.trace_id),
48
+ start_time=self.start_time,
49
+ name=self.name,
50
+ )
51
+ if self.end_time is not None:
52
+ signal["end_time"] = NullTime(
53
+ Time=self.end_time,
54
+ Valid=True,
55
+ )
56
+ return signal
57
+
58
+ @staticmethod
59
+ def unix_nano_to_iso(unix_nano: int) -> str:
60
+ return datetime.utcfromtimestamp(unix_nano / 1e9).isoformat()
61
+
62
+ def start(self):
63
+ """Start the trace by recording the current time in nanoseconds."""
64
+ self.start_time = datetime.now(UTC).isoformat()
65
+ self.upsert_trace()
66
+
67
+ def end(self):
68
+ """End the trace by recording the end time in nanoseconds."""
69
+ if self.start_time is None:
70
+ raise ValueError("Span must be started before ending.")
71
+ self.end_time = datetime.now(UTC).isoformat()
72
+ self.upsert_trace()
73
+
74
+ def upsert_trace(self):
75
+ """Upsert trace with datapark."""
76
+ if divi._datapark:
77
+ divi._datapark.upsert_traces(
78
+ session_id=self.session_id, traces=[self.signal]
79
+ )
divi/utils.py ADDED
@@ -0,0 +1,49 @@
1
+ import inspect
2
+ import pathlib
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
31
+
32
+
33
+ def get_server_path() -> str:
34
+ """Get the path to the server binary."""
35
+ path = pathlib.Path(__file__).parent / "bin" / "core"
36
+ if not path.exists():
37
+ raise FileNotFoundError(f"Server binary not found: {path}")
38
+
39
+ return str(path)
40
+
41
+
42
+ def is_async(func: Callable) -> bool:
43
+ """Inspect function or wrapped function to see if it is async."""
44
+ unwrapped_func = inspect.unwrap(func)
45
+ return inspect.iscoroutinefunction(unwrapped_func)
46
+
47
+
48
+ if __name__ == "__main__":
49
+ print(get_server_path())
@@ -0,0 +1,18 @@
1
+ Metadata-Version: 2.4
2
+ Name: divi
3
+ Version: 0.0.1b0
4
+ Summary: The Agent Platform for Observability & Evaluation
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.11
7
+ Requires-Dist: grpcio>=1.71.0
8
+ Requires-Dist: openai>=1.65.2
9
+ Requires-Dist: protobuf<6.0.dev0,>=5.26.1
10
+ Requires-Dist: pyjwt>=2.10.1
11
+ Requires-Dist: requests>=2.32.3
12
+ Description-Content-Type: text/markdown
13
+
14
+ # Divine Agent
15
+
16
+ Agent Platform for Observability • Evaluation • Playground
17
+
18
+ > The project is still in the development stage. 😇
@@ -0,0 +1,51 @@
1
+ divi/__init__.py,sha256=Mxu0UYIKgegwUU7gTe5jMHYZ0wf3rzwtiHfHyLcTysA,486
2
+ divi/utils.py,sha256=fXkjoyo_Lh8AZliKICOP460m0czUcNQjcEcceJbaOVA,1439
3
+ divi/decorators/__init__.py,sha256=HkyWdC1ctTsVFucCWCkj57JB4NmwONus1d2S2dUbvs4,110
4
+ divi/decorators/collect.py,sha256=5iUxAnbHYx4ISkFg64IK_4miGdrWgbOXLJxKz8lGIv8,1074
5
+ divi/decorators/obs_openai.py,sha256=ouw3GYDFg6S27tcUzY0dIqz8JX_JM8IOXttzo7HK7nk,1359
6
+ divi/decorators/observable.py,sha256=isUS3P_07wbZBj2UcRAoYNDceQTIn6zdein3-PWVsi8,2289
7
+ divi/decorators/observe.py,sha256=I2RVsp2WQep6iTLSxkAlMP8wiRsSYiiYrxR2hJzPxcI,1211
8
+ divi/evaluation/__init__.py,sha256=3qMHWu_zBh6FJa6-1dZZEWiAblQZurn5doa0OjGvDGs,93
9
+ divi/evaluation/evaluate.py,sha256=lVMCw5vHGa5sJvUyhVDZ9m3Sgl4baCjWhw2OKazhvgM,1861
10
+ divi/evaluation/evaluator.py,sha256=ulTyfSg2JXxzCCL7hRsn-EBb9UKcpQFA6rVT42mouVQ,5819
11
+ divi/evaluation/prompts.py,sha256=4oL8K8X2-zdmTDuAetc53o7Ys_vph-eWYhPmvRKYAng,960
12
+ divi/evaluation/scores.py,sha256=ZgSxfve-ZivX3WU4TGcgPOSpUQVMbG5a15IQNPeq_bQ,173
13
+ divi/proto/common/v1/common.proto,sha256=Rx8wr0_tOtQ1NseTMnsav4ApD1MDALzQDBA2IvLRTU0,1775
14
+ divi/proto/common/v1/common_pb2.py,sha256=br61OHQVAi6SI3baFcb5xJv2Xd-AZ04A19xeSjLNMXo,2442
15
+ divi/proto/common/v1/common_pb2.pyi,sha256=LmTpFFLxHg2a_qPIdNXXwGEMkbiDcTJdarR9eC-6Fq8,2133
16
+ divi/proto/core/health/v1/health_service.proto,sha256=y39MNiwYiSqxb_JPQI7AmPfNNtP6pbX-WKTseMkjWUM,369
17
+ divi/proto/core/health/v1/health_service_pb2.py,sha256=PKAxy_z7lU1HrwDNIa7AFHha9i8gICCVrcEhpOM79E0,1999
18
+ divi/proto/core/health/v1/health_service_pb2.pyi,sha256=VbYHHTcp3epUVg0lTSxo3xpG52ZGhA7KKMRL5LR5e1c,691
19
+ divi/proto/core/health/v1/health_service_pb2_grpc.py,sha256=YmlO94d-G71YBW1XZDSbYkwnHEUe6Z7R3_KAmoDWyA0,3953
20
+ divi/proto/metric/v1/metric.proto,sha256=YHRMLUW-MtakHuibR3PJ0s2w5KgV12kc4737iHw0DTk,585
21
+ divi/proto/metric/v1/metric_pb2.py,sha256=uvBhyy8QpaES3Jl82yVfsGazW5654XpRnsdGlpVgIRE,1974
22
+ divi/proto/metric/v1/metric_pb2.pyi,sha256=S7ipsojkD7QZAYefDE4b3PO99Yzc6mOdtSLxH3-b67A,1304
23
+ divi/proto/trace/v1/trace.proto,sha256=tPRIgBZB5KOKj7AoD3NoDZvLwoiJkbLiLqW53Ah-2-0,1367
24
+ divi/proto/trace/v1/trace_pb2.py,sha256=zMuQO5mN2xl11USHkhi0lLwBAPlYXRU_UG1r0Uu3mJg,2369
25
+ divi/proto/trace/v1/trace_pb2.pyi,sha256=k4dHYKAusH4I-XSW9KP3maogSWdRL7hVy8HCHhqFWzM,2231
26
+ divi/services/__init__.py,sha256=AuFqnDAhi_ZGIhEVMvTu5cUmw1tEzjQSjP6m9uB3df4,203
27
+ divi/services/finish.py,sha256=XKPKGJ5cWd5H95G_VpIOlOZOLrcf9StoTs7ayRic2jY,173
28
+ divi/services/init.py,sha256=uJi6Mma8XQgShwWB5HyoFXaeIw8iQy9TCMQ40vuARbk,249
29
+ divi/services/service.py,sha256=539MhcYfMvsVGjDdu0UtYSZnL2cloaPeYeOSMl2eUy8,1532
30
+ divi/services/auth/__init__.py,sha256=vZkOIozH_TB6CGV3IOt9NYCuj5KNHO5tfpL4YqAnZ-Y,84
31
+ divi/services/auth/auth.py,sha256=eRcE6Kq8jbBr6YL93HCGDIoga90SoZf3ogOAKeza9WY,445
32
+ divi/services/auth/init.py,sha256=ydwR70ue-0S5C5e3mhc8eeHzS8zphUkHGfvRGdEi-cI,624
33
+ divi/services/auth/tokman.py,sha256=V03wcV6TEy1k9o55teDVB9JZ3Ve2cJdwzWstQhWx4BQ,1070
34
+ divi/services/core/__init__.py,sha256=y0GAqZyYz1YuZY2PVbU3O34nldeCcZeSlkgAOUlWwGs,121
35
+ divi/services/core/core.py,sha256=PRwPtLgrgmCrejUfKf7HJNrAhGS0paFNZ7JwDToEUAk,1264
36
+ divi/services/core/finish.py,sha256=dIGQpVXcJY4-tKe7A1_VV3yoSHNCDPfOlUltvzvk6VI,231
37
+ divi/services/core/init.py,sha256=s8ZnQDu4leP8XaarpXpDttnOp7bseAJCgIPRYjj8zag,1931
38
+ divi/services/datapark/__init__.py,sha256=xDkN0GPVhVuhwMenzVnkz5qguhQIyOpbFAkS1GUe-EM,104
39
+ divi/services/datapark/datapark.py,sha256=f-qE2kmkLAniIj9mOP3nCbI3A3qkfIUnoVekwQ5w0QE,2781
40
+ divi/services/datapark/init.py,sha256=XcqVPQn5PytQo8b9fgLUQdRSFHug0ZQiiL56cn4fBus,506
41
+ divi/session/__init__.py,sha256=6lYemv21VQCIHx-xIdi7BxXcPNxVdvE60--8ArReUew,82
42
+ divi/session/session.py,sha256=QxtEezI447PbtKG2U6cxL1ACae55e8nFfTufAY8pEYI,811
43
+ divi/session/setup.py,sha256=rC1QdCxpdCOaRXmcLEQs4Yuu5UC_aRzKSaqWRPJN4Og,1390
44
+ divi/session/teardown.py,sha256=YiBz_3yCiljMFEofZ60VmRL5sb8WA5GT7EYF8nFznZ4,133
45
+ divi/signals/__init__.py,sha256=wfSkkCwkRsFP4aLj8aGHk_k6Y50P5yN44WWlO3XyW18,43
46
+ divi/signals/span.py,sha256=FQWql6ivAeXGk1HPZCsCjL5mXW6S6Nn9SmOiKH4aXik,2629
47
+ divi/signals/trace.py,sha256=IoYeTfd6x_Xmxcp4HbFSEne0d48hol4ng2Mb_AO8hZw,2144
48
+ divi-0.0.1b0.dist-info/METADATA,sha256=BW1GBcbxovWLvVJGoDylvomnBVaApGtJkTSgHIRBtuw,493
49
+ divi-0.0.1b0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
50
+ divi-0.0.1b0.dist-info/licenses/LICENSE,sha256=5OJuZ4wMMEV0DgF0tofhAlS_KLkaUsZwwwDS2U_GwQ0,1063
51
+ divi-0.0.1b0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Kaikai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.