polygres 0.1.0__py3-none-any.whl
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/__init__.py +47 -0
- polygres/client.py +696 -0
- polygres/errors.py +48 -0
- polygres/models.py +336 -0
- polygres/py.typed +1 -0
- polygres-0.1.0.dist-info/METADATA +390 -0
- polygres-0.1.0.dist-info/RECORD +9 -0
- polygres-0.1.0.dist-info/WHEEL +4 -0
- polygres-0.1.0.dist-info/licenses/LICENSE +202 -0
polygres/errors.py
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PolygresError(Exception):
|
|
7
|
+
"""Base Polygres SDK exception."""
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PolygresValidationError(PolygresError, ValueError):
|
|
11
|
+
"""Raised before a request is sent when local validation fails."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PolygresAPIError(PolygresError):
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
message: str,
|
|
18
|
+
*,
|
|
19
|
+
status_code: int | None = None,
|
|
20
|
+
request_id: str | None = None,
|
|
21
|
+
code: str | None = None,
|
|
22
|
+
details: dict[str, Any] | None = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
super().__init__(message)
|
|
25
|
+
self.status_code = status_code
|
|
26
|
+
self.request_id = request_id
|
|
27
|
+
self.code = code
|
|
28
|
+
self.details = details or {}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class PolygresAuthError(PolygresAPIError):
|
|
32
|
+
"""Raised for authentication failures."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class PolygresPermissionError(PolygresAPIError):
|
|
36
|
+
"""Raised for authorization failures."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class PolygresNotFoundError(PolygresAPIError):
|
|
40
|
+
"""Raised when a requested resource is not found."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class PolygresRateLimitError(PolygresAPIError):
|
|
44
|
+
"""Raised when the API rate limits a request."""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class PolygresRuntimeError(PolygresAPIError):
|
|
48
|
+
"""Raised for transient API/runtime failures."""
|
polygres/models.py
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Iterator
|
|
4
|
+
from dataclasses import asdict, dataclass, field
|
|
5
|
+
from typing import Any, Generic, TypeVar
|
|
6
|
+
|
|
7
|
+
T = TypeVar("T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _to_dict(value: Any) -> Any:
|
|
11
|
+
if hasattr(value, "to_dict"):
|
|
12
|
+
return value.to_dict()
|
|
13
|
+
if isinstance(value, list):
|
|
14
|
+
return [_to_dict(item) for item in value]
|
|
15
|
+
if isinstance(value, dict):
|
|
16
|
+
return {key: _to_dict(item) for key, item in value.items()}
|
|
17
|
+
return value
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass
|
|
21
|
+
class Page(Generic[T]):
|
|
22
|
+
results: list[T]
|
|
23
|
+
next_cursor: str | None
|
|
24
|
+
has_more: bool
|
|
25
|
+
request_id: str | None = None
|
|
26
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
27
|
+
_fetch_next: Callable[[str], Page[T]] | None = field(
|
|
28
|
+
default=None, repr=False, compare=False
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
@classmethod
|
|
32
|
+
def from_api(
|
|
33
|
+
cls,
|
|
34
|
+
payload: dict[str, Any],
|
|
35
|
+
parser: Callable[[dict[str, Any]], T],
|
|
36
|
+
fetch_next: Callable[[str], Page[T]] | None = None,
|
|
37
|
+
) -> Page[T]:
|
|
38
|
+
return cls(
|
|
39
|
+
results=[parser(item) for item in payload.get("results", [])],
|
|
40
|
+
next_cursor=payload.get("next_cursor"),
|
|
41
|
+
has_more=bool(payload.get("has_more", False)),
|
|
42
|
+
request_id=payload.get("request_id"),
|
|
43
|
+
metadata={
|
|
44
|
+
key: value
|
|
45
|
+
for key, value in payload.items()
|
|
46
|
+
if key not in {"results", "next_cursor", "has_more", "request_id"}
|
|
47
|
+
},
|
|
48
|
+
_fetch_next=fetch_next,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def auto_paging_iter(self) -> Iterator[T]:
|
|
52
|
+
page: Page[T] = self
|
|
53
|
+
while True:
|
|
54
|
+
yield from page.results
|
|
55
|
+
if not page.has_more or not page.next_cursor or page._fetch_next is None:
|
|
56
|
+
return
|
|
57
|
+
page = page._fetch_next(page.next_cursor)
|
|
58
|
+
|
|
59
|
+
def to_dict(self) -> dict[str, Any]:
|
|
60
|
+
return {
|
|
61
|
+
"results": [_to_dict(item) for item in self.results],
|
|
62
|
+
"next_cursor": self.next_cursor,
|
|
63
|
+
"has_more": self.has_more,
|
|
64
|
+
"request_id": self.request_id,
|
|
65
|
+
"metadata": _to_dict(self.metadata),
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class ConnectionInfo:
|
|
71
|
+
project_id: str
|
|
72
|
+
database: str
|
|
73
|
+
username: str
|
|
74
|
+
port: int
|
|
75
|
+
direct_host: str
|
|
76
|
+
pooled_host: str
|
|
77
|
+
direct_url_without_password: str
|
|
78
|
+
pooled_url_without_password: str
|
|
79
|
+
request_id: str | None = None
|
|
80
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def from_api(cls, payload: dict[str, Any]) -> ConnectionInfo:
|
|
84
|
+
direct = payload.get("direct", {})
|
|
85
|
+
pooled = payload.get("pooled", {})
|
|
86
|
+
return cls(
|
|
87
|
+
project_id=payload["project_id"],
|
|
88
|
+
database=payload["database"],
|
|
89
|
+
username=payload["username"],
|
|
90
|
+
port=int(payload["port"]),
|
|
91
|
+
direct_host=direct["host"],
|
|
92
|
+
pooled_host=pooled["host"],
|
|
93
|
+
direct_url_without_password=direct["connection_string_without_password"],
|
|
94
|
+
pooled_url_without_password=pooled["connection_string_without_password"],
|
|
95
|
+
request_id=payload.get("request_id"),
|
|
96
|
+
metadata=dict(payload.get("metadata", {})),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
def to_dict(self) -> dict[str, Any]:
|
|
100
|
+
return asdict(self)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
@dataclass
|
|
104
|
+
class RetrievalReadiness:
|
|
105
|
+
project_id: str
|
|
106
|
+
graph: dict[str, Any]
|
|
107
|
+
vector: dict[str, Any]
|
|
108
|
+
hybrid: dict[str, Any]
|
|
109
|
+
request_id: str | None = None
|
|
110
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
111
|
+
|
|
112
|
+
@classmethod
|
|
113
|
+
def from_api(cls, payload: dict[str, Any]) -> RetrievalReadiness:
|
|
114
|
+
return cls(
|
|
115
|
+
project_id=payload["project_id"],
|
|
116
|
+
graph=dict(payload.get("graph", {})),
|
|
117
|
+
vector=dict(payload.get("vector", {})),
|
|
118
|
+
hybrid=dict(payload.get("hybrid", {})),
|
|
119
|
+
request_id=payload.get("request_id"),
|
|
120
|
+
metadata=dict(payload.get("metadata", {})),
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def to_dict(self) -> dict[str, Any]:
|
|
124
|
+
return asdict(self)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@dataclass
|
|
128
|
+
class GraphNode:
|
|
129
|
+
schema: str
|
|
130
|
+
table: str
|
|
131
|
+
id: str
|
|
132
|
+
properties: dict[str, Any] = field(default_factory=dict)
|
|
133
|
+
|
|
134
|
+
@classmethod
|
|
135
|
+
def from_api(cls, payload: dict[str, Any]) -> GraphNode:
|
|
136
|
+
return cls(
|
|
137
|
+
schema=payload["schema"],
|
|
138
|
+
table=payload["table"],
|
|
139
|
+
id=str(payload["id"]),
|
|
140
|
+
properties=dict(payload.get("properties", {})),
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
def to_dict(self) -> dict[str, Any]:
|
|
144
|
+
return asdict(self)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
@dataclass
|
|
148
|
+
class GraphPathStep:
|
|
149
|
+
step: int
|
|
150
|
+
node: GraphNode
|
|
151
|
+
edge_label: str | None = None
|
|
152
|
+
readable_path: str | None = None
|
|
153
|
+
|
|
154
|
+
@classmethod
|
|
155
|
+
def from_api(cls, payload: dict[str, Any]) -> GraphPathStep:
|
|
156
|
+
node = payload.get("node", payload)
|
|
157
|
+
return cls(
|
|
158
|
+
step=int(payload.get("step", 0)),
|
|
159
|
+
node=GraphNode.from_api(node),
|
|
160
|
+
edge_label=payload.get("edge_label"),
|
|
161
|
+
readable_path=payload.get("readable_path"),
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def to_dict(self) -> dict[str, Any]:
|
|
165
|
+
return {
|
|
166
|
+
"step": self.step,
|
|
167
|
+
"node": self.node.to_dict(),
|
|
168
|
+
"edge_label": self.edge_label,
|
|
169
|
+
"readable_path": self.readable_path,
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
@dataclass
|
|
174
|
+
class GraphResult:
|
|
175
|
+
node: GraphNode
|
|
176
|
+
depth: int
|
|
177
|
+
rank: int | None
|
|
178
|
+
graph_score: float | None
|
|
179
|
+
path: list[Any] | None
|
|
180
|
+
edge_path: list[Any] | None
|
|
181
|
+
readable_path: str | None
|
|
182
|
+
relationships: list[Any] = field(default_factory=list)
|
|
183
|
+
|
|
184
|
+
@classmethod
|
|
185
|
+
def from_api(cls, payload: dict[str, Any]) -> GraphResult:
|
|
186
|
+
node_payload = dict(payload["node"])
|
|
187
|
+
if "properties" not in node_payload:
|
|
188
|
+
node_payload["properties"] = payload.get("properties", {})
|
|
189
|
+
return cls(
|
|
190
|
+
node=GraphNode.from_api(node_payload),
|
|
191
|
+
depth=int(payload.get("depth", 0)),
|
|
192
|
+
rank=payload.get("rank"),
|
|
193
|
+
graph_score=payload.get("graph_score"),
|
|
194
|
+
path=payload.get("path"),
|
|
195
|
+
edge_path=payload.get("edge_path"),
|
|
196
|
+
readable_path=payload.get("readable_path"),
|
|
197
|
+
relationships=list(payload.get("relationships", [])),
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def to_dict(self) -> dict[str, Any]:
|
|
201
|
+
return {
|
|
202
|
+
"node": self.node.to_dict(),
|
|
203
|
+
"depth": self.depth,
|
|
204
|
+
"rank": self.rank,
|
|
205
|
+
"graph_score": self.graph_score,
|
|
206
|
+
"path": self.path,
|
|
207
|
+
"edge_path": self.edge_path,
|
|
208
|
+
"readable_path": self.readable_path,
|
|
209
|
+
"relationships": self.relationships,
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@dataclass
|
|
214
|
+
class VectorResult:
|
|
215
|
+
schema: str
|
|
216
|
+
table: str
|
|
217
|
+
id: str
|
|
218
|
+
properties: dict[str, Any]
|
|
219
|
+
distance: float
|
|
220
|
+
similarity: float | None
|
|
221
|
+
score: float
|
|
222
|
+
|
|
223
|
+
@classmethod
|
|
224
|
+
def from_api(cls, payload: dict[str, Any]) -> VectorResult:
|
|
225
|
+
return cls(
|
|
226
|
+
schema=payload["schema"],
|
|
227
|
+
table=payload["table"],
|
|
228
|
+
id=str(payload["id"]),
|
|
229
|
+
properties=dict(payload.get("properties", {})),
|
|
230
|
+
distance=float(payload["distance"]),
|
|
231
|
+
similarity=payload.get("similarity"),
|
|
232
|
+
score=float(payload["score"]),
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
def to_dict(self) -> dict[str, Any]:
|
|
236
|
+
return asdict(self)
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
@dataclass
|
|
240
|
+
class TextResult:
|
|
241
|
+
schema: str
|
|
242
|
+
table: str
|
|
243
|
+
id: str
|
|
244
|
+
properties: dict[str, Any]
|
|
245
|
+
score: float
|
|
246
|
+
similarity: float | None = None
|
|
247
|
+
|
|
248
|
+
@classmethod
|
|
249
|
+
def from_api(cls, payload: dict[str, Any]) -> TextResult:
|
|
250
|
+
return cls(
|
|
251
|
+
schema=payload["schema"],
|
|
252
|
+
table=payload["table"],
|
|
253
|
+
id=str(payload["id"]),
|
|
254
|
+
properties=dict(payload.get("properties", {})),
|
|
255
|
+
score=float(payload["score"]),
|
|
256
|
+
similarity=payload.get("similarity"),
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
def to_dict(self) -> dict[str, Any]:
|
|
260
|
+
return asdict(self)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
@dataclass
|
|
264
|
+
class HybridResult:
|
|
265
|
+
schema: str
|
|
266
|
+
table: str
|
|
267
|
+
id: str
|
|
268
|
+
properties: dict[str, Any]
|
|
269
|
+
score: float
|
|
270
|
+
vector_score: float | None
|
|
271
|
+
graph_score: float | None
|
|
272
|
+
distance: float | None
|
|
273
|
+
similarity: float | None
|
|
274
|
+
relationships: list[Any]
|
|
275
|
+
|
|
276
|
+
@classmethod
|
|
277
|
+
def from_api(cls, payload: dict[str, Any]) -> HybridResult:
|
|
278
|
+
node = payload.get("node", payload)
|
|
279
|
+
score = payload.get("score", payload.get("rrf_score", 0.0))
|
|
280
|
+
return cls(
|
|
281
|
+
schema=node["schema"],
|
|
282
|
+
table=node["table"],
|
|
283
|
+
id=str(node["id"]),
|
|
284
|
+
properties=dict(payload.get("properties", node.get("properties", {}))),
|
|
285
|
+
score=float(score),
|
|
286
|
+
vector_score=payload.get("vector_score"),
|
|
287
|
+
graph_score=payload.get("graph_score"),
|
|
288
|
+
distance=payload.get("distance"),
|
|
289
|
+
similarity=payload.get("similarity"),
|
|
290
|
+
relationships=list(payload.get("relationships", [])),
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
def to_dict(self) -> dict[str, Any]:
|
|
294
|
+
return asdict(self)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
@dataclass
|
|
298
|
+
class GraphPathResponse:
|
|
299
|
+
paths: list[dict[str, Any]]
|
|
300
|
+
request_id: str | None = None
|
|
301
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
302
|
+
|
|
303
|
+
@classmethod
|
|
304
|
+
def from_api(cls, payload: dict[str, Any]) -> GraphPathResponse:
|
|
305
|
+
return cls(
|
|
306
|
+
paths=list(payload.get("paths", [])),
|
|
307
|
+
request_id=payload.get("request_id"),
|
|
308
|
+
metadata={
|
|
309
|
+
key: value for key, value in payload.items() if key not in {"paths", "request_id"}
|
|
310
|
+
},
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
def to_dict(self) -> dict[str, Any]:
|
|
314
|
+
return asdict(self)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@dataclass
|
|
318
|
+
class GraphConnectionResponse:
|
|
319
|
+
connections: list[dict[str, Any]]
|
|
320
|
+
request_id: str | None = None
|
|
321
|
+
metadata: dict[str, Any] = field(default_factory=dict)
|
|
322
|
+
|
|
323
|
+
@classmethod
|
|
324
|
+
def from_api(cls, payload: dict[str, Any]) -> GraphConnectionResponse:
|
|
325
|
+
return cls(
|
|
326
|
+
connections=list(payload.get("connections", [])),
|
|
327
|
+
request_id=payload.get("request_id"),
|
|
328
|
+
metadata={
|
|
329
|
+
key: value
|
|
330
|
+
for key, value in payload.items()
|
|
331
|
+
if key not in {"connections", "request_id"}
|
|
332
|
+
},
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
def to_dict(self) -> dict[str, Any]:
|
|
336
|
+
return asdict(self)
|
polygres/py.typed
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|