memorysync 1.3.0__tar.gz → 1.4.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
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: memorysync
3
- Version: 1.3.0
3
+ Version: 1.4.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
@@ -1,44 +1,44 @@
1
- [build-system]
2
- requires = ["hatchling"]
3
- build-backend = "hatchling.build"
4
-
5
- [project]
6
- name = "memorysync"
7
- version = "1.3.0"
8
- description = "Official Python client for the MemorySync API."
9
- readme = "README.md"
10
- license = { text = "MIT" }
11
- requires-python = ">=3.9"
12
- authors = [{ name = "MemorySync" }]
13
- keywords = ["memorysync", "memory", "sdk", "client"]
14
- classifiers = [
15
- "Development Status :: 5 - Production/Stable",
16
- "Intended Audience :: Developers",
17
- "License :: OSI Approved :: MIT License",
18
- "Programming Language :: Python :: 3",
19
- "Programming Language :: Python :: 3.9",
20
- "Programming Language :: Python :: 3.10",
21
- "Programming Language :: Python :: 3.11",
22
- "Programming Language :: Python :: 3.12",
23
- "Topic :: Software Development :: Libraries :: Python Modules",
24
- "Typing :: Typed",
25
- ]
26
- dependencies = [
27
- "httpx>=0.25,<1.0",
28
- "typing-extensions>=4.5; python_version < '3.11'",
29
- ]
30
-
31
- [project.urls]
32
- Homepage = "https://memorysync.io"
33
- Documentation = "https://docs.memorysync.io"
34
-
35
- [tool.hatch.build.targets.wheel]
36
- packages = ["src/memorysync"]
37
-
38
- [tool.hatch.build.targets.sdist]
39
- include = [
40
- "src/memorysync",
41
- "README.md",
42
- "LICENSE",
43
- "pyproject.toml",
44
- ]
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "memorysync"
7
+ version = "1.4.0"
8
+ description = "Official Python client for the MemorySync API."
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.9"
12
+ authors = [{ name = "MemorySync" }]
13
+ keywords = ["memorysync", "memory", "sdk", "client"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Programming Language :: Python :: 3.9",
20
+ "Programming Language :: Python :: 3.10",
21
+ "Programming Language :: Python :: 3.11",
22
+ "Programming Language :: Python :: 3.12",
23
+ "Topic :: Software Development :: Libraries :: Python Modules",
24
+ "Typing :: Typed",
25
+ ]
26
+ dependencies = [
27
+ "httpx>=0.25,<1.0",
28
+ "typing-extensions>=4.5; python_version < '3.11'",
29
+ ]
30
+
31
+ [project.urls]
32
+ Homepage = "https://memorysync.io"
33
+ Documentation = "https://docs.memorysync.io"
34
+
35
+ [tool.hatch.build.targets.wheel]
36
+ packages = ["src/memorysync"]
37
+
38
+ [tool.hatch.build.targets.sdist]
39
+ include = [
40
+ "src/memorysync",
41
+ "README.md",
42
+ "LICENSE",
43
+ "pyproject.toml",
44
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "1.4.0"
@@ -38,7 +38,7 @@ shapes (memories, history, feedback, ontology).
38
38
 
39
39
  from __future__ import annotations
40
40
 
41
- from typing import Any, Callable, Dict, Optional, Sequence
41
+ from typing import Any, Callable, Dict, Mapping, Optional, Sequence
42
42
  from urllib.parse import quote
43
43
 
44
44
  _V2 = "/api/v2/integrations"
@@ -58,6 +58,20 @@ def _seg(value: Any) -> str:
58
58
  return quote(str(value), safe="")
59
59
 
60
60
 
61
+ def _as_item(value: Any, key: str) -> Dict[str, Any]:
62
+ """Normalize one selection entry into the object the API expects.
63
+
64
+ Two of the approval endpoints take a list of objects rather than a list of
65
+ ids — Slack channels carry an optional name and type, S3 prefixes carry an
66
+ optional bucket and label. Callers almost always have just the id, so both
67
+ forms are accepted here and widened to the object form, rather than making
68
+ every caller write ``{"id": channel_id}`` and making the simple case ugly.
69
+ """
70
+ if isinstance(value, Mapping):
71
+ return dict(value)
72
+ return {key: value}
73
+
74
+
61
75
  class _Namespace:
62
76
  """Holds the bound request function. Subclasses add methods."""
63
77
 
@@ -97,11 +111,22 @@ class SlackNamespace(_Namespace):
97
111
  "GET", f"{_V2}/connections/{_seg(connection_id)}/slack/channels"
98
112
  )
99
113
 
100
- def add_channels(self, connection_id: str, channel_ids: Sequence[str]) -> Any:
101
- """Select channels for syncing."""
114
+ def add_channels(self, connection_id: str, channels: Sequence[Any]) -> Any:
115
+ """Select channels for syncing.
116
+
117
+ Accepts bare channel ids, which is the common case::
118
+
119
+ client.connections.slack.add_channels("c1", ["C0123", "C0456"])
120
+
121
+ or dicts when you want to carry the name and type across so the server
122
+ does not have to look them up again::
123
+
124
+ add_channels("c1", [{"id": "C0123", "name": "support",
125
+ "is_private": False}])
126
+ """
102
127
  return self._request(
103
128
  "POST", f"{_V2}/connections/{_seg(connection_id)}/slack/channels",
104
- json={"channel_ids": list(channel_ids)},
129
+ json={"channels": [_as_item(c, "id") for c in channels]},
105
130
  )
