visionai-sdk-python 2.2.0.dev4__tar.gz → 2.2.0.dev5__tar.gz
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.
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/PKG-INFO +4 -3
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/README.md +3 -2
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/async_client.py +5 -1
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/auth/credentials.py +2 -1
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/client.py +5 -1
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/__init__.py +4 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/protocol.py +27 -2
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python.egg-info/PKG-INFO +4 -3
- visionai_sdk_python-2.2.0.dev5/src/visionai_sdk_python.egg-info/scm_version.json +8 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/relay/test_protocol.py +30 -0
- visionai_sdk_python-2.2.0.dev4/src/visionai_sdk_python.egg-info/scm_version.json +0 -8
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/.github/dependabot.yml +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/.github/workflows/ci.yml +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/.github/workflows/publish.yml +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/.gitignore +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/pyproject.toml +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/setup.cfg +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/__init__.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/_base.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/_jwt_verifier.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/_request_attribution.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/_source_header.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/auth/__init__.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/auth/_mixin.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/auth/async_resource.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/auth/models.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/auth/resource.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/constants.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/endpoints.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/exceptions.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/instrumentation.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/py.typed +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/examples/__init__.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/examples/error_connection.json +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/examples/error_request.json +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/examples/heartbeat.json +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/examples/register.json +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/examples/registered.json +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/examples/request.json +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/examples/response.json +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/relay/examples/response_text.json +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/vlm/__init__.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/vlm/_mixin.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/vlm/async_resource.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/vlm/models.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/vlm/preprocess.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/vlm/resource.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python.egg-info/SOURCES.txt +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python.egg-info/dependency_links.txt +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python.egg-info/requires.txt +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python.egg-info/scm_file_list.json +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python.egg-info/top_level.txt +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/__init__.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/constants.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/relay/__init__.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test__base.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test_async_client.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test_client.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test_credentials.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test_instrumentation.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test_request.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test_vlm_preprocess.py +0 -0
- {visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: visionai-sdk-python
|
|
3
|
-
Version: 2.2.0.
|
|
3
|
+
Version: 2.2.0.dev5
|
|
4
4
|
Summary: VisionAI SDK for Python
|
|
5
5
|
Author-email: Tony Yang <tonyyang@linkervision.com>
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -148,7 +148,8 @@ client = Client(auth_url="...", vlm_url="...", credential=cred) # vlm.chat() u
|
|
|
148
148
|
|
|
149
149
|
token = cred.get_token().token # for a WebSocket upgrade or another library
|
|
150
150
|
|
|
151
|
-
# Any other HTTP call with the same token; a 401 gets one
|
|
151
|
+
# Any other HTTP call with the same token, sent to the host you name; a 401 gets one
|
|
152
|
+
# fresh token and one retry
|
|
152
153
|
nodes = client.request("GET", "https://vs.example.com/mgmt/nodes").json()
|
|
153
154
|
```
|
|
154
155
|
|
|
@@ -168,7 +169,7 @@ authentication wins. `vlm_url` is optional for clients that only authenticate.
|
|
|
168
169
|
|
|
169
170
|
`visionai_sdk_python.relay` holds the wire format between platform-relay and
|
|
170
171
|
platform-edge-agent: the frame models, the constants and `parse_agent_frame()` /
|
|
171
|
-
`parse_relay_frame()` / `dump_frame()
|
|
172
|
+
`parse_relay_frame()` / `dump_frame()` (which refuses a frame over `MAX_FRAME_BYTES`). The spec is platform-service's
|
|
172
173
|
`docs/adr/0001-relay-protocol.md`; both sides import this module instead of keeping a
|
|
173
174
|
copy. The ADR's JSON examples ship with it (`visionai_sdk_python.relay.examples`:
|
|
174
175
|
`example(name)` / `example_text(name)`), so every implementation tests against the same
|
|
@@ -130,7 +130,8 @@ client = Client(auth_url="...", vlm_url="...", credential=cred) # vlm.chat() u
|
|
|
130
130
|
|
|
131
131
|
token = cred.get_token().token # for a WebSocket upgrade or another library
|
|
132
132
|
|
|
133
|
-
# Any other HTTP call with the same token; a 401 gets one
|
|
133
|
+
# Any other HTTP call with the same token, sent to the host you name; a 401 gets one
|
|
134
|
+
# fresh token and one retry
|
|
134
135
|
nodes = client.request("GET", "https://vs.example.com/mgmt/nodes").json()
|
|
135
136
|
```
|
|
136
137
|
|
|
@@ -150,7 +151,7 @@ authentication wins. `vlm_url` is optional for clients that only authenticate.
|
|
|
150
151
|
|
|
151
152
|
`visionai_sdk_python.relay` holds the wire format between platform-relay and
|
|
152
153
|
platform-edge-agent: the frame models, the constants and `parse_agent_frame()` /
|
|
153
|
-
`parse_relay_frame()` / `dump_frame()
|
|
154
|
+
`parse_relay_frame()` / `dump_frame()` (which refuses a frame over `MAX_FRAME_BYTES`). The spec is platform-service's
|
|
154
155
|
`docs/adr/0001-relay-protocol.md`; both sides import this module instead of keeping a
|
|
155
156
|
copy. The ADR's JSON examples ship with it (`visionai_sdk_python.relay.examples`:
|
|
156
157
|
`example(name)` / `example_text(name)`), so every implementation tests against the same
|
|
@@ -58,7 +58,11 @@ class AsyncClient(_BaseClient):
|
|
|
58
58
|
async def request(self, method: str, url: str, **kwargs) -> httpx.Response:
|
|
59
59
|
"""Send any request with this client's bearer token.
|
|
60
60
|
|
|
61
|
-
``url`` is a full URL (the client has two bases); ``kwargs`` go to httpx.
|
|
61
|
+
``url`` is a full URL (the client has two bases); ``kwargs`` go to httpx. The
|
|
62
|
+
bearer token goes to whatever host ``url`` names, so ``url`` must come from
|
|
63
|
+
configuration or code, never from a caller's input; redirects are not
|
|
64
|
+
followed unless asked, and httpx drops the header on a cross-origin redirect.
|
|
65
|
+
|
|
62
66
|
With a ``credential``, a 401 buys one retry with the credential's current
|
|
63
67
|
token, which it replaces when the rejected one is old enough to have been
|
|
64
68
|
revoked (``REPLACE_COOLDOWN_S``); a 401 on the retry raises. Without a
|
|
@@ -237,7 +237,8 @@ class AsyncClientCredentials(_Credentials):
|
|
|
237
237
|
|
|
238
238
|
Pass it to ``AsyncClient(credential=...)`` and use ``await get_token()`` for
|
|
239
239
|
requests the SDK does not make itself, such as a WebSocket upgrade. One
|
|
240
|
-
instance serves any number of clients and callers
|
|
240
|
+
instance serves any number of clients and callers on one event loop; its
|
|
241
|
+
lock and ``httpx.AsyncClient`` bind to that loop.
|
|
241
242
|
"""
|
|
242
243
|
|
|
243
244
|
def __init__(
|
{visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/client.py
RENAMED
|
@@ -58,7 +58,11 @@ class Client(_BaseClient):
|
|
|
58
58
|
def request(self, method: str, url: str, **kwargs) -> httpx.Response:
|
|
59
59
|
"""Send any request with this client's bearer token.
|
|
60
60
|
|
|
61
|
-
``url`` is a full URL (the client has two bases); ``kwargs`` go to httpx.
|
|
61
|
+
``url`` is a full URL (the client has two bases); ``kwargs`` go to httpx. The
|
|
62
|
+
bearer token goes to whatever host ``url`` names, so ``url`` must come from
|
|
63
|
+
configuration or code, never from a caller's input; redirects are not
|
|
64
|
+
followed unless asked, and httpx drops the header on a cross-origin redirect.
|
|
65
|
+
|
|
62
66
|
With a ``credential``, a 401 buys one retry with the credential's current
|
|
63
67
|
token, which it replaces when the rejected one is old enough to have been
|
|
64
68
|
revoked (``REPLACE_COOLDOWN_S``); a 401 on the retry raises. Without a
|
|
@@ -9,6 +9,7 @@ from .protocol import (
|
|
|
9
9
|
HEARTBEAT_INTERVAL_S,
|
|
10
10
|
MAX_BODY_BYTES,
|
|
11
11
|
MAX_FRAME_BYTES,
|
|
12
|
+
MAX_MESSAGE_CHARS,
|
|
12
13
|
REGISTER_DEADLINE_S,
|
|
13
14
|
RELAY_TIMEOUT_S,
|
|
14
15
|
STALE_AFTER_S,
|
|
@@ -18,6 +19,7 @@ from .protocol import (
|
|
|
18
19
|
Error,
|
|
19
20
|
ErrorCode,
|
|
20
21
|
Frame,
|
|
22
|
+
FrameTooLarge,
|
|
21
23
|
Heartbeat,
|
|
22
24
|
InvalidFrame,
|
|
23
25
|
ModelEntry,
|
|
@@ -40,6 +42,7 @@ __all__ = [
|
|
|
40
42
|
"HEARTBEAT_INTERVAL_S",
|
|
41
43
|
"MAX_BODY_BYTES",
|
|
42
44
|
"MAX_FRAME_BYTES",
|
|
45
|
+
"MAX_MESSAGE_CHARS",
|
|
43
46
|
"REGISTER_DEADLINE_S",
|
|
44
47
|
"RELAY_TIMEOUT_S",
|
|
45
48
|
"STALE_AFTER_S",
|
|
@@ -49,6 +52,7 @@ __all__ = [
|
|
|
49
52
|
"Error",
|
|
50
53
|
"ErrorCode",
|
|
51
54
|
"Frame",
|
|
55
|
+
"FrameTooLarge",
|
|
52
56
|
"Heartbeat",
|
|
53
57
|
"InvalidFrame",
|
|
54
58
|
"ModelEntry",
|
|
@@ -28,6 +28,8 @@ MAX_BODY_BYTES = 15 * 1024 * 1024
|
|
|
28
28
|
# Timeouts nest: the agent waits for VS, the relay waits a little longer for the agent.
|
|
29
29
|
AGENT_TIMEOUT_S = 100
|
|
30
30
|
RELAY_TIMEOUT_S = 110.0
|
|
31
|
+
# An `error` echoes parts of the peer's frame; cut them so the error fits a frame.
|
|
32
|
+
MAX_MESSAGE_CHARS = 200
|
|
31
33
|
|
|
32
34
|
# Platform workspace ids: lowercase hyphenated UUIDs.
|
|
33
35
|
UUID_RE = re.compile(r"[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")
|
|
@@ -58,6 +60,7 @@ class ErrorCode(StrEnum):
|
|
|
58
60
|
PATH_NOT_ALLOWED = "path_not_allowed"
|
|
59
61
|
UPSTREAM_UNREACHABLE = "upstream_unreachable"
|
|
60
62
|
UPSTREAM_TIMEOUT = "upstream_timeout"
|
|
63
|
+
UPSTREAM_TOO_LARGE = "upstream_too_large"
|
|
61
64
|
|
|
62
65
|
|
|
63
66
|
class Frame(BaseModel):
|
|
@@ -133,13 +136,30 @@ _relay_frame = TypeAdapter(Annotated[RelayFrame, Field(discriminator="type")])
|
|
|
133
136
|
|
|
134
137
|
|
|
135
138
|
class InvalidFrame(ValueError):
|
|
136
|
-
"""A frame the receiver cannot read; the text is safe to send back in an ``error``.
|
|
139
|
+
"""A frame the receiver cannot read; the text is safe to send back in an ``error``.
|
|
140
|
+
|
|
141
|
+
Parts of the text come from the peer's frame, so it is cut at ``MAX_MESSAGE_CHARS``
|
|
142
|
+
and a ``request_id`` longer than that is dropped.
|
|
143
|
+
"""
|
|
137
144
|
|
|
138
145
|
def __init__(self, message: str, request_id: str | None = None) -> None:
|
|
146
|
+
if len(message) > MAX_MESSAGE_CHARS:
|
|
147
|
+
message = message[: MAX_MESSAGE_CHARS - 1] + "…"
|
|
148
|
+
if request_id is not None and len(request_id) > MAX_MESSAGE_CHARS:
|
|
149
|
+
request_id = None
|
|
150
|
+
message += " (request_id too long, dropped)"
|
|
139
151
|
super().__init__(message)
|
|
140
152
|
self.request_id = request_id
|
|
141
153
|
|
|
142
154
|
|
|
155
|
+
class FrameTooLarge(ValueError):
|
|
156
|
+
"""The encoded frame exceeds ``MAX_FRAME_BYTES``; the receiver would close with 1009."""
|
|
157
|
+
|
|
158
|
+
def __init__(self, size: int) -> None:
|
|
159
|
+
super().__init__(f"frame is {size} bytes; the limit is {MAX_FRAME_BYTES}")
|
|
160
|
+
self.size = size
|
|
161
|
+
|
|
162
|
+
|
|
143
163
|
def parse_agent_frame(text: str) -> AgentFrame:
|
|
144
164
|
"""A frame the relay received from an agent."""
|
|
145
165
|
return _parse(text, _agent_frame)
|
|
@@ -175,4 +195,9 @@ def _parse(text: str, adapter: TypeAdapter):
|
|
|
175
195
|
|
|
176
196
|
|
|
177
197
|
def dump_frame(frame: BaseModel) -> str:
|
|
178
|
-
|
|
198
|
+
"""The frame as JSON text, or ``FrameTooLarge`` when a receiver would refuse it."""
|
|
199
|
+
text = frame.model_dump_json(exclude_none=True)
|
|
200
|
+
size = len(text.encode())
|
|
201
|
+
if size > MAX_FRAME_BYTES:
|
|
202
|
+
raise FrameTooLarge(size)
|
|
203
|
+
return text
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: visionai-sdk-python
|
|
3
|
-
Version: 2.2.0.
|
|
3
|
+
Version: 2.2.0.dev5
|
|
4
4
|
Summary: VisionAI SDK for Python
|
|
5
5
|
Author-email: Tony Yang <tonyyang@linkervision.com>
|
|
6
6
|
Requires-Python: >=3.11
|
|
@@ -148,7 +148,8 @@ client = Client(auth_url="...", vlm_url="...", credential=cred) # vlm.chat() u
|
|
|
148
148
|
|
|
149
149
|
token = cred.get_token().token # for a WebSocket upgrade or another library
|
|
150
150
|
|
|
151
|
-
# Any other HTTP call with the same token; a 401 gets one
|
|
151
|
+
# Any other HTTP call with the same token, sent to the host you name; a 401 gets one
|
|
152
|
+
# fresh token and one retry
|
|
152
153
|
nodes = client.request("GET", "https://vs.example.com/mgmt/nodes").json()
|
|
153
154
|
```
|
|
154
155
|
|
|
@@ -168,7 +169,7 @@ authentication wins. `vlm_url` is optional for clients that only authenticate.
|
|
|
168
169
|
|
|
169
170
|
`visionai_sdk_python.relay` holds the wire format between platform-relay and
|
|
170
171
|
platform-edge-agent: the frame models, the constants and `parse_agent_frame()` /
|
|
171
|
-
`parse_relay_frame()` / `dump_frame()
|
|
172
|
+
`parse_relay_frame()` / `dump_frame()` (which refuses a frame over `MAX_FRAME_BYTES`). The spec is platform-service's
|
|
172
173
|
`docs/adr/0001-relay-protocol.md`; both sides import this module instead of keeping a
|
|
173
174
|
copy. The ADR's JSON examples ship with it (`visionai_sdk_python.relay.examples`:
|
|
174
175
|
`example(name)` / `example_text(name)`), so every implementation tests against the same
|
{visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/relay/test_protocol.py
RENAMED
|
@@ -5,7 +5,10 @@ import json
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
7
|
from visionai_sdk_python.relay import (
|
|
8
|
+
MAX_FRAME_BYTES,
|
|
9
|
+
MAX_MESSAGE_CHARS,
|
|
8
10
|
Error,
|
|
11
|
+
FrameTooLarge,
|
|
9
12
|
Heartbeat,
|
|
10
13
|
InvalidFrame,
|
|
11
14
|
Register,
|
|
@@ -173,3 +176,30 @@ def test_workspace_id_format(value, ok):
|
|
|
173
176
|
def test_every_shipped_example_is_valid_json_with_a_type():
|
|
174
177
|
for name in NAMES:
|
|
175
178
|
assert isinstance(json.loads(example(name))["type"], str), name
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def test_an_error_message_is_cut_so_it_fits_a_frame():
|
|
182
|
+
with pytest.raises(InvalidFrame) as exc:
|
|
183
|
+
parse_agent_frame(json.dumps({"type": "x" * 1_000_000}))
|
|
184
|
+
|
|
185
|
+
assert len(str(exc.value)) == MAX_MESSAGE_CHARS
|
|
186
|
+
assert str(exc.value).endswith("…")
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def test_an_oversized_request_id_is_dropped_from_the_error():
|
|
190
|
+
frame = {"type": "request", "request_id": "r" * 1_000_000}
|
|
191
|
+
with pytest.raises(InvalidFrame) as exc:
|
|
192
|
+
parse_relay_frame(json.dumps(frame))
|
|
193
|
+
|
|
194
|
+
assert exc.value.request_id is None
|
|
195
|
+
assert str(exc.value).endswith("(request_id too long, dropped)")
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_dump_frame_refuses_a_frame_the_receiver_would_close_on():
|
|
199
|
+
frame = Response(request_id="r1", status=200, body="x" * MAX_FRAME_BYTES)
|
|
200
|
+
|
|
201
|
+
with pytest.raises(FrameTooLarge) as exc:
|
|
202
|
+
dump_frame(frame)
|
|
203
|
+
|
|
204
|
+
assert exc.value.size > MAX_FRAME_BYTES
|
|
205
|
+
assert dump_frame(Response(request_id="r1", status=200, body="x"))
|
|
File without changes
|
|
File without changes
|
{visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/.github/workflows/publish.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/_base.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/src/visionai_sdk_python/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test_async_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test_instrumentation.py
RENAMED
|
File without changes
|
|
File without changes
|
{visionai_sdk_python-2.2.0.dev4 → visionai_sdk_python-2.2.0.dev5}/tests/test_vlm_preprocess.py
RENAMED
|
File without changes
|
|
File without changes
|