memorysync 1.8.0__tar.gz → 1.9.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: memorysync
3
- Version: 1.8.0
3
+ Version: 1.9.0
4
4
  Summary: Official Python client for the MemorySync API.
5
5
  Project-URL: Homepage, https://memorysync.io
6
6
  Project-URL: Documentation, https://docs.memorysync.io
@@ -0,0 +1 @@
1
+ __version__ = "1.9.0"
@@ -612,6 +612,81 @@ class MemorySyncClient:
612
612
  return skipped
613
613
  return Memory.from_dict(raw)
614
614
 
615
+ # ── Bulk import ──────────────────────────────────────────────────
616
+
617
+ def create_import(
618
+ self,
619
+ file: Union[bytes, "IO[bytes]"],
620
+ *,
621
+ filename: str,
622
+ end_user_id: Optional[str] = None,
623
+ resume: bool = False,
624
+ continue_on_error: bool = False,
625
+ ) -> Dict[str, Any]:
626
+ """Upload a payload and get an import job back, without waiting for it.
627
+
628
+ ``bulk_add`` accepts fifty records per call because each one runs the
629
+ extraction pipeline. For a migration out of another system, upload the
630
+ whole file here and poll :meth:`get_import`.
631
+
632
+ ``resume`` requires ``client_ref`` on every record and is what makes
633
+ re-running the same file safe: records a previous run already stored come
634
+ back counted as ``already_imported`` rather than stored again.
635
+
636
+ The payload is JSONL, or JSON when ``filename`` ends in ``.json``. The
637
+ extension is how the server picks the reader, so it is required.
638
+ """
639
+ if not filename or not filename.strip():
640
+ raise ValidationError("filename is required so the server can pick a reader")
641
+ form = _strip_none(
642
+ {
643
+ "end_user_id": end_user_id,
644
+ "resume": "true" if resume else "false",
645
+ "continue_on_error": "true" if continue_on_error else "false",
646
+ }
647
+ )
648
+ return (
649
+ self._request(
650
+ "POST",
651
+ "/imports",
652
+ files={"file": (filename, file)},
653
+ data=form,
654
+ end_user_override=end_user_id,
655
+ )
656
+ or {}
657
+ )
658
+
659
+ def get_import(self, job_id: str) -> Dict[str, Any]:
660
+ """One import job's status, progress and per-outcome counters.
661
+
662
+ ``progress_percentage`` stays below 100 until the job is genuinely
663
+ finished, so reaching 100 means done.
664
+ """
665
+ if not job_id or not str(job_id).strip():
666
+ raise ValidationError("job_id is required")
667
+ return self._request("GET", f"/imports/{job_id}") or {}
668
+
669
+ def list_imports(
670
+ self, *, status: Optional[str] = None, limit: Optional[int] = None
671
+ ) -> Dict[str, Any]:
672
+ """Recent import jobs, newest first."""
673
+ return (
674
+ self._request(
675
+ "GET", "/imports", params={"status": status, "limit": limit}
676
+ )
677
+ or {}
678
+ )
679
+
680
+ def cancel_import(self, job_id: str) -> Dict[str, Any]:
681
+ """Ask the worker to stop between batches.
682
+
683
+ Records already imported stay imported, and the counters report how far it
684
+ reached. Cancelling a finished job is a no-op rather than an error.
685
+ """
686
+ if not job_id or not str(job_id).strip():
687
+ raise ValidationError("job_id is required")
688
+ return self._request("POST", f"/imports/{job_id}/cancel") or {}
689
+
615
690
  # ── Bulk edit ────────────────────────────────────────────────────
616
691
 
617
692
  def batch_update(
@@ -1296,6 +1371,61 @@ class AsyncMemorySyncClient:
1296
1371
 
1297
1372
  # ── Bulk edit ────────────────────────────────────────────────────
1298
1373
 
1374
+ # ── Bulk import ──────────────────────────────────────────────────
1375
+
1376
+ async def create_import(
1377
+ self,
1378
+ file: Union[bytes, "IO[bytes]"],
1379
+ *,
1380
+ filename: str,
1381
+ end_user_id: Optional[str] = None,
1382
+ resume: bool = False,
1383
+ continue_on_error: bool = False,
1384
+ ) -> Dict[str, Any]:
1385
+ """Async twin of :meth:`MemorySyncClient.create_import`."""
1386
+ if not filename or not filename.strip():
1387
+ raise ValidationError("filename is required so the server can pick a reader")
1388
+ form = _strip_none(
1389
+ {
1390
+ "end_user_id": end_user_id,
1391
+ "resume": "true" if resume else "false",
1392
+ "continue_on_error": "true" if continue_on_error else "false",
1393
+ }
1394
+ )
1395
+ return (
1396
+ await self._request(
1397
+ "POST",
1398
+ "/imports",
1399
+ files={"file": (filename, file)},
1400
+ data=form,
1401
+ end_user_override=end_user_id,
1402
+ )
1403
+ or {}
1404
+ )
1405
+
1406
+ async def get_import(self, job_id: str) -> Dict[str, Any]:
1407
+ """Async twin of :meth:`MemorySyncClient.get_import`."""
1408
+ if not job_id or not str(job_id).strip():
1409
+ raise ValidationError("job_id is required")
1410
+ return await self._request("GET", f"/imports/{job_id}") or {}
1411
+
1412
+ async def list_imports(
1413
+ self, *, status: Optional[str] = None, limit: Optional[int] = None
1414
+ ) -> Dict[str, Any]:
1415
+ """Async twin of :meth:`MemorySyncClient.list_imports`."""
1416
+ return (
1417
+ await self._request(
1418
+ "GET", "/imports", params={"status": status, "limit": limit}
1419
+ )
1420
+ or {}
1421
+ )
1422
+
1423
+ async def cancel_import(self, job_id: str) -> Dict[str, Any]:
1424
+ """Async twin of :meth:`MemorySyncClient.cancel_import`."""
1425
+ if not job_id or not str(job_id).strip():
1426
+ raise ValidationError("job_id is required")
1427
+ return await self._request("POST", f"/imports/{job_id}/cancel") or {}
1428
+
1299
1429
  async def batch_update(
1300
1430
  self,
1301
1431
  items: Sequence[Union[BatchUpdateItem, Dict[str, Any]]],
@@ -1 +0,0 @@
1
- __version__ = "1.8.0"
File without changes
File without changes
File without changes
File without changes