ailogger-client 0.1.1__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.
@@ -0,0 +1,96 @@
1
+ Metadata-Version: 2.4
2
+ Name: ailogger-client
3
+ Version: 0.1.1
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.10.44
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
+ # Ask for the working set of recent and closest messages
84
+ working = await client.working_set(
85
+ session_id="session-uuid",
86
+ anchor_text="What does it mean?"
87
+ )
88
+ ```
89
+
90
+ ## What Is Not Implemented
91
+
92
+ 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.
93
+
94
+ ## Where the Agreement Is Proven
95
+
96
+ 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,86 @@
1
+ # ai-logger Client
2
+
3
+ This package is the client for the ai-logger dialogue service. It is installed independently from the service itself through the package index.
4
+
5
+ ## What This Distribution Is
6
+
7
+ 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.
8
+
9
+ Install directly from the package index as an ordinary Python package. Never install from a source checkout, repository URL, or local path.
10
+
11
+ ## What It Is Made Of
12
+
13
+ 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.
14
+
15
+ 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.
16
+
17
+ ## How It Is Configured
18
+
19
+ 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:
20
+
21
+ - A top-level `protocol` (http, https, or mtls)
22
+ - A `server` sub-section with `host` and `port`
23
+ - Optional `client` and `ssl` sub-sections carrying certificate material
24
+ - An optional `auth` sub-section carrying a token and the HTTP header name it travels in
25
+
26
+ Construct the client by passing the section mapping to AiLoggerClient, or use AiLoggerClient.from_document_path() to load configuration from a JSON file.
27
+
28
+ ## What Happens When a Call Fails
29
+
30
+ 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.
31
+
32
+ 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.
33
+
34
+ ## How a Command Is Called
35
+
36
+ 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.
37
+
38
+ 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.
39
+
40
+ ## A Usage Flow
41
+
42
+ ```python
43
+ import json
44
+ from ailogger_client import AiLoggerClient
45
+
46
+ # Load configuration and construct the client
47
+ async with AiLoggerClient.from_document_path("config.json") as client:
48
+ # Open a dialogue session
49
+ session = await client.open_session(
50
+ session_id="session-uuid",
51
+ project_id="project-id",
52
+ dialogue_kind=("human", "model"),
53
+ participants=["user", "assistant"]
54
+ )
55
+
56
+ # Append a message with a declared relation
57
+ msg = await client.append_message(
58
+ session_id="session-uuid",
59
+ text="What is the answer?",
60
+ sender_kind="human",
61
+ sender_identity="user",
62
+ receiver_kind="model",
63
+ receiver_identity="assistant",
64
+ relations=[{"to_message_id": "earlier-msg-id", "kind": "reply"}]
65
+ )
66
+
67
+ # Read a window of messages
68
+ window = await client.messages_read(
69
+ session_id="session-uuid",
70
+ start_sequence_number=0
71
+ )
72
+
73
+ # Ask for the working set of recent and closest messages
74
+ working = await client.working_set(
75
+ session_id="session-uuid",
76
+ anchor_text="What does it mean?"
77
+ )
78
+ ```
79
+
80
+ ## What Is Not Implemented
81
+
82
+ 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.
83
+
84
+ ## Where the Agreement Is Proven
85
+
86
+ 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,93 @@
1
+ """
2
+ ailogger service client.
3
+
4
+ This package provides the client for the ai-logger service, distributed and
5
+ installed independently of the service itself. It is a wrapper around the
6
+ adapter framework's ``JsonRpcClient`` (from ``mcp_proxy_adapter.client.jsonrpc_client``)
7
+ and adds only the dialogue vocabulary of the ai-logger service: the command
8
+ methods, the generic call surface and the typed error vocabulary.
9
+
10
+ The wrapper is constructed from a named section of the consuming process's
11
+ configuration document - the same sectioned shape every fleet client in the
12
+ ai-logger fleet uses: a top-level ``protocol``, a ``server`` sub-section
13
+ carrying host and port, a ``client`` sub-section that may carry ssl material
14
+ overriding the server's own, a top-level ``ssl`` sub-section used as a
15
+ fallback, and an ``auth`` sub-section carrying a token and the header it
16
+ travels in.
17
+
18
+ Transport, request serialization, response parsing, retries, job tracking,
19
+ queue operations, file transfer with integrity checking and connection
20
+ management all belong to the adapter framework's client: this package wraps
21
+ that client and adds only the dialogue vocabulary on top.
22
+
23
+ A-009 SECTION: Published Surface
24
+
25
+ The package's public surface is declared explicitly by ``__all__`` and holds
26
+ the client class, the construction entry points, and the complete typed error
27
+ vocabulary. A caller that wants to catch everything this client can raise
28
+ catches the single base kind (``AiLoggerClientError``), and every specific kind
29
+ is reachable from the package root. This closes the vocabulary: a consumer
30
+ never reaches into a private module path to catch a typed error of this client.
31
+
32
+ Deliberately unpublished from the package root (but still importable via
33
+ submodule paths like ``ailogger_client.errors`` or ``ailogger_client.client``):
34
+ the adapter framework's client class and its entire transport and exception
35
+ hierarchy; the seven mixin classes (InvocationMixin, SessionMixin,
36
+ MessageMixin, ReadMixin, RetrievalMixin, ServiceMixin, FileTransferMixin)
37
+ that compose the client; the translation table and its supporting machinery
38
+ (``translate_framework_failure``, ``translated_failures``, ``refusal_from_answer``);
39
+ and the private attribute holding the framework client. This design keeps
40
+ this package a wrapper over the framework rather than a second way of
41
+ importing it, and ensures that transport details remain behind the client's
42
+ boundary.
43
+ """
44
+
45
+ from __future__ import annotations
46
+
47
+ from .client import AiLoggerClient
48
+ from .config import (
49
+ ConfigError,
50
+ load_client_section,
51
+ load_config_kwargs,
52
+ load_config_kwargs_from_document,
53
+ )
54
+ from .errors import (
55
+ AiLoggerClientError,
56
+ CompletionFailedError,
57
+ CompletionTimeoutError,
58
+ DialogueFileTransferError,
59
+ InadmissibleArgumentValueError,
60
+ MissingArgumentError,
61
+ QueuedCompletionError,
62
+ QueuedProtocolError,
63
+ RequestRejectedError,
64
+ ResultUnavailableError,
65
+ SchemaDisagreementError,
66
+ ServiceRefusalError,
67
+ ServiceUnreachableError,
68
+ UnknownCommandError,
69
+ WrongArgumentTypeError,
70
+ )
71
+
72
+ __all__ = [
73
+ "AiLoggerClient",
74
+ "AiLoggerClientError",
75
+ "CompletionFailedError",
76
+ "CompletionTimeoutError",
77
+ "ConfigError",
78
+ "DialogueFileTransferError",
79
+ "InadmissibleArgumentValueError",
80
+ "MissingArgumentError",
81
+ "QueuedCompletionError",
82
+ "QueuedProtocolError",
83
+ "RequestRejectedError",
84
+ "ResultUnavailableError",
85
+ "SchemaDisagreementError",
86
+ "ServiceRefusalError",
87
+ "ServiceUnreachableError",
88
+ "UnknownCommandError",
89
+ "WrongArgumentTypeError",
90
+ "load_client_section",
91
+ "load_config_kwargs",
92
+ "load_config_kwargs_from_document",
93
+ ]
@@ -0,0 +1,209 @@
1
+ """
2
+ Client for the ai-logger service.
3
+
4
+ This module declares the client class of the ai-logger service: the construction
5
+ and encapsulation boundary before any command of the surface reaches it.
6
+
7
+ CONSTRUCTION SHAPE. The client is built from its own named section of the
8
+ configuration document of the process that uses it, in the sectioned fleet shape,
9
+ by handing that section to the configuration translation of ailogger_client.config
10
+ and passing the resulting keyword arguments straight to the framework client's
11
+ constructor. Two construction routes are offered: from a section already read as
12
+ a mapping, and from a configuration document identified by path together with the
13
+ name of the section to read. Both routes end in the same single place where the
14
+ framework client is built, so there is exactly one construction path in the class.
15
+
16
+ ENCAPSULATION. The client holds an instance of JsonRpcClient from
17
+ mcp_proxy_adapter.client.jsonrpc_client on a private attribute. The held instance
18
+ leaves the object by no route: no public attribute, no property, no accessor method,
19
+ no return value, no argument default, no representation string or attribute-forwarding
20
+ hook. A caller holding this client cannot reach a connection through it. Inside the
21
+ package a single private route to the held instance exists, so the typed methods
22
+ have somewhere to call through without the attribute becoming part of the caller-facing
23
+ surface.
24
+
25
+ LIFECYCLE. Whatever opening and closing the client needs is delegated to the held
26
+ instance. The client supports use as an asynchronous context manager, so entering
27
+ and leaving a client is entering and leaving what the framework client already
28
+ provides. Closing twice is harmless.
29
+
30
+ CALLER SURFACE. No host, no connection, no retry, no status code and no transport
31
+ envelope appears in any argument, in any return value or in any readable attribute
32
+ of this class. The caller works only in the terms the service itself publishes.
33
+ """
34
+
35
+ from __future__ import annotations
36
+
37
+ import json
38
+ from collections.abc import Mapping
39
+ from pathlib import Path
40
+ from typing import Any
41
+
42
+ from mcp_proxy_adapter.client.jsonrpc_client import JsonRpcClient
43
+
44
+ from .config import (
45
+ ConfigError,
46
+ load_client_section,
47
+ load_config_kwargs,
48
+ )
49
+ from .file_transfer import FileTransferMixin
50
+ from .invocation import InvocationMixin
51
+ from .message_methods import MessageMixin
52
+ from .read_methods import ReadMixin
53
+ from .retrieval_methods import RetrievalMixin
54
+ from .service_methods import ServiceMixin
55
+ from .session_methods import SessionMixin
56
+
57
+ __all__ = ["AiLoggerClient"]
58
+
59
+
60
+ class AiLoggerClient(
61
+ InvocationMixin,
62
+ SessionMixin,
63
+ MessageMixin,
64
+ ReadMixin,
65
+ RetrievalMixin,
66
+ ServiceMixin,
67
+ FileTransferMixin,
68
+ ):
69
+ """
70
+ Wrapper over the installed adapter framework's JsonRpcClient, adding the
71
+ dialogue vocabulary of the ai-logger service.
72
+
73
+ The client is constructed from the consuming process's configuration document,
74
+ in the sectioned fleet shape. It holds an instance of JsonRpcClient privately,
75
+ delegating all transport, connection, retry and job tracking to the framework
76
+ client.
77
+
78
+ The client supports use as an asynchronous context manager. Lifecycle operations
79
+ (enter, exit, close) are delegated to the held JsonRpcClient instance.
80
+
81
+ COMPOSITION (G-012/T-003/A-008). The client is assembled from seven mixin
82
+ classes, each contributing a set of typed methods to the caller-facing surface:
83
+ InvocationMixin provides the private invocation seam and the generic call, plus
84
+ job observation; SessionMixin provides session lifecycle commands; MessageMixin
85
+ provides message append; ReadMixin provides the five session reads and session
86
+ listing; RetrievalMixin provides working-set assembly and similarity search;
87
+ ServiceMixin provides info and health; FileTransferMixin declares dialogue
88
+ import and export (not yet designed). Every caller-facing method reaches the
89
+ held JsonRpcClient through the single private _invoke entry point that
90
+ InvocationMixin establishes, so there is one seam and one route. The composition
91
+ is order-independent for callers: all typed methods are reachable on an instance
92
+ regardless of the base order, because each mixin declares disjoint public names.
93
+ """
94
+
95
+ def __init__(
96
+ self,
97
+ section: Mapping[str, Any],
98
+ base_dir: str | None = None,
99
+ ) -> None:
100
+ """
101
+ Construct the client from a configuration section.
102
+
103
+ Args:
104
+ section: The "ailogger_client" section from the consuming process's
105
+ configuration document, as a mapping. Must carry the sectioned
106
+ fleet shape: a top-level ``protocol``, a ``server`` sub-section
107
+ with ``host`` and ``port``, optional ``client`` and ``ssl``
108
+ sub-sections, and an optional ``auth`` sub-section.
109
+
110
+ base_dir: When provided, the directory against which relative certificate
111
+ paths in the section are resolved. When absent, paths are resolved
112
+ against the calling process's current working directory.
113
+
114
+ Raises:
115
+ ConfigError: If the section is missing required keys or if any key
116
+ has the wrong type.
117
+ """
118
+ kwargs = load_config_kwargs(section, base_dir)
119
+ self._client: JsonRpcClient = JsonRpcClient(**kwargs)
120
+
121
+ @classmethod
122
+ def from_document_path(
123
+ cls,
124
+ path: str | Path,
125
+ section_name: str = "ailogger_client",
126
+ ) -> AiLoggerClient:
127
+ """
128
+ Construct the client from a configuration document file.
129
+
130
+ Reads the specified JSON configuration file, extracts the named section,
131
+ resolves relative certificate paths against the document's directory,
132
+ and constructs the client. This is a convenience route that combines
133
+ file reading, section extraction, and client construction.
134
+
135
+ Args:
136
+ path: The filesystem path to a JSON configuration document.
137
+
138
+ section_name: The name of the section to read from the document.
139
+ Defaults to "ailogger_client". This parameter exists for
140
+ flexibility but should remain at the default for the ai-logger client.
141
+
142
+ Returns:
143
+ An AiLoggerClient instance.
144
+
145
+ Raises:
146
+ ConfigError: If the document carries no section with the given name
147
+ or if the section is malformed.
148
+ FileNotFoundError: If the file does not exist.
149
+ json.JSONDecodeError: If the file is not valid JSON.
150
+ """
151
+ path_obj = Path(path).resolve()
152
+ with open(path_obj) as f:
153
+ document = json.load(f)
154
+
155
+ base_dir = str(path_obj.parent)
156
+
157
+ # Extract the section from the document
158
+ if section_name == "ailogger_client":
159
+ section = load_client_section(document)
160
+ else:
161
+ section = document.get(section_name)
162
+ if not isinstance(section, Mapping):
163
+ raise ConfigError(
164
+ f"configuration document carries no '{section_name}' "
165
+ "section for this client"
166
+ )
167
+
168
+ # Use the main constructor (single construction path)
169
+ return cls(section, base_dir=base_dir)
170
+
171
+ async def close(self) -> None:
172
+ """
173
+ Close the underlying transport and release resources.
174
+
175
+ Delegates to the held JsonRpcClient's close method. Closing twice
176
+ is harmless; the underlying client handles idempotency.
177
+ """
178
+ await self._client.close()
179
+
180
+ async def __aenter__(self) -> AiLoggerClient:
181
+ """
182
+ Async context manager entry.
183
+
184
+ Returns self for use in `async with` statements. The held JsonRpcClient
185
+ does not require explicit initialization, so no setup is performed here.
186
+ """
187
+ return self
188
+
189
+ async def __aexit__(
190
+ self,
191
+ exc_type: type[BaseException] | None,
192
+ exc_val: BaseException | None,
193
+ exc_tb: Any,
194
+ ) -> None:
195
+ """
196
+ Async context manager exit.
197
+
198
+ Closes the underlying transport and releases resources by delegating
199
+ to close().
200
+ """
201
+ await self.close()
202
+
203
+ def __repr__(self) -> str:
204
+ """
205
+ Return a string representation of the client.
206
+
207
+ Does not expose the held instance or any transport details.
208
+ """
209
+ return f"{self.__class__.__name__}()"