memwal 0.1.6.dev1__tar.gz → 0.1.6.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.
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/PKG-INFO +1 -1
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/memwal/__init__.py +1 -1
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/memwal/client.py +56 -10
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/pyproject.toml +1 -1
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/.gitignore +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/CHANGELOG.md +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/README.md +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/examples/.env.example +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/examples/.gitignore +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/examples/async_remember_demo.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/examples/interactive_demo.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/examples/verify_credentials.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/memwal/compatibility.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/memwal/middleware.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/memwal/mock.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/memwal/types.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/memwal/utils.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/notebooks/walrus_memory_python_sdk.ipynb +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/run_tests.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/tests/__init__.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/tests/test_auth_rejected_message.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/tests/test_client.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/tests/test_env_presets.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/tests/test_integration.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/tests/test_middleware.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.dev2}/tests/test_mock.py +0 -0
- {memwal-0.1.6.dev1 → memwal-0.1.6.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.6.
|
|
3
|
+
Version: 0.1.6.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
|
|
@@ -30,6 +30,7 @@ import base64
|
|
|
30
30
|
import json
|
|
31
31
|
import random
|
|
32
32
|
import time
|
|
33
|
+
import uuid
|
|
33
34
|
from datetime import datetime, timezone
|
|
34
35
|
from typing import Any, Dict, List, Optional, Sequence, Tuple, TypeVar, Union
|
|
35
36
|
|
|
@@ -223,6 +224,9 @@ class MemWal:
|
|
|
223
224
|
self._session_build_task: Optional[asyncio.Task[str]] = None
|
|
224
225
|
self._relayer_version_metadata: Optional[Dict[str, Any]] = None
|
|
225
226
|
self._compatibility_lock: Optional[asyncio.Lock] = None
|
|
227
|
+
# Preserve a generated key across an ambiguous transport failure. A
|
|
228
|
+
# subsequent identical call then collapses onto the accepted paid job.
|
|
229
|
+
self._pending_remember_keys: Dict[str, str] = {}
|
|
226
230
|
|
|
227
231
|
@classmethod
|
|
228
232
|
def create(
|
|
@@ -281,7 +285,10 @@ class MemWal:
|
|
|
281
285
|
# ============================================================
|
|
282
286
|
|
|
283
287
|
async def remember(
|
|
284
|
-
self,
|
|
288
|
+
self,
|
|
289
|
+
text: str,
|
|
290
|
+
namespace: Optional[str] = None,
|
|
291
|
+
idempotency_key: Optional[str] = None,
|
|
285
292
|
) -> RememberAcceptedResult:
|
|
286
293
|
"""Submit a remember request and return as soon as the server accepts it.
|
|
287
294
|
|
|
@@ -299,12 +306,27 @@ class MemWal:
|
|
|
299
306
|
:class:`RememberAcceptedResult` with ``job_id`` and initial
|
|
300
307
|
status (``"pending"``).
|
|
301
308
|
"""
|
|
309
|
+
resolved_namespace = namespace or self._namespace
|
|
310
|
+
request_identity = f"{resolved_namespace}\0{text}"
|
|
311
|
+
generated_key = idempotency_key is None
|
|
312
|
+
resolved_key = idempotency_key or self._pending_remember_keys.get(request_identity)
|
|
313
|
+
if resolved_key is None:
|
|
314
|
+
resolved_key = str(uuid.uuid4())
|
|
315
|
+
if generated_key:
|
|
316
|
+
self._pending_remember_keys[request_identity] = resolved_key
|
|
317
|
+
|
|
302
318
|
data = await self._signed_request(
|
|
303
319
|
"POST",
|
|
304
320
|
"/api/remember",
|
|
305
|
-
{
|
|
321
|
+
{
|
|
322
|
+
"text": text,
|
|
323
|
+
"namespace": resolved_namespace,
|
|
324
|
+
"idempotency_key": resolved_key,
|
|
325
|
+
},
|
|
306
326
|
accepted_statuses=(200, 202),
|
|
307
327
|
)
|
|
328
|
+
if generated_key:
|
|
329
|
+
self._pending_remember_keys.pop(request_identity, None)
|
|
308
330
|
return RememberAcceptedResult(
|
|
309
331
|
job_id=data["job_id"],
|
|
310
332
|
status=data.get("status", "pending"),
|
|
@@ -312,9 +334,12 @@ class MemWal:
|
|
|
312
334
|
|
|
313
335
|
# Alias for parity with TS SDK ``rememberAsync``.
|
|
314
336
|
async def remember_async(
|
|
315
|
-
self,
|
|
337
|
+
self,
|
|
338
|
+
text: str,
|
|
339
|
+
namespace: Optional[str] = None,
|
|
340
|
+
idempotency_key: Optional[str] = None,
|
|
316
341
|
) -> RememberAcceptedResult:
|
|
317
|
-
return await self.remember(text, namespace)
|
|
342
|
+
return await self.remember(text, namespace, idempotency_key)
|
|
318
343
|
|
|
319
344
|
async def wait_for_remember_job(
|
|
320
345
|
self,
|
|
@@ -380,6 +405,7 @@ class MemWal:
|
|
|
380
405
|
namespace: Optional[str] = None,
|
|
381
406
|
poll_interval_ms: int = 1500,
|
|
382
407
|
timeout_ms: int = 60_000,
|
|
408
|
+
idempotency_key: Optional[str] = None,
|
|
383
409
|
) -> RememberResult:
|
|
384
410
|
"""Submit a remember and wait for the background worker to finish.
|
|
385
411
|
|
|
@@ -388,12 +414,24 @@ class MemWal:
|
|
|
388
414
|
``failed``). Mirrors TS ``rememberAndWait``.
|
|
389
415
|
"""
|
|
390
416
|
|
|
391
|
-
|
|
392
|
-
|
|
417
|
+
resolved_namespace = namespace or self._namespace
|
|
418
|
+
request_identity = f"{resolved_namespace}\0{text}"
|
|
419
|
+
generated_key = idempotency_key is None
|
|
420
|
+
resolved_key = idempotency_key or self._pending_remember_keys.get(request_identity)
|
|
421
|
+
if resolved_key is None:
|
|
422
|
+
resolved_key = str(uuid.uuid4())
|
|
423
|
+
if generated_key:
|
|
424
|
+
self._pending_remember_keys[request_identity] = resolved_key
|
|
425
|
+
|
|
426
|
+
accepted = await self.remember(text, resolved_namespace, resolved_key)
|
|
427
|
+
result = await self.wait_for_remember_job(
|
|
393
428
|
accepted.job_id,
|
|
394
429
|
poll_interval_ms=poll_interval_ms,
|
|
395
430
|
timeout_ms=timeout_ms,
|
|
396
431
|
)
|
|
432
|
+
if generated_key:
|
|
433
|
+
self._pending_remember_keys.pop(request_identity, None)
|
|
434
|
+
return result
|
|
397
435
|
|
|
398
436
|
# ============================================================
|
|
399
437
|
# Bulk remember (ENG-1408)
|
|
@@ -1285,16 +1323,22 @@ class MemWalSync:
|
|
|
1285
1323
|
return asyncio.run(coro)
|
|
1286
1324
|
|
|
1287
1325
|
def remember(
|
|
1288
|
-
self,
|
|
1326
|
+
self,
|
|
1327
|
+
text: str,
|
|
1328
|
+
namespace: Optional[str] = None,
|
|
1329
|
+
idempotency_key: Optional[str] = None,
|
|
1289
1330
|
) -> RememberAcceptedResult:
|
|
1290
1331
|
"""Synchronous version of :meth:`MemWal.remember` (async accept)."""
|
|
1291
|
-
return self._run(self._inner.remember(text, namespace))
|
|
1332
|
+
return self._run(self._inner.remember(text, namespace, idempotency_key))
|
|
1292
1333
|
|
|
1293
1334
|
# Alias for parity with TS SDK ``rememberAsync``.
|
|
1294
1335
|
def remember_async(
|
|
1295
|
-
self,
|
|
1336
|
+
self,
|
|
1337
|
+
text: str,
|
|
1338
|
+
namespace: Optional[str] = None,
|
|
1339
|
+
idempotency_key: Optional[str] = None,
|
|
1296
1340
|
) -> RememberAcceptedResult:
|
|
1297
|
-
return self._run(self._inner.remember_async(text, namespace))
|
|
1341
|
+
return self._run(self._inner.remember_async(text, namespace, idempotency_key))
|
|
1298
1342
|
|
|
1299
1343
|
def wait_for_remember_job(
|
|
1300
1344
|
self,
|
|
@@ -1317,6 +1361,7 @@ class MemWalSync:
|
|
|
1317
1361
|
namespace: Optional[str] = None,
|
|
1318
1362
|
poll_interval_ms: int = 1500,
|
|
1319
1363
|
timeout_ms: int = 60_000,
|
|
1364
|
+
idempotency_key: Optional[str] = None,
|
|
1320
1365
|
) -> RememberResult:
|
|
1321
1366
|
"""Synchronous version of :meth:`MemWal.remember_and_wait`."""
|
|
1322
1367
|
return self._run(
|
|
@@ -1325,6 +1370,7 @@ class MemWalSync:
|
|
|
1325
1370
|
namespace,
|
|
1326
1371
|
poll_interval_ms=poll_interval_ms,
|
|
1327
1372
|
timeout_ms=timeout_ms,
|
|
1373
|
+
idempotency_key=idempotency_key,
|
|
1328
1374
|
)
|
|
1329
1375
|
)
|
|
1330
1376
|
|
|
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
|