memwal 0.1.11.dev11__tar.gz → 0.1.11.dev12__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.
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/PKG-INFO +1 -1
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/memwal/__init__.py +1 -1
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/memwal/client.py +3 -5
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/memwal/mock.py +5 -4
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/memwal/utils.py +3 -1
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/pyproject.toml +1 -1
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_client.py +47 -1
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_mock.py +5 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_signing.py +9 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/.gitignore +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/CHANGELOG.md +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/README.md +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/examples/.env.example +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/examples/.gitignore +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/examples/async_remember_demo.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/examples/interactive_demo.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/examples/verify_credentials.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/memwal/compatibility.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/memwal/middleware.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/memwal/types.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/notebooks/walrus_memory_python_sdk.ipynb +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/run_tests.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/__init__.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_auth_rejected_message.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_blank_text.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_env_presets.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_integration.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_list_namespaces.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_middleware.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_normalize_server_url.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_polling_delay.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_remember_derived_key.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_retry_after_http_date.py +0 -0
- {memwal-0.1.11.dev11 → memwal-0.1.11.dev12}/tests/test_transport_retry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: memwal
|
|
3
|
-
Version: 0.1.11.
|
|
3
|
+
Version: 0.1.11.dev12
|
|
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
|
|
@@ -1063,14 +1063,12 @@ class MemWal:
|
|
|
1063
1063
|
dropped = int(raw_dropped)
|
|
1064
1064
|
except (TypeError, ValueError):
|
|
1065
1065
|
dropped = 0
|
|
1066
|
+
total_count = data.get("total", len(memories))
|
|
1066
1067
|
if max_distance is not None:
|
|
1067
1068
|
memories = [m for m in memories if m.distance < max_distance]
|
|
1068
|
-
return RecallResult(
|
|
1069
|
-
results=memories, total=len(memories), dropped_count=dropped
|
|
1070
|
-
)
|
|
1071
1069
|
return RecallResult(
|
|
1072
1070
|
results=memories,
|
|
1073
|
-
total=
|
|
1071
|
+
total=total_count,
|
|
1074
1072
|
dropped_count=dropped,
|
|
1075
1073
|
)
|
|
1076
1074
|
|
|
@@ -1817,7 +1815,7 @@ class MemWalClockDriftError(MemWalError):
|
|
|
1817
1815
|
|
|
1818
1816
|
def _redact_internal_urls(text: str) -> str:
|
|
1819
1817
|
return re.sub(
|
|
1820
|
-
r"https?://(?:localhost|127\.0\.0\.
|
|
1818
|
+
r"https?://(?:(?:[\w-]+\.)*localhost|127\.\d{1,3}\.\d{1,3}\.\d{1,3}|\[::1\]|0\.0\.0\.0)(?::\d+)?[^\s)\]>'\"]*",
|
|
1821
1819
|
"[internal]",
|
|
1822
1820
|
text,
|
|
1823
1821
|
flags=re.IGNORECASE,
|
|
@@ -278,14 +278,15 @@ class MemWalMock:
|
|
|
278
278
|
for memory in self._memories
|
|
279
279
|
if memory.namespace == resolved_namespace
|
|
280
280
|
]
|
|
281
|
-
if max_distance is not None:
|
|
282
|
-
ranked = [item for item in ranked if item[1] < max_distance]
|
|
283
281
|
ranked.sort(key=lambda item: (item[1], item[0].sequence))
|
|
282
|
+
page = ranked[:limit]
|
|
283
|
+
if max_distance is not None:
|
|
284
|
+
page = [item for item in page if item[1] < max_distance]
|
|
284
285
|
results = [
|
|
285
286
|
RecallMemory(blob_id=memory.blob_id, text=memory.text, distance=score)
|
|
286
|
-
for memory, score in
|
|
287
|
+
for memory, score in page
|
|
287
288
|
]
|
|
288
|
-
return RecallResult(results=results, total=len(
|
|
289
|
+
return RecallResult(results=results, total=len(ranked[:limit]))
|
|
289
290
|
|
|
290
291
|
async def analyze(
|
|
291
292
|
self,
|
|
@@ -299,7 +299,9 @@ def normalize_private_key(key: str) -> str:
|
|
|
299
299
|
candidate = key.strip()
|
|
300
300
|
if candidate.lower().startswith("suiprivkey1"):
|
|
301
301
|
return bytes_to_hex(decode_sui_private_key(candidate))
|
|
302
|
-
|
|
302
|
+
if candidate.startswith(("0x", "0X")):
|
|
303
|
+
return candidate[2:]
|
|
304
|
+
return candidate
|
|
303
305
|
|
|
304
306
|
|
|
305
307
|
def encode_sui_private_key(seed_bytes: bytes) -> str:
|
|
@@ -935,9 +935,33 @@ class TestRecall:
|
|
|
935
935
|
body = json.loads(route.calls[0].request.content)
|
|
936
936
|
assert body["limit"] == 10
|
|
937
937
|
assert len(result.results) == 1
|
|
938
|
-
assert result.total ==
|
|
938
|
+
assert result.total == 2
|
|
939
939
|
assert result.results[0].blob_id == "b1"
|
|
940
940
|
|
|
941
|
+
@respx.mock
|
|
942
|
+
async def test_max_distance_filters_all_preserves_total(
|
|
943
|
+
self, memwal_client: MemWal
|
|
944
|
+
) -> None:
|
|
945
|
+
"""When all matches exceed max_distance, results is empty but total
|
|
946
|
+
preserves matches (#1066)."""
|
|
947
|
+
mock_seal_session_prereqs()
|
|
948
|
+
respx.post(f"{_TEST_SERVER}/api/recall").mock(
|
|
949
|
+
return_value=httpx.Response(
|
|
950
|
+
200,
|
|
951
|
+
json={
|
|
952
|
+
"results": [
|
|
953
|
+
{"blob_id": "b1", "text": "I love coffee", "distance": 0.8},
|
|
954
|
+
{"blob_id": "b2", "text": "I live in Tokyo", "distance": 0.9},
|
|
955
|
+
],
|
|
956
|
+
"total": 2,
|
|
957
|
+
},
|
|
958
|
+
)
|
|
959
|
+
)
|
|
960
|
+
|
|
961
|
+
result = await memwal_client.recall("coffee", limit=10, max_distance=0.5)
|
|
962
|
+
assert len(result.results) == 0
|
|
963
|
+
assert result.total == 2
|
|
964
|
+
|
|
941
965
|
@respx.mock
|
|
942
966
|
async def test_get_signed_request_uses_empty_body_hash_and_no_wire_body(
|
|
943
967
|
self, memwal_client: MemWal
|
|
@@ -1583,3 +1607,25 @@ class TestSealSession:
|
|
|
1583
1607
|
assert exported["ttlMin"] == 5
|
|
1584
1608
|
assert exported["sessionKey"].startswith("suiprivkey")
|
|
1585
1609
|
assert exported["personalMessageSignature"]
|
|
1610
|
+
|
|
1611
|
+
|
|
1612
|
+
class TestRedactInternalUrls:
|
|
1613
|
+
@pytest.mark.parametrize(
|
|
1614
|
+
"raw, expected",
|
|
1615
|
+
[
|
|
1616
|
+
("Error at http://localhost:8080/path", "Error at [internal]"),
|
|
1617
|
+
("Error at http://127.0.0.1:3000/api", "Error at [internal]"),
|
|
1618
|
+
("Error at http://127.0.0.2:8000/status", "Error at [internal]"),
|
|
1619
|
+
("Error at http://127.128.0.1:9000/info", "Error at [internal]"),
|
|
1620
|
+
("Error at http://[::1]:9000/seal/encrypt", "Error at [internal]"),
|
|
1621
|
+
("Error at http://0.0.0.0:8000/health", "Error at [internal]"),
|
|
1622
|
+
("Error at http://sidecar.localhost:8080/v1", "Error at [internal]"),
|
|
1623
|
+
("Error at http://sub.domain.localhost:3000/test", "Error at [internal]"),
|
|
1624
|
+
("Error at https://api.wal.app/v1/remember", "Error at https://api.wal.app/v1/remember"),
|
|
1625
|
+
],
|
|
1626
|
+
)
|
|
1627
|
+
def test_redact_internal_urls(self, raw: str, expected: str) -> None:
|
|
1628
|
+
from memwal.client import _redact_internal_urls
|
|
1629
|
+
|
|
1630
|
+
assert _redact_internal_urls(raw) == expected
|
|
1631
|
+
|
|
@@ -57,6 +57,11 @@ async def test_mock_isolates_namespaces_and_honors_max_distance():
|
|
|
57
57
|
assert [memory.text for memory in alice.results] == ["Alice likes ramen"]
|
|
58
58
|
assert [memory.text for memory in bob.results] == ["Bob likes tacos"]
|
|
59
59
|
assert empty.total == 0
|
|
60
|
+
dropped = await mock.recall(
|
|
61
|
+
RecallParams(query="likes", namespace="user-a", max_distance=0)
|
|
62
|
+
)
|
|
63
|
+
assert dropped.results == []
|
|
64
|
+
assert dropped.total == 1
|
|
60
65
|
|
|
61
66
|
|
|
62
67
|
@pytest.mark.asyncio
|
|
@@ -324,11 +324,20 @@ class TestPrivateKeyFormats:
|
|
|
324
324
|
for form in (
|
|
325
325
|
self._SEED_HEX,
|
|
326
326
|
f"0x{self._SEED_HEX}",
|
|
327
|
+
f"0X{self._SEED_HEX}",
|
|
328
|
+
f" 0X{self._SEED_HEX} ",
|
|
327
329
|
encode_sui_private_key(seed),
|
|
328
330
|
f" {encode_sui_private_key(seed)} ",
|
|
329
331
|
):
|
|
330
332
|
assert normalize_private_key(form) == self._SEED_HEX
|
|
331
333
|
|
|
334
|
+
def test_uppercase_0x_prefix_key_produces_valid_client(self) -> None:
|
|
335
|
+
from memwal.client import MemWal
|
|
336
|
+
|
|
337
|
+
upper_0x = f"0X{self._SEED_HEX}"
|
|
338
|
+
client = MemWal.create(key=upper_0x, account_id="0xtest")
|
|
339
|
+
assert client._private_key_hex == self._SEED_HEX
|
|
340
|
+
|
|
332
341
|
def test_bech32_key_produces_the_same_client_identity(self) -> None:
|
|
333
342
|
from memwal.client import MemWal
|
|
334
343
|
from memwal.utils import encode_sui_private_key
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|