memwal 0.1.8.dev0__tar.gz → 0.1.8.dev2__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.
Files changed (27) hide show
  1. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/PKG-INFO +1 -1
  2. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/memwal/__init__.py +1 -1
  3. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/memwal/client.py +58 -5
  4. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/pyproject.toml +1 -1
  5. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/tests/test_client.py +6 -0
  6. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/tests/test_middleware.py +6 -0
  7. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/.gitignore +0 -0
  8. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/CHANGELOG.md +0 -0
  9. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/README.md +0 -0
  10. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/examples/.env.example +0 -0
  11. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/examples/.gitignore +0 -0
  12. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/examples/async_remember_demo.py +0 -0
  13. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/examples/interactive_demo.py +0 -0
  14. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/examples/verify_credentials.py +0 -0
  15. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/memwal/compatibility.py +0 -0
  16. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/memwal/middleware.py +0 -0
  17. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/memwal/mock.py +0 -0
  18. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/memwal/types.py +0 -0
  19. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/memwal/utils.py +0 -0
  20. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/notebooks/walrus_memory_python_sdk.ipynb +0 -0
  21. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/run_tests.py +0 -0
  22. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/tests/__init__.py +0 -0
  23. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/tests/test_auth_rejected_message.py +0 -0
  24. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/tests/test_env_presets.py +0 -0
  25. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/tests/test_integration.py +0 -0
  26. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/tests/test_mock.py +0 -0
  27. {memwal-0.1.8.dev0 → memwal-0.1.8.dev2}/tests/test_signing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: memwal
3
- Version: 0.1.8.dev0
3
+ Version: 0.1.8.dev2
4
4
  Summary: Python SDK for Walrus Memory — Privacy-first AI memory with Ed25519 signing
5
5
  Project-URL: Homepage, https://memory.walrus.xyz
6
6
  Project-URL: Documentation, https://memory.walrus.xyz
@@ -122,4 +122,4 @@ __all__ = [
122
122
  "RecallManualResult",
123
123
  ]
124
124
 
