divi 0.0.1.dev15__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 +1 -1
- divi/decorators/observable.py +5 -2
- divi/proto/core/health/v1/health_service_pb2_grpc.py +1 -1
- divi/services/auth/auth.py +1 -9
- divi/services/datapark/datapark.py +44 -40
- divi/services/service.py +43 -0
- divi/utils.py +28 -1
- {divi-0.0.1.dev15.dist-info → divi-0.0.1.dev21.dist-info}/METADATA +2 -1
- {divi-0.0.1.dev15.dist-info → divi-0.0.1.dev21.dist-info}/RECORD +11 -11
- {divi-0.0.1.dev15.dist-info → divi-0.0.1.dev21.dist-info}/WHEEL +0 -0
- {divi-0.0.1.dev15.dist-info → divi-0.0.1.dev21.dist-info}/licenses/LICENSE +0 -0
divi/__init__.py
CHANGED
divi/decorators/observable.py
CHANGED
@@ -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
|
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.
|
8
|
+
GRPC_GENERATED_VERSION = '1.71.0'
|
9
9
|
GRPC_VERSION = grpc.__version__
|
10
10
|
_version_not_supported = False
|
11
11
|
|
divi/services/auth/auth.py
CHANGED
@@ -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
|
-
|
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
|
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
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
if
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
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,
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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,10 +1,11 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: divi
|
3
|
-
Version: 0.0.1.
|
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
7
|
Requires-Dist: grpcio>=1.71.0
|
8
|
+
Requires-Dist: openai>=1.65.2
|
8
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
|
@@ -1,16 +1,16 @@
|
|
1
|
-
divi/__init__.py,sha256
|
2
|
-
divi/utils.py,sha256=
|
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=
|
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=
|
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
|
@@ -20,9 +20,9 @@ divi/proto/trace/v1/trace_pb2.pyi,sha256=rPo2Oa3NWrINE_dyOVU9HUYHo5LY82Bm5TMenj5
|
|
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=
|
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=
|
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=
|
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.
|
43
|
-
divi-0.0.1.
|
44
|
-
divi-0.0.1.
|
45
|
-
divi-0.0.1.
|
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,,
|
File without changes
|
File without changes
|