From 0d4f70adebcf280520bb3b1fdf7984a0e0917e32 Mon Sep 17 00:00:00 2001 From: Nic-dorman Date: Mon, 4 May 2026 17:58:08 +0100 Subject: [PATCH] feat(antd-py): expose new HealthStatus diagnostic fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors antd-go v0.5.0: HealthStatus now carries version, evm_network, uptime_seconds, build_commit, payment_token_address, and payment_vault_address. All default to "" / 0 so the dataclass remains constructable when talking to a pre-0.4.0 daemon that doesn't report them. Both REST clients (sync + async) and both gRPC clients (sync + async) share helpers — _health_status_from_json in _rest.py and _health_status_from_resp in _grpc.py — so the parsing only lives in one place each. Proto stubs regenerated. Mock RestClient test extended to populate + assert all 6 new fields. Added a regression test asserting that older-daemon responses (just status + network) still parse. Part of #37. Co-Authored-By: Claude Opus 4.7 (1M context) --- antd-py/README.md | 2 +- antd-py/src/antd/_grpc.py | 18 +++++++++++-- antd-py/src/antd/_proto/antd/v1/chunks_pb2.py | 4 +-- antd-py/src/antd/_proto/antd/v1/common_pb2.py | 4 +-- antd-py/src/antd/_proto/antd/v1/data_pb2.py | 4 +-- antd-py/src/antd/_proto/antd/v1/events_pb2.py | 4 +-- antd-py/src/antd/_proto/antd/v1/files_pb2.py | 4 +-- antd-py/src/antd/_proto/antd/v1/graph_pb2.py | 2 +- antd-py/src/antd/_proto/antd/v1/health_pb2.py | 12 ++++----- .../src/antd/_proto/antd/v1/health_pb2.pyi | 16 ++++++++++-- antd-py/src/antd/_rest.py | 19 ++++++++++++-- antd-py/src/antd/models.py | 14 +++++++++- antd-py/tests/test_rest_client.py | 26 ++++++++++++++++++- 13 files changed, 103 insertions(+), 26 deletions(-) diff --git a/antd-py/README.md b/antd-py/README.md index 5087f05..028c224 100644 --- a/antd-py/README.md +++ b/antd-py/README.md @@ -69,7 +69,7 @@ await aclient.close() | Method | Returns | Description | |--------|---------|-------------| -| `health()` | `HealthStatus` | Check daemon health | +| `health()` | `HealthStatus` | Check daemon health — also surfaces antd version, EVM network, uptime, build commit, and payment contract addresses (antd ≥ 0.4.0) | #### Data diff --git a/antd-py/src/antd/_grpc.py b/antd-py/src/antd/_grpc.py index 123307b..3b6c8ac 100644 --- a/antd-py/src/antd/_grpc.py +++ b/antd-py/src/antd/_grpc.py @@ -24,6 +24,20 @@ ) +def _health_status_from_resp(resp) -> HealthStatus: + """Convert a HealthCheckResponse pb message into a HealthStatus.""" + return HealthStatus( + ok=resp.status == "ok", + network=resp.network or "unknown", + version=resp.version, + evm_network=resp.evm_network, + uptime_seconds=resp.uptime_seconds, + build_commit=resp.build_commit, + payment_token_address=resp.payment_token_address, + payment_vault_address=resp.payment_vault_address, + ) + + def _file_upload_result_from_resp(resp) -> FileUploadResult: return FileUploadResult( address=resp.address, @@ -109,7 +123,7 @@ def __exit__(self, *exc): def health(self) -> HealthStatus: try: resp = self._health.Check(health_pb2.HealthCheckRequest()) - return HealthStatus(ok=resp.status == "ok", network=resp.network or "unknown") + return _health_status_from_resp(resp) except grpc.RpcError as e: if e.code() == grpc.StatusCode.UNAVAILABLE: return HealthStatus(ok=False, network="unknown") @@ -270,7 +284,7 @@ async def __aexit__(self, *exc): async def health(self) -> HealthStatus: try: resp = await self._health.Check(health_pb2.HealthCheckRequest()) - return HealthStatus(ok=resp.status == "ok", network=resp.network or "unknown") + return _health_status_from_resp(resp) except grpc.RpcError as e: if e.code() == grpc.StatusCode.UNAVAILABLE: return HealthStatus(ok=False, network="unknown") diff --git a/antd-py/src/antd/_proto/antd/v1/chunks_pb2.py b/antd-py/src/antd/_proto/antd/v1/chunks_pb2.py index 406ada2..746ad6c 100644 --- a/antd-py/src/antd/_proto/antd/v1/chunks_pb2.py +++ b/antd-py/src/antd/_proto/antd/v1/chunks_pb2.py @@ -25,14 +25,14 @@ from antd._proto.antd.v1 import common_pb2 as antd_dot_v1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61ntd/v1/chunks.proto\x12\x07\x61ntd.v1\x1a\x14\x61ntd/v1/common.proto\"\"\n\x0fGetChunkRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\" \n\x10GetChunkResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x1f\n\x0fPutChunkRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"@\n\x10PutChunkResponse\x12\x1b\n\x04\x63ost\x18\x01 \x01(\x0b\x32\r.antd.v1.Cost\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t2\x86\x01\n\x0c\x43hunkService\x12:\n\x03Get\x12\x18.antd.v1.GetChunkRequest\x1a\x19.antd.v1.GetChunkResponse\x12:\n\x03Put\x12\x18.antd.v1.PutChunkRequest\x1a\x19.antd.v1.PutChunkResponseB\n\xaa\x02\x07\x41ntd.V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61ntd/v1/chunks.proto\x12\x07\x61ntd.v1\x1a\x14\x61ntd/v1/common.proto\"\"\n\x0fGetChunkRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\" \n\x10GetChunkResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"\x1f\n\x0fPutChunkRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"@\n\x10PutChunkResponse\x12\x1b\n\x04\x63ost\x18\x01 \x01(\x0b\x32\r.antd.v1.Cost\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t2\x86\x01\n\x0c\x43hunkService\x12:\n\x03Get\x12\x18.antd.v1.GetChunkRequest\x1a\x19.antd.v1.GetChunkResponse\x12:\n\x03Put\x12\x18.antd.v1.PutChunkRequest\x1a\x19.antd.v1.PutChunkResponseBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\x07\x41ntd.V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'antd._proto.antd.v1.chunks_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'\252\002\007Antd.V1' + _globals['DESCRIPTOR']._serialized_options = b'Z8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\252\002\007Antd.V1' _globals['_GETCHUNKREQUEST']._serialized_start=55 _globals['_GETCHUNKREQUEST']._serialized_end=89 _globals['_GETCHUNKRESPONSE']._serialized_start=91 diff --git a/antd-py/src/antd/_proto/antd/v1/common_pb2.py b/antd-py/src/antd/_proto/antd/v1/common_pb2.py index dd21537..37c8c68 100644 --- a/antd-py/src/antd/_proto/antd/v1/common_pb2.py +++ b/antd-py/src/antd/_proto/antd/v1/common_pb2.py @@ -24,14 +24,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61ntd/v1/common.proto\x12\x07\x61ntd.v1\"y\n\x04\x43ost\x12\x13\n\x0b\x61tto_tokens\x18\x01 \x01(\t\x12\x11\n\tfile_size\x18\x02 \x01(\x04\x12\x13\n\x0b\x63hunk_count\x18\x03 \x01(\r\x12\x1e\n\x16\x65stimated_gas_cost_wei\x18\x04 \x01(\t\x12\x14\n\x0cpayment_mode\x18\x05 \x01(\t\"\x16\n\x07\x41\x64\x64ress\x12\x0b\n\x03hex\x18\x01 \x01(\t\"\x1d\n\x0ePublicKeyProto\x12\x0b\n\x03hex\x18\x01 \x01(\t\"\x1d\n\x0eSecretKeyProto\x12\x0b\n\x03hex\x18\x01 \x01(\t\"6\n\x0fGraphDescendant\x12\x12\n\npublic_key\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\tB\n\xaa\x02\x07\x41ntd.V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61ntd/v1/common.proto\x12\x07\x61ntd.v1\"y\n\x04\x43ost\x12\x13\n\x0b\x61tto_tokens\x18\x01 \x01(\t\x12\x11\n\tfile_size\x18\x02 \x01(\x04\x12\x13\n\x0b\x63hunk_count\x18\x03 \x01(\r\x12\x1e\n\x16\x65stimated_gas_cost_wei\x18\x04 \x01(\t\x12\x14\n\x0cpayment_mode\x18\x05 \x01(\t\"\x16\n\x07\x41\x64\x64ress\x12\x0b\n\x03hex\x18\x01 \x01(\t\"\x1d\n\x0ePublicKeyProto\x12\x0b\n\x03hex\x18\x01 \x01(\t\"\x1d\n\x0eSecretKeyProto\x12\x0b\n\x03hex\x18\x01 \x01(\t\"6\n\x0fGraphDescendant\x12\x12\n\npublic_key\x18\x01 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x02 \x01(\tBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\x07\x41ntd.V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'antd._proto.antd.v1.common_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'\252\002\007Antd.V1' + _globals['DESCRIPTOR']._serialized_options = b'Z8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\252\002\007Antd.V1' _globals['_COST']._serialized_start=33 _globals['_COST']._serialized_end=154 _globals['_ADDRESS']._serialized_start=156 diff --git a/antd-py/src/antd/_proto/antd/v1/data_pb2.py b/antd-py/src/antd/_proto/antd/v1/data_pb2.py index 308ad82..e45470c 100644 --- a/antd-py/src/antd/_proto/antd/v1/data_pb2.py +++ b/antd-py/src/antd/_proto/antd/v1/data_pb2.py @@ -25,14 +25,14 @@ from antd._proto.antd.v1 import common_pb2 as antd_dot_v1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61ntd/v1/data.proto\x12\x07\x61ntd.v1\x1a\x14\x61ntd/v1/common.proto\"\'\n\x14GetPublicDataRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\"%\n\x15GetPublicDataResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"$\n\x14PutPublicDataRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"E\n\x15PutPublicDataResponse\x12\x1b\n\x04\x63ost\x18\x01 \x01(\x0b\x32\r.antd.v1.Cost\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"*\n\x17StreamPublicDataRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\"\x19\n\tDataChunk\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\")\n\x15GetPrivateDataRequest\x12\x10\n\x08\x64\x61ta_map\x18\x01 \x01(\t\"&\n\x16GetPrivateDataResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"%\n\x15PutPrivateDataRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"G\n\x16PutPrivateDataResponse\x12\x1b\n\x04\x63ost\x18\x01 \x01(\x0b\x32\r.antd.v1.Cost\x12\x10\n\x08\x64\x61ta_map\x18\x02 \x01(\t\"\x1f\n\x0f\x44\x61taCostRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x32\xbf\x03\n\x0b\x44\x61taService\x12J\n\tGetPublic\x12\x1d.antd.v1.GetPublicDataRequest\x1a\x1e.antd.v1.GetPublicDataResponse\x12J\n\tPutPublic\x12\x1d.antd.v1.PutPublicDataRequest\x1a\x1e.antd.v1.PutPublicDataResponse\x12\x46\n\x0cStreamPublic\x12 .antd.v1.StreamPublicDataRequest\x1a\x12.antd.v1.DataChunk0\x01\x12M\n\nGetPrivate\x12\x1e.antd.v1.GetPrivateDataRequest\x1a\x1f.antd.v1.GetPrivateDataResponse\x12M\n\nPutPrivate\x12\x1e.antd.v1.PutPrivateDataRequest\x1a\x1f.antd.v1.PutPrivateDataResponse\x12\x32\n\x07GetCost\x12\x18.antd.v1.DataCostRequest\x1a\r.antd.v1.CostB\n\xaa\x02\x07\x41ntd.V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12\x61ntd/v1/data.proto\x12\x07\x61ntd.v1\x1a\x14\x61ntd/v1/common.proto\"\'\n\x14GetPublicDataRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\"%\n\x15GetPublicDataResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"$\n\x14PutPublicDataRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"E\n\x15PutPublicDataResponse\x12\x1b\n\x04\x63ost\x18\x01 \x01(\x0b\x32\r.antd.v1.Cost\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\"*\n\x17StreamPublicDataRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\"\x19\n\tDataChunk\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\")\n\x15GetPrivateDataRequest\x12\x10\n\x08\x64\x61ta_map\x18\x01 \x01(\t\"&\n\x16GetPrivateDataResponse\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"%\n\x15PutPrivateDataRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\"G\n\x16PutPrivateDataResponse\x12\x1b\n\x04\x63ost\x18\x01 \x01(\x0b\x32\r.antd.v1.Cost\x12\x10\n\x08\x64\x61ta_map\x18\x02 \x01(\t\"\x1f\n\x0f\x44\x61taCostRequest\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x32\xbf\x03\n\x0b\x44\x61taService\x12J\n\tGetPublic\x12\x1d.antd.v1.GetPublicDataRequest\x1a\x1e.antd.v1.GetPublicDataResponse\x12J\n\tPutPublic\x12\x1d.antd.v1.PutPublicDataRequest\x1a\x1e.antd.v1.PutPublicDataResponse\x12\x46\n\x0cStreamPublic\x12 .antd.v1.StreamPublicDataRequest\x1a\x12.antd.v1.DataChunk0\x01\x12M\n\nGetPrivate\x12\x1e.antd.v1.GetPrivateDataRequest\x1a\x1f.antd.v1.GetPrivateDataResponse\x12M\n\nPutPrivate\x12\x1e.antd.v1.PutPrivateDataRequest\x1a\x1f.antd.v1.PutPrivateDataResponse\x12\x32\n\x07GetCost\x12\x18.antd.v1.DataCostRequest\x1a\r.antd.v1.CostBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\x07\x41ntd.V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'antd._proto.antd.v1.data_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'\252\002\007Antd.V1' + _globals['DESCRIPTOR']._serialized_options = b'Z8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\252\002\007Antd.V1' _globals['_GETPUBLICDATAREQUEST']._serialized_start=53 _globals['_GETPUBLICDATAREQUEST']._serialized_end=92 _globals['_GETPUBLICDATARESPONSE']._serialized_start=94 diff --git a/antd-py/src/antd/_proto/antd/v1/events_pb2.py b/antd-py/src/antd/_proto/antd/v1/events_pb2.py index d815ada..62b0f8a 100644 --- a/antd-py/src/antd/_proto/antd/v1/events_pb2.py +++ b/antd-py/src/antd/_proto/antd/v1/events_pb2.py @@ -24,14 +24,14 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61ntd/v1/events.proto\x12\x07\x61ntd.v1\"\x12\n\x10SubscribeRequest\"j\n\x10\x43lientEventProto\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x14\n\x0crecords_paid\x18\x02 \x01(\x04\x12\x1c\n\x14records_already_paid\x18\x03 \x01(\x04\x12\x14\n\x0ctokens_spent\x18\x04 \x01(\t2S\n\x0c\x45ventService\x12\x43\n\tSubscribe\x12\x19.antd.v1.SubscribeRequest\x1a\x19.antd.v1.ClientEventProto0\x01\x42\n\xaa\x02\x07\x41ntd.V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61ntd/v1/events.proto\x12\x07\x61ntd.v1\"\x12\n\x10SubscribeRequest\"j\n\x10\x43lientEventProto\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12\x14\n\x0crecords_paid\x18\x02 \x01(\x04\x12\x1c\n\x14records_already_paid\x18\x03 \x01(\x04\x12\x14\n\x0ctokens_spent\x18\x04 \x01(\t2S\n\x0c\x45ventService\x12\x43\n\tSubscribe\x12\x19.antd.v1.SubscribeRequest\x1a\x19.antd.v1.ClientEventProto0\x01\x42\x44Z8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\x07\x41ntd.V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'antd._proto.antd.v1.events_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'\252\002\007Antd.V1' + _globals['DESCRIPTOR']._serialized_options = b'Z8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\252\002\007Antd.V1' _globals['_SUBSCRIBEREQUEST']._serialized_start=33 _globals['_SUBSCRIBEREQUEST']._serialized_end=51 _globals['_CLIENTEVENTPROTO']._serialized_start=53 diff --git a/antd-py/src/antd/_proto/antd/v1/files_pb2.py b/antd-py/src/antd/_proto/antd/v1/files_pb2.py index 8479035..c37d2ae 100644 --- a/antd-py/src/antd/_proto/antd/v1/files_pb2.py +++ b/antd-py/src/antd/_proto/antd/v1/files_pb2.py @@ -25,14 +25,14 @@ from antd._proto.antd.v1 import common_pb2 as antd_dot_v1_dot_common__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13\x61ntd/v1/files.proto\x12\x07\x61ntd.v1\x1a\x14\x61ntd/v1/common.proto\"!\n\x11UploadFileRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x96\x01\n\x14UploadPublicResponse\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12\x19\n\x11storage_cost_atto\x18\x03 \x01(\t\x12\x14\n\x0cgas_cost_wei\x18\x04 \x01(\t\x12\x15\n\rchunks_stored\x18\x05 \x01(\x04\x12\x19\n\x11payment_mode_used\x18\x06 \x01(\tJ\x04\x08\x01\x10\x02R\x04\x63ost\";\n\x15\x44ownloadPublicRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tdest_path\x18\x02 \x01(\t\"\x12\n\x10\x44ownloadResponse\"2\n\x0f\x46ileCostRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x11\n\tis_public\x18\x02 \x01(\x08\x32\xfb\x02\n\x0b\x46ileService\x12I\n\x0cUploadPublic\x12\x1a.antd.v1.UploadFileRequest\x1a\x1d.antd.v1.UploadPublicResponse\x12K\n\x0e\x44ownloadPublic\x12\x1e.antd.v1.DownloadPublicRequest\x1a\x19.antd.v1.DownloadResponse\x12L\n\x0f\x44irUploadPublic\x12\x1a.antd.v1.UploadFileRequest\x1a\x1d.antd.v1.UploadPublicResponse\x12N\n\x11\x44irDownloadPublic\x12\x1e.antd.v1.DownloadPublicRequest\x1a\x19.antd.v1.DownloadResponse\x12\x36\n\x0bGetFileCost\x12\x18.antd.v1.FileCostRequest\x1a\r.antd.v1.CostB\n\xaa\x02\x07\x41ntd.V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13\x61ntd/v1/files.proto\x12\x07\x61ntd.v1\x1a\x14\x61ntd/v1/common.proto\"!\n\x11UploadFileRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\"\x96\x01\n\x14UploadPublicResponse\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12\x19\n\x11storage_cost_atto\x18\x03 \x01(\t\x12\x14\n\x0cgas_cost_wei\x18\x04 \x01(\t\x12\x15\n\rchunks_stored\x18\x05 \x01(\x04\x12\x19\n\x11payment_mode_used\x18\x06 \x01(\tJ\x04\x08\x01\x10\x02R\x04\x63ost\";\n\x15\x44ownloadPublicRequest\x12\x0f\n\x07\x61\x64\x64ress\x18\x01 \x01(\t\x12\x11\n\tdest_path\x18\x02 \x01(\t\"\x12\n\x10\x44ownloadResponse\"2\n\x0f\x46ileCostRequest\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x11\n\tis_public\x18\x02 \x01(\x08\x32\xfb\x02\n\x0b\x46ileService\x12I\n\x0cUploadPublic\x12\x1a.antd.v1.UploadFileRequest\x1a\x1d.antd.v1.UploadPublicResponse\x12K\n\x0e\x44ownloadPublic\x12\x1e.antd.v1.DownloadPublicRequest\x1a\x19.antd.v1.DownloadResponse\x12L\n\x0f\x44irUploadPublic\x12\x1a.antd.v1.UploadFileRequest\x1a\x1d.antd.v1.UploadPublicResponse\x12N\n\x11\x44irDownloadPublic\x12\x1e.antd.v1.DownloadPublicRequest\x1a\x19.antd.v1.DownloadResponse\x12\x36\n\x0bGetFileCost\x12\x18.antd.v1.FileCostRequest\x1a\r.antd.v1.CostBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\x07\x41ntd.V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'antd._proto.antd.v1.files_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'\252\002\007Antd.V1' + _globals['DESCRIPTOR']._serialized_options = b'Z8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\252\002\007Antd.V1' _globals['_UPLOADFILEREQUEST']._serialized_start=54 _globals['_UPLOADFILEREQUEST']._serialized_end=87 _globals['_UPLOADPUBLICRESPONSE']._serialized_start=90 diff --git a/antd-py/src/antd/_proto/antd/v1/graph_pb2.py b/antd-py/src/antd/_proto/antd/v1/graph_pb2.py index 8ee59a3..c2d7d59 100644 --- a/antd-py/src/antd/_proto/antd/v1/graph_pb2.py +++ b/antd-py/src/antd/_proto/antd/v1/graph_pb2.py @@ -29,7 +29,7 @@ _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'antd._proto.antd._proto.antd._proto.antd.v1.graph_pb2', _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'antd._proto.antd._proto.antd._proto.antd._proto.antd.v1.graph_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'\252\002\007Antd.V1' diff --git a/antd-py/src/antd/_proto/antd/v1/health_pb2.py b/antd-py/src/antd/_proto/antd/v1/health_pb2.py index d0163c5..d9b9b34 100644 --- a/antd-py/src/antd/_proto/antd/v1/health_pb2.py +++ b/antd-py/src/antd/_proto/antd/v1/health_pb2.py @@ -24,18 +24,18 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61ntd/v1/health.proto\x12\x07\x61ntd.v1\"\x14\n\x12HealthCheckRequest\"6\n\x13HealthCheckResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\x12\x0f\n\x07network\x18\x02 \x01(\t2S\n\rHealthService\x12\x42\n\x05\x43heck\x12\x1b.antd.v1.HealthCheckRequest\x1a\x1c.antd.v1.HealthCheckResponseB\n\xaa\x02\x07\x41ntd.V1b\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61ntd/v1/health.proto\x12\x07\x61ntd.v1\"\x14\n\x12HealthCheckRequest\"\xc8\x01\n\x13HealthCheckResponse\x12\x0e\n\x06status\x18\x01 \x01(\t\x12\x0f\n\x07network\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x13\n\x0b\x65vm_network\x18\x04 \x01(\t\x12\x16\n\x0euptime_seconds\x18\x05 \x01(\x04\x12\x14\n\x0c\x62uild_commit\x18\x06 \x01(\t\x12\x1d\n\x15payment_token_address\x18\x07 \x01(\t\x12\x1d\n\x15payment_vault_address\x18\x08 \x01(\t2S\n\rHealthService\x12\x42\n\x05\x43heck\x12\x1b.antd.v1.HealthCheckRequest\x1a\x1c.antd.v1.HealthCheckResponseBDZ8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\xaa\x02\x07\x41ntd.V1b\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'antd._proto.antd.v1.health_pb2', _globals) if not _descriptor._USE_C_DESCRIPTORS: _globals['DESCRIPTOR']._loaded_options = None - _globals['DESCRIPTOR']._serialized_options = b'\252\002\007Antd.V1' + _globals['DESCRIPTOR']._serialized_options = b'Z8github.com/WithAutonomi/ant-sdk/antd-go/proto/antd/v1;v1\252\002\007Antd.V1' _globals['_HEALTHCHECKREQUEST']._serialized_start=33 _globals['_HEALTHCHECKREQUEST']._serialized_end=53 - _globals['_HEALTHCHECKRESPONSE']._serialized_start=55 - _globals['_HEALTHCHECKRESPONSE']._serialized_end=109 - _globals['_HEALTHSERVICE']._serialized_start=111 - _globals['_HEALTHSERVICE']._serialized_end=194 + _globals['_HEALTHCHECKRESPONSE']._serialized_start=56 + _globals['_HEALTHCHECKRESPONSE']._serialized_end=256 + _globals['_HEALTHSERVICE']._serialized_start=258 + _globals['_HEALTHSERVICE']._serialized_end=341 # @@protoc_insertion_point(module_scope) diff --git a/antd-py/src/antd/_proto/antd/v1/health_pb2.pyi b/antd-py/src/antd/_proto/antd/v1/health_pb2.pyi index 73fbefe..0835da7 100644 --- a/antd-py/src/antd/_proto/antd/v1/health_pb2.pyi +++ b/antd-py/src/antd/_proto/antd/v1/health_pb2.pyi @@ -9,9 +9,21 @@ class HealthCheckRequest(_message.Message): def __init__(self) -> None: ... class HealthCheckResponse(_message.Message): - __slots__ = ("status", "network") + __slots__ = ("status", "network", "version", "evm_network", "uptime_seconds", "build_commit", "payment_token_address", "payment_vault_address") STATUS_FIELD_NUMBER: _ClassVar[int] NETWORK_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + EVM_NETWORK_FIELD_NUMBER: _ClassVar[int] + UPTIME_SECONDS_FIELD_NUMBER: _ClassVar[int] + BUILD_COMMIT_FIELD_NUMBER: _ClassVar[int] + PAYMENT_TOKEN_ADDRESS_FIELD_NUMBER: _ClassVar[int] + PAYMENT_VAULT_ADDRESS_FIELD_NUMBER: _ClassVar[int] status: str network: str - def __init__(self, status: _Optional[str] = ..., network: _Optional[str] = ...) -> None: ... + version: str + evm_network: str + uptime_seconds: int + build_commit: str + payment_token_address: str + payment_vault_address: str + def __init__(self, status: _Optional[str] = ..., network: _Optional[str] = ..., version: _Optional[str] = ..., evm_network: _Optional[str] = ..., uptime_seconds: _Optional[int] = ..., build_commit: _Optional[str] = ..., payment_token_address: _Optional[str] = ..., payment_vault_address: _Optional[str] = ...) -> None: ... diff --git a/antd-py/src/antd/_rest.py b/antd-py/src/antd/_rest.py index 197a867..a075e0d 100644 --- a/antd-py/src/antd/_rest.py +++ b/antd-py/src/antd/_rest.py @@ -23,6 +23,21 @@ ) +def _health_status_from_json(j: dict) -> HealthStatus: + """Parse a /health JSON response. Diagnostic fields default to empty when + talking to a pre-0.4.0 daemon.""" + return HealthStatus( + ok=j.get("status") == "ok", + network=j.get("network", "unknown"), + version=j.get("version", ""), + evm_network=j.get("evm_network", ""), + uptime_seconds=int(j.get("uptime_seconds", 0)), + build_commit=j.get("build_commit", ""), + payment_token_address=j.get("payment_token_address", ""), + payment_vault_address=j.get("payment_vault_address", ""), + ) + + def _parse_file_upload_result(j: dict) -> FileUploadResult: """Parse a file/dir upload public JSON response into a FileUploadResult.""" return FileUploadResult( @@ -144,7 +159,7 @@ def health(self) -> HealthStatus: resp = self._http.get("/health") _check(resp) j = resp.json() - return HealthStatus(ok=j.get("status") == "ok", network=j.get("network", "unknown")) + return _health_status_from_json(j) # --- Data --- @@ -360,7 +375,7 @@ async def health(self) -> HealthStatus: resp = await self._http.get("/health") _check(resp) j = resp.json() - return HealthStatus(ok=j.get("status") == "ok", network=j.get("network", "unknown")) + return _health_status_from_json(j) # --- Data --- diff --git a/antd-py/src/antd/models.py b/antd-py/src/antd/models.py index 2cac8dc..8029823 100644 --- a/antd-py/src/antd/models.py +++ b/antd-py/src/antd/models.py @@ -6,9 +6,21 @@ @dataclass(frozen=True) class HealthStatus: - """Health check result from the antd daemon.""" + """Health check result from the antd daemon. + + The diagnostic fields (version, evm_network, uptime_seconds, build_commit, + payment_token_address, payment_vault_address) were added in antd 0.4.0. + They default to empty/zero so the dataclass remains constructable when + talking to an older daemon that doesn't report them. + """ ok: bool network: str # "default", "local", "alpha" + version: str = "" # antd crate version, e.g. "0.4.0" + evm_network: str = "" # "arbitrum-one", "arbitrum-sepolia", "local", "custom" + uptime_seconds: int = 0 # seconds since the daemon process started + build_commit: str = "" # short git SHA, "" if unknown + payment_token_address: str = "" # "" if unconfigured + payment_vault_address: str = "" # "" if unconfigured @dataclass(frozen=True) diff --git a/antd-py/tests/test_rest_client.py b/antd-py/tests/test_rest_client.py index d6a4ebc..7df868b 100644 --- a/antd-py/tests/test_rest_client.py +++ b/antd-py/tests/test_rest_client.py @@ -53,7 +53,16 @@ def do_GET(self): # noqa: N802 query = self.path.split("?")[1] if "?" in self.path else "" if path == "/health": - self._json_response(200, {"status": "ok", "network": "local"}) + self._json_response(200, { + "status": "ok", + "network": "local", + "version": "0.4.0", + "evm_network": "local", + "uptime_seconds": 42, + "build_commit": "abcdef123456", + "payment_token_address": "0xtoken", + "payment_vault_address": "0xvault", + }) elif path.startswith("/v1/data/public/"): addr = path.split("/v1/data/public/")[1] @@ -202,6 +211,21 @@ def test_returns_health_status(self, client: RestClient): assert isinstance(status, HealthStatus) assert status.ok is True assert status.network == "local" + assert status.version == "0.4.0" + assert status.evm_network == "local" + assert status.uptime_seconds == 42 + assert status.build_commit == "abcdef123456" + assert status.payment_token_address == "0xtoken" + assert status.payment_vault_address == "0xvault" + + def test_pre_0_4_0_daemon_leaves_diagnostics_empty(self, client: RestClient): + # Older daemons reply with just status + network. The dataclass + # defaults make this case still parse cleanly. + from antd._rest import _health_status_from_json + s = _health_status_from_json({"status": "ok", "network": "default"}) + assert s.ok is True and s.network == "default" + assert s.version == "" + assert s.uptime_seconds == 0 class TestDataPutPublic: