polygres-sdk 0.2.1__tar.gz → 0.3.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.
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/CONTRIBUTING.md +8 -5
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/PKG-INFO +27 -3
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/README.md +26 -2
- polygres_sdk-0.3.0/compatibility/0.2.1/README.md +8 -0
- {polygres_sdk-0.2.1/src/polygres/spec → polygres_sdk-0.3.0/compatibility/0.2.1}/python-sdk-v1.methods.json +7 -4
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/docs/reference-v1.md +42 -1
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/pyproject.toml +1 -1
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/__init__.py +8 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/auth/enums.py +2 -0
- polygres_sdk-0.3.0/src/polygres/_vendor/polygres_lib/auth/errors.py +230 -0
- polygres_sdk-0.3.0/src/polygres/_vendor/polygres_lib/context/errors.py +103 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/context/models.py +3 -3
- polygres_sdk-0.3.0/src/polygres/_vendor/polygres_lib/errors/__init__.py +27 -0
- polygres_sdk-0.3.0/src/polygres/_vendor/polygres_lib/errors/catalog.py +205 -0
- polygres_sdk-0.3.0/src/polygres/_vendor/polygres_lib/errors/generated.py +9385 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/client.py +55 -4
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/context_wait.py +23 -14
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/errors.py +15 -32
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/models.py +99 -0
- polygres_sdk-0.3.0/src/polygres/rows.py +384 -0
- polygres_sdk-0.3.0/src/polygres/spec/python-sdk-v1.methods.json +4892 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/spec/runtime-v1.openapi.json +3554 -2114
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tests/fixtures/context/contract-fixtures.json +20 -15
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tests/test_client.py +15 -2
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tests/test_context.py +13 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tests/test_method_spec.py +1 -1
- polygres_sdk-0.3.0/tests/test_release_workflow.py +95 -0
- polygres_sdk-0.3.0/tests/test_rows.py +279 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tests/test_version.py +6 -6
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tests/test_version_notices.py +9 -9
- polygres_sdk-0.3.0/tools/extract_release_notes.py +57 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tools/generate_sdk_method_spec.py +2 -0
- polygres_sdk-0.2.1/src/polygres/_vendor/polygres_lib/auth/errors.py +0 -895
- polygres_sdk-0.2.1/src/polygres/_vendor/polygres_lib/context/errors.py +0 -266
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/.gitignore +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/LICENSE +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/compatibility/0.1.0/README.md +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/compatibility/0.1.0/python-sdk-v1.methods.json +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/migrations/README.md +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/migrations/vector-configuration-creation.md +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_api_version.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_method_metadata.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/__init__.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/__init__.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/api_versions.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/auth/__init__.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/auth/api_keys.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/auth/models.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/auth/ports.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/auth/principals.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/auth/records.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/auth/types.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/context/__init__.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/context/enums.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/context/validation.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/core/__init__.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/core/models.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/core/types.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_version.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/context.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/context_models.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/context_validation.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/py.typed +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/spec/__init__.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/spec/python-sdk-v1.methods.schema.json +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/version_notices.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tests/conftest.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tests/test_compatibility.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tests/test_packaged_resources.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tools/check_sdk_compatibility.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tools/generate_runtime_openapi_snapshot.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tools/run_context_live_test.sh +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tools/runtime_operation_exclusions.json +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tools/test_context_live.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tools/verify_distribution.py +0 -0
- {polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/tools/verify_release_version.py +0 -0
|
@@ -23,7 +23,7 @@ python tools/generate_runtime_openapi_snapshot.py --check
|
|
|
23
23
|
python tools/generate_sdk_method_spec.py --check
|
|
24
24
|
python tools/generate_sdk_method_spec.py --validate-only
|
|
25
25
|
python tools/check_sdk_compatibility.py \
|
|
26
|
-
--baseline compatibility/0.1
|
|
26
|
+
--baseline compatibility/0.2.1/python-sdk-v1.methods.json
|
|
27
27
|
python -m build
|
|
28
28
|
python tools/verify_distribution.py dist/*.whl dist/*.tar.gz
|
|
29
29
|
```
|
|
@@ -60,7 +60,7 @@ Release rehearsal happens first on TestPyPI:
|
|
|
60
60
|
/tmp/polygres-sdk-testpypi/bin/python -m pip install \
|
|
61
61
|
--index-url https://test.pypi.org/simple/ \
|
|
62
62
|
--extra-index-url https://pypi.org/simple/ \
|
|
63
|
-
polygres-sdk==0.
|
|
63
|
+
polygres-sdk==0.3.0
|
|
64
64
|
/tmp/polygres-sdk-testpypi/bin/python - <<'PY'
|
|
65
65
|
import importlib.metadata
|
|
66
66
|
from polygres import Polygres
|
|
@@ -74,12 +74,15 @@ Publish to PyPI only after the same commit has passed TestPyPI validation. Push
|
|
|
74
74
|
a release tag from the public repository:
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
git tag polygres-sdk-v0.
|
|
78
|
-
git push origin polygres-sdk-v0.
|
|
77
|
+
git tag polygres-sdk-v0.3.0
|
|
78
|
+
git push origin polygres-sdk-v0.3.0
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
The `pypi` GitHub environment must be approved before the upload proceeds.
|
|
82
|
-
|
|
82
|
+
After PyPI accepts the package, the workflow creates the matching GitHub
|
|
83
|
+
Release from the version's changelog entry and attaches the wheel and source
|
|
84
|
+
distribution. Project URLs in `pyproject.toml` must use valid HTTPS URLs
|
|
85
|
+
accepted by PyPI.
|
|
83
86
|
|
|
84
87
|
## Scope
|
|
85
88
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: polygres-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Python SDK for Polygres
|
|
5
5
|
Project-URL: Homepage, https://polygres.com
|
|
6
6
|
Project-URL: Documentation, https://docs.polygres.com/sdk
|
|
@@ -285,13 +285,37 @@ print(connection.direct_url_without_password)
|
|
|
285
285
|
|
|
286
286
|
Use a PostgreSQL driver such as psycopg or SQLAlchemy when your application needs a database connection. The Polygres SDK is an HTTP retrieval client and does not bundle a PostgreSQL driver.
|
|
287
287
|
|
|
288
|
+
## Single-row writes
|
|
289
|
+
|
|
290
|
+
Use `project.rows` for one JSON-native row. Context reconciliation is explicit:
|
|
291
|
+
omit both Context options for a generic table, or select one collection so the
|
|
292
|
+
same operation writes the row and creates its pgContext point.
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
result = project.rows.upsert(
|
|
296
|
+
schema="public",
|
|
297
|
+
table="memories",
|
|
298
|
+
row={"id": "memory_123", "content": "Remember the deployment window."},
|
|
299
|
+
conflict_columns=["id"],
|
|
300
|
+
returning=["id"],
|
|
301
|
+
context_collection_id="2e172638-bd77-4a2c-bc42-406f4f2938d7",
|
|
302
|
+
idempotency_key="memory-123-v1",
|
|
303
|
+
wait_for_context=True,
|
|
304
|
+
)
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
UUIDs and timestamps are JSON strings. Arrays and vectors are JSON arrays.
|
|
308
|
+
Never automatically retry a row-only write after a timeout; its outcome may be
|
|
309
|
+
ambiguous. A Context-backed request may be resumed only with the exact same
|
|
310
|
+
payload and idempotency key.
|
|
311
|
+
|
|
288
312
|
## Version and support
|
|
289
313
|
|
|
290
|
-
Package version: [`0.
|
|
314
|
+
Package version: [`0.3.0`](https://github.com/Evokoa/polygres-sdk/releases/tag/polygres-sdk-v0.3.0).
|
|
291
315
|
|
|
292
316
|
When contacting support, include the installed SDK version and the request ID.
|
|
293
317
|
|
|
294
|
-
See the [SDK 0.
|
|
318
|
+
See the [SDK 0.3.0 release notes](https://github.com/Evokoa/polygres-sdk/releases/tag/polygres-sdk-v0.3.0) for release changes.
|
|
295
319
|
|
|
296
320
|
## Optional Agent Skill
|
|
297
321
|
|
|
@@ -243,13 +243,37 @@ print(connection.direct_url_without_password)
|
|
|
243
243
|
|
|
244
244
|
Use a PostgreSQL driver such as psycopg or SQLAlchemy when your application needs a database connection. The Polygres SDK is an HTTP retrieval client and does not bundle a PostgreSQL driver.
|
|
245
245
|
|
|
246
|
+
## Single-row writes
|
|
247
|
+
|
|
248
|
+
Use `project.rows` for one JSON-native row. Context reconciliation is explicit:
|
|
249
|
+
omit both Context options for a generic table, or select one collection so the
|
|
250
|
+
same operation writes the row and creates its pgContext point.
|
|
251
|
+
|
|
252
|
+
```python
|
|
253
|
+
result = project.rows.upsert(
|
|
254
|
+
schema="public",
|
|
255
|
+
table="memories",
|
|
256
|
+
row={"id": "memory_123", "content": "Remember the deployment window."},
|
|
257
|
+
conflict_columns=["id"],
|
|
258
|
+
returning=["id"],
|
|
259
|
+
context_collection_id="2e172638-bd77-4a2c-bc42-406f4f2938d7",
|
|
260
|
+
idempotency_key="memory-123-v1",
|
|
261
|
+
wait_for_context=True,
|
|
262
|
+
)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
UUIDs and timestamps are JSON strings. Arrays and vectors are JSON arrays.
|
|
266
|
+
Never automatically retry a row-only write after a timeout; its outcome may be
|
|
267
|
+
ambiguous. A Context-backed request may be resumed only with the exact same
|
|
268
|
+
payload and idempotency key.
|
|
269
|
+
|
|
246
270
|
## Version and support
|
|
247
271
|
|
|
248
|
-
Package version: [`0.
|
|
272
|
+
Package version: [`0.3.0`](https://github.com/Evokoa/polygres-sdk/releases/tag/polygres-sdk-v0.3.0).
|
|
249
273
|
|
|
250
274
|
When contacting support, include the installed SDK version and the request ID.
|
|
251
275
|
|
|
252
|
-
See the [SDK 0.
|
|
276
|
+
See the [SDK 0.3.0 release notes](https://github.com/Evokoa/polygres-sdk/releases/tag/polygres-sdk-v0.3.0) for release changes.
|
|
253
277
|
|
|
254
278
|
## Optional Agent Skill
|
|
255
279
|
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# SDK compatibility baseline 0.2.1
|
|
2
|
+
|
|
3
|
+
`python-sdk-v1.methods.json` is the explicit released 0.2.1 public network
|
|
4
|
+
surface used by CI and release checks for 0.3.0. It is copied byte-for-byte
|
|
5
|
+
from the generated method contract at the 0.2.1 release boundary.
|
|
6
|
+
|
|
7
|
+
Release automation must name this file with `--baseline`; the compatibility
|
|
8
|
+
tool refuses to infer a baseline from an arbitrary local artifact.
|
|
@@ -3653,7 +3653,8 @@
|
|
|
3653
3653
|
"limit",
|
|
3654
3654
|
"max_depth",
|
|
3655
3655
|
"relationship_types",
|
|
3656
|
-
"start"
|
|
3656
|
+
"start",
|
|
3657
|
+
"target_table"
|
|
3657
3658
|
],
|
|
3658
3659
|
"required_fields": [
|
|
3659
3660
|
"start"
|
|
@@ -3723,7 +3724,8 @@
|
|
|
3723
3724
|
"limit",
|
|
3724
3725
|
"max_depth",
|
|
3725
3726
|
"relationship_types",
|
|
3726
|
-
"start"
|
|
3727
|
+
"start",
|
|
3728
|
+
"target_table"
|
|
3727
3729
|
],
|
|
3728
3730
|
"required_fields": [
|
|
3729
3731
|
"start"
|
|
@@ -3862,7 +3864,8 @@
|
|
|
3862
3864
|
"limit",
|
|
3863
3865
|
"max_depth",
|
|
3864
3866
|
"relationship_types",
|
|
3865
|
-
"start"
|
|
3867
|
+
"start",
|
|
3868
|
+
"target_table"
|
|
3866
3869
|
],
|
|
3867
3870
|
"required_fields": [
|
|
3868
3871
|
"start"
|
|
@@ -4454,7 +4457,7 @@
|
|
|
4454
4457
|
"stability": "stable"
|
|
4455
4458
|
}
|
|
4456
4459
|
],
|
|
4457
|
-
"openapi_sha256": "
|
|
4460
|
+
"openapi_sha256": "7dac7258e9dd8e7c92019025effee85ec71a19bca98154d20d7c00f7d8450f8a",
|
|
4458
4461
|
"schema_version": 1,
|
|
4459
4462
|
"sdk": "polygres-sdk",
|
|
4460
4463
|
"sdk_version": "0.2.1"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This file is generated. Edit Python method metadata, docstrings, or the Runtime OpenAPI contract instead.
|
|
4
4
|
|
|
5
|
-
- SDK package version: `0.
|
|
5
|
+
- SDK package version: `0.3.0`
|
|
6
6
|
- HTTP API family: `v1`
|
|
7
7
|
- Default API behavior version: `2026-08-04`
|
|
8
8
|
- Method specification schema: `1`
|
|
@@ -503,6 +503,47 @@ Run vector-first hybrid retrieval.
|
|
|
503
503
|
page = project.hybrid.vector_first(embedding)
|
|
504
504
|
```
|
|
505
505
|
|
|
506
|
+
## `project.rows`
|
|
507
|
+
|
|
508
|
+
| SDK method | HTTP operation | Since | Retry | Pagination | Deprecated |
|
|
509
|
+
| --- | --- | --- | --- | --- | --- |
|
|
510
|
+
| `project.rows.ignore()` | `POST /v1/tables/{schema_name}/{table_name}/rows` (`runtime_write_row`) | `0.3.0` | `none` | `none` | No |
|
|
511
|
+
| `project.rows.insert()` | `POST /v1/tables/{schema_name}/{table_name}/rows` (`runtime_write_row`) | `0.3.0` | `none` | `none` | No |
|
|
512
|
+
| `project.rows.upsert()` | `POST /v1/tables/{schema_name}/{table_name}/rows` (`runtime_write_row`) | `0.3.0` | `none` | `none` | No |
|
|
513
|
+
| `project.rows.validate()` | `POST /v1/tables/{schema_name}/{table_name}/rows/validate` (`runtime_validate_row_write`) | `0.3.0` | `read_only_post` | `none` | No |
|
|
514
|
+
|
|
515
|
+
### `project.rows.ignore()`
|
|
516
|
+
|
|
517
|
+
Insert one row and ignore a matching primary or unique conflict.
|
|
518
|
+
|
|
519
|
+
```python
|
|
520
|
+
result = project.rows.ignore(schema="public", table="events", row={"id": "1"}, conflict_columns=["id"])
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### `project.rows.insert()`
|
|
524
|
+
|
|
525
|
+
Insert one row into an eligible project-owned table.
|
|
526
|
+
|
|
527
|
+
```python
|
|
528
|
+
result = project.rows.insert(schema="public", table="events", row={"id": "1"})
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
### `project.rows.upsert()`
|
|
532
|
+
|
|
533
|
+
Upsert one row through a primary or unique constraint.
|
|
534
|
+
|
|
535
|
+
```python
|
|
536
|
+
result = project.rows.upsert(schema="public", table="events", row={"id": "1"}, conflict_columns=["id"])
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
### `project.rows.validate()`
|
|
540
|
+
|
|
541
|
+
Validate a single-row write without executing it.
|
|
542
|
+
|
|
543
|
+
```python
|
|
544
|
+
validation = project.rows.validate(schema="public", table="events", row={"id": "1"})
|
|
545
|
+
```
|
|
546
|
+
|
|
506
547
|
## `project.text`
|
|
507
548
|
|
|
508
549
|
| SDK method | HTTP operation | Since | Retry | Pagination | Deprecated |
|
|
@@ -3,6 +3,7 @@ from polygres.client import Polygres
|
|
|
3
3
|
from polygres.context_models import * # noqa: F403
|
|
4
4
|
from polygres.context_models import __all__ as _context_model_exports
|
|
5
5
|
from polygres.errors import (
|
|
6
|
+
PolygresAmbiguousWriteError,
|
|
6
7
|
PolygresAPIError,
|
|
7
8
|
PolygresAuthError,
|
|
8
9
|
PolygresError,
|
|
@@ -23,6 +24,9 @@ from polygres.models import (
|
|
|
23
24
|
HybridResult,
|
|
24
25
|
Page,
|
|
25
26
|
RetrievalReadiness,
|
|
27
|
+
RowContextReconciliationResult,
|
|
28
|
+
RowWriteResult,
|
|
29
|
+
RowWriteValidation,
|
|
26
30
|
TextResult,
|
|
27
31
|
VectorResult,
|
|
28
32
|
)
|
|
@@ -39,6 +43,7 @@ __all__ = [
|
|
|
39
43
|
"Page",
|
|
40
44
|
"Polygres",
|
|
41
45
|
"PolygresAPIError",
|
|
46
|
+
"PolygresAmbiguousWriteError",
|
|
42
47
|
"PolygresAuthError",
|
|
43
48
|
"PolygresError",
|
|
44
49
|
"PolygresMaintenanceError",
|
|
@@ -50,6 +55,9 @@ __all__ = [
|
|
|
50
55
|
"PolygresVersionNotice",
|
|
51
56
|
"PolygresVersionWarning",
|
|
52
57
|
"RetrievalReadiness",
|
|
58
|
+
"RowContextReconciliationResult",
|
|
59
|
+
"RowWriteResult",
|
|
60
|
+
"RowWriteValidation",
|
|
53
61
|
"TextResult",
|
|
54
62
|
"VectorResult",
|
|
55
63
|
"__version__",
|
|
@@ -233,6 +233,7 @@ class RuntimeScope(str, Enum):
|
|
|
233
233
|
HYBRID_READ = "hybrid:read"
|
|
234
234
|
CONTEXT_READ = "context:read"
|
|
235
235
|
CONTEXT_MANAGE = "context:manage"
|
|
236
|
+
ROWS_WRITE = "rows:write"
|
|
236
237
|
|
|
237
238
|
|
|
238
239
|
class RuntimeClientKind(str, Enum):
|
|
@@ -366,6 +367,7 @@ class PolicyId(str, Enum):
|
|
|
366
367
|
PROJECT_PERMISSION = "project_permission"
|
|
367
368
|
PROJECT_API_KEY = "project_api_key"
|
|
368
369
|
GATEWAY_RUNTIME = "gateway_runtime"
|
|
370
|
+
RUNTIME_ROW_WRITE = "runtime_row_write"
|
|
369
371
|
CLI_FLOW = "cli_flow"
|
|
370
372
|
RECOVERY_SESSION = "recovery_session"
|
|
371
373
|
LEGACY_DEMO = "legacy_demo"
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from types import MappingProxyType
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
from ..errors import ERROR_CATALOG, PolygresError, error_record
|
|
12
|
+
from .enums import (
|
|
13
|
+
CliExitCode,
|
|
14
|
+
PasswordRequirement,
|
|
15
|
+
ProjectStatus,
|
|
16
|
+
ResetClass,
|
|
17
|
+
RetryClass,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AuthErrorCategory(str, Enum):
|
|
22
|
+
VALIDATION = "validation"
|
|
23
|
+
AUTHENTICATION = "authentication"
|
|
24
|
+
AUTHORIZATION = "authorization"
|
|
25
|
+
ACCOUNT_GATE = "account_gate"
|
|
26
|
+
CONFLICT = "conflict"
|
|
27
|
+
RATE_LIMIT = "rate_limit"
|
|
28
|
+
DEPENDENCY = "dependency"
|
|
29
|
+
TERMINAL_SESSION = "terminal_session"
|
|
30
|
+
UNEXPECTED = "unexpected"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AuthErrorCode(str, Enum):
|
|
34
|
+
AUTH_REQUIRED = "AUTH_REQUIRED"
|
|
35
|
+
AUTH_MODE_NOT_ALLOWED = "AUTH_MODE_NOT_ALLOWED"
|
|
36
|
+
AUTH_NOT_CONFIGURED = "AUTH_NOT_CONFIGURED"
|
|
37
|
+
AUTH_INVALID_CREDENTIALS = "AUTH_INVALID_CREDENTIALS"
|
|
38
|
+
AUTH_EMAIL_REQUIRED = "AUTH_EMAIL_REQUIRED"
|
|
39
|
+
INVALID_TOKEN = "INVALID_TOKEN"
|
|
40
|
+
TOKEN_EXPIRED = "TOKEN_EXPIRED"
|
|
41
|
+
LOCAL_SESSION_CORRUPT = "LOCAL_SESSION_CORRUPT"
|
|
42
|
+
LOCAL_SESSION_RESET_LIMIT_REACHED = "LOCAL_SESSION_RESET_LIMIT_REACHED"
|
|
43
|
+
AUTH_PROVIDER_UNAVAILABLE = "AUTH_PROVIDER_UNAVAILABLE"
|
|
44
|
+
AUTH_PKCE_EXCHANGE_FAILED = "AUTH_PKCE_EXCHANGE_FAILED"
|
|
45
|
+
AUTH_PKCE_VERIFIER_MISSING = "AUTH_PKCE_VERIFIER_MISSING"
|
|
46
|
+
AUTH_ACTION_INVALID = "AUTH_ACTION_INVALID"
|
|
47
|
+
AUTH_ACTION_EXPIRED = "AUTH_ACTION_EXPIRED"
|
|
48
|
+
AUTH_RECOVERY_REQUIRED = "AUTH_RECOVERY_REQUIRED"
|
|
49
|
+
RECOVERY_GRANT_INVALID = "RECOVERY_GRANT_INVALID"
|
|
50
|
+
AUTH_REAUTHENTICATION_REQUIRED = "AUTH_REAUTHENTICATION_REQUIRED"
|
|
51
|
+
AUTH_REAUTHENTICATION_INVALID = "AUTH_REAUTHENTICATION_INVALID"
|
|
52
|
+
AUTH_PASSWORD_MODE_MISMATCH = "AUTH_PASSWORD_MODE_MISMATCH"
|
|
53
|
+
AUTH_WEAK_PASSWORD = "AUTH_WEAK_PASSWORD"
|
|
54
|
+
AUTH_IDENTITY_CHANGED = "AUTH_IDENTITY_CHANGED"
|
|
55
|
+
EMAIL_NOT_VERIFIED = "EMAIL_NOT_VERIFIED"
|
|
56
|
+
ACCOUNT_NOT_ELIGIBLE = "ACCOUNT_NOT_ELIGIBLE"
|
|
57
|
+
APPROVAL_REQUIRED = "APPROVAL_REQUIRED"
|
|
58
|
+
ADMIN_NOT_ALLOWED = "ADMIN_NOT_ALLOWED"
|
|
59
|
+
PERMISSION_DENIED = "PERMISSION_DENIED"
|
|
60
|
+
NOT_FOUND = "NOT_FOUND"
|
|
61
|
+
PROJECT_NOT_READY = "PROJECT_NOT_READY"
|
|
62
|
+
INVITATION_NOT_FOUND = "INVITATION_NOT_FOUND"
|
|
63
|
+
INVITATION_EMAIL_MISMATCH = "INVITATION_EMAIL_MISMATCH"
|
|
64
|
+
INVITATION_EXISTS = "INVITATION_EXISTS"
|
|
65
|
+
INVITATION_EXPIRED = "INVITATION_EXPIRED"
|
|
66
|
+
INVITATION_NOT_PENDING = "INVITATION_NOT_PENDING"
|
|
67
|
+
INVITATION_NOT_RESENDABLE = "INVITATION_NOT_RESENDABLE"
|
|
68
|
+
INVITATION_RESEND_TOO_SOON = "INVITATION_RESEND_TOO_SOON"
|
|
69
|
+
INVITATION_DELIVERY_FAILED = "INVITATION_DELIVERY_FAILED"
|
|
70
|
+
SELF_INVITE_NOT_ALLOWED = "SELF_INVITE_NOT_ALLOWED"
|
|
71
|
+
INVALID_INVITATION_ROLE = "INVALID_INVITATION_ROLE"
|
|
72
|
+
ORG_MEMBERSHIP_LIMIT_EXCEEDED = "ORG_MEMBERSHIP_LIMIT_EXCEEDED"
|
|
73
|
+
LEGAL_ACCEPTANCE_REQUIRED = "LEGAL_ACCEPTANCE_REQUIRED"
|
|
74
|
+
VALIDATION_ERROR = "VALIDATION_ERROR"
|
|
75
|
+
CLI_AUTH_NOT_CONFIGURED = "CLI_AUTH_NOT_CONFIGURED"
|
|
76
|
+
CLI_AUTH_SESSION_CREATE_FAILED = "CLI_AUTH_SESSION_CREATE_FAILED"
|
|
77
|
+
CLI_AUTH_SESSION_INVALID = "CLI_AUTH_SESSION_INVALID"
|
|
78
|
+
CLI_AUTH_SESSION_NOT_FOUND = "CLI_AUTH_SESSION_NOT_FOUND"
|
|
79
|
+
CLI_AUTH_SESSION_TERMINAL = "CLI_AUTH_SESSION_TERMINAL"
|
|
80
|
+
CLI_AUTH_DENIED = "CLI_AUTH_DENIED"
|
|
81
|
+
CLI_AUTH_EXPIRED = "CLI_AUTH_EXPIRED"
|
|
82
|
+
CLI_AUTH_TIMEOUT = "CLI_AUTH_TIMEOUT"
|
|
83
|
+
CLI_AUTH_RESPONSE_INVALID = "CLI_AUTH_RESPONSE_INVALID"
|
|
84
|
+
API_KEY_INVALID = "API_KEY_INVALID"
|
|
85
|
+
API_KEY_NOT_FOUND = "API_KEY_NOT_FOUND"
|
|
86
|
+
PROJECT_HEADER_REQUIRED = "PROJECT_HEADER_REQUIRED"
|
|
87
|
+
RUNTIME_API_KEY_SNAPSHOT_UNAVAILABLE = "RUNTIME_API_KEY_SNAPSHOT_UNAVAILABLE"
|
|
88
|
+
GATEWAY_RUNTIME_JWT_REQUIRED = "GATEWAY_RUNTIME_JWT_REQUIRED"
|
|
89
|
+
GATEWAY_RUNTIME_JWT_INVALID = "GATEWAY_RUNTIME_JWT_INVALID"
|
|
90
|
+
GATEWAY_RUNTIME_PROJECT_MISMATCH = "GATEWAY_RUNTIME_PROJECT_MISMATCH"
|
|
91
|
+
GATEWAY_RUNTIME_SCOPE_INVALID = "GATEWAY_RUNTIME_SCOPE_INVALID"
|
|
92
|
+
GATEWAY_RUNTIME_SCOPE_DENIED = "GATEWAY_RUNTIME_SCOPE_DENIED"
|
|
93
|
+
GATEWAY_RUNTIME_JWKS_UNAVAILABLE = "GATEWAY_RUNTIME_JWKS_UNAVAILABLE"
|
|
94
|
+
RUNTIME_HOST_INVALID = "RUNTIME_HOST_INVALID"
|
|
95
|
+
RUNTIME_ROUTING_HEADER_REJECTED = "RUNTIME_ROUTING_HEADER_REJECTED"
|
|
96
|
+
RUNTIME_PROJECT_NOT_FOUND = "RUNTIME_PROJECT_NOT_FOUND"
|
|
97
|
+
RUNTIME_PROJECT_NOT_READY = "RUNTIME_PROJECT_NOT_READY"
|
|
98
|
+
EMAIL_DELIVERY_FAILED = "EMAIL_DELIVERY_FAILED"
|
|
99
|
+
RATE_LIMITED = "RATE_LIMITED"
|
|
100
|
+
RATE_LIMIT_UNAVAILABLE = "RATE_LIMIT_UNAVAILABLE"
|
|
101
|
+
INTERNAL_ERROR = "INTERNAL_ERROR"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
@dataclass(frozen=True, slots=True)
|
|
105
|
+
class AuthErrorDescriptor:
|
|
106
|
+
code: AuthErrorCode
|
|
107
|
+
category: AuthErrorCategory
|
|
108
|
+
safe_message: str
|
|
109
|
+
message_key: str
|
|
110
|
+
http_status: int | None
|
|
111
|
+
cli_exit_code: CliExitCode
|
|
112
|
+
retry_class: RetryClass
|
|
113
|
+
reset_class: ResetClass
|
|
114
|
+
safe_detail_keys: frozenset[str]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
AUTH_ERROR_CATALOG: Mapping[AuthErrorCode, AuthErrorDescriptor] = MappingProxyType(
|
|
118
|
+
{
|
|
119
|
+
code: AuthErrorDescriptor(
|
|
120
|
+
code=code,
|
|
121
|
+
category=AuthErrorCategory(ERROR_CATALOG[code.value].category),
|
|
122
|
+
safe_message=ERROR_CATALOG[code.value].message,
|
|
123
|
+
message_key=ERROR_CATALOG[code.value].message_key,
|
|
124
|
+
http_status=ERROR_CATALOG[code.value].http_status,
|
|
125
|
+
cli_exit_code=CliExitCode(ERROR_CATALOG[code.value].cli_exit_code),
|
|
126
|
+
retry_class=RetryClass(ERROR_CATALOG[code.value].retry_class),
|
|
127
|
+
reset_class=ResetClass(ERROR_CATALOG[code.value].reset_class),
|
|
128
|
+
safe_detail_keys=ERROR_CATALOG[code.value].safe_detail_fields,
|
|
129
|
+
)
|
|
130
|
+
for code in AuthErrorCode
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if set(AUTH_ERROR_CATALOG) != set(AuthErrorCode):
|
|
135
|
+
raise RuntimeError("global error catalog must define every AuthErrorCode exactly once")
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
_FIELD_PATH = re.compile(r"^[a-z][a-z0-9_.]{0,127}$")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _validate_safe_details(code: AuthErrorCode, details: Mapping[str, object]) -> None:
|
|
142
|
+
if code is AuthErrorCode.VALIDATION_ERROR and details:
|
|
143
|
+
field = details.get("field")
|
|
144
|
+
if not isinstance(field, str) or _FIELD_PATH.fullmatch(field) is None:
|
|
145
|
+
raise ValueError("field must be a schema-owned field path")
|
|
146
|
+
elif code is AuthErrorCode.AUTH_WEAK_PASSWORD and details:
|
|
147
|
+
raw_requirements = details.get("requirements")
|
|
148
|
+
if not isinstance(raw_requirements, (list, tuple)):
|
|
149
|
+
raise ValueError("requirements must be present")
|
|
150
|
+
try:
|
|
151
|
+
requirements = tuple(PasswordRequirement(item) for item in raw_requirements)
|
|
152
|
+
except (TypeError, ValueError) as exc:
|
|
153
|
+
raise ValueError("unknown password requirement") from exc
|
|
154
|
+
canonical = tuple(item for item in PasswordRequirement if item in requirements)
|
|
155
|
+
if requirements != canonical:
|
|
156
|
+
raise ValueError("password requirements must be unique and canonical")
|
|
157
|
+
minimum = details.get("minimum_length")
|
|
158
|
+
if minimum is not None and (
|
|
159
|
+
PasswordRequirement.LENGTH not in requirements
|
|
160
|
+
or isinstance(minimum, bool)
|
|
161
|
+
or not isinstance(minimum, int)
|
|
162
|
+
or not 1 <= minimum <= 1024
|
|
163
|
+
):
|
|
164
|
+
raise ValueError("minimum_length requires a known length requirement")
|
|
165
|
+
elif (
|
|
166
|
+
code in {AuthErrorCode.PROJECT_NOT_READY, AuthErrorCode.RUNTIME_PROJECT_NOT_READY}
|
|
167
|
+
and details
|
|
168
|
+
):
|
|
169
|
+
try:
|
|
170
|
+
ProjectStatus(details.get("status"))
|
|
171
|
+
except (TypeError, ValueError) as exc:
|
|
172
|
+
raise ValueError("status must be a ProjectStatus") from exc
|
|
173
|
+
elif code is AuthErrorCode.INVITATION_RESEND_TOO_SOON and details:
|
|
174
|
+
timestamp = details.get("resend_available_at")
|
|
175
|
+
seconds = details.get("remaining_seconds")
|
|
176
|
+
if not isinstance(timestamp, str):
|
|
177
|
+
raise ValueError("resend_available_at must be an RFC 3339 timestamp")
|
|
178
|
+
try:
|
|
179
|
+
parsed = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
|
|
180
|
+
except ValueError as exc:
|
|
181
|
+
raise ValueError("resend_available_at must be an RFC 3339 timestamp") from exc
|
|
182
|
+
if parsed.tzinfo is None or parsed.utcoffset() is None:
|
|
183
|
+
raise ValueError("resend_available_at must include a UTC offset")
|
|
184
|
+
if isinstance(seconds, bool) or not isinstance(seconds, int) or seconds < 0:
|
|
185
|
+
raise ValueError("remaining_seconds must be a non-negative integer")
|
|
186
|
+
elif code is AuthErrorCode.ORG_MEMBERSHIP_LIMIT_EXCEEDED and details:
|
|
187
|
+
try:
|
|
188
|
+
for key in ("current_organization_id", "invited_organization_id"):
|
|
189
|
+
raw = details[key]
|
|
190
|
+
if not isinstance(raw, str) or str(UUID(raw)) != raw:
|
|
191
|
+
raise ValueError
|
|
192
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
193
|
+
raise ValueError("organization details must contain canonical UUIDs") from exc
|
|
194
|
+
elif code is AuthErrorCode.AUTH_IDENTITY_CHANGED and details:
|
|
195
|
+
for key in ("previous_epoch", "current_epoch"):
|
|
196
|
+
value = details.get(key)
|
|
197
|
+
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
|
|
198
|
+
raise ValueError("identity epochs must be non-negative integers")
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class AuthError(PolygresError):
|
|
202
|
+
def __init__(
|
|
203
|
+
self,
|
|
204
|
+
code: AuthErrorCode,
|
|
205
|
+
*,
|
|
206
|
+
details: Mapping[str, object] | None = None,
|
|
207
|
+
retry_after_seconds: int | None = None,
|
|
208
|
+
) -> None:
|
|
209
|
+
descriptor = AUTH_ERROR_CATALOG[code]
|
|
210
|
+
supplied = dict(details or {})
|
|
211
|
+
unknown = supplied.keys() - descriptor.safe_detail_keys
|
|
212
|
+
if unknown:
|
|
213
|
+
raise ValueError(f"unsafe error detail keys: {sorted(unknown)!r}")
|
|
214
|
+
_validate_safe_details(code, supplied)
|
|
215
|
+
if retry_after_seconds is not None and (
|
|
216
|
+
isinstance(retry_after_seconds, bool)
|
|
217
|
+
or not isinstance(retry_after_seconds, int)
|
|
218
|
+
or not 0 <= retry_after_seconds <= 86_400
|
|
219
|
+
or descriptor.retry_class is RetryClass.NEVER
|
|
220
|
+
):
|
|
221
|
+
raise ValueError("invalid retry_after_seconds for auth error")
|
|
222
|
+
super().__init__(error_record(code.value, details=supplied))
|
|
223
|
+
self.code = code
|
|
224
|
+
self.retry_class = descriptor.retry_class
|
|
225
|
+
self.reset_class = descriptor.reset_class
|
|
226
|
+
self.message_key = descriptor.message_key
|
|
227
|
+
self.retry_after_seconds = retry_after_seconds
|
|
228
|
+
|
|
229
|
+
def __repr__(self) -> str:
|
|
230
|
+
return f"AuthError(code={self.code.value!r})"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import Enum
|
|
5
|
+
|
|
6
|
+
from ..errors import ERROR_CATALOG
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ContextErrorCode(str, Enum):
|
|
10
|
+
REQUEST_INVALID = "CONTEXT_REQUEST_INVALID"
|
|
11
|
+
IDENTIFIER_INVALID = "CONTEXT_IDENTIFIER_INVALID"
|
|
12
|
+
SOURCE_INVALID = "CONTEXT_SOURCE_INVALID"
|
|
13
|
+
DELETE_CONFIRMATION_INVALID = "CONTEXT_DELETE_CONFIRMATION_INVALID"
|
|
14
|
+
POINT_KEY_INVALID = "CONTEXT_POINT_KEY_INVALID"
|
|
15
|
+
EMBEDDING_INVALID = "CONTEXT_EMBEDDING_INVALID"
|
|
16
|
+
FILTER_INVALID = "CONTEXT_FILTER_INVALID"
|
|
17
|
+
LIMIT_EXCEEDED = "CONTEXT_LIMIT_EXCEEDED"
|
|
18
|
+
GRAPH_START_REQUIRED = "CONTEXT_GRAPH_START_REQUIRED"
|
|
19
|
+
GRAPH_DIRECTION_INVALID = "CONTEXT_GRAPH_DIRECTION_INVALID"
|
|
20
|
+
RANKING_WEIGHTS_INVALID = "CONTEXT_RANKING_WEIGHTS_INVALID"
|
|
21
|
+
RESOURCE_NAMESPACE_MISMATCH = "CONTEXT_RESOURCE_NAMESPACE_MISMATCH"
|
|
22
|
+
COLLECTION_NOT_FOUND = "CONTEXT_COLLECTION_NOT_FOUND"
|
|
23
|
+
OPERATION_NOT_FOUND = "CONTEXT_OPERATION_NOT_FOUND"
|
|
24
|
+
COLLECTION_NOT_READY = "CONTEXT_COLLECTION_NOT_READY"
|
|
25
|
+
TEXT_COLUMN_REQUIRED = "CONTEXT_TEXT_COLUMN_REQUIRED"
|
|
26
|
+
GRAPH_NOT_READY = "CONTEXT_GRAPH_NOT_READY"
|
|
27
|
+
SOURCE_KEY_ALIGNMENT_INVALID = "CONTEXT_SOURCE_KEY_ALIGNMENT_INVALID"
|
|
28
|
+
CAPABILITY_UNAVAILABLE = "CONTEXT_CAPABILITY_UNAVAILABLE"
|
|
29
|
+
SOURCE_KEY_NOT_FOUND = "CONTEXT_SOURCE_KEY_NOT_FOUND"
|
|
30
|
+
SOURCE_VECTOR_INVALID = "CONTEXT_SOURCE_VECTOR_INVALID"
|
|
31
|
+
FILTER_REGISTRATION_CONFLICT = "CONTEXT_FILTER_REGISTRATION_CONFLICT"
|
|
32
|
+
POINT_CURSOR_INVALID = "CONTEXT_POINT_CURSOR_INVALID"
|
|
33
|
+
RECALL_UNAVAILABLE = "CONTEXT_RECALL_UNAVAILABLE"
|
|
34
|
+
COLLECTION_NAME_CONFLICT = "CONTEXT_COLLECTION_NAME_CONFLICT"
|
|
35
|
+
PREFLIGHT_BLOCKED = "CONTEXT_PREFLIGHT_BLOCKED"
|
|
36
|
+
OPERATION_CONFLICT = "CONTEXT_OPERATION_CONFLICT"
|
|
37
|
+
IDEMPOTENCY_CONFLICT = "CONTEXT_IDEMPOTENCY_CONFLICT"
|
|
38
|
+
OPERATION_NOT_CANCELLABLE = "CONTEXT_OPERATION_NOT_CANCELLABLE"
|
|
39
|
+
OPERATION_NOT_RETRYABLE = "CONTEXT_OPERATION_NOT_RETRYABLE"
|
|
40
|
+
OPERATION_CANCELLED = "CONTEXT_OPERATION_CANCELLED"
|
|
41
|
+
ONBOARDING_NOT_ELIGIBLE = "CONTEXT_ONBOARDING_NOT_ELIGIBLE"
|
|
42
|
+
ONBOARDING_NOT_AVAILABLE = "CONTEXT_ONBOARDING_NOT_AVAILABLE"
|
|
43
|
+
RUNTIME_UNSUPPORTED = "CONTEXT_RUNTIME_UNSUPPORTED"
|
|
44
|
+
EXTENSION_UNAVAILABLE = "CONTEXT_EXTENSION_UNAVAILABLE"
|
|
45
|
+
SOURCE_TABLE_UNSUPPORTED = "CONTEXT_SOURCE_TABLE_UNSUPPORTED"
|
|
46
|
+
SOURCE_KEY_REQUIRED = "CONTEXT_SOURCE_KEY_REQUIRED"
|
|
47
|
+
SOURCE_KEY_TYPE_UNSUPPORTED = "CONTEXT_SOURCE_KEY_TYPE_UNSUPPORTED"
|
|
48
|
+
SOURCE_KEY_TEXT_CONSTRAINT_REQUIRED = "CONTEXT_SOURCE_KEY_TEXT_CONSTRAINT_REQUIRED"
|
|
49
|
+
SOURCE_PRIVILEGE_REQUIRED = "CONTEXT_SOURCE_PRIVILEGE_REQUIRED"
|
|
50
|
+
VECTOR_COLUMN_MISSING = "CONTEXT_VECTOR_COLUMN_MISSING"
|
|
51
|
+
VECTOR_COLUMN_CONFLICT = "CONTEXT_VECTOR_COLUMN_CONFLICT"
|
|
52
|
+
VECTOR_TYPE_UNSUPPORTED = "CONTEXT_VECTOR_TYPE_UNSUPPORTED"
|
|
53
|
+
VECTOR_NULLABLE = "CONTEXT_VECTOR_NULLABLE"
|
|
54
|
+
VECTOR_DIMENSION_INVALID = "CONTEXT_VECTOR_DIMENSION_INVALID"
|
|
55
|
+
COSINE_ZERO_VECTOR = "CONTEXT_COSINE_ZERO_VECTOR"
|
|
56
|
+
RESULT_COLUMN_INVALID = "CONTEXT_RESULT_COLUMN_INVALID"
|
|
57
|
+
FILTER_COLUMN_INVALID = "CONTEXT_FILTER_COLUMN_INVALID"
|
|
58
|
+
JSONB_FILTER_PATH_INVALID = "CONTEXT_JSONB_FILTER_PATH_INVALID"
|
|
59
|
+
RESERVED_FILTER_CONFLICT = "CONTEXT_RESERVED_FILTER_CONFLICT"
|
|
60
|
+
TEXT_COLUMN_INVALID = "CONTEXT_TEXT_COLUMN_INVALID"
|
|
61
|
+
HNSW_UNSUPPORTED = "CONTEXT_HNSW_UNSUPPORTED"
|
|
62
|
+
INDEX_CONFLICT = "CONTEXT_INDEX_CONFLICT"
|
|
63
|
+
MEMORY_PRESSURE = "CONTEXT_MEMORY_PRESSURE"
|
|
64
|
+
PGVECTOR_SOURCE = "CONTEXT_PGVECTOR_SOURCE"
|
|
65
|
+
UNSUPPORTED_VECTOR_TYPE = "CONTEXT_UNSUPPORTED_VECTOR_TYPE"
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@dataclass(frozen=True, slots=True)
|
|
69
|
+
class ContextErrorDescriptor:
|
|
70
|
+
status_code: int
|
|
71
|
+
message: str
|
|
72
|
+
safe_detail_fields: tuple[str, ...] = ()
|
|
73
|
+
retryable: bool = False
|
|
74
|
+
conflict: bool = False
|
|
75
|
+
availability: bool = False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
CONTEXT_ERROR_CATALOG: dict[ContextErrorCode, ContextErrorDescriptor] = {
|
|
79
|
+
code: ContextErrorDescriptor(
|
|
80
|
+
status_code=ERROR_CATALOG[code.value].http_status,
|
|
81
|
+
message=ERROR_CATALOG[code.value].message,
|
|
82
|
+
safe_detail_fields=tuple(sorted(ERROR_CATALOG[code.value].safe_detail_fields)),
|
|
83
|
+
retryable=ERROR_CATALOG[code.value].retry_class
|
|
84
|
+
in {"bounded_retry", "dependency_retry", "after_delay"},
|
|
85
|
+
conflict=ERROR_CATALOG[code.value].http_status == 409,
|
|
86
|
+
availability=(
|
|
87
|
+
"UNAVAILABLE" in code.value
|
|
88
|
+
or "NOT_READY" in code.value
|
|
89
|
+
or code is ContextErrorCode.MEMORY_PRESSURE
|
|
90
|
+
),
|
|
91
|
+
)
|
|
92
|
+
for code in ContextErrorCode
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if set(CONTEXT_ERROR_CATALOG) != set(ContextErrorCode):
|
|
96
|
+
raise RuntimeError("global error catalog must define every ContextErrorCode exactly once")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
__all__ = [
|
|
100
|
+
"CONTEXT_ERROR_CATALOG",
|
|
101
|
+
"ContextErrorCode",
|
|
102
|
+
"ContextErrorDescriptor",
|
|
103
|
+
]
|
{polygres_sdk-0.2.1 → polygres_sdk-0.3.0}/src/polygres/_vendor/polygres_lib/context/models.py
RENAMED
|
@@ -511,6 +511,7 @@ class JointSearchRequest(GraphSearchBase):
|
|
|
511
511
|
|
|
512
512
|
class ErrorBody(ContextResponse):
|
|
513
513
|
code: str
|
|
514
|
+
variant: str | None = None
|
|
514
515
|
message: str
|
|
515
516
|
details: dict[str, Any]
|
|
516
517
|
|
|
@@ -749,6 +750,7 @@ class CollectionGetResponse(ContextResponse):
|
|
|
749
750
|
|
|
750
751
|
class ContextOperationFailure(ContextResponse):
|
|
751
752
|
code: str
|
|
753
|
+
variant: str | None = None
|
|
752
754
|
message: str
|
|
753
755
|
details: dict[str, Any]
|
|
754
756
|
http_status: int
|
|
@@ -1080,9 +1082,7 @@ class ContextJointResult(ContextSearchResult):
|
|
|
1080
1082
|
raise ValueError("graph-introduced results require null baseline fields")
|
|
1081
1083
|
if self.introduced_by_graph and self.graph is None:
|
|
1082
1084
|
raise ValueError("graph-introduced results require graph evidence")
|
|
1083
|
-
if not self.introduced_by_graph and (
|
|
1084
|
-
self.baseline_rank is None or self.rank_lift is None
|
|
1085
|
-
):
|
|
1085
|
+
if not self.introduced_by_graph and (self.baseline_rank is None or self.rank_lift is None):
|
|
1086
1086
|
raise ValueError("non-graph-introduced results require baseline fields")
|
|
1087
1087
|
if not math.isclose(
|
|
1088
1088
|
self.score,
|