106
131
 
107
132
  def remove_channel(self, connection_id: str, channel_id: str) -> Any:
@@ -195,18 +220,47 @@ class S3Namespace(_Namespace):
195
220
  "GET", f"{_V2}/connections/{_seg(connection_id)}/s3/prefixes"
196
221
  )
197
222
 
198
- def add_prefixes(self, connection_id: str, prefixes: Sequence[str]) -> Any:
199
- """Select prefixes for syncing."""
223
+ def add_prefixes(self, connection_id: str, prefixes: Sequence[Any]) -> Any:
224
+ """Select prefixes for syncing.
225
+
226
+ Accepts bare prefixes, or dicts carrying ``bucket`` and ``label``::
227
+
228
+ add_prefixes("c1", ["handbook/", "policies/"])
229
+ add_prefixes("c1", [{"prefix": "handbook/", "label": "Handbook"}])
230
+
231
+ An empty string means the bucket root. The bucket defaults to the one the
232
+ connection's credentials were validated against, and the API rejects any
233
+ other bucket rather than indexing one nobody proved access to.
234
+ """
200
235
  return self._request(
201
236
  "POST", f"{_V2}/connections/{_seg(connection_id)}/s3/prefixes",
202
- json={"prefixes": list(prefixes)},
203
- )
204
-
205
- def remove_prefixes(self, connection_id: str, prefixes: Sequence[str]) -> Any:
206
- """Stop syncing the given prefixes."""
237
+ json={"prefixes": [_as_item(p, "prefix") for p in prefixes]},
238
+ )
239
+
240
+ def remove_prefix(
241
+ self,
242
+ connection_id: str,
243
+ prefix: str = "",
244
+ *,
245
+ bucket: Optional[str] = None,
246
+ purge: bool = False,
247
+ ) -> Any:
248
+ """Revoke one prefix approval, optionally purging what it produced.
249
+
250
+ The prefix travels as a query parameter, not in the body and not as a path
251
+ segment: prefixes contain slashes, which a path segment cannot carry
252
+ unambiguously, and this endpoint reads no body at all.
253
+
254
+ Pass ``purge=True`` to also delete the memories already derived from the
255
+ prefix. The default leaves them in place, so revoking an approval does not
256
+ silently destroy knowledge.
257
+ """
258
+ params: Dict[str, Any] = {"prefix": prefix, "purge": purge}
259
+ if bucket is not None:
260
+ params["bucket"] = bucket
207
261
  return self._request(
208
262
  "DELETE", f"{_V2}/connections/{_seg(connection_id)}/s3/prefixes",
209
- json={"prefixes": list(prefixes)},
263
+ params=params,
210
264
  )
211
265
 
212
266
  def exclusion_policy(self, connection_id: str) -> Any:
@@ -286,10 +340,15 @@ class GranolaNamespace(_Namespace):
286
340
  )
287
341
 
288
342
  def relink_identity(self, connection_id: str, **body: Any) -> Any:
289
- """Move an existing mapping to a different end user."""
343
+ """Re-run identity matching for this connection.
344
+
345
+ Takes no arguments: the route reads no body and re-matches the whole
346
+ roster. ``**body`` is kept only so a forward-compatible field can be
347
+ passed once the route grows one.
348
+ """
290
349
  return self._request(
291
350
  "POST", f"{_V2}/connections/{_seg(connection_id)}/granola/identities/relink",
292
- json=body,
351
+ json=body or None,
293
352
  )
294
353
 
295
354
  def settings(self, connection_id: str) -> Any:
@@ -321,7 +380,7 @@ class ConnectionOAuthNamespace(_Namespace):
321
380
  platform back. Poll :meth:`status` to find out how it went — the callback
322
381
  does not come to your backend.
323
382
  """
324
- payload = {"provider": provider}
383
+ payload = {"provider_id": provider}
325
384
  payload.update(body)
326
385
  return self._request("POST", f"{_V2}/oauth/initiate", json=payload)
327
386
 
@@ -361,14 +420,18 @@ class ConnectionsNamespace(_Namespace):
361
420
  return self._request("GET", f"{_V2}/connections/{_seg(connection_id)}")
362
421
 
363
422
  def create_with_api_key(self, provider: str, api_key: str, **body: Any) -> Any:
364
- """Connect a provider that authenticates with an API key or bot token."""
365
- payload: Dict[str, Any] = {"provider": provider, "api_key": api_key}
423
+ """Connect a provider that authenticates with an API key or bot token.
424
+
425
+ The wire field is ``provider_id``; the argument is named ``provider``
426
+ because that is what the rest of this namespace calls it.
427
+ """
428
+ payload: Dict[str, Any] = {"provider_id": provider, "api_key": api_key}
366
429
  payload.update(body)
367
430
  return self._request("POST", f"{_V2}/connections/api-key", json=payload)
368
431
 
369
432
  def create_with_credentials(self, provider: str, credentials: Dict[str, Any], **body: Any) -> Any:
370
433
  """Connect a provider that needs a credential bundle, such as S3 keys."""
371
- payload: Dict[str, Any] = {"provider": provider, "credentials": credentials}
434
+ payload: Dict[str, Any] = {"provider_id": provider, "credentials": credentials}
372
435
  payload.update(body)
373
436
  return self._request("POST", f"{_V2}/connections/credentials", json=payload)
374
437
 
@@ -1 +0,0 @@
1
- __version__ = "1.3.0"
File without changes
File without changes
File without changes