memwal 0.1.11.dev4__tar.gz → 0.1.11.dev5__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.dev4 → memwal-0.1.11.dev5}/CHANGELOG.md +2 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/PKG-INFO +1 -1
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/__init__.py +1 -1
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/client.py +19 -1
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/pyproject.toml +1 -1
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_auth_rejected_message.py +1 -0
- memwal-0.1.11.dev5/tests/test_blank_text.py +22 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/.gitignore +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/README.md +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/.env.example +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/.gitignore +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/async_remember_demo.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/interactive_demo.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/verify_credentials.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/compatibility.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/middleware.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/mock.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/types.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/utils.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/notebooks/walrus_memory_python_sdk.ipynb +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/run_tests.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/__init__.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_client.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_env_presets.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_integration.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_list_namespaces.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_middleware.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_mock.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_normalize_server_url.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_polling_delay.py +0 -0
- {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_signing.py +0 -0
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
### Fixed
|
|
6
6
|
|
|
7
|
+
- `remember`, `remember_bulk`, `analyze`, and `embed` reject whitespace-only text before the request. A blank body used to be accepted and then sit until the caller timed out. (#1129)
|
|
8
|
+
- A 401 `AUTH_REJECTED` now says a wallet address is not a MemWal account ID. (#1132)
|
|
7
9
|
- `wait_for_remember_jobs` / `remember_bulk_and_wait` no longer return a batch they could not read as if it were still uploading. When every status poll is rate-limited, the wait makes one confirming read and raises `MemWalRateLimited` (`status` 429, `job_ids`, `retry_after`) if that is refused too. An item still unsettled at the deadline names its last known status, or says no read got through. (WALM-671, #967)
|
|
8
10
|
|
|
9
11
|
## 0.1.12
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: memwal
|
|
3
|
-
Version: 0.1.11.
|
|
3
|
+
Version: 0.1.11.dev5
|
|
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
|
|
@@ -96,7 +96,8 @@ PACKAGE_VERSION_CHECK_TIMEOUT_S = 10.0
|
|
|
96
96
|
SEAL_SESSION_SAFETY_MARGIN_MS = 30_000
|
|
97
97
|
AUTH_REJECTED_MESSAGE = (
|
|
98
98
|
"401 from relayer: typically wrong private key, key not registered on this "
|
|
99
|
-
"account, account ID mismatch
|
|
99
|
+
"account, account ID mismatch (a wallet address is not a MemWal account ID), "
|
|
100
|
+
"or staging/mainnet mismatch. Check .env.local "
|
|
100
101
|
"and dashboard credentials. Full troubleshooting: "
|
|
101
102
|
"https://docs.wal.app/walrus-memory/troubleshooting/overview#401-auth_rejected-errors"
|
|
102
103
|
)
|
|
@@ -121,6 +122,18 @@ def _server_url_for_log(parsed: ParseResult) -> str:
|
|
|
121
122
|
return f"{parsed.scheme}://{host}"
|
|
122
123
|
|
|
123
124
|
|
|
125
|
+
def _reject_blank_text(text: object, message: str = "Text cannot be empty") -> None:
|
|
126
|
+
"""Reject empty and whitespace-only text before a request is sent.
|
|
127
|
+
|
|
128
|
+
The relayer does the same (#1129). Failing here means an older relayer
|
|
129
|
+
cannot accept the job and leave the caller polling until timeout.
|
|
130
|
+
Non-blank text is not trimmed.
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
if not isinstance(text, str) or not text.strip():
|
|
134
|
+
raise ValueError(message)
|
|
135
|
+
|
|
136
|
+
|
|
124
137
|
def normalize_server_url(url: str) -> str:
|
|
125
138
|
"""Strip a trailing slash and warn on plaintext HTTP to a remote host.
|
|
126
139
|
|
|
@@ -414,6 +427,7 @@ class MemWal:
|
|
|
414
427
|
:class:`RememberAcceptedResult` with ``job_id`` and initial
|
|
415
428
|
status (``"pending"``).
|
|
416
429
|
"""
|
|
430
|
+
_reject_blank_text(text)
|
|
417
431
|
resolved_namespace = namespace or self._namespace
|
|
418
432
|
request_identity = f"{resolved_namespace}\0{text}"
|
|
419
433
|
generated_key = idempotency_key is None
|
|
@@ -569,6 +583,8 @@ class MemWal:
|
|
|
569
583
|
|
|
570
584
|
if not items:
|
|
571
585
|
raise ValueError("remember_bulk_async: items must be a non-empty array")
|
|
586
|
+
for index, item in enumerate(items):
|
|
587
|
+
_reject_blank_text(item.text, f"items[{index}].text cannot be empty")
|
|
572
588
|
|
|
573
589
|
payload_items: List[Dict[str, Any]] = [
|
|
574
590
|
{
|
|
@@ -898,6 +914,7 @@ class MemWal:
|
|
|
898
914
|
:class:`AnalyzeResult` with extracted ``facts`` + per-fact
|
|
899
915
|
``job_ids`` for downstream polling.
|
|
900
916
|
"""
|
|
917
|
+
_reject_blank_text(text)
|
|
901
918
|
body: Dict[str, Any] = {
|
|
902
919
|
"text": text,
|
|
903
920
|
"namespace": namespace or self._namespace,
|
|
@@ -970,6 +987,7 @@ class MemWal:
|
|
|
970
987
|
:meth:`remember` (server handles embed + encrypt + upload).
|
|
971
988
|
"""
|
|
972
989
|
|
|
990
|
+
_reject_blank_text(text, "text cannot be empty")
|
|
973
991
|
data = await self._signed_request(
|
|
974
992
|
"POST",
|
|
975
993
|
"/api/embed",
|
|
@@ -12,6 +12,7 @@ from memwal.client import (
|
|
|
12
12
|
|
|
13
13
|
def test_auth_rejected_message_points_to_troubleshooting_guide() -> None:
|
|
14
14
|
assert "docs.wal.app/walrus-memory/troubleshooting/overview" in AUTH_REJECTED_MESSAGE
|
|
15
|
+
assert "wallet address" in AUTH_REJECTED_MESSAGE
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def test_auth_503_is_retryable_not_a_credential_failure() -> None:
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Whitespace-only text is rejected before a request is sent (#1129)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from memwal.client import _reject_blank_text
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@pytest.mark.parametrize("text", ["", " ", "\t", "\n", " \t\n "])
|
|
11
|
+
def test_blank_text_rejected(text: str) -> None:
|
|
12
|
+
with pytest.raises(ValueError, match="Text cannot be empty"):
|
|
13
|
+
_reject_blank_text(text)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_padded_text_is_not_trimmed() -> None:
|
|
17
|
+
_reject_blank_text(" hello ")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_bulk_item_message_names_the_index() -> None:
|
|
21
|
+
with pytest.raises(ValueError, match=r"items\[0\].text cannot be empty"):
|
|
22
|
+
_reject_blank_text(" \t", "items[0].text cannot be empty")
|
|
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
|