fluxvector 0.1.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.
@@ -0,0 +1,250 @@
1
+ Metadata-Version: 2.4
2
+ Name: fluxvector
3
+ Version: 0.1.0
4
+ Summary: Official Python SDK for FluxVector — semantic search API by FluxSoft Labs
5
+ Project-URL: Homepage, https://fluxsoftlabs.com/fluxvector
6
+ Project-URL: Documentation, https://docs.fluxsoftlabs.com/fluxvector
7
+ Project-URL: Repository, https://github.com/fluxsoftlabs/fluxvector-python
8
+ Project-URL: Issues, https://github.com/fluxsoftlabs/fluxvector-python/issues
9
+ Author-email: FluxSoft Labs <hello@fluxsoftlabs.com>
10
+ License-Expression: MIT
11
+ Keywords: ai,embeddings,fluxvector,semantic-search,vector
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.9
23
+ Requires-Dist: httpx>=0.25.0
24
+ Requires-Dist: pydantic>=2.0.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: mypy>=1.0; extra == 'dev'
27
+ Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
28
+ Requires-Dist: pytest>=7.0; extra == 'dev'
29
+ Requires-Dist: respx>=0.21; extra == 'dev'
30
+ Requires-Dist: ruff>=0.1; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # FluxVector Python SDK
34
+
35
+ Official Python SDK for [FluxVector](https://fluxsoftlabs.com/fluxvector) — semantic search API by FluxSoft Labs.
36
+
37
+ ## Installation
38
+
39
+ ```bash
40
+ pip install fluxvector
41
+ ```
42
+
43
+ ## Quick Start
44
+
45
+ ```python
46
+ from fluxvector import FluxVector
47
+
48
+ fv = FluxVector(api_key="fv_live_abc123")
49
+
50
+ # Create a collection
51
+ col = fv.collections.create("products", dimension=1536, metric="cosine")
52
+
53
+ # Upsert vectors (auto-chunks at 1000)
54
+ fv.vectors.upsert("products", [
55
+ {"id": "p1", "text": "Red running shoes", "metadata": {"price": 89, "brand": "Nike"}},
56
+ {"id": "p2", "text": "Blue hiking boots", "metadata": {"price": 149, "brand": "Merrell"}},
57
+ {"id": "p3", "text": "White tennis sneakers", "metadata": {"price": 65, "brand": "Adidas"}},
58
+ ])
59
+
60
+ # Semantic search
61
+ results = fv.search("products", "comfortable shoes for running", top_k=5)
62
+ for r in results:
63
+ print(f"{r.id}: {r.score:.2f} — {r.text}")
64
+
65
+ # Search with metadata filters
66
+ results = fv.search("products", "shoes", filter={"price": {"$lt": 100}})
67
+ ```
68
+
69
+ ## Async Support
70
+
71
+ ```python
72
+ from fluxvector import AsyncFluxVector
73
+
74
+ async with AsyncFluxVector(api_key="fv_live_abc123") as fv:
75
+ results = await fv.search("products", "comfortable shoes")
76
+ for r in results:
77
+ print(f"{r.id}: {r.score:.2f}")
78
+ ```
79
+
80
+ ## Configuration
81
+
82
+ ```python
83
+ fv = FluxVector(
84
+ api_key="fv_live_abc123", # or set FLUXVECTOR_API_KEY env var
85
+ base_url="https://custom.host", # default: https://fluxvector.dev
86
+ timeout=30.0, # request timeout in seconds
87
+ max_retries=3, # retries on 429 / 5xx with exponential backoff
88
+ )
89
+ ```
90
+
91
+ ## API Reference
92
+
93
+ ### Collections
94
+
95
+ ```python
96
+ # Create
97
+ col = fv.collections.create("products", dimension=1536, metric="cosine", description="Product catalog")
98
+
99
+ # List (cursor pagination)
100
+ page = fv.collections.list(limit=10)
101
+ for col in page:
102
+ print(col.name)
103
+ # Next page
104
+ if page.has_more:
105
+ next_page = fv.collections.list(cursor=page.next_cursor)
106
+
107
+ # Get
108
+ col = fv.collections.get("products")
109
+
110
+ # Delete
111
+ fv.collections.delete("products")
112
+ ```
113
+
114
+ ### Vectors
115
+
116
+ ```python
117
+ # Upsert (auto-chunks at 1000 vectors per request)
118
+ fv.vectors.upsert("products", [
119
+ {"id": "p1", "text": "Red shoes", "metadata": {"price": 89}},
120
+ {"id": "p2", "text": "Blue hat", "values": [0.1, 0.2, ...]}, # raw vector
121
+ ])
122
+
123
+ # Query by text
124
+ results = fv.vectors.query("products", text="shoes", top_k=10)
125
+
126
+ # Query by raw vector
127
+ results = fv.vectors.query("products", vector=[0.1, 0.2, ...], top_k=5)
128
+
129
+ # Query with filter
130
+ results = fv.vectors.query(
131
+ "products",
132
+ text="shoes",
133
+ filter={"brand": {"$in": ["Nike", "Adidas"]}},
134
+ include_metadata=True,
135
+ include_text=True,
136
+ )
137
+
138
+ # Fetch by IDs
139
+ vectors = fv.vectors.fetch("products", ["p1", "p2"])
140
+
141
+ # Delete by IDs
142
+ fv.vectors.delete("products", ids=["p1", "p2"])
143
+
144
+ # Delete by filter
145
+ fv.vectors.delete("products", filter={"brand": {"$eq": "discontinued"}})
146
+ ```
147
+
148
+ ### Search
149
+
150
+ The signature method — one-line semantic search:
151
+
152
+ ```python
153
+ results = fv.search("products", "comfortable running shoes", top_k=10)
154
+ for r in results:
155
+ print(f"{r.id}: {r.score:.4f} — {r.text}")
156
+ print(f" metadata: {r.metadata}")
157
+ ```
158
+
159
+ ### Embeddings
160
+
161
+ ```python
162
+ # Single text
163
+ resp = fv.embeddings.create("Hello world")
164
+ print(resp.embedding) # [0.012, -0.034, ...]
165
+ print(resp.dimension) # 1536
166
+
167
+ # Batch
168
+ resp = fv.embeddings.batch(["Hello", "World", "Foo"])
169
+ for emb in resp.embeddings:
170
+ print(len(emb)) # 1536
171
+ ```
172
+
173
+ ### API Keys
174
+
175
+ ```python
176
+ # Create
177
+ key = fv.api_keys.create("Production Key", env="live")
178
+ print(key.key) # fv_live_... (only shown once)
179
+
180
+ # List
181
+ keys = fv.api_keys.list()
182
+ for k in keys:
183
+ print(f"{k.name}: {k.prefix}...")
184
+
185
+ # Rename
186
+ fv.api_keys.update("key_id", name="New Name")
187
+
188
+ # Delete
189
+ fv.api_keys.delete("key_id")
190
+ ```
191
+
192
+ ### Usage
193
+
194
+ ```python
195
+ # Current usage
196
+ usage = fv.usage.get()
197
+ print(f"Plan: {usage.plan}")
198
+ print(f"Requests: {usage.requests}")
199
+ print(f"Vectors stored: {usage.vectors_stored}")
200
+
201
+ # Historical usage
202
+ history = fv.usage.history(days=30)
203
+ for day in history:
204
+ print(f"{day.date}: {day.requests} requests")
205
+ ```
206
+
207
+ ## Filter Operators
208
+
209
+ Use metadata filters with any search or query method:
210
+
211
+ | Operator | Description | Example |
212
+ |----------|-------------|---------|
213
+ | `$eq` | Equal | `{"status": {"$eq": "active"}}` |
214
+ | `$ne` | Not equal | `{"status": {"$ne": "deleted"}}` |
215
+ | `$gt` | Greater than | `{"price": {"$gt": 50}}` |
216
+ | `$gte` | Greater than or equal | `{"price": {"$gte": 50}}` |
217
+ | `$lt` | Less than | `{"price": {"$lt": 100}}` |
218
+ | `$lte` | Less than or equal | `{"price": {"$lte": 100}}` |
219
+ | `$in` | In array | `{"brand": {"$in": ["Nike", "Adidas"]}}` |
220
+ | `$nin` | Not in array | `{"brand": {"$nin": ["Generic"]}}` |
221
+
222
+ ## Error Handling
223
+
224
+ ```python
225
+ from fluxvector import FluxVector, FluxVectorError, AuthenticationError, RateLimitError, NotFoundError
226
+
227
+ fv = FluxVector(api_key="fv_live_abc123")
228
+
229
+ try:
230
+ results = fv.search("products", "shoes")
231
+ except AuthenticationError:
232
+ print("Invalid API key")
233
+ except RateLimitError as e:
234
+ print(f"Rate limited: {e.message}")
235
+ except NotFoundError:
236
+ print("Collection not found")
237
+ except FluxVectorError as e:
238
+ print(f"API error {e.status_code}: {e.message}")
239
+ ```
240
+
241
+ ## Environment Variables
242
+
243
+ | Variable | Description |
244
+ |----------|-------------|
245
+ | `FLUXVECTOR_API_KEY` | Default API key (if not passed to constructor) |
246
+ | `FLUXVECTOR_BASE_URL` | Override base URL for self-hosted instances |
247
+
248
+ ## License
249
+
250
+ MIT
@@ -0,0 +1,218 @@
1
+ # FluxVector Python SDK
2
+
3
+ Official Python SDK for [FluxVector](https://fluxsoftlabs.com/fluxvector) — semantic search API by FluxSoft Labs.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pip install fluxvector
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```python
14
+ from fluxvector import FluxVector
15
+
16
+ fv = FluxVector(api_key="fv_live_abc123")
17
+
18
+ # Create a collection
19
+ col = fv.collections.create("products", dimension=1536, metric="cosine")
20
+
21
+ # Upsert vectors (auto-chunks at 1000)
22
+ fv.vectors.upsert("products", [
23
+ {"id": "p1", "text": "Red running shoes", "metadata": {"price": 89, "brand": "Nike"}},
24
+ {"id": "p2", "text": "Blue hiking boots", "metadata": {"price": 149, "brand": "Merrell"}},
25
+ {"id": "p3", "text": "White tennis sneakers", "metadata": {"price": 65, "brand": "Adidas"}},
26
+ ])
27
+
28
+ # Semantic search
29
+ results = fv.search("products", "comfortable shoes for running", top_k=5)
30
+ for r in results:
31
+ print(f"{r.id}: {r.score:.2f} — {r.text}")
32
+
33
+ # Search with metadata filters
34
+ results = fv.search("products", "shoes", filter={"price": {"$lt": 100}})
35
+ ```
36
+
37
+ ## Async Support
38
+
39
+ ```python
40
+ from fluxvector import AsyncFluxVector
41
+
42
+ async with AsyncFluxVector(api_key="fv_live_abc123") as fv:
43
+ results = await fv.search("products", "comfortable shoes")
44
+ for r in results:
45
+ print(f"{r.id}: {r.score:.2f}")
46
+ ```
47
+
48
+ ## Configuration
49
+
50
+ ```python
51
+ fv = FluxVector(
52
+ api_key="fv_live_abc123", # or set FLUXVECTOR_API_KEY env var
53
+ base_url="https://custom.host", # default: https://fluxvector.dev
54
+ timeout=30.0, # request timeout in seconds
55
+ max_retries=3, # retries on 429 / 5xx with exponential backoff
56
+ )
57
+ ```
58
+
59
+ ## API Reference
60
+
61
+ ### Collections
62
+
63
+ ```python
64
+ # Create
65
+ col = fv.collections.create("products", dimension=1536, metric="cosine", description="Product catalog")
66
+
67
+ # List (cursor pagination)
68
+ page = fv.collections.list(limit=10)
69
+ for col in page:
70
+ print(col.name)
71
+ # Next page
72
+ if page.has_more:
73
+ next_page = fv.collections.list(cursor=page.next_cursor)
74
+
75
+ # Get
76
+ col = fv.collections.get("products")
77
+
78
+ # Delete
79
+ fv.collections.delete("products")
80
+ ```
81
+
82
+ ### Vectors
83
+
84
+ ```python
85
+ # Upsert (auto-chunks at 1000 vectors per request)
86
+ fv.vectors.upsert("products", [
87
+ {"id": "p1", "text": "Red shoes", "metadata": {"price": 89}},
88
+ {"id": "p2", "text": "Blue hat", "values": [0.1, 0.2, ...]}, # raw vector
89
+ ])
90
+
91
+ # Query by text
92
+ results = fv.vectors.query("products", text="shoes", top_k=10)
93
+
94
+ # Query by raw vector
95
+ results = fv.vectors.query("products", vector=[0.1, 0.2, ...], top_k=5)
96
+
97
+ # Query with filter
98
+ results = fv.vectors.query(
99
+ "products",
100
+ text="shoes",
101
+ filter={"brand": {"$in": ["Nike", "Adidas"]}},
102
+ include_metadata=True,
103
+ include_text=True,
104
+ )
105
+
106
+ # Fetch by IDs
107
+ vectors = fv.vectors.fetch("products", ["p1", "p2"])
108
+
109
+ # Delete by IDs
110
+ fv.vectors.delete("products", ids=["p1", "p2"])
111
+
112
+ # Delete by filter
113
+ fv.vectors.delete("products", filter={"brand": {"$eq": "discontinued"}})
114
+ ```
115
+
116
+ ### Search
117
+
118
+ The signature method — one-line semantic search:
119
+
120
+ ```python
121
+ results = fv.search("products", "comfortable running shoes", top_k=10)
122
+ for r in results:
123
+ print(f"{r.id}: {r.score:.4f} — {r.text}")
124
+ print(f" metadata: {r.metadata}")
125
+ ```
126
+
127
+ ### Embeddings
128
+
129
+ ```python
130
+ # Single text
131
+ resp = fv.embeddings.create("Hello world")
132
+ print(resp.embedding) # [0.012, -0.034, ...]
133
+ print(resp.dimension) # 1536
134
+
135
+ # Batch
136
+ resp = fv.embeddings.batch(["Hello", "World", "Foo"])
137
+ for emb in resp.embeddings:
138
+ print(len(emb)) # 1536
139
+ ```
140
+
141
+ ### API Keys
142
+
143
+ ```python
144
+ # Create
145
+ key = fv.api_keys.create("Production Key", env="live")
146
+ print(key.key) # fv_live_... (only shown once)
147
+
148
+ # List
149
+ keys = fv.api_keys.list()
150
+ for k in keys:
151
+ print(f"{k.name}: {k.prefix}...")
152
+
153
+ # Rename
154
+ fv.api_keys.update("key_id", name="New Name")
155
+
156
+ # Delete
157
+ fv.api_keys.delete("key_id")
158
+ ```
159
+
160
+ ### Usage
161
+
162
+ ```python
163
+ # Current usage
164
+ usage = fv.usage.get()
165
+ print(f"Plan: {usage.plan}")
166
+ print(f"Requests: {usage.requests}")
167
+ print(f"Vectors stored: {usage.vectors_stored}")
168
+
169
+ # Historical usage
170
+ history = fv.usage.history(days=30)
171
+ for day in history:
172
+ print(f"{day.date}: {day.requests} requests")
173
+ ```
174
+
175
+ ## Filter Operators
176
+
177
+ Use metadata filters with any search or query method:
178
+
179
+ | Operator | Description | Example |
180
+ |----------|-------------|---------|
181
+ | `$eq` | Equal | `{"status": {"$eq": "active"}}` |
182
+ | `$ne` | Not equal | `{"status": {"$ne": "deleted"}}` |
183
+ | `$gt` | Greater than | `{"price": {"$gt": 50}}` |
184
+ | `$gte` | Greater than or equal | `{"price": {"$gte": 50}}` |
185
+ | `$lt` | Less than | `{"price": {"$lt": 100}}` |
186
+ | `$lte` | Less than or equal | `{"price": {"$lte": 100}}` |
187
+ | `$in` | In array | `{"brand": {"$in": ["Nike", "Adidas"]}}` |
188
+ | `$nin` | Not in array | `{"brand": {"$nin": ["Generic"]}}` |
189
+
190
+ ## Error Handling
191
+
192
+ ```python
193
+ from fluxvector import FluxVector, FluxVectorError, AuthenticationError, RateLimitError, NotFoundError
194
+
195
+ fv = FluxVector(api_key="fv_live_abc123")
196
+
197
+ try:
198
+ results = fv.search("products", "shoes")
199
+ except AuthenticationError:
200
+ print("Invalid API key")
201
+ except RateLimitError as e:
202
+ print(f"Rate limited: {e.message}")
203
+ except NotFoundError:
204
+ print("Collection not found")
205
+ except FluxVectorError as e:
206
+ print(f"API error {e.status_code}: {e.message}")
207
+ ```
208
+
209
+ ## Environment Variables
210
+
211
+ | Variable | Description |
212
+ |----------|-------------|
213
+ | `FLUXVECTOR_API_KEY` | Default API key (if not passed to constructor) |
214
+ | `FLUXVECTOR_BASE_URL` | Override base URL for self-hosted instances |
215
+
216
+ ## License
217
+
218
+ MIT
@@ -0,0 +1,49 @@
1
+ """FluxVector Python SDK — Semantic search by FluxSoft Labs."""
2
+
3
+ from fluxvector._version import __version__
4
+ from fluxvector.async_client import AsyncFluxVector
5
+ from fluxvector.client import FluxVector
6
+ from fluxvector.errors import (
7
+ AuthenticationError,
8
+ FluxVectorError,
9
+ InternalServerError,
10
+ NotFoundError,
11
+ PermissionDeniedError,
12
+ RateLimitError,
13
+ ValidationError,
14
+ )
15
+ from fluxvector.types import (
16
+ ApiKey,
17
+ Collection,
18
+ EmbedBatchResponse,
19
+ EmbedResponse,
20
+ SearchResponse,
21
+ SearchResult,
22
+ UpsertResponse,
23
+ UsageSummary,
24
+ Vector,
25
+ )
26
+
27
+ __all__ = [
28
+ "__version__",
29
+ "FluxVector",
30
+ "AsyncFluxVector",
31
+ # Errors
32
+ "FluxVectorError",
33
+ "AuthenticationError",
34
+ "NotFoundError",
35
+ "PermissionDeniedError",
36
+ "RateLimitError",
37
+ "ValidationError",
38
+ "InternalServerError",
39
+ # Types
40
+ "ApiKey",
41
+ "Collection",
42
+ "EmbedBatchResponse",
43
+ "EmbedResponse",
44
+ "SearchResponse",
45
+ "SearchResult",
46
+ "UpsertResponse",
47
+ "UsageSummary",
48
+ "Vector",
49
+ ]
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,139 @@
1
+ """Asynchronous FluxVector client."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import os
7
+ from typing import Any, Optional
8
+
9
+ import httpx
10
+
11
+ from fluxvector._version import __version__
12
+ from fluxvector.errors import ConnectionError as FVConnectionError
13
+ from fluxvector.errors import raise_for_status
14
+ from fluxvector.resources.api_keys import AsyncApiKeysResource
15
+ from fluxvector.resources.collections import AsyncCollectionsResource
16
+ from fluxvector.resources.embeddings import AsyncEmbeddingsResource
17
+ from fluxvector.resources.search import AsyncSearchResource
18
+ from fluxvector.resources.usage import AsyncUsageResource
19
+ from fluxvector.resources.vectors import AsyncVectorsResource
20
+ from fluxvector.types import SearchResponse
21
+
22
+ _DEFAULT_BASE_URL = "https://fluxvector.dev"
23
+ _DEFAULT_TIMEOUT = 30.0
24
+ _MAX_RETRIES = 3
25
+ _RETRY_STATUSES = {429, 500, 502, 503, 504}
26
+ _BACKOFF_BASE = 0.5
27
+
28
+
29
+ class AsyncFluxVector:
30
+ """Asynchronous FluxVector API client.
31
+
32
+ Usage::
33
+
34
+ async with AsyncFluxVector(api_key="fv_live_abc123") as fv:
35
+ results = await fv.search("products", "comfortable shoes")
36
+ """
37
+
38
+ def __init__(
39
+ self,
40
+ *,
41
+ api_key: Optional[str] = None,
42
+ base_url: Optional[str] = None,
43
+ timeout: float = _DEFAULT_TIMEOUT,
44
+ max_retries: int = _MAX_RETRIES,
45
+ ) -> None:
46
+ self.api_key = api_key or os.environ.get("FLUXVECTOR_API_KEY", "")
47
+ if not self.api_key:
48
+ raise ValueError(
49
+ "API key is required. Pass api_key= or set FLUXVECTOR_API_KEY."
50
+ )
51
+
52
+ self.base_url = (base_url or os.environ.get("FLUXVECTOR_BASE_URL", _DEFAULT_BASE_URL)).rstrip("/")
53
+ self.max_retries = max_retries
54
+
55
+ self._client = httpx.AsyncClient(
56
+ base_url=self.base_url,
57
+ timeout=timeout,
58
+ headers={
59
+ "Authorization": f"Bearer {self.api_key}",
60
+ "Content-Type": "application/json",
61
+ "User-Agent": f"fluxvector-python/{__version__}",
62
+ },
63
+ )
64
+
65
+ # Resources
66
+ self.collections = AsyncCollectionsResource(self)
67
+ self.vectors = AsyncVectorsResource(self)
68
+ self.search = AsyncSearchResource(self)
69
+ self.embeddings = AsyncEmbeddingsResource(self)
70
+ self.api_keys = AsyncApiKeysResource(self)
71
+ self.usage = AsyncUsageResource(self)
72
+
73
+ def __repr__(self) -> str:
74
+ return f"AsyncFluxVector(base_url={self.base_url!r})"
75
+
76
+ async def __aenter__(self) -> AsyncFluxVector:
77
+ return self
78
+
79
+ async def __aexit__(self, *args: Any) -> None:
80
+ await self.close()
81
+
82
+ async def close(self) -> None:
83
+ """Close the underlying HTTP connection pool."""
84
+ await self._client.aclose()
85
+
86
+ # ── Internal HTTP methods ────────────────────────────────────────────
87
+
88
+ async def _request(self, method: str, path: str, **kwargs: Any) -> Any:
89
+ """Make an HTTP request with retry logic."""
90
+ last_exc: Optional[Exception] = None
91
+ for attempt in range(self.max_retries + 1):
92
+ try:
93
+ response = await self._client.request(method, path, **kwargs)
94
+ if response.status_code in _RETRY_STATUSES and attempt < self.max_retries:
95
+ await self._backoff(attempt, response.headers)
96
+ continue
97
+ body = response.json() if response.content else {}
98
+ raise_for_status(response.status_code, body, dict(response.headers))
99
+ return body
100
+ except httpx.HTTPStatusError as exc:
101
+ last_exc = exc
102
+ if attempt < self.max_retries:
103
+ await self._backoff(attempt)
104
+ continue
105
+ raise
106
+ except (httpx.ConnectError, httpx.TimeoutException) as exc:
107
+ last_exc = exc
108
+ if attempt < self.max_retries:
109
+ await self._backoff(attempt)
110
+ continue
111
+ raise FVConnectionError(
112
+ f"Failed to connect to {self.base_url}: {exc}"
113
+ ) from exc
114
+ raise last_exc # type: ignore[misc]
115
+
116
+ @staticmethod
117
+ async def _backoff(attempt: int, headers: Optional[Any] = None) -> None:
118
+ """Exponential backoff with optional Retry-After header support."""
119
+ if headers and hasattr(headers, "get"):
120
+ retry_after = headers.get("Retry-After")
121
+ if retry_after:
122
+ try:
123
+ await asyncio.sleep(float(retry_after))
124
+ return
125
+ except (ValueError, TypeError):
126
+ pass
127
+ await asyncio.sleep(_BACKOFF_BASE * (2 ** attempt))
128
+
129
+ async def _get(self, path: str, *, params: Optional[dict] = None) -> Any:
130
+ return await self._request("GET", path, params=params)
131
+
132
+ async def _post(self, path: str, *, json: Optional[dict] = None) -> Any:
133
+ return await self._request("POST", path, json=json)
134
+
135
+ async def _patch(self, path: str, *, json: Optional[dict] = None) -> Any:
136
+ return await self._request("PATCH", path, json=json)
137
+
138
+ async def _delete(self, path: str) -> Any:
139
+ return await self._request("DELETE", path)