ailogger-client-dev 0.1.51__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.
- ailogger_client/__init__.py +96 -0
- ailogger_client/client.py +220 -0
- ailogger_client/config.py +386 -0
- ailogger_client/errors.py +610 -0
- ailogger_client/file_transfer.py +110 -0
- ailogger_client/invocation.py +550 -0
- ailogger_client/message_methods.py +332 -0
- ailogger_client/observation_methods.py +195 -0
- ailogger_client/py.typed +0 -0
- ailogger_client/queued_methods.py +247 -0
- ailogger_client/read_methods.py +585 -0
- ailogger_client/retrieval_methods.py +314 -0
- ailogger_client/service_methods.py +148 -0
- ailogger_client/session_methods.py +236 -0
- ailogger_client_dev-0.1.51.dist-info/METADATA +108 -0
- ailogger_client_dev-0.1.51.dist-info/RECORD +18 -0
- ailogger_client_dev-0.1.51.dist-info/WHEEL +5 -0
- ailogger_client_dev-0.1.51.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Typed methods for appending messages to dialogue sessions.
|
|
3
|
+
|
|
4
|
+
This module carries the single mixin class whose typed method lets a caller
|
|
5
|
+
append a message to an existing dialogue session. The method names its
|
|
6
|
+
arguments for what the corresponding command's parameter schema declares,
|
|
7
|
+
so a caller appends a message in the service's own terms and never assembles
|
|
8
|
+
a request envelope, never names a command as a free string and never guesses
|
|
9
|
+
a parameter name.
|
|
10
|
+
|
|
11
|
+
A parameter the command declares as required is a required argument of the
|
|
12
|
+
method; a parameter the command declares as optional is optional on the method
|
|
13
|
+
and is simply not carried when the caller does not supply it, so the service's
|
|
14
|
+
own resolution of an unsupplied optional parameter is never pre-empted by a
|
|
15
|
+
value invented in this client.
|
|
16
|
+
|
|
17
|
+
The method carries its arguments to the invocation seam of G-012/T-002/A-001
|
|
18
|
+
and returns what the seam returns. The method performs no validation of a
|
|
19
|
+
parameter value beyond what the language's own typing expresses: the command's
|
|
20
|
+
declared schema is validated at the service.
|
|
21
|
+
|
|
22
|
+
NETWORK LAW. This module imports nothing from the adapter framework's
|
|
23
|
+
implementation and adds no transport of its own. All network interaction goes
|
|
24
|
+
through the invocation seam and thence through the privately held framework
|
|
25
|
+
client instance, and through nothing else.
|
|
26
|
+
|
|
27
|
+
BOUNDARIES. This module declares no generic call, no completion default, no
|
|
28
|
+
non-blocking path or any job-observing call (all owned by earlier steps).
|
|
29
|
+
Every declaration in this file is fully annotated for the pipeline's named
|
|
30
|
+
strict type check, and the module satisfies its named lint check.
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
from typing import Any
|
|
36
|
+
|
|
37
|
+
__all__ = ["MessageMixin"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MessageMixin:
|
|
41
|
+
"""Mixin providing a typed method for appending messages.
|
|
42
|
+
|
|
43
|
+
Declares the single typed method by which a caller appends a message to
|
|
44
|
+
an existing dialogue session.
|
|
45
|
+
|
|
46
|
+
This mixin expects to be composed with InvocationMixin, which provides
|
|
47
|
+
the _invoke method.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
async def _invoke(
|
|
51
|
+
self,
|
|
52
|
+
command: str,
|
|
53
|
+
params: dict[str, Any] | None = None,
|
|
54
|
+
*,
|
|
55
|
+
wait_for_result: bool = True,
|
|
56
|
+
) -> Any:
|
|
57
|
+
"""Private invocation entry point.
|
|
58
|
+
|
|
59
|
+
This method is provided by InvocationMixin when this mixin is composed
|
|
60
|
+
into a class. This stub is declared here for type checking purposes
|
|
61
|
+
only.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
command: The name of the command to execute.
|
|
65
|
+
params: An optional mapping of command parameters.
|
|
66
|
+
wait_for_result: When True (default), wait for queued commands to
|
|
67
|
+
completion and return the finished result.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
The command result.
|
|
71
|
+
"""
|
|
72
|
+
raise NotImplementedError # Provided by InvocationMixin
|
|
73
|
+
|
|
74
|
+
async def _submit_queued(self, command: str, params: dict[str, Any]) -> str:
|
|
75
|
+
"""Provided by QueuedMixin when this mixin is composed."""
|
|
76
|
+
raise NotImplementedError # Provided by QueuedMixin
|
|
77
|
+
|
|
78
|
+
async def _await_queued_result(
|
|
79
|
+
self,
|
|
80
|
+
job_id: str,
|
|
81
|
+
*,
|
|
82
|
+
command: str,
|
|
83
|
+
timeout: float | None = None,
|
|
84
|
+
) -> list[Any]:
|
|
85
|
+
"""Provided by QueuedMixin when this mixin is composed."""
|
|
86
|
+
raise NotImplementedError # Provided by QueuedMixin
|
|
87
|
+
|
|
88
|
+
async def append_message( # noqa: PLR0913, PLR0917
|
|
89
|
+
self,
|
|
90
|
+
session_id: str,
|
|
91
|
+
text: str,
|
|
92
|
+
sender_kind: str,
|
|
93
|
+
sender_identity: str,
|
|
94
|
+
receiver_kind: str,
|
|
95
|
+
receiver_identity: str,
|
|
96
|
+
priority: int | None = None,
|
|
97
|
+
message_id: str | None = None,
|
|
98
|
+
relations: list[dict[str, Any]] | None = None,
|
|
99
|
+
corrects_message_id: str | None = None,
|
|
100
|
+
attributes: dict[str, Any] | None = None,
|
|
101
|
+
occurred_at: str | None = None,
|
|
102
|
+
force_overwrite: bool | None = None,
|
|
103
|
+
) -> Any:
|
|
104
|
+
"""Append one message to a dialogue session.
|
|
105
|
+
|
|
106
|
+
Appends one message to a dialogue session this service already holds.
|
|
107
|
+
Six values are required of every append without exception: the session
|
|
108
|
+
the message goes into, the text, and each of the two participants as a
|
|
109
|
+
kind together with an identity - the sender and the receiver.
|
|
110
|
+
|
|
111
|
+
Seven more are optional: the priority the message is recorded under
|
|
112
|
+
(recorded; it does not affect ordering or scheduling, and message_search
|
|
113
|
+
accepts it as an equality filter), the identifier it is accepted under
|
|
114
|
+
(the service generates one when none is supplied), the links it
|
|
115
|
+
declares to earlier messages of the same session, the message it
|
|
116
|
+
corrects, free-form attributes recorded alongside its text, the
|
|
117
|
+
moment the message happened
|
|
118
|
+
(``occurred_at``), and ``force_overwrite``.
|
|
119
|
+
|
|
120
|
+
When no identifier is supplied, the ordinary case, the service
|
|
121
|
+
generates a version 4 UUID and the answer carries it back. Sending
|
|
122
|
+
that identifier back with the same content is a retry and is answered
|
|
123
|
+
exactly as the first submission was, with no second message created.
|
|
124
|
+
Sending it back with ANY different content - text, participants,
|
|
125
|
+
priority, correction target, attributes, relations or stated moment -
|
|
126
|
+
is refused as a conflict, unless ``force_overwrite`` is true.
|
|
127
|
+
|
|
128
|
+
Each declared relation names the earlier message it points at and the
|
|
129
|
+
kind of the link. Relations the relation model does not admit are
|
|
130
|
+
refused and nothing is stored. The correction target is stated
|
|
131
|
+
separately and is never one entry of the relations collection. The
|
|
132
|
+
free-form attributes are recorded verbatim and never interpreted - no
|
|
133
|
+
key inside them alters routing, ordering or acceptance.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
session_id: Reference to the dialogue session this message is
|
|
137
|
+
appended to, as the identifier that session was opened under.
|
|
138
|
+
Required of every append: this service appends to no default
|
|
139
|
+
session.
|
|
140
|
+
text: The text of the message being appended, recorded exactly as
|
|
141
|
+
submitted. Required of every append.
|
|
142
|
+
sender_kind: The capacity the sender of this message stands in, as
|
|
143
|
+
one of the four declared participant kinds (human, model, tool,
|
|
144
|
+
system). Required of every append, including one that records
|
|
145
|
+
plain application logging.
|
|
146
|
+
sender_identity: The concrete participant this message is
|
|
147
|
+
attributed to. Required of every append: two senders of the
|
|
148
|
+
same kind stay two participants whenever their identity differs,
|
|
149
|
+
and are never collapsed into one.
|
|
150
|
+
receiver_kind: The capacity the receiver of this message stands in,
|
|
151
|
+
as one of the four declared participant kinds. Required of
|
|
152
|
+
every append, including one that records plain application
|
|
153
|
+
logging.
|
|
154
|
+
receiver_identity: The concrete participant this message is
|
|
155
|
+
addressed to. Required of every append: two receivers of the
|
|
156
|
+
same kind stay two participants whenever their identity differs,
|
|
157
|
+
and are never collapsed into one.
|
|
158
|
+
priority: The priority this message is recorded under, as a
|
|
159
|
+
non-negative whole number. Zero marks the ordinary linear flow
|
|
160
|
+
of the dialogue and a higher value says the message was
|
|
161
|
+
inserted from outside that flow. Optional: omit it to record
|
|
162
|
+
under the priority the service resolves.
|
|
163
|
+
message_id: The identifier this message is to be accepted under, as
|
|
164
|
+
a well-formed version 4 UUID. Optional and legitimately absent:
|
|
165
|
+
the ordinary append supplies none, the service generates one
|
|
166
|
+
and the answer carries it back, and such a submission is a
|
|
167
|
+
first acceptance by construction. Supply the identifier a
|
|
168
|
+
previous answer returned to have a retry recognised as the same
|
|
169
|
+
message.
|
|
170
|
+
relations: The links this message declares to earlier messages of
|
|
171
|
+
the same session, in the order declared: each entry is a
|
|
172
|
+
mapping naming the earlier message (as to_message_id) and the
|
|
173
|
+
kind of the link (as kind). Optional: omit it or declare the
|
|
174
|
+
empty collection when this message declares none.
|
|
175
|
+
corrects_message_id: The identifier of the message this one
|
|
176
|
+
corrects, as a well-formed version 4 UUID, present only when
|
|
177
|
+
this message is issued as a correction of an earlier one.
|
|
178
|
+
Optional: omit it when this message corrects nothing.
|
|
179
|
+
attributes: Free-form attributes recorded alongside the message
|
|
180
|
+
text, stored verbatim and never interpreted. Optional and
|
|
181
|
+
legitimately absent or empty.
|
|
182
|
+
occurred_at: The moment the message happened, as an RFC 3339
|
|
183
|
+
timestamp WITH an explicit UTC offset (``Z`` or ``+hh:mm``;
|
|
184
|
+
fractional seconds allowed). It becomes the message's
|
|
185
|
+
effective event time, stored and returned normalised to UTC:
|
|
186
|
+
reads order by it, the period filters and the working-set
|
|
187
|
+
recency use it. Optional: omit it and the service's receive
|
|
188
|
+
time stands. Passed through unchanged; the service validates
|
|
189
|
+
it.
|
|
190
|
+
force_overwrite: When true, a ``message_id`` that is already
|
|
191
|
+
stored is REPLACED by this payload without any comparison,
|
|
192
|
+
keeping its identifier and sequence number and being analysed
|
|
193
|
+
afresh. Optional, default false (the identifier is recognised:
|
|
194
|
+
an identical repeat returns the original, a differing one is
|
|
195
|
+
a conflict). Passed through unchanged; the service validates
|
|
196
|
+
it.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Exactly five facts about the accepted message: the identity it
|
|
200
|
+
was accepted under, as the identifier supplied when one was
|
|
201
|
+
supplied and the one the service generated when none was
|
|
202
|
+
(``message_id``); the position assigned to it within its session
|
|
203
|
+
by the arrival-order model (``sequence_number``); the audience it
|
|
204
|
+
carries (``audience``); and ``text_head`` and ``text_tail``, the
|
|
205
|
+
first and the last five words of the STORED text (words as
|
|
206
|
+
``str.split()`` yields, joined by one space; they overlap when the
|
|
207
|
+
text has fewer than ten words, each holds all the words when it
|
|
208
|
+
has fewer than five, and both are ``""`` for a text without
|
|
209
|
+
words). A recognised retry answers with the identity, position,
|
|
210
|
+
audience and excerpts already accepted for that identifier; a
|
|
211
|
+
force overwrite answers with the excerpts of the text now stored.
|
|
212
|
+
"""
|
|
213
|
+
params: dict[str, Any] = {
|
|
214
|
+
"session_id": session_id,
|
|
215
|
+
"text": text,
|
|
216
|
+
"sender_kind": sender_kind,
|
|
217
|
+
"sender_identity": sender_identity,
|
|
218
|
+
"receiver_kind": receiver_kind,
|
|
219
|
+
"receiver_identity": receiver_identity,
|
|
220
|
+
}
|
|
221
|
+
if priority is not None:
|
|
222
|
+
params["priority"] = priority
|
|
223
|
+
if message_id is not None:
|
|
224
|
+
params["message_id"] = message_id
|
|
225
|
+
if relations is not None:
|
|
226
|
+
params["relations"] = relations
|
|
227
|
+
if corrects_message_id is not None:
|
|
228
|
+
params["corrects_message_id"] = corrects_message_id
|
|
229
|
+
if attributes is not None:
|
|
230
|
+
params["attributes"] = attributes
|
|
231
|
+
if occurred_at is not None:
|
|
232
|
+
params["occurred_at"] = occurred_at
|
|
233
|
+
if force_overwrite is not None:
|
|
234
|
+
params["force_overwrite"] = force_overwrite
|
|
235
|
+
return await self._invoke("append_message", params)
|
|
236
|
+
|
|
237
|
+
async def messages_import(
|
|
238
|
+
self,
|
|
239
|
+
messages: list[dict[str, Any]],
|
|
240
|
+
*,
|
|
241
|
+
timeout: float | None = None,
|
|
242
|
+
) -> list[dict[str, Any]]:
|
|
243
|
+
"""Import a batch of messages and return the per-item outcomes.
|
|
244
|
+
|
|
245
|
+
``messages_import`` is ALWAYS queued by the service. This method
|
|
246
|
+
submits the job, waits for it to finish through the client's own job
|
|
247
|
+
tracking and returns the job's result: a list with exactly one entry
|
|
248
|
+
per input item, in input order, each ``{"message_id": <id or None>,
|
|
249
|
+
"success": <bool>}`` and, when ``success`` is true, also
|
|
250
|
+
``"text_head"`` and ``"text_tail"`` (the first and the last five
|
|
251
|
+
words of the item's stored text, as for ``append_message``).
|
|
252
|
+
``success`` is true when the message is stored
|
|
253
|
+
(newly created, an identical repeat, or overwritten) and false when it
|
|
254
|
+
was not stored (the item failed validation or conflicts with a stored
|
|
255
|
+
message). Items are independent: a false item does not roll back the
|
|
256
|
+
others. Running the same import again answers true for every item and
|
|
257
|
+
adds no row.
|
|
258
|
+
|
|
259
|
+
Args:
|
|
260
|
+
messages: The items to import - exactly the ``append_message``
|
|
261
|
+
parameter set per item, with ``message_id`` and
|
|
262
|
+
``occurred_at`` required. Passed through unchanged; the
|
|
263
|
+
service validates them.
|
|
264
|
+
timeout: The longest to wait for the job, in seconds. Optional:
|
|
265
|
+
the client's own bound applies when omitted.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
The per-item outcomes, in input order.
|
|
269
|
+
|
|
270
|
+
Raises:
|
|
271
|
+
ServiceRefusalError: When the whole call is refused (messages
|
|
272
|
+
missing, not an array, empty, over the configured maximum, an
|
|
273
|
+
element that is not an object, an unnamed parameter or a
|
|
274
|
+
repeated message_id) - nothing is written and no job exists.
|
|
275
|
+
CompletionTimeoutError: When the job does not finish in time.
|
|
276
|
+
CompletionFailedError: When the job fails.
|
|
277
|
+
"""
|
|
278
|
+
job_id = await self._submit_queued("messages_import", {"messages": messages})
|
|
279
|
+
return [
|
|
280
|
+
dict(entry)
|
|
281
|
+
for entry in await self._await_queued_result(
|
|
282
|
+
job_id, command="messages_import", timeout=timeout
|
|
283
|
+
)
|
|
284
|
+
]
|
|
285
|
+
|
|
286
|
+
async def mark_messages_for_deletion(
|
|
287
|
+
self,
|
|
288
|
+
message_ids: list[str],
|
|
289
|
+
marked_for_deletion: bool,
|
|
290
|
+
) -> Any:
|
|
291
|
+
"""Atomically set or clear the soft-deletion marker for messages.
|
|
292
|
+
|
|
293
|
+
Returns the command's boolean outcome. A true value means every UUID
|
|
294
|
+
reached the requested marker state. A false value means the batch was
|
|
295
|
+
empty, malformed, duplicate, unknown, or could not be changed, and no
|
|
296
|
+
marker changed. Repeating a valid set or clear is successful.
|
|
297
|
+
"""
|
|
298
|
+
return await self._invoke(
|
|
299
|
+
"messages_mark_for_deletion",
|
|
300
|
+
{
|
|
301
|
+
"message_ids": message_ids,
|
|
302
|
+
"marked_for_deletion": marked_for_deletion,
|
|
303
|
+
},
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
async def message_get(self, message_id: str) -> Any:
|
|
307
|
+
"""Read one message by its identifier with its vectorization state.
|
|
308
|
+
|
|
309
|
+
Returns the messages_read entry of the message plus
|
|
310
|
+
``vectorization_state`` (``"pending"``, ``"vectorized"`` or the terminal
|
|
311
|
+
``"not_vectorizable"`` for whitespace-only text, which is never
|
|
312
|
+
embedded). A
|
|
313
|
+
soft-deleted message is returned with ``soft_deleted`` true.
|
|
314
|
+
|
|
315
|
+
Raises:
|
|
316
|
+
ServiceRefusalError: MESSAGE_UNKNOWN for an unknown id, or the
|
|
317
|
+
REQUEST_PARAMETER_* code for a refused parameter.
|
|
318
|
+
"""
|
|
319
|
+
return await self._invoke("message_get", {"message_id": message_id})
|
|
320
|
+
|
|
321
|
+
async def message_delete(self, message_id: str, mode: str) -> Any:
|
|
322
|
+
"""Delete one message in the service's soft or hard mode.
|
|
323
|
+
|
|
324
|
+
``mode="soft"`` marks the message deleted so ordinary reads hide it.
|
|
325
|
+
``mode="hard"`` physically removes an unreferenced message and its
|
|
326
|
+
outbound derived rows. The service validates the identifier and mode,
|
|
327
|
+
and returns the deletion report it publishes.
|
|
328
|
+
"""
|
|
329
|
+
return await self._invoke(
|
|
330
|
+
"message_delete",
|
|
331
|
+
{"message_id": message_id, "mode": mode},
|
|
332
|
+
)
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Public observation methods of the client: the negative-case probe, the typed
|
|
3
|
+
help, the typed health route and the answer-mode observation.
|
|
4
|
+
|
|
5
|
+
These are not typed methods of any one command of the service. They are the
|
|
6
|
+
caller-facing surface a consumer that has to look at the service from the
|
|
7
|
+
outside - a verification suite above all - needs, so that it never reaches for
|
|
8
|
+
a private attribute of the client, the framework's own client, a raw JSON-RPC
|
|
9
|
+
call, a queue poll or an envelope:
|
|
10
|
+
|
|
11
|
+
- ``probe_command`` sends EXACTLY the parameters it is given to a named
|
|
12
|
+
command - missing, unknown, null and wrongly typed values included, nothing
|
|
13
|
+
added, dropped or validated here - and returns or raises exactly as a typed
|
|
14
|
+
method does. An always-queued command is waited for like its typed method.
|
|
15
|
+
- ``help`` is the service's own ``help`` command, typed.
|
|
16
|
+
- ``health_route`` is the one declared exception to "everything is a command":
|
|
17
|
+
the ``GET /health`` route, answered as the bare report it is.
|
|
18
|
+
- ``command_answer_mode`` reports HOW the service answered a command - inline
|
|
19
|
+
(``direct``) or with a job identity (``queued``) - without the caller looking
|
|
20
|
+
at an envelope and without waiting for a job.
|
|
21
|
+
|
|
22
|
+
This module is deliberately outside the set of modules whose public coroutine
|
|
23
|
+
methods the client-surface agreement counts as typed commands: none of these
|
|
24
|
+
methods is a command of the service.
|
|
25
|
+
|
|
26
|
+
NETWORK LAW. This module imports nothing from the adapter framework and adds
|
|
27
|
+
no transport of its own; everything goes through the invocation seam and the
|
|
28
|
+
privately held framework client that ``ailogger_client.invocation`` guards.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
from collections.abc import Mapping
|
|
34
|
+
from typing import Any
|
|
35
|
+
|
|
36
|
+
from .errors import translated_failures
|
|
37
|
+
from .queued_methods import find_job_id
|
|
38
|
+
|
|
39
|
+
__all__ = ["ObservationMixin"]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ObservationMixin:
|
|
43
|
+
"""
|
|
44
|
+
Mixin providing the probe, help, health-route and answer-mode methods.
|
|
45
|
+
|
|
46
|
+
This mixin expects to be composed with InvocationMixin (``_invoke``,
|
|
47
|
+
``_invoke_unconverted`` and the private framework client) and QueuedMixin
|
|
48
|
+
(``_await_queued_result``).
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
_client: Any
|
|
52
|
+
|
|
53
|
+
async def _invoke(
|
|
54
|
+
self,
|
|
55
|
+
command: str,
|
|
56
|
+
params: dict[str, Any] | None = None,
|
|
57
|
+
*,
|
|
58
|
+
wait_for_result: bool = True,
|
|
59
|
+
) -> Any:
|
|
60
|
+
"""Private invocation entry point, provided by InvocationMixin."""
|
|
61
|
+
raise NotImplementedError # Provided by InvocationMixin
|
|
62
|
+
|
|
63
|
+
async def _invoke_unconverted(
|
|
64
|
+
self,
|
|
65
|
+
command: str,
|
|
66
|
+
params: dict[str, Any] | None,
|
|
67
|
+
wait_for_result: bool,
|
|
68
|
+
) -> Any:
|
|
69
|
+
"""Unconverted invocation, provided by InvocationMixin."""
|
|
70
|
+
raise NotImplementedError # Provided by InvocationMixin
|
|
71
|
+
|
|
72
|
+
async def _await_queued_result(
|
|
73
|
+
self,
|
|
74
|
+
job_id: str,
|
|
75
|
+
*,
|
|
76
|
+
command: str,
|
|
77
|
+
timeout: float | None = None,
|
|
78
|
+
) -> list[Any]:
|
|
79
|
+
"""Queued completion, provided by QueuedMixin."""
|
|
80
|
+
raise NotImplementedError # Provided by QueuedMixin
|
|
81
|
+
|
|
82
|
+
async def probe_command(
|
|
83
|
+
self, command: str, params: Mapping[str, Any] | None
|
|
84
|
+
) -> Any:
|
|
85
|
+
"""Send EXACTLY these parameters to a command and return its answer.
|
|
86
|
+
|
|
87
|
+
The negative-case probe. The parameter map is sent as given: a missing
|
|
88
|
+
required parameter, an unknown one, an explicit ``None`` and a value of
|
|
89
|
+
the wrong type all go through unchanged - nothing is added, removed,
|
|
90
|
+
defaulted or validated by the client, so the answer is the service's
|
|
91
|
+
own verdict on exactly this request. ``None`` sends no parameters.
|
|
92
|
+
|
|
93
|
+
The outcome is what a typed method's would be: the command's answer in
|
|
94
|
+
the service's ``{success, data}`` form, or a typed error of this
|
|
95
|
+
client (``ServiceRefusalError`` carrying the service's stable code for
|
|
96
|
+
a refusal, ``ServiceUnreachableError``, ...). A command the service
|
|
97
|
+
always queues is waited for as its typed method waits: the answer is
|
|
98
|
+
then ``{"success": True, "data": <the job's result array>}``.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
command: The name of the command to send.
|
|
102
|
+
params: The parameter map to send exactly, or ``None`` for none.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
The command's answer.
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
ServiceRefusalError: When the service refuses the request.
|
|
109
|
+
AiLoggerClientError: For every other failure, translated.
|
|
110
|
+
"""
|
|
111
|
+
sent = None if params is None else dict(params)
|
|
112
|
+
answer = await self._invoke(command, sent, wait_for_result=False)
|
|
113
|
+
job_id = find_job_id(answer)
|
|
114
|
+
if job_id is None:
|
|
115
|
+
return answer
|
|
116
|
+
result = await self._await_queued_result(job_id, command=command)
|
|
117
|
+
return {"success": True, "data": result}
|
|
118
|
+
|
|
119
|
+
async def help(
|
|
120
|
+
self,
|
|
121
|
+
cmdname: str | None = None,
|
|
122
|
+
limit: int | None = None,
|
|
123
|
+
offset: int | None = None,
|
|
124
|
+
) -> Any:
|
|
125
|
+
"""The service's ``help`` command: the catalogue, or one command.
|
|
126
|
+
|
|
127
|
+
Only the arguments that are not ``None`` are sent. The service
|
|
128
|
+
validates them (its help command names ``cmdname``); this method adds
|
|
129
|
+
no check of its own.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
cmdname: A command to describe in detail; omitted lists them all.
|
|
133
|
+
limit: How many entries to return, when the service pages them.
|
|
134
|
+
offset: Where the page starts, when the service pages them.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
The answer of the ``help`` command.
|
|
138
|
+
"""
|
|
139
|
+
params: dict[str, Any] = {}
|
|
140
|
+
if cmdname is not None:
|
|
141
|
+
params["cmdname"] = cmdname
|
|
142
|
+
if limit is not None:
|
|
143
|
+
params["limit"] = limit
|
|
144
|
+
if offset is not None:
|
|
145
|
+
params["offset"] = offset
|
|
146
|
+
return await self._invoke("help", params)
|
|
147
|
+
|
|
148
|
+
async def health_route(self) -> dict[str, Any]:
|
|
149
|
+
"""The service's ``GET /health`` route, answered as its bare report.
|
|
150
|
+
|
|
151
|
+
The one declared exception to "everything is a command": the health
|
|
152
|
+
route is not a command of the service, and its answer is the report
|
|
153
|
+
itself - not a command envelope.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
The health report mapping, as the route answers it.
|
|
157
|
+
|
|
158
|
+
Raises:
|
|
159
|
+
ServiceUnreachableError: When the route cannot be reached.
|
|
160
|
+
AiLoggerClientError: For every other failure, translated.
|
|
161
|
+
"""
|
|
162
|
+
with translated_failures():
|
|
163
|
+
report: dict[str, Any] = await self._client.health()
|
|
164
|
+
return report
|
|
165
|
+
|
|
166
|
+
async def command_answer_mode(
|
|
167
|
+
self, command: str, params: Mapping[str, Any] | None = None
|
|
168
|
+
) -> dict[str, Any]:
|
|
169
|
+
"""Report how the service answers a command: inline or queued.
|
|
170
|
+
|
|
171
|
+
Sends the command once with the given parameters and classifies the
|
|
172
|
+
answer: a job identity means ``queued``, anything else means
|
|
173
|
+
``direct``. A queued command is never waited for - its job is left to
|
|
174
|
+
run. A direct command is run, and an inline refusal of it still answers
|
|
175
|
+
``direct``: the service answered on the spot, which is what is being
|
|
176
|
+
observed. A request the service declined before queueing, and every
|
|
177
|
+
transport failure, arrives as a typed error.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
command: The command to observe.
|
|
181
|
+
params: The parameters to send, or ``None`` for none.
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
Exactly ``{"answer_mode": "direct" | "queued", "job_id": str | None}``.
|
|
185
|
+
|
|
186
|
+
Raises:
|
|
187
|
+
AiLoggerClientError: When the request fails before the service
|
|
188
|
+
answers it.
|
|
189
|
+
"""
|
|
190
|
+
sent = None if params is None else dict(params)
|
|
191
|
+
answer = await self._invoke_unconverted(command, sent, False)
|
|
192
|
+
job_id = find_job_id(answer)
|
|
193
|
+
if job_id is None:
|
|
194
|
+
return {"answer_mode": "direct", "job_id": None}
|
|
195
|
+
return {"answer_mode": "queued", "job_id": job_id}
|
ailogger_client/py.typed
ADDED
|
File without changes
|