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.
@@ -0,0 +1,236 @@
1
+ """
2
+ Typed methods for session lifecycle commands.
3
+
4
+ This module carries the single mixin class whose typed methods let a caller
5
+ act on the existence and the state of a dialogue session. Each method names
6
+ its arguments for what the corresponding command's parameter schema declares,
7
+ so a caller opens, closes, reopens and deletes a session in the service's own
8
+ terms and never assembles a request envelope, never names a command as a free
9
+ string and never guesses 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 opening command accepts session_id from the caller and never generates one
18
+ - this client never mints a session identifier, never supplies a default for
19
+ one and calls no identifier-generating function on any path reaching the open
20
+ method. Required and optional parameters are mirrored exactly from the service
21
+ command declarations read in the steps that established them (G-009/T-002 for
22
+ the first three commands and G-009/T-007 for the delete command).
23
+
24
+ Each method carries its arguments to the invocation seam of G-012/T-002/A-001
25
+ and returns what the seam returns. Performance performs no validation of a
26
+ parameter value beyond what the language's own typing expresses: the command's
27
+ declared schema is validated at the service.
28
+
29
+ NETWORK LAW. This module imports nothing from the adapter framework's
30
+ implementation and adds no transport of its own. All network interaction goes
31
+ through the invocation seam and thence through the privately held framework
32
+ client instance, and through nothing else.
33
+
34
+ BOUNDARIES. This module declares no generic call, no completion default, no
35
+ non-blocking path or any job-observing call (all owned by earlier steps).
36
+ Every declaration in this file is fully annotated for the pipeline's named
37
+ strict type check, and the module satisfies its named lint check.
38
+ """
39
+
40
+ from __future__ import annotations
41
+
42
+ from typing import Any
43
+
44
+ __all__ = ["SessionMixin"]
45
+
46
+
47
+ class SessionMixin:
48
+ """Mixin providing typed methods for session lifecycle commands.
49
+
50
+ Declares the four typed methods by which a caller acts on the existence
51
+ and the state of a dialogue session: opening, closing, reopening and
52
+ deleting a session in the service's own terms.
53
+
54
+ This mixin expects to be composed with InvocationMixin, which provides
55
+ the _invoke method.
56
+ """
57
+
58
+ async def _invoke(
59
+ self,
60
+ command: str,
61
+ params: dict[str, Any] | None = None,
62
+ *,
63
+ wait_for_result: bool = True,
64
+ ) -> Any:
65
+ """Private invocation entry point.
66
+
67
+ This method is provided by InvocationMixin when this mixin is composed
68
+ into a class. This stub is declared here for type checking purposes
69
+ only.
70
+
71
+ Args:
72
+ command: The name of the command to execute.
73
+ params: An optional mapping of command parameters.
74
+ wait_for_result: When True (default), wait for queued commands to
75
+ completion and return the finished result.
76
+
77
+ Returns:
78
+ The command result.
79
+ """
80
+ raise NotImplementedError # Provided by InvocationMixin
81
+
82
+ async def open_session( # noqa: PLR0913, PLR0917
83
+ self,
84
+ session_id: str,
85
+ project_id: str,
86
+ dialogue_kind: tuple[str, str],
87
+ participants: list[str],
88
+ title: str | None = None,
89
+ metadata: dict[str, Any] | None = None,
90
+ ) -> Any:
91
+ """Open a dialogue session under a caller-supplied identifier.
92
+
93
+ Opens a dialogue session under the session identifier the caller
94
+ supplies. That identifier originates in the code analysis server: the
95
+ caller obtains it there through that server's published client and
96
+ hands it to this method, which carries it unchanged to the service.
97
+ This client never generates a session identifier and never supplies a
98
+ default for one.
99
+
100
+ Beside the identifier the caller states the owning project, the
101
+ dialogue kind as an ordered pair of participant kinds, the participants
102
+ of the session, and optionally a title and free-form metadata; the
103
+ attributes the caller does not supply - the creation time and the
104
+ initial lifecycle state - are established by the creation itself as the
105
+ session model defines them.
106
+
107
+ The owning project identifier is ordinary dialogue data recorded as
108
+ given: this method validates it against no other service, looks up no
109
+ project name for it, and treats nothing outside the service as
110
+ authority over it.
111
+
112
+ Args:
113
+ session_id: Identifier of the session the dialogue is opened under,
114
+ issued by the code analysis server and obtained by the caller
115
+ through that server's published client. This service never
116
+ generates one and never defaults one, and records the identifier
117
+ the caller supplies unchanged.
118
+ project_id: Identifier of the project this session belongs to,
119
+ required and non-empty. Ordinary dialogue data recorded exactly
120
+ as given: no other service is consulted to validate it, to look
121
+ up a name for it or to act as authority over it.
122
+ dialogue_kind: Tuple[str, str] with exactly two participant kinds
123
+ - the sender's kind and the receiver's kind in that order. Which
124
+ two of the four participant kinds (human, model, tool, system)
125
+ the sender and receiver carry that this session runs between.
126
+ The two may repeat, so model-to-model and system-to-system are
127
+ expressible. The service never infers this from messages that
128
+ arrive afterwards.
129
+ participants: The concrete participants of this session, required
130
+ and non-empty. Nothing else populates them, so this is the only
131
+ point at which a session becomes findable by participant.
132
+ title: Human-readable title of the session. Optional and
133
+ legitimately absent: a session opened without one carries none.
134
+ metadata: Free-form metadata to record on the session. Optional and
135
+ legitimately absent or empty. Carried exactly as given and never
136
+ interpreted, inspected or rewritten by the service.
137
+
138
+ Returns:
139
+ The identity of the session together with its resulting lifecycle
140
+ state, and nothing beyond those two.
141
+ """
142
+ params: dict[str, Any] = {
143
+ "session_id": session_id,
144
+ "project_id": project_id,
145
+ "dialogue_kind": list(dialogue_kind),
146
+ "participants": participants,
147
+ }
148
+ if title is not None:
149
+ params["title"] = title
150
+ if metadata is not None:
151
+ params["metadata"] = metadata
152
+ return await self._invoke("open_session", params)
153
+
154
+ async def close_session(self, session_id: str) -> Any:
155
+ """Close an open dialogue session, removing nothing.
156
+
157
+ Moves the addressed dialogue session's lifecycle state from open to
158
+ closed. That is the entire effect: closing is a state transition and
159
+ nothing more. It removes no session, no message, no declared relation
160
+ and no vector record, and it alters no attribute of the session other
161
+ than its lifecycle state.
162
+
163
+ Args:
164
+ session_id: Identity of the existing dialogue session to close.
165
+ Required: this method addresses a session that was already
166
+ opened and never brings one into being.
167
+
168
+ Returns:
169
+ The identity of the session together with its resulting lifecycle
170
+ state, which is closed, and nothing beyond those two.
171
+ """
172
+ return await self._invoke("close_session", {"session_id": session_id})
173
+
174
+ async def reopen_session(self, session_id: str) -> Any:
175
+ """Return a closed dialogue session to the open state.
176
+
177
+ Moves the addressed dialogue session's lifecycle state from closed back
178
+ to open, because the closed state is not final: a dialogue that was
179
+ closed is not a dead end, and mutability returns with the open state.
180
+ That is the entire effect - reopening is a state transition and nothing
181
+ more. It changes no other attribute of the session and touches none of
182
+ the messages recorded in it.
183
+
184
+ Args:
185
+ session_id: Identity of the existing dialogue session to reopen.
186
+ Required: this method addresses a session that was already
187
+ opened and later closed, and never brings one into being.
188
+
189
+ Returns:
190
+ The identity of the session together with its resulting lifecycle
191
+ state, which is open, and nothing beyond those two.
192
+ """
193
+ return await self._invoke("reopen_session", {"session_id": session_id})
194
+
195
+ async def session_mark_removed(
196
+ self, session_id: str, removed_at: str | None = None
197
+ ) -> Any:
198
+ """Record terminal source removal, retaining all stored dialogue data."""
199
+ params: dict[str, Any] = {"session_id": session_id}
200
+ if removed_at is not None:
201
+ params["removed_at"] = removed_at
202
+ return await self._invoke("session_mark_removed", params)
203
+
204
+ async def delete_session(self, session_id: str) -> Any:
205
+ """Delete one dialogue session together with everything derived from it.
206
+
207
+ Removes the addressed dialogue session together with everything
208
+ recorded under it: its messages, the relations declared on those
209
+ messages, and the vector records built from them. The removal is one
210
+ whole - the caller names the session and nothing else, and cannot ask
211
+ for a partial deletion.
212
+
213
+ This deletion is caller-triggered and immediate: it happens because
214
+ this method was called and takes effect within this request, the answer
215
+ being returned only after the removal has actually gone through.
216
+
217
+ Deleting is not a stronger form of closing, and closing is not a soft
218
+ form of deleting. Closing a session moves its lifecycle state and
219
+ removes nothing: the whole dialogue stays recorded and readable. This
220
+ method ends the existence of the session and of everything recorded
221
+ under it; a deleted session cannot be recovered by any method of the
222
+ client, and no method of this surface undoes what this one does.
223
+
224
+ Args:
225
+ session_id: Identity of the dialogue session to delete, as the
226
+ canonical text of the identifier the session was recorded
227
+ under. The session and everything derived from it are removed
228
+ as one whole.
229
+
230
+ Returns:
231
+ The identity of the session that was deleted, together with the
232
+ statement that its messages, the relations declared on those
233
+ messages and the vector records built from them were removed with
234
+ it.
235
+ """
236
+ return await self._invoke("delete_session", {"session_id": session_id})
@@ -0,0 +1,108 @@
1
+ Metadata-Version: 2.4
2
+ Name: ailogger-client-dev
3
+ Version: 0.1.51
4
+ Summary: Published client of the ai-logger dialogue service - a typed wrapper over the mcp-proxy-adapter framework client
5
+ Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
6
+ License: Proprietary
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: mcp-proxy-adapter>=8.11.3
10
+
11
+ # ai-logger Client
12
+
13
+ This package is the client for the ai-logger dialogue service. It is installed independently from the service itself through the package index.
14
+
15
+ ## What This Distribution Is
16
+
17
+ The ai-logger client is a published wrapper over the adapter framework's own client, carrying the dialogue vocabulary of the ai-logger service. It depends on the adapter framework and never on the server package, so installing it brings in no part of the service's runtime, storage, or configuration. Version tracks the service version and the client and server are released in step; to work with a particular server instance, select the client version matching that server.
18
+
19
+ Install directly from the package index as an ordinary Python package. Never install from a source checkout, repository URL, or local path.
20
+
21
+ ## What It Is Made Of
22
+
23
+ This client wraps the framework's JsonRpcClient privately, holding it as a single private attribute that no caller can reach. Transport, connection handling, retries, request serialization, response parsing, job tracking, queue operations, schema validation, and file transfer all belong to the framework client. This distribution adds only the dialogue vocabulary of the ai-logger service: typed methods named for the service's own commands, and nothing else.
24
+
25
+ The client carries no transport of its own and imports no additional network library beyond the adapter framework. Auditing the package dependencies finds the adapter framework and nothing else network-bearing.
26
+
27
+ ## How It Is Configured
28
+
29
+ The client is constructed from a named section of the configuration document of the process that uses it, never from the service's own configuration. The section must be named `ailogger_client` and must carry the same sectioned shape every fleet client uses:
30
+
31
+ - A top-level `protocol` (http, https, or mtls)
32
+ - A `server` sub-section with `host` and `port`
33
+ - Optional `client` and `ssl` sub-sections carrying certificate material
34
+ - An optional `auth` sub-section carrying a token and the HTTP header name it travels in
35
+
36
+ Construct the client by passing the section mapping to AiLoggerClient, or use AiLoggerClient.from_document_path() to load configuration from a JSON file.
37
+
38
+ ## What Happens When a Call Fails
39
+
40
+ Every failure reaches the caller as a typed error of this client, never as an exception of the framework or transport. Catch AiLoggerClientError to catch everything the client can raise.
41
+
42
+ A transport that failed to reach the service arrives as ServiceUnreachableError. A service that was reached but answered in refusal arrives as ServiceRefusalError, which carries three attributes: a `code` as a plain string from the service's own domain error vocabulary, a `statement` for the human-readable message, and the `command` name. A refusal reaches the caller as ServiceRefusalError whichever route it arrived by, whether as a raised request failure or as a refusing answer; ServiceUnreachableError means the service could not be reached at all. Compare the code to distinguish refusals, not the prose. No host, connection, retry, status code, or transport envelope is ever visible.
43
+
44
+ ## How a Command Is Called
45
+
46
+ The client declares one typed method per published command of the service, plus a generic `call()` method for commands not yet named, so new commands published after the client release stay reachable without a new client release.
47
+
48
+ When a command is queued by the service, the client polls it to completion by default and returns the finished result directly. To receive the job identity instead and poll manually, pass `wait_for_result=False` to the command method or to `call()`. Job observation methods `get_job_status()` and `get_job_messages()` let you track progress; `cancel_command()` cancels a queued job.
49
+
50
+ ## A Usage Flow
51
+
52
+ ```python
53
+ import json
54
+ from ailogger_client import AiLoggerClient
55
+
56
+ # Load configuration and construct the client
57
+ async with AiLoggerClient.from_document_path("config.json") as client:
58
+ # Open a dialogue session
59
+ session = await client.open_session(
60
+ session_id="session-uuid",
61
+ project_id="project-id",
62
+ dialogue_kind=("human", "model"),
63
+ participants=["user", "assistant"]
64
+ )
65
+
66
+ # Append a message with a declared relation
67
+ msg = await client.append_message(
68
+ session_id="session-uuid",
69
+ text="What is the answer?",
70
+ sender_kind="human",
71
+ sender_identity="user",
72
+ receiver_kind="model",
73
+ receiver_identity="assistant",
74
+ relations=[{"to_message_id": "earlier-msg-id", "kind": "reply"}]
75
+ )
76
+
77
+ # Read a window of messages
78
+ window = await client.messages_read(
79
+ session_id="session-uuid",
80
+ start_sequence_number=0
81
+ )
82
+
83
+ # Atomically hide or restore recorded messages
84
+ marked = await client.mark_messages_for_deletion(
85
+ message_ids=["message-uuid"],
86
+ marked_for_deletion=True,
87
+ )
88
+
89
+ # Soft-delete one message, or pass mode="hard" for an unreferenced row
90
+ deleted = await client.message_delete(
91
+ message_id="message-uuid",
92
+ mode="soft",
93
+ )
94
+
95
+ # Ask for the working set of recent and closest messages
96
+ working = await client.working_set(
97
+ session_id="session-uuid",
98
+ anchor_text="What does it mean?"
99
+ )
100
+ ```
101
+
102
+ ## What Is Not Implemented
103
+
104
+ Dialogue import from a file and export to a file are the only reasons this client would need file transfer. These methods are deliberately not designed yet: calling `import_dialogue()` or `export_dialogue()` raises NotImplementedError, deliberately and not by omission, and the gap is tracked as an open item of the project. Transfer will be built on the framework client's own transfer surface when import and export are designed.
105
+
106
+ ## Where the Agreement Is Proven
107
+
108
+ This distribution ships separately from the service, so nothing shared keeps their command sets in step. The project's named pipeline check `client-surface-agreement` proves the agreement by comparing the shipped client's command methods against the surface the service registers as live, distinct from the pipeline's command-set agreement check which compares the service's own contained, declared and published sets. That check is authored under the verification work of G-010/T-004/A-008. This is the reference the distribution owes; the check itself is not explained here.
@@ -0,0 +1,18 @@
1
+ ailogger_client/__init__.py,sha256=ah8bHZfu9AenulLeUxuhu3ukrp97mPGEQoehRvsr8WA,3752
2
+ ailogger_client/client.py,sha256=AHGwpG3ubyurIJA7HT9JWz-dhxDQiCI6AFEnlw03cto,8976
3
+ ailogger_client/config.py,sha256=ZrpigmlIkpFCtB836EyNjZKzXRx716z3Xv3bUDbOAhk,14001
4
+ ailogger_client/errors.py,sha256=Ay1v3qoQBHfxJXQjB7v2m5wF37BxFPeHnFSVsXgTNoI,25487
5
+ ailogger_client/file_transfer.py,sha256=1faCVYmoJpPebffWu5bCHSH67NyUbhLNW3ENJ7Y0k8M,5290
6
+ ailogger_client/invocation.py,sha256=e6h7qFjvMdRq9gykoVjkY97nW8CnRTBYIvyLHn4sf2M,23458
7
+ ailogger_client/message_methods.py,sha256=ZGty6PoVhfi_jUzSZGJgpEW2iyixnG8NapuCqpRZDZA,16261
8
+ ailogger_client/observation_methods.py,sha256=SPeKPatsXx_spQip3xK4hde57bK2_3XItLLFeWVsZhY,7868
9
+ ailogger_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ ailogger_client/queued_methods.py,sha256=FcShGJBUjQvwMM55ovjZ2QvQTRuUMjKYqIH7_u5KpNo,9441
11
+ ailogger_client/read_methods.py,sha256=j24svGlxW3cl-P2IfuKp7yFOee2VwA2PbUZrHbYJ1Aw,30150
12
+ ailogger_client/retrieval_methods.py,sha256=koirEpslvTzxPjeMzZ_YisyVVlod2WbgeSaIbfcUvwo,17509
13
+ ailogger_client/service_methods.py,sha256=i8zXaLcmRArCDlo5ieeXZMM3GEDoKzEcGuW8FsCI09k,6967
14
+ ailogger_client/session_methods.py,sha256=GXbm_i2vC3S0AHPguqkkUx19UqvN1mRkL0vadMukAe0,11365
15
+ ailogger_client_dev-0.1.51.dist-info/METADATA,sha256=b6vuawdLQScl8YrkXE62tBkfEgmYBA7XPAkivwyoAZs,6707
16
+ ailogger_client_dev-0.1.51.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
17
+ ailogger_client_dev-0.1.51.dist-info/top_level.txt,sha256=6ECXCYY3140gfyNGX4QFEg1TssYQelfiBFJ-q-OvG0Q,16
18
+ ailogger_client_dev-0.1.51.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.2.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ ailogger_client