125
- __version__ = "0.1.8.dev0"
125
+ __version__ = "0.1.8.dev2"
@@ -83,6 +83,13 @@ from .utils import (
83
83
 
84
84
  T = TypeVar("T")
85
85
  SEAL_SESSION_TTL_MIN = 5
86
+
87
+ # Networks with a public Sui GraphQL read endpoint (https://graphql.<network>.sui.io).
88
+ PUBLIC_SUI_GRAPHQL_NETWORKS = ("mainnet", "testnet", "devnet")
89
+
90
+ # Fail the package-version preflight fast instead of hanging client init when a
91
+ # Sui read endpoint is unresponsive.
92
+ PACKAGE_VERSION_CHECK_TIMEOUT_S = 10.0
86
93
  SEAL_SESSION_SAFETY_MARGIN_MS = 30_000
87
94
  AUTH_REJECTED_MESSAGE = (
88
95
  "401 from relayer: typically wrong private key, key not registered on this "
@@ -1039,17 +1046,46 @@ class MemWal:
1039
1046
  package_id = data.get("packageId")
1040
1047
  network = data.get("network")
1041
1048
  sui_rpc_url = data.get("suiRpcUrl")
1042
- if not package_id or not network or not sui_rpc_url:
1043
- raise MemWalError("GET /config response missing packageId / network / suiRpcUrl")
1049
+ if not package_id or not network:
1050
+ raise MemWalError("GET /config response missing packageId / network")
1044
1051
 
1045
1052
  self._server_config = {
1046
1053
  "packageId": package_id,
1047
1054
  "network": network,
1048
- "suiRpcUrl": sui_rpc_url,
1055
+ "suiRpcUrl": sui_rpc_url or "",
1049
1056
  }
1050
1057
  return self._server_config
1051
1058
 
1052
- async def _assert_first_package_version(self, sui_rpc_url: str, package_id: str) -> None:
1059
+ async def _fetch_package_version_graphql(self, network: str, package_id: str):
1060
+ """Read the package object version via Sui GraphQL RPC.
1061
+
1062
+ JSON-RPC on public fullnodes is sunset (see
1063
+ https://docs.sui.io/develop/accessing-data/json-rpc-migration), and the
1064
+ third-party JSON-RPC mirror the relayer used to advertise has gone
1065
+ unresponsive, hanging every client init. GraphQL is the plain-HTTPS
1066
+ read layer that survives the sunset without pulling gRPC deps into
1067
+ this SDK.
1068
+ """
1069
+ graphql_url = f"https://graphql.{network}.sui.io/graphql"
1070
+ response = await self._http.post(
1071
+ graphql_url,
1072
+ json={
1073
+ "query": "query($a:SuiAddress!){object(address:$a){version}}",
1074
+ "variables": {"a": package_id},
1075
+ },
1076
+ timeout=PACKAGE_VERSION_CHECK_TIMEOUT_S,
1077
+ )
1078
+ if response.status_code != 200:
1079
+ raise MemWalError(f"Sui GraphQL returned {response.status_code}")
1080
+ body = response.json()
1081
+ obj = (body.get("data") or {}).get("object")
1082
+ if not isinstance(obj, dict) or obj.get("version") is None:
1083
+ raise MemWalError(f"Sui GraphQL could not resolve package {package_id}")
1084
+ return obj["version"]
1085
+
1086
+ async def _fetch_package_version_jsonrpc(self, sui_rpc_url: str, package_id: str):
1087
+ """Legacy JSON-RPC fallback for custom / local networks that have no
1088
+ public GraphQL endpoint and still serve `sui_getObject`."""
1053
1089
  response = await self._http.post(
1054
1090
  sui_rpc_url,
1055
1091
  json={
@@ -1058,6 +1094,7 @@ class MemWal:
1058
1094
  "method": "sui_getObject",
1059
1095
  "params": [package_id, {"showBcs": False, "showContent": False, "showType": False}],
1060
1096
  },
1097
+ timeout=PACKAGE_VERSION_CHECK_TIMEOUT_S,
1061
1098
  )
1062
1099
  if response.status_code != 200:
1063
1100
  raise MemWalError(f"sui_getObject returned {response.status_code}")
@@ -1073,6 +1110,20 @@ class MemWal:
1073
1110
  obj = result.get("object")
1074
1111
  if isinstance(obj, dict):
1075
1112
  version = obj.get("version")
1113
+ return version
1114
+
1115
+ async def _assert_first_package_version(
1116
+ self, network: str, sui_rpc_url: str, package_id: str
1117
+ ) -> None:
1118
+ if network in PUBLIC_SUI_GRAPHQL_NETWORKS:
1119
+ version = await self._fetch_package_version_graphql(network, package_id)
1120
+ elif sui_rpc_url:
1121
+ version = await self._fetch_package_version_jsonrpc(sui_rpc_url, package_id)
1122
+ else:
1123
+ raise MemWalError(
1124
+ f"No Sui read endpoint available for network {network!r}: "
1125
+ "no public GraphQL endpoint and GET /config carried no suiRpcUrl"
1126
+ )
1076
1127
  if str(version) != "1":
1077
1128
  raise MemWalError(
1078
1129
  f"SEAL package {package_id} must be at version 1 to build "
@@ -1081,7 +1132,9 @@ class MemWal:
1081
1132
 
1082
1133
  async def _build_seal_session_inner(self) -> str:
1083
1134
  cfg = await self._fetch_server_config()
1084
- await self._assert_first_package_version(cfg["suiRpcUrl"], cfg["packageId"])
1135
+ await self._assert_first_package_version(
1136
+ cfg["network"], cfg["suiRpcUrl"], cfg["packageId"]
1137
+ )
1085
1138
 
1086
1139
  session_signing_key = nacl.signing.SigningKey.generate()
1087
1140
  session_public_key = bytes(session_signing_key.verify_key)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "memwal"
7
- version = "0.1.8.dev0"
7
+ version = "0.1.8.dev2"
8
8
  description = "Python SDK for Walrus Memory — Privacy-first AI memory with Ed25519 signing"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -86,6 +86,12 @@ def mock_seal_session_prereqs() -> None:
86
86
  },
87
87
  )
88
88
  )
89
+ respx.post("https://graphql.testnet.sui.io/graphql").mock(
90
+ return_value=httpx.Response(
91
+ 200,
92
+ json={"data": {"object": {"version": 1}}},
93
+ )
94
+ )
89
95
  respx.post(_TEST_SUI_RPC).mock(
90
96
  return_value=httpx.Response(
91
97
  200,
@@ -99,6 +99,12 @@ def _mock_seal_session_prereqs() -> None:
99
99
  },
100
100
  )
101
101
  )
102
+ respx.post("https://graphql.testnet.sui.io/graphql").mock(
103
+ return_value=httpx.Response(
104
+ 200,
105
+ json={"data": {"object": {"version": 1}}},
106
+ )
107
+ )
102
108
  respx.post(_SUI_RPC_URL).mock(
103
109
  return_value=httpx.Response(
104
110
  200,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes