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.
Files changed (31) hide show
  1. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/CHANGELOG.md +2 -0
  2. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/PKG-INFO +1 -1
  3. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/__init__.py +1 -1
  4. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/client.py +19 -1
  5. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/pyproject.toml +1 -1
  6. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_auth_rejected_message.py +1 -0
  7. memwal-0.1.11.dev5/tests/test_blank_text.py +22 -0
  8. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/.gitignore +0 -0
  9. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/README.md +0 -0
  10. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/.env.example +0 -0
  11. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/.gitignore +0 -0
  12. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/async_remember_demo.py +0 -0
  13. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/interactive_demo.py +0 -0
  14. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/examples/verify_credentials.py +0 -0
  15. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/compatibility.py +0 -0
  16. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/middleware.py +0 -0
  17. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/mock.py +0 -0
  18. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/types.py +0 -0
  19. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/memwal/utils.py +0 -0
  20. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/notebooks/walrus_memory_python_sdk.ipynb +0 -0
  21. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/run_tests.py +0 -0
  22. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/__init__.py +0 -0
  23. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_client.py +0 -0
  24. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_env_presets.py +0 -0
  25. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_integration.py +0 -0
  26. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_list_namespaces.py +0 -0
  27. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_middleware.py +0 -0
  28. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_mock.py +0 -0
  29. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_normalize_server_url.py +0 -0
  30. {memwal-0.1.11.dev4 → memwal-0.1.11.dev5}/tests/test_polling_delay.py +0 -0
  31. {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.dev4
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
@@ -128,4 +128,4 @@ __all__ = [
128
128
  "RecallManualResult",
129
129
  ]
130
130
 
131
- __version__ = "0.1.11.dev4"
131
+ __version__ = "0.1.11.dev5"
@@ -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, or staging/mainnet mismatch. Check .env.local "
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",
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "memwal"
7
- version = "0.1.11.dev4"
7
+ version = "0.1.11.dev5"
8
8
  description = "Python SDK for Walrus Memory — Privacy-first AI memory with Ed25519 signing"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -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