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,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 @@
|
|
|
1
|
+
ailogger_client
|