raidiantai-analytics 0.1.0__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.
- raidiantai_analytics/__init__.py +42 -0
- raidiantai_analytics/_admission.py +218 -0
- raidiantai_analytics/_client.py +2163 -0
- raidiantai_analytics/_config.py +311 -0
- raidiantai_analytics/_contract.py +407 -0
- raidiantai_analytics/_diagnostics.py +215 -0
- raidiantai_analytics/_identity.py +124 -0
- raidiantai_analytics/_locks.py +195 -0
- raidiantai_analytics/_pool.py +334 -0
- raidiantai_analytics/_prepare.py +52 -0
- raidiantai_analytics/_retry_after.py +154 -0
- raidiantai_analytics/_runtime.py +61 -0
- raidiantai_analytics/_serialize.py +139 -0
- raidiantai_analytics/_text.py +136 -0
- raidiantai_analytics/_transport.py +375 -0
- raidiantai_analytics/_validate.py +229 -0
- raidiantai_analytics/_version.py +4 -0
- raidiantai_analytics/py.typed +0 -0
- raidiantai_analytics-0.1.0.dist-info/METADATA +123 -0
- raidiantai_analytics-0.1.0.dist-info/RECORD +23 -0
- raidiantai_analytics-0.1.0.dist-info/WHEEL +5 -0
- raidiantai_analytics-0.1.0.dist-info/licenses/LICENSE +21 -0
- raidiantai_analytics-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Send a tool's usage to Raidiant AI from a Python server, with a tool key.
|
|
2
|
+
|
|
3
|
+
Standard library only. Python 3.10 or later.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from ._client import (
|
|
7
|
+
ActorHandle,
|
|
8
|
+
Analytics,
|
|
9
|
+
CaptureDropped,
|
|
10
|
+
CaptureInvalid,
|
|
11
|
+
CaptureQueued,
|
|
12
|
+
CaptureResult,
|
|
13
|
+
CheckKeyFailed,
|
|
14
|
+
CheckKeyOk,
|
|
15
|
+
CheckKeyResult,
|
|
16
|
+
DrainResult,
|
|
17
|
+
Stats,
|
|
18
|
+
)
|
|
19
|
+
from ._diagnostics import Diagnostic
|
|
20
|
+
from ._identity import Actor, IdentityOk, IdentityRefused, email_identity
|
|
21
|
+
from ._version import PACKAGE_NAME, __version__
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"PACKAGE_NAME",
|
|
25
|
+
"Actor",
|
|
26
|
+
"ActorHandle",
|
|
27
|
+
"Analytics",
|
|
28
|
+
"CaptureDropped",
|
|
29
|
+
"CaptureInvalid",
|
|
30
|
+
"CaptureQueued",
|
|
31
|
+
"CaptureResult",
|
|
32
|
+
"CheckKeyFailed",
|
|
33
|
+
"CheckKeyOk",
|
|
34
|
+
"CheckKeyResult",
|
|
35
|
+
"Diagnostic",
|
|
36
|
+
"DrainResult",
|
|
37
|
+
"IdentityOk",
|
|
38
|
+
"IdentityRefused",
|
|
39
|
+
"Stats",
|
|
40
|
+
"__version__",
|
|
41
|
+
"email_identity",
|
|
42
|
+
]
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Host admission: may this host value become wire text at all?
|
|
2
|
+
|
|
3
|
+
One iterative traversal that copies the host's value into a snapshot (each
|
|
4
|
+
member read once, through the base type's own methods, so no method a host
|
|
5
|
+
subclass overrides runs), admitting exactly None, bool, an int (not bool) with
|
|
6
|
+
|n| <= 2^53, a finite float, a str with no high-then-low surrogate pair, a
|
|
7
|
+
list or tuple, and a dict whose keys are all str. int, float and str
|
|
8
|
+
subclasses are admitted and copied as their base type. Everything else is
|
|
9
|
+
refused as `invalid_event` at its path. Two representability limits apply
|
|
10
|
+
during the same walk: nesting depth (the event is depth 1) and a running size
|
|
11
|
+
count L, a strict lower bound on the canonical text's UTF-8 bytes, refused as
|
|
12
|
+
`event_too_large` at "" once it passes the ceiling.
|
|
13
|
+
|
|
14
|
+
L accounting, in traversal order (pre-order, an object's key visited just
|
|
15
|
+
before its member, members in ECMAScript key order): every value adds 1, plus
|
|
16
|
+
its code points if it is a str; a container adds its member count when it is
|
|
17
|
+
entered, before any of its members are read; every key adds its code points.
|
|
18
|
+
Inside one value the checks run: size, then type, then depth, then the member
|
|
19
|
+
count. The snapshot holds only exact types, its dicts already in ECMAScript
|
|
20
|
+
key order.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import math
|
|
26
|
+
from dataclasses import dataclass
|
|
27
|
+
from typing import Any
|
|
28
|
+
|
|
29
|
+
from . import _text
|
|
30
|
+
from ._contract import CONTRACT
|
|
31
|
+
|
|
32
|
+
ADMISSION_SIZE_CEILING: int = CONTRACT["sdk_limits"]["ADMISSION_SIZE_CEILING"]
|
|
33
|
+
ADMISSION_DEPTH_CEILING: int = CONTRACT["sdk_limits"]["ADMISSION_DEPTH_CEILING"]
|
|
34
|
+
_MAX_SAFE = 2**53
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass(frozen=True)
|
|
38
|
+
class AdmissionRefusal:
|
|
39
|
+
reason: str # "invalid_event" | "event_too_large"
|
|
40
|
+
path: str
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class Admitted:
|
|
45
|
+
value: Any
|
|
46
|
+
count: int
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True)
|
|
50
|
+
class Refused:
|
|
51
|
+
refusal: AdmissionRefusal
|
|
52
|
+
count: int
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class _Probe:
|
|
56
|
+
"""The most recent traversal's final count, for tests that measure where the walk stopped."""
|
|
57
|
+
|
|
58
|
+
last_count = 0
|
|
59
|
+
strings_scanned = 0
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
admission_probe = _Probe()
|
|
63
|
+
|
|
64
|
+
# Work items. ("value", value, path, depth, setter); ("key", key); setter writes the copy.
|
|
65
|
+
_VALUE = 0
|
|
66
|
+
_KEY = 1
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def admit(root: Any) -> Admitted | Refused:
|
|
70
|
+
"""Admit and snapshot `root` (the whole event). Never recurses and never calls host code."""
|
|
71
|
+
count = 0
|
|
72
|
+
ceiling = ADMISSION_SIZE_CEILING
|
|
73
|
+
holder: list[Any] = [None]
|
|
74
|
+
|
|
75
|
+
def set_root(x: Any) -> None:
|
|
76
|
+
holder[0] = x
|
|
77
|
+
|
|
78
|
+
stack: list[tuple[Any, ...]] = [(_VALUE, root, "", 1, set_root)]
|
|
79
|
+
|
|
80
|
+
def too_large() -> Refused:
|
|
81
|
+
admission_probe.last_count = count
|
|
82
|
+
return Refused(AdmissionRefusal("event_too_large", ""), count)
|
|
83
|
+
|
|
84
|
+
def invalid(path: str) -> Refused:
|
|
85
|
+
admission_probe.last_count = count
|
|
86
|
+
return Refused(AdmissionRefusal("invalid_event", path), count)
|
|
87
|
+
|
|
88
|
+
while stack:
|
|
89
|
+
item = stack.pop()
|
|
90
|
+
if item[0] == _KEY:
|
|
91
|
+
# A key that is not a str, or a str holding a surrogate pair, is the
|
|
92
|
+
# holding object's fault: its path is the holder's.
|
|
93
|
+
_, k, holder_path = item
|
|
94
|
+
if k is _NOT_A_STR:
|
|
95
|
+
return invalid(holder_path)
|
|
96
|
+
n = len(k)
|
|
97
|
+
if n > ceiling - count:
|
|
98
|
+
count = ceiling + 1
|
|
99
|
+
return too_large()
|
|
100
|
+
count += n
|
|
101
|
+
admission_probe.strings_scanned += 1
|
|
102
|
+
if _text.has_surrogate_pair(k):
|
|
103
|
+
return invalid(holder_path)
|
|
104
|
+
continue
|
|
105
|
+
|
|
106
|
+
_, v, path, depth, setter = item
|
|
107
|
+
t = type(v)
|
|
108
|
+
|
|
109
|
+
# 1. Size.
|
|
110
|
+
count += 1
|
|
111
|
+
is_str = issubclass(t, str)
|
|
112
|
+
if is_str:
|
|
113
|
+
n = str.__len__(v)
|
|
114
|
+
if n > ceiling - count:
|
|
115
|
+
# Counted only up to one past the ceiling: the verdict is the same.
|
|
116
|
+
count = ceiling + 1
|
|
117
|
+
return too_large()
|
|
118
|
+
count += n
|
|
119
|
+
if count > ceiling:
|
|
120
|
+
return too_large()
|
|
121
|
+
|
|
122
|
+
# 2. Type.
|
|
123
|
+
if v is None or t is bool:
|
|
124
|
+
setter(v)
|
|
125
|
+
continue
|
|
126
|
+
if is_str:
|
|
127
|
+
s = v if t is str else str.__str__(v)
|
|
128
|
+
admission_probe.strings_scanned += 1
|
|
129
|
+
if _text.has_surrogate_pair(s):
|
|
130
|
+
return invalid(path)
|
|
131
|
+
setter(s)
|
|
132
|
+
continue
|
|
133
|
+
if issubclass(t, int):
|
|
134
|
+
i = v if t is int else int.__int__(v)
|
|
135
|
+
if i > _MAX_SAFE or i < -_MAX_SAFE:
|
|
136
|
+
return invalid(path)
|
|
137
|
+
setter(i)
|
|
138
|
+
continue
|
|
139
|
+
if issubclass(t, float):
|
|
140
|
+
f = v if t is float else float.__float__(v)
|
|
141
|
+
if not math.isfinite(f):
|
|
142
|
+
return invalid(path)
|
|
143
|
+
setter(f)
|
|
144
|
+
continue
|
|
145
|
+
|
|
146
|
+
is_list = issubclass(t, list)
|
|
147
|
+
if is_list or issubclass(t, tuple):
|
|
148
|
+
# 3. Depth.
|
|
149
|
+
if depth > ADMISSION_DEPTH_CEILING:
|
|
150
|
+
return invalid(path)
|
|
151
|
+
# 4. Member count, on entry.
|
|
152
|
+
length = list.__len__(v) if is_list else tuple.__len__(v)
|
|
153
|
+
count += length
|
|
154
|
+
if count > ceiling:
|
|
155
|
+
return too_large()
|
|
156
|
+
members = list.copy(v) if is_list else list(tuple.__iter__(v))
|
|
157
|
+
copy: list[Any] = [None] * len(members)
|
|
158
|
+
setter(copy)
|
|
159
|
+
for i in range(len(members) - 1, -1, -1):
|
|
160
|
+
stack.append((_VALUE, members[i], _text.child_path(path, i), depth + 1, _list_setter(copy, i)))
|
|
161
|
+
continue
|
|
162
|
+
|
|
163
|
+
if issubclass(t, dict):
|
|
164
|
+
if depth > ADMISSION_DEPTH_CEILING:
|
|
165
|
+
return invalid(path)
|
|
166
|
+
length = dict.__len__(v)
|
|
167
|
+
count += length
|
|
168
|
+
if count > ceiling:
|
|
169
|
+
return too_large()
|
|
170
|
+
pairs = [(_key_name(k), m) for k, m in dict.items(v)]
|
|
171
|
+
ordered = _text._es_key_order(pairs, name=lambda p: p[0])
|
|
172
|
+
obj: dict[str, Any] = {}
|
|
173
|
+
setter(obj)
|
|
174
|
+
for i in range(len(ordered) - 1, -1, -1):
|
|
175
|
+
k, m = ordered[i]
|
|
176
|
+
member_path = path if k is _NOT_A_STR else _text.child_path(path, k)
|
|
177
|
+
stack.append((_VALUE, m, member_path, depth + 1, _dict_setter(obj, k)))
|
|
178
|
+
stack.append((_KEY, k, path))
|
|
179
|
+
continue
|
|
180
|
+
|
|
181
|
+
return invalid(path)
|
|
182
|
+
|
|
183
|
+
admission_probe.last_count = count
|
|
184
|
+
return Admitted(holder[0], count)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class _NotAStr:
|
|
188
|
+
__slots__ = ()
|
|
189
|
+
|
|
190
|
+
def __repr__(self) -> str:
|
|
191
|
+
return "<not a str>"
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
_NOT_A_STR: Any = _NotAStr()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def _key_name(k: Any) -> Any:
|
|
198
|
+
t = type(k)
|
|
199
|
+
if t is str:
|
|
200
|
+
return k
|
|
201
|
+
if issubclass(t, str):
|
|
202
|
+
return str.__str__(k)
|
|
203
|
+
return _NOT_A_STR
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _list_setter(copy: list[Any], i: int) -> Any:
|
|
207
|
+
def set_item(x: Any) -> None:
|
|
208
|
+
copy[i] = x
|
|
209
|
+
|
|
210
|
+
return set_item
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def _dict_setter(obj: dict[str, Any], k: Any) -> Any:
|
|
214
|
+
def set_member(x: Any) -> None:
|
|
215
|
+
if k is not _NOT_A_STR:
|
|
216
|
+
obj[k] = x
|
|
217
|
+
|
|
218
|
+
return set_member
|