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.
- ailogger_client-0.1.1/PKG-INFO +96 -0
- ailogger_client-0.1.1/README.md +86 -0
- ailogger_client-0.1.1/ailogger_client/__init__.py +93 -0
- ailogger_client-0.1.1/ailogger_client/client.py +209 -0
- ailogger_client-0.1.1/ailogger_client/config.py +329 -0
- ailogger_client-0.1.1/ailogger_client/errors.py +514 -0
- ailogger_client-0.1.1/ailogger_client/file_transfer.py +110 -0
- ailogger_client-0.1.1/ailogger_client/invocation.py +409 -0
- ailogger_client-0.1.1/ailogger_client/message_methods.py +190 -0
- ailogger_client-0.1.1/ailogger_client/py.typed +0 -0
- ailogger_client-0.1.1/ailogger_client/read_methods.py +546 -0
- ailogger_client-0.1.1/ailogger_client/retrieval_methods.py +314 -0
- ailogger_client-0.1.1/ailogger_client/service_methods.py +148 -0
- ailogger_client-0.1.1/ailogger_client/session_methods.py +227 -0
- ailogger_client-0.1.1/ailogger_client.egg-info/PKG-INFO +96 -0
- ailogger_client-0.1.1/ailogger_client.egg-info/SOURCES.txt +19 -0
- ailogger_client-0.1.1/ailogger_client.egg-info/dependency_links.txt +1 -0
- ailogger_client-0.1.1/ailogger_client.egg-info/requires.txt +1 -0
- ailogger_client-0.1.1/ailogger_client.egg-info/top_level.txt +1 -0
- ailogger_client-0.1.1/pyproject.toml +36 -0
- ailogger_client-0.1.1/setup.cfg +4 -0
|
@@ -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__}()"
|