divi 0.0.1.dev7__py3-none-any.whl → 0.0.1.dev14__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 (59) hide show
  1. divi/__init__.py +11 -4
  2. divi/config/config.py +0 -0
  3. divi/decorators/__init__.py +4 -0
  4. divi/decorators/obs_openai.py +33 -0
  5. divi/decorators/observable.py +137 -0
  6. divi/proto/common/v1/common.proto +49 -0
  7. divi/proto/common/v1/common_pb2.py +43 -0
  8. divi/proto/common/v1/common_pb2.pyi +44 -0
  9. divi/proto/core/health/v1/health_service.proto +17 -0
  10. divi/proto/core/{v1/health_check_response_pb2.py → health/v1/health_service_pb2.py} +11 -7
  11. divi/proto/core/{v1/health_check_response_pb2.pyi → health/v1/health_service_pb2.pyi} +6 -0
  12. divi/proto/core/{v1/core_pb2_grpc.py → health/v1/health_service_pb2_grpc.py} +19 -20
  13. divi/proto/metric/v1/metric.proto +26 -0
  14. divi/proto/metric/v1/metric_pb2.py +40 -0
  15. divi/proto/metric/v1/metric_pb2.pyi +25 -0
  16. divi/proto/trace/v1/trace.proto +47 -0
  17. divi/proto/trace/v1/trace_pb2.py +44 -0
  18. divi/proto/trace/v1/trace_pb2.pyi +52 -0
  19. divi/services/__init__.py +7 -0
  20. divi/services/auth/__init__.py +4 -0
  21. divi/services/auth/auth.py +21 -0
  22. divi/services/auth/init.py +21 -0
  23. divi/services/auth/tokman.py +42 -0
  24. divi/services/core/__init__.py +5 -0
  25. divi/services/core/core.py +35 -0
  26. divi/{core → services/core}/finish.py +4 -4
  27. divi/{core → services/core}/init.py +23 -29
  28. divi/services/datapark/__init__.py +4 -0
  29. divi/services/datapark/datapark.py +63 -0
  30. divi/services/datapark/init.py +5 -0
  31. divi/services/finish.py +9 -0
  32. divi/services/init.py +14 -0
  33. divi/services/service.py +11 -0
  34. divi/session/__init__.py +3 -0
  35. divi/session/session.py +40 -0
  36. divi/session/setup.py +62 -0
  37. divi/session/teardown.py +7 -0
  38. divi/signals/__init__.py +3 -0
  39. divi/signals/trace/__init__.py +3 -0
  40. divi/signals/trace/trace.py +151 -0
  41. divi/utils.py +8 -0
  42. divi-0.0.1.dev14.dist-info/METADATA +17 -0
  43. divi-0.0.1.dev14.dist-info/RECORD +45 -0
  44. divi/bin/core +0 -0
  45. divi/core/__init__.py +0 -5
  46. divi/core/run.py +0 -35
  47. divi/proto/core/v1/core.proto +0 -13
  48. divi/proto/core/v1/core_pb2.py +0 -39
  49. divi/proto/core/v1/core_pb2.pyi +0 -6
  50. divi/proto/core/v1/health_check_request.proto +0 -7
  51. divi/proto/core/v1/health_check_request_pb2.py +0 -37
  52. divi/proto/core/v1/health_check_request_pb2.pyi +0 -11
  53. divi/proto/core/v1/health_check_request_pb2_grpc.py +0 -24
  54. divi/proto/core/v1/health_check_response.proto +0 -10
  55. divi/proto/core/v1/health_check_response_pb2_grpc.py +0 -24
  56. divi-0.0.1.dev7.dist-info/METADATA +0 -33
  57. divi-0.0.1.dev7.dist-info/RECORD +0 -23
  58. {divi-0.0.1.dev7.dist-info → divi-0.0.1.dev14.dist-info}/WHEEL +0 -0
  59. {divi-0.0.1.dev7.dist-info → divi-0.0.1.dev14.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,44 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
3
+ # NO CHECKED-IN PROTOBUF GENCODE
4
+ # source: divi/proto/trace/v1/trace.proto
5
+ # Protobuf Python Version: 5.29.0
6
+ """Generated protocol buffer code."""
7
+ from google.protobuf import descriptor as _descriptor
8
+ from google.protobuf import descriptor_pool as _descriptor_pool
9
+ from google.protobuf import runtime_version as _runtime_version
10
+ from google.protobuf import symbol_database as _symbol_database
11
+ from google.protobuf.internal import builder as _builder
12
+ _runtime_version.ValidateProtobufRuntimeVersion(
13
+ _runtime_version.Domain.PUBLIC,
14
+ 5,
15
+ 29,
16
+ 0,
17
+ '',
18
+ 'divi/proto/trace/v1/trace.proto'
19
+ )
20
+ # @@protoc_insertion_point(imports)
21
+
22
+ _sym_db = _symbol_database.Default()
23
+
24
+
25
+ from divi.proto.common.v1 import common_pb2 as divi_dot_proto_dot_common_dot_v1_dot_common__pb2
26
+
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')
29
+
30
+ _globals = globals()
31
+ _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
32
+ _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'divi.proto.trace.v1.trace_pb2', _globals)
33
+ if not _descriptor._USE_C_DESCRIPTORS:
34
+ _globals['DESCRIPTOR']._loaded_options = None
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
44
+ # @@protoc_insertion_point(module_scope)
@@ -0,0 +1,52 @@
1
+ from divi.proto.common.v1 import common_pb2 as _common_pb2
2
+ from google.protobuf.internal import containers as _containers
3
+ from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper
4
+ from google.protobuf import descriptor as _descriptor
5
+ from google.protobuf import message as _message
6
+ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union
7
+
8
+ DESCRIPTOR: _descriptor.FileDescriptor
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
+ class ScopeSpans(_message.Message):
23
+ __slots__ = ("spans",)
24
+ SPANS_FIELD_NUMBER: _ClassVar[int]
25
+ spans: _containers.RepeatedCompositeFieldContainer[Span]
26
+ def __init__(self, spans: _Optional[_Iterable[_Union[Span, _Mapping]]] = ...) -> None: ...
27
+
28
+ class Span(_message.Message):
29
+ __slots__ = ("trace_id", "span_id", "parent_span_id", "name", "kind", "start_time_unix_nano", "end_time_unix_nano", "metadata")
30
+ class SpanKind(int, metaclass=_enum_type_wrapper.EnumTypeWrapper):
31
+ __slots__ = ()
32
+ SPAN_KIND_FUNCTION: _ClassVar[Span.SpanKind]
33
+ SPAN_KIND_LLM: _ClassVar[Span.SpanKind]
34
+ SPAN_KIND_FUNCTION: Span.SpanKind
35
+ SPAN_KIND_LLM: Span.SpanKind
36
+ TRACE_ID_FIELD_NUMBER: _ClassVar[int]
37
+ SPAN_ID_FIELD_NUMBER: _ClassVar[int]
38
+ PARENT_SPAN_ID_FIELD_NUMBER: _ClassVar[int]
39
+ NAME_FIELD_NUMBER: _ClassVar[int]
40
+ KIND_FIELD_NUMBER: _ClassVar[int]
41
+ START_TIME_UNIX_NANO_FIELD_NUMBER: _ClassVar[int]
42
+ END_TIME_UNIX_NANO_FIELD_NUMBER: _ClassVar[int]
43
+ METADATA_FIELD_NUMBER: _ClassVar[int]
44
+ trace_id: bytes
45
+ span_id: bytes
46
+ parent_span_id: bytes
47
+ name: str
48
+ kind: Span.SpanKind
49
+ start_time_unix_nano: int
50
+ end_time_unix_nano: int
51
+ metadata: _containers.RepeatedCompositeFieldContainer[_common_pb2.KeyValue]
52
+ def __init__(self, trace_id: _Optional[bytes] = ..., span_id: _Optional[bytes] = ..., parent_span_id: _Optional[bytes] = ..., name: _Optional[str] = ..., kind: _Optional[_Union[Span.SpanKind, str]] = ..., start_time_unix_nano: _Optional[int] = ..., end_time_unix_nano: _Optional[int] = ..., metadata: _Optional[_Iterable[_Union[_common_pb2.KeyValue, _Mapping]]] = ...) -> None: ...
@@ -0,0 +1,7 @@
1
+ from .auth import Auth
2
+ from .core import Core
3
+ from .datapark import DataPark
4
+ from .finish import finish
5
+ from .init import init
6
+
7
+ __all__ = ["init", "finish", "Core", "Auth", "DataPark"]
@@ -0,0 +1,4 @@
1
+ from .auth import Auth
2
+ from .init import init
3
+
4
+ __all__ = ["Auth", "init"]
@@ -0,0 +1,21 @@
1
+ import requests
2
+
3
+ from divi.services.auth.tokman import Token
4
+ from divi.services.service import Service
5
+
6
+
7
+ class Auth(Service):
8
+ def __init__(self, api_key: str, host="localhost", port=3000):
9
+ super().__init__(host, port)
10
+ self.api_key = api_key
11
+ self.token = Token(self)
12
+
13
+ def auth_with_api_key(self) -> str:
14
+ """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"])
@@ -0,0 +1,21 @@
1
+ import os
2
+ from typing import Optional
3
+
4
+ from divi.services.auth import Auth
5
+
6
+ DIVI_API_KEY = "DIVI_API_KEY"
7
+
8
+
9
+ def init(api_key: Optional[str] = None) -> Auth:
10
+ key = api_key if api_key else os.getenv(DIVI_API_KEY)
11
+ if not key:
12
+ raise ValueError("API key is required")
13
+ return Auth(api_key=key)
14
+
15
+
16
+ if __name__ == "__main__":
17
+ auth = init()
18
+ if not auth:
19
+ raise ValueError("Auth object is not available")
20
+ print("=== Auth ===")
21
+ print(auth.token)
@@ -0,0 +1,42 @@
1
+ import time
2
+ from weakref import ref
3
+
4
+ import jwt
5
+
6
+
7
+ class Token:
8
+ """JWT Manager Class."""
9
+
10
+ def __init__(self, auth) -> None:
11
+ self.auth = ref(auth)
12
+ self.claims: dict = {}
13
+ self.__token: str = ""
14
+
15
+ def __str__(self) -> str:
16
+ return self.token
17
+
18
+ @property
19
+ def exp(self) -> int:
20
+ """Return the expiration time."""
21
+ return self.claims.get("exp", 0)
22
+
23
+ @property
24
+ def token(self) -> str:
25
+ """Return the token string."""
26
+ # If the token is expired, get a new one
27
+ if not self.__token or self.exp - time.time() < 3600:
28
+ self._init_token()
29
+ return self.__token
30
+
31
+ def _init_token(self):
32
+ """Initialize the token."""
33
+ auth = self.auth()
34
+ if not auth:
35
+ raise ValueError("Auth object is not available")
36
+ self.__token = auth.auth_with_api_key()
37
+ self.claims = _decode_token(self.__token)
38
+
39
+
40
+ def _decode_token(token: str) -> dict:
41
+ """Decode the token payload."""
42
+ return jwt.decode(token, options={"verify_signature": False})
@@ -0,0 +1,5 @@
1
+ from .core import Core
2
+ from .finish import finish
3
+ from .init import init
4
+
5
+ __all__ = ["init", "finish", "Core"]
@@ -0,0 +1,35 @@
1
+ from subprocess import Popen
2
+ from typing import Callable, List, Optional
3
+
4
+ import grpc
5
+
6
+ import divi
7
+ from divi.proto.core.health.v1.health_service_pb2 import HealthCheckRequest
8
+ from divi.proto.core.health.v1.health_service_pb2_grpc import HealthServiceStub
9
+ from divi.services.service import Service
10
+
11
+
12
+ class Core(Service):
13
+ """Core Runtime Class."""
14
+
15
+ def __init__(self, host="localhost", port=50051) -> None:
16
+ super().__init__(host, port)
17
+ self.process: Optional[Popen] = None
18
+ self.hooks: List[Callable[[], None]] = []
19
+
20
+ def check_health(self) -> bool:
21
+ """Check the health of the service."""
22
+ with grpc.insecure_channel(self.target) as channel:
23
+ stub = HealthServiceStub(channel)
24
+ response, call = stub.Check.with_call(
25
+ HealthCheckRequest(version=divi.__version__),
26
+ # Note: ((),) notice the `,` at the end of the tuple
27
+ metadata=(("version", divi.__version__),),
28
+ )
29
+ print(f"Health check: {response.message}")
30
+ for key, value in call.trailing_metadata():
31
+ print(
32
+ "python client received trailing metadata: key=%s value=%s"
33
+ % (key, value)
34
+ )
35
+ return response.status
@@ -4,12 +4,12 @@ import divi
4
4
 
5
5
 
6
6
  def finish():
7
- """Clean up the run."""
8
- run = divi.run
9
- if run is None:
7
+ """Clean up the core."""
8
+ core = divi._core
9
+ if core is None:
10
10
  return
11
11
 
12
12
  # Clean up the hooks
13
- for hook in run.hooks:
13
+ for hook in core.hooks:
14
14
  hook()
15
15
  atexit.unregister(hook)
@@ -1,60 +1,50 @@
1
- import grpc
2
- import time
3
- import socket
4
1
  import atexit
2
+ import socket
5
3
  import subprocess
4
+ import time
6
5
 
7
- from typing import Union
8
-
9
- import divi
6
+ import grpc
10
7
 
11
- from divi.core.run import Run
8
+ from divi.services.core import Core
12
9
  from divi.utils import get_server_path
13
10
 
14
11
 
15
- def init(
16
- host: Union[str, None] = None, port: Union[str, None] = None
17
- ) -> Union[Run, None]:
18
- divi.run = Run(host, port)
19
- _start_server()
12
+ def init(host="localhost", port=50051) -> Core:
13
+ core = Core(host=host, port=port)
14
+ _start_server(core)
15
+ return core
20
16
 
21
17
 
22
- def _start_server():
18
+ def _start_server(core: Core):
23
19
  """Start the backend server."""
24
- # get the run object
25
- run = divi.run
26
- if run is None:
27
- return
28
-
29
20
  # start the server
30
21
  bin_path = get_server_path()
31
- command = [bin_path]
32
- run.process = subprocess.Popen(command)
22
+ command = [bin_path, "-port", str(core.port)]
23
+ core.process = subprocess.Popen(command)
33
24
 
34
25
  # Wait for the port to be open
35
- if not _wait_for_port(run.host, run.port, 10):
36
- run.process.terminate()
26
+ if not _wait_for_port(core.host, core.port, 10):
27
+ core.process.terminate()
37
28
  raise RuntimeError("Service failed to start: port not open")
38
29
 
39
30
  # Check if the gRPC channel is ready
40
- channel = grpc.insecure_channel(run.target)
31
+ channel = grpc.insecure_channel(core.target)
41
32
  try:
42
33
  grpc.channel_ready_future(channel).result(timeout=10)
43
34
  except grpc.FutureTimeoutError:
44
- run.process.terminate()
35
+ core.process.terminate()
45
36
  raise RuntimeError("gRPC channel not ready")
46
37
  finally:
47
38
  channel.close()
48
39
 
40
+ core.hooks.append(core.process.terminate)
41
+ atexit.register(core.process.terminate)
42
+
49
43
  # Health check
50
- status = run.check_health()
44
+ status = core.check_health()
51
45
  if not status:
52
- run.process.terminate()
53
46
  raise RuntimeError("Service failed health check")
54
47
 
55
- run.hooks.append(run.process.terminate)
56
- atexit.register(run.process.terminate)
57
-
58
48
 
59
49
  def _wait_for_port(host, port, timeout_seconds):
60
50
  """Wait until the specified port is open."""
@@ -78,3 +68,7 @@ def _is_port_open(host, port):
78
68
  except Exception as e:
79
69
  print(f"Error checking port: {e}")
80
70
  return False
71
+
72
+
73
+ if __name__ == "__main__":
74
+ init()
@@ -0,0 +1,4 @@
1
+ from .datapark import DataPark
2
+ from .init import init
3
+
4
+ __all__ = ["DataPark", "init"]
@@ -0,0 +1,63 @@
1
+ import requests
2
+ from google.protobuf.json_format import MessageToDict
3
+ from openai.types.chat.chat_completion import ChatCompletion
4
+ from pydantic import UUID4
5
+
6
+ import divi
7
+ from divi.proto.trace.v1.trace_pb2 import ScopeSpans
8
+ from divi.services.service import Service
9
+ from divi.session.session import SessionSignal
10
+ from divi.signals.trace.trace import TraceSignal
11
+
12
+
13
+ class DataPark(Service):
14
+ def __init__(self, host="localhost", port=3001):
15
+ super().__init__(host, port)
16
+ if not divi._auth:
17
+ raise ValueError("No auth service")
18
+ self.token = divi._auth.token
19
+
20
+ @property
21
+ def headers(self):
22
+ return {"Authorization": f"Bearer {self.token}"}
23
+
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"])
50
+
51
+ 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
+ },
61
+ )
62
+ if r.status_code != 201:
63
+ raise ValueError(r.json()["message"])
@@ -0,0 +1,5 @@
1
+ from divi.services.datapark import DataPark
2
+
3
+
4
+ def init() -> DataPark:
5
+ return DataPark()
@@ -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,14 @@
1
+ import divi
2
+ from divi.services.auth import init as init_auth
3
+ from divi.services.core import init as init_core
4
+ from divi.services.datapark import init as init_datapark
5
+
6
+
7
+ def init():
8
+ if not divi._auth:
9
+ divi._auth = init_auth()
10
+ if not divi._datapark:
11
+ divi._datapark = init_datapark()
12
+ # TODO - Uncomment this when the core service is ready
13
+ # if not divi._core:
14
+ # divi._core = init_core(port=3002)
@@ -0,0 +1,11 @@
1
+ class Service:
2
+ """Service management class."""
3
+
4
+ def __init__(self, host: str, port: int):
5
+ self.host = host
6
+ self.port = port
7
+
8
+ @property
9
+ def target(self) -> str:
10
+ """Return the target string: host:port."""
11
+ return f"{self.host}:{self.port}"
@@ -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.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: Optional[Trace]
13
+ """Trace ID UUID4"""
14
+ parent_span_id: Optional[bytes]
15
+ """Parent Span ID fixed string(8)"""
16
+
17
+
18
+ class SessionSignal(TypedDict, total=False):
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,62 @@
1
+ import divi
2
+ from divi.services import init as init_services
3
+ from divi.session import Session, SessionExtra
4
+ from divi.signals.trace import Span
5
+ from divi.signals.trace.trace import Trace
6
+
7
+
8
+ def init(session_extra: SessionExtra) -> Session:
9
+ """init initializes the services and the Run"""
10
+ init_services()
11
+ session = Session(name=session_extra.get("session_name"))
12
+ if divi._datapark:
13
+ divi._datapark.create_session(session.signal)
14
+ return session
15
+
16
+
17
+ def setup(
18
+ span: Span,
19
+ session_extra: SessionExtra | None,
20
+ ):
21
+ """setup trace
22
+
23
+ Args:
24
+ span (Span): Span instance
25
+ session_extra (SessionExtra | None): Extra information from user input
26
+ """
27
+ # TOOD: merge run_extra input by user with the one from the context
28
+ # temp solution: Priority: run_extra_context.get() > run_extra
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_extra=session_extra)
34
+
35
+ # setup trace
36
+ # init current span
37
+ trace = session_extra.get("trace")
38
+ parent_span_id = session_extra.get("parent_span_id")
39
+ if trace and parent_span_id:
40
+ span._add_parent(trace.trace_id, parent_span_id)
41
+ else:
42
+ trace = Trace(divi._session.id)
43
+ trace.start()
44
+ span._as_root(trace.trace_id)
45
+ # create the trace
46
+ if divi._datapark:
47
+ divi._datapark.upsert_traces(
48
+ session_id=divi._session.id, traces=[trace.signal]
49
+ )
50
+
51
+ # update the session_extra with the current span
52
+ # session_extra["trace_id"] = span.trace_id
53
+ # session_extra["parent_span_id"] = span.span_id
54
+ session_extra = SessionExtra(
55
+ session_name=divi._session.name,
56
+ trace=trace,
57
+ # set the parent_span_id to the current span_id
58
+ parent_span_id=span.span_id,
59
+ )
60
+
61
+ # offer end hook to collect data at whe end of the span ?
62
+ return session_extra
@@ -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 .trace import Span
2
+
3
+ __all__ = ["Span"]
@@ -0,0 +1,3 @@
1
+ from .trace import Span
2
+
3
+ __all__ = ["Span"]