anamdb 1.0.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.
- anamdb/__init__.py +31 -0
- anamdb/client.py +264 -0
- anamdb/exceptions.py +25 -0
- anamdb/models.py +67 -0
- anamdb-1.0.0.dist-info/METADATA +117 -0
- anamdb-1.0.0.dist-info/RECORD +7 -0
- anamdb-1.0.0.dist-info/WHEEL +4 -0
anamdb/__init__.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""AnamDB Python SDK — async client for the AnamDB neurosymbolic database engine."""
|
|
2
|
+
|
|
3
|
+
from anamdb.client import AnamClient
|
|
4
|
+
from anamdb.exceptions import (
|
|
5
|
+
AnamDBError,
|
|
6
|
+
ConnectionError,
|
|
7
|
+
QueryError,
|
|
8
|
+
ProtocolError,
|
|
9
|
+
)
|
|
10
|
+
from anamdb.models import (
|
|
11
|
+
QueryResult,
|
|
12
|
+
ServerHealth,
|
|
13
|
+
TableResponse,
|
|
14
|
+
RuleResponse,
|
|
15
|
+
ModelResponse,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__version__ = "1.0.0"
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
"AnamClient",
|
|
22
|
+
"AnamDBError",
|
|
23
|
+
"ConnectionError",
|
|
24
|
+
"QueryError",
|
|
25
|
+
"ProtocolError",
|
|
26
|
+
"QueryResult",
|
|
27
|
+
"ServerHealth",
|
|
28
|
+
"TableResponse",
|
|
29
|
+
"RuleResponse",
|
|
30
|
+
"ModelResponse",
|
|
31
|
+
]
|
anamdb/client.py
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Async client for connecting to a running AnamDB server.
|
|
2
|
+
|
|
3
|
+
Uses the JSON-over-TCP wire protocol defined in the AnamDB server module.
|
|
4
|
+
Each command is a single JSON line; each response is a single JSON line back.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import asyncio
|
|
10
|
+
import json
|
|
11
|
+
import logging
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
from anamdb.exceptions import (
|
|
15
|
+
AnamDBError,
|
|
16
|
+
ConnectionError,
|
|
17
|
+
ProtocolError,
|
|
18
|
+
QueryError,
|
|
19
|
+
)
|
|
20
|
+
from anamdb.models import (
|
|
21
|
+
ModelResponse,
|
|
22
|
+
QueryResult,
|
|
23
|
+
RuleResponse,
|
|
24
|
+
ServerHealth,
|
|
25
|
+
TableResponse,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("anamdb")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AnamClient:
|
|
32
|
+
"""Async client for the AnamDB neurosymbolic database engine.
|
|
33
|
+
|
|
34
|
+
Connects to a running AnamDB server over TCP and communicates using
|
|
35
|
+
the JSON-over-TCP wire protocol.
|
|
36
|
+
|
|
37
|
+
Use as an async context manager::
|
|
38
|
+
|
|
39
|
+
async with AnamClient("127.0.0.1:8080") as client:
|
|
40
|
+
result = await client.query("SELECT * FROM txns LIMIT 10")
|
|
41
|
+
|
|
42
|
+
Or manage the connection manually::
|
|
43
|
+
|
|
44
|
+
client = AnamClient("127.0.0.1:8080")
|
|
45
|
+
await client.connect()
|
|
46
|
+
result = await client.query("SELECT * FROM txns LIMIT 10")
|
|
47
|
+
await client.close()
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
addr: str = "127.0.0.1:8080",
|
|
53
|
+
*,
|
|
54
|
+
connect_timeout: float = 5.0,
|
|
55
|
+
max_retries: int = 3,
|
|
56
|
+
) -> None:
|
|
57
|
+
self._addr = addr
|
|
58
|
+
self._connect_timeout = connect_timeout
|
|
59
|
+
self._max_retries = max_retries
|
|
60
|
+
|
|
61
|
+
self._reader: asyncio.StreamReader | None = None
|
|
62
|
+
self._writer: asyncio.StreamWriter | None = None
|
|
63
|
+
|
|
64
|
+
# ── Connection lifecycle ──────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
async def connect(self) -> None:
|
|
67
|
+
"""Establish the TCP connection to the AnamDB server."""
|
|
68
|
+
if self._writer is not None:
|
|
69
|
+
return # Already connected.
|
|
70
|
+
|
|
71
|
+
host, _, port_str = self._addr.rpartition(":")
|
|
72
|
+
if not host:
|
|
73
|
+
host = "127.0.0.1"
|
|
74
|
+
port = int(port_str) if port_str else 8080
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
self._reader, self._writer = await asyncio.wait_for(
|
|
78
|
+
asyncio.open_connection(host, port),
|
|
79
|
+
timeout=self._connect_timeout,
|
|
80
|
+
)
|
|
81
|
+
logger.info("Connected to AnamDB at %s", self._addr)
|
|
82
|
+
except asyncio.TimeoutError as exc:
|
|
83
|
+
raise ConnectionError(
|
|
84
|
+
f"Connection to {self._addr} timed out after {self._connect_timeout}s"
|
|
85
|
+
) from exc
|
|
86
|
+
except OSError as exc:
|
|
87
|
+
raise ConnectionError(
|
|
88
|
+
f"Failed to connect to {self._addr}: {exc}"
|
|
89
|
+
) from exc
|
|
90
|
+
|
|
91
|
+
async def close(self) -> None:
|
|
92
|
+
"""Close the TCP connection."""
|
|
93
|
+
if self._writer is not None:
|
|
94
|
+
self._writer.close()
|
|
95
|
+
try:
|
|
96
|
+
await self._writer.wait_closed()
|
|
97
|
+
except Exception:
|
|
98
|
+
pass # Best effort.
|
|
99
|
+
self._writer = None
|
|
100
|
+
self._reader = None
|
|
101
|
+
logger.info("Disconnected from AnamDB")
|
|
102
|
+
|
|
103
|
+
async def __aenter__(self) -> "AnamClient":
|
|
104
|
+
await self.connect()
|
|
105
|
+
return self
|
|
106
|
+
|
|
107
|
+
async def __aexit__(self, *exc: Any) -> None:
|
|
108
|
+
await self.close()
|
|
109
|
+
|
|
110
|
+
# ── Wire protocol ────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
async def _send_command(self, cmd: dict) -> dict:
|
|
113
|
+
"""Send a JSON command and receive the JSON response."""
|
|
114
|
+
if self._reader is None or self._writer is None:
|
|
115
|
+
raise ConnectionError("Not connected — call connect() first")
|
|
116
|
+
|
|
117
|
+
payload = json.dumps(cmd, separators=(",", ":")) + "\n"
|
|
118
|
+
self._writer.write(payload.encode())
|
|
119
|
+
await self._writer.drain()
|
|
120
|
+
|
|
121
|
+
line = await self._reader.readline()
|
|
122
|
+
if not line:
|
|
123
|
+
raise ConnectionError("Server closed the connection")
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
return json.loads(line.decode())
|
|
127
|
+
except json.JSONDecodeError as exc:
|
|
128
|
+
raise ProtocolError(f"Invalid JSON response: {exc}") from exc
|
|
129
|
+
|
|
130
|
+
async def _send_with_retry(self, cmd: dict) -> dict:
|
|
131
|
+
"""Send a command with automatic retry on transient failures."""
|
|
132
|
+
last_exc: Exception | None = None
|
|
133
|
+
for attempt in range(1, self._max_retries + 1):
|
|
134
|
+
try:
|
|
135
|
+
return await self._send_command(cmd)
|
|
136
|
+
except ConnectionError as exc:
|
|
137
|
+
last_exc = exc
|
|
138
|
+
logger.warning(
|
|
139
|
+
"Attempt %d/%d failed: %s",
|
|
140
|
+
attempt,
|
|
141
|
+
self._max_retries,
|
|
142
|
+
exc,
|
|
143
|
+
)
|
|
144
|
+
# Try to reconnect.
|
|
145
|
+
await self.close()
|
|
146
|
+
try:
|
|
147
|
+
await self.connect()
|
|
148
|
+
except ConnectionError:
|
|
149
|
+
pass
|
|
150
|
+
raise last_exc or ConnectionError("All retry attempts failed")
|
|
151
|
+
|
|
152
|
+
# ── Public API ───────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
async def query(self, sql: str) -> QueryResult:
|
|
155
|
+
"""Execute a SQL query on the AnamDB server.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
sql: The SQL query string.
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
A :class:`QueryResult` with row count, reasoning tree, and anomalies.
|
|
162
|
+
|
|
163
|
+
Raises:
|
|
164
|
+
QueryError: If the server reports a query execution error.
|
|
165
|
+
"""
|
|
166
|
+
resp = await self._send_with_retry({"method": "query", "sql": sql})
|
|
167
|
+
|
|
168
|
+
if not resp.get("ok", False):
|
|
169
|
+
raise QueryError(
|
|
170
|
+
resp.get("error", "unknown server error"),
|
|
171
|
+
sql=sql,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
return QueryResult(
|
|
175
|
+
row_count=resp.get("ipc_bytes", 0),
|
|
176
|
+
reasoning_tree=resp.get("reasoning_tree") or None,
|
|
177
|
+
anomalies=resp.get("anomalies", []),
|
|
178
|
+
raw_response=resp,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
async def register_table(self, name: str, lance_path: str) -> TableResponse:
|
|
182
|
+
"""Register a Lance dataset as a queryable table.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
name: Logical table name.
|
|
186
|
+
lance_path: Filesystem path to the Lance dataset.
|
|
187
|
+
"""
|
|
188
|
+
resp = await self._send_with_retry(
|
|
189
|
+
{"method": "register_table", "name": name, "lance_path": lance_path}
|
|
190
|
+
)
|
|
191
|
+
return TableResponse(
|
|
192
|
+
success=resp.get("ok", False),
|
|
193
|
+
message=resp.get("message", ""),
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
async def register_rule(self, name: str, datalog: str) -> RuleResponse:
|
|
197
|
+
"""Register a Datalog rule as a query filter.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
name: Rule name.
|
|
201
|
+
datalog: Datalog expression (e.g. ``"fraud_prob > 0.90 AND amount > 10000"``).
|
|
202
|
+
"""
|
|
203
|
+
resp = await self._send_with_retry(
|
|
204
|
+
{"method": "register_rule", "name": name, "datalog": datalog}
|
|
205
|
+
)
|
|
206
|
+
return RuleResponse(
|
|
207
|
+
success=resp.get("ok", False),
|
|
208
|
+
message=resp.get("message", ""),
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
async def load_model(
|
|
212
|
+
self,
|
|
213
|
+
name: str,
|
|
214
|
+
version: str,
|
|
215
|
+
model_path: str,
|
|
216
|
+
function_id: str,
|
|
217
|
+
*,
|
|
218
|
+
num_features: int = 3,
|
|
219
|
+
avg_latency_ms: float = 1.0,
|
|
220
|
+
accuracy: float = 0.95,
|
|
221
|
+
) -> ModelResponse:
|
|
222
|
+
"""Load an ONNX model into the AnamDB model registry.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
name: Model name (becomes the SQL function name).
|
|
226
|
+
version: Model version string.
|
|
227
|
+
model_path: Path to the ONNX model file.
|
|
228
|
+
function_id: SQL function identifier.
|
|
229
|
+
num_features: Number of input features.
|
|
230
|
+
avg_latency_ms: Expected average latency in milliseconds.
|
|
231
|
+
accuracy: Expected model accuracy (0.0–1.0).
|
|
232
|
+
"""
|
|
233
|
+
resp = await self._send_with_retry(
|
|
234
|
+
{
|
|
235
|
+
"method": "load_model",
|
|
236
|
+
"name": name,
|
|
237
|
+
"version": version,
|
|
238
|
+
"model_path": model_path,
|
|
239
|
+
"function_id": function_id,
|
|
240
|
+
"num_features": num_features,
|
|
241
|
+
"avg_latency_ms": avg_latency_ms,
|
|
242
|
+
"accuracy": accuracy,
|
|
243
|
+
}
|
|
244
|
+
)
|
|
245
|
+
return ModelResponse(
|
|
246
|
+
success=resp.get("ok", False),
|
|
247
|
+
model_id=resp.get("model_id", ""),
|
|
248
|
+
message=resp.get("message", ""),
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
async def health(self) -> ServerHealth:
|
|
252
|
+
"""Check the health of the AnamDB server.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
A :class:`ServerHealth` with server status and resource counts.
|
|
256
|
+
"""
|
|
257
|
+
resp = await self._send_with_retry({"method": "health"})
|
|
258
|
+
return ServerHealth(
|
|
259
|
+
status=resp.get("status", "UNKNOWN"),
|
|
260
|
+
version=resp.get("version", "?"),
|
|
261
|
+
table_count=resp.get("tables", 0),
|
|
262
|
+
model_count=resp.get("models", 0),
|
|
263
|
+
rule_count=resp.get("rules", 0),
|
|
264
|
+
)
|
anamdb/exceptions.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Exception hierarchy for the AnamDB Python SDK."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AnamDBError(Exception):
|
|
5
|
+
"""Base exception for all AnamDB client errors."""
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ConnectionError(AnamDBError):
|
|
9
|
+
"""Raised when the client cannot connect to the AnamDB server."""
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class QueryError(AnamDBError):
|
|
13
|
+
"""Raised when a SQL query fails on the server."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, message: str, sql: str | None = None):
|
|
16
|
+
self.sql = sql
|
|
17
|
+
super().__init__(message)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ProtocolError(AnamDBError):
|
|
21
|
+
"""Raised when the server sends an invalid or unexpected response."""
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class TimeoutError(AnamDBError):
|
|
25
|
+
"""Raised when an operation exceeds the configured timeout."""
|
anamdb/models.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Data models for AnamDB responses."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True, slots=True)
|
|
9
|
+
class QueryResult:
|
|
10
|
+
"""Result of a SQL query execution."""
|
|
11
|
+
|
|
12
|
+
row_count: int
|
|
13
|
+
"""Number of rows returned (from server-reported ipc_bytes, or 0)."""
|
|
14
|
+
|
|
15
|
+
reasoning_tree: str | None = None
|
|
16
|
+
"""Provenance reasoning trace (if provenance mode is enabled)."""
|
|
17
|
+
|
|
18
|
+
anomalies: list[str] = field(default_factory=list)
|
|
19
|
+
"""Semantic anomaly descriptions detected during execution."""
|
|
20
|
+
|
|
21
|
+
raw_response: dict = field(default_factory=dict, repr=False)
|
|
22
|
+
"""Full JSON response from the server."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True, slots=True)
|
|
26
|
+
class ServerHealth:
|
|
27
|
+
"""Server health status."""
|
|
28
|
+
|
|
29
|
+
status: str
|
|
30
|
+
"""``'SERVING'`` or ``'NOT_SERVING'``."""
|
|
31
|
+
|
|
32
|
+
version: str
|
|
33
|
+
"""AnamDB server version string."""
|
|
34
|
+
|
|
35
|
+
table_count: int = 0
|
|
36
|
+
"""Number of registered tables."""
|
|
37
|
+
|
|
38
|
+
model_count: int = 0
|
|
39
|
+
"""Number of loaded ONNX models."""
|
|
40
|
+
|
|
41
|
+
rule_count: int = 0
|
|
42
|
+
"""Number of active Datalog rules."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True, slots=True)
|
|
46
|
+
class TableResponse:
|
|
47
|
+
"""Response from a table registration request."""
|
|
48
|
+
|
|
49
|
+
success: bool
|
|
50
|
+
message: str
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dataclass(frozen=True, slots=True)
|
|
54
|
+
class RuleResponse:
|
|
55
|
+
"""Response from a Datalog rule registration request."""
|
|
56
|
+
|
|
57
|
+
success: bool
|
|
58
|
+
message: str
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@dataclass(frozen=True, slots=True)
|
|
62
|
+
class ModelResponse:
|
|
63
|
+
"""Response from a model loading request."""
|
|
64
|
+
|
|
65
|
+
success: bool
|
|
66
|
+
model_id: str
|
|
67
|
+
message: str
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: anamdb
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Python client for AnamDB — the AI-native neurosymbolic database engine.
|
|
5
|
+
Project-URL: Homepage, https://jorge-nexsys.github.io/anam
|
|
6
|
+
Project-URL: Repository, https://github.com/jorge-nexsys/anam
|
|
7
|
+
Project-URL: Documentation, https://jorge-nexsys.github.io/anam
|
|
8
|
+
Project-URL: Issues, https://github.com/jorge-nexsys/anam/issues
|
|
9
|
+
Author: Jorge Martinez
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: ai,arrow,database,datalog,neurosymbolic
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Database
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Provides-Extra: arrow
|
|
25
|
+
Requires-Dist: pyarrow>=14.0; extra == 'arrow'
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# AnamDB Python SDK
|
|
32
|
+
|
|
33
|
+
Python client for [AnamDB](https://github.com/jorge-nexsys/anam) — the AI-native neurosymbolic database engine.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install anamdb
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
For Arrow IPC support (decode query results into PyArrow tables):
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip install anamdb[arrow]
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Quick Start
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import asyncio
|
|
51
|
+
from anamdb import AnamClient
|
|
52
|
+
|
|
53
|
+
async def main():
|
|
54
|
+
# Connect to a running AnamDB server
|
|
55
|
+
async with AnamClient("127.0.0.1:8080") as client:
|
|
56
|
+
# Check server health
|
|
57
|
+
health = await client.health()
|
|
58
|
+
print(f"Server: {health.status} (v{health.version})")
|
|
59
|
+
|
|
60
|
+
# Register a table
|
|
61
|
+
await client.register_table("txns", "/data/transactions.lance")
|
|
62
|
+
|
|
63
|
+
# Register a Datalog rule
|
|
64
|
+
await client.register_rule("high_risk", "fraud_prob > 0.90 AND amount > 10000")
|
|
65
|
+
|
|
66
|
+
# Run a SQL query
|
|
67
|
+
result = await client.query(
|
|
68
|
+
"SELECT region, COUNT(1) AS count "
|
|
69
|
+
"FROM txns WHERE fraud_prob > 0.90 "
|
|
70
|
+
"GROUP BY region ORDER BY count DESC"
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
print(f"Rows: {result.row_count}")
|
|
74
|
+
if result.reasoning_tree:
|
|
75
|
+
print(f"Reasoning: {result.reasoning_tree}")
|
|
76
|
+
|
|
77
|
+
asyncio.run(main())
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API Reference
|
|
81
|
+
|
|
82
|
+
### `AnamClient(addr, *, connect_timeout=5.0, max_retries=3)`
|
|
83
|
+
|
|
84
|
+
Async context manager for connecting to an AnamDB server.
|
|
85
|
+
|
|
86
|
+
**Methods:**
|
|
87
|
+
|
|
88
|
+
| Method | Description |
|
|
89
|
+
|:---|:---|
|
|
90
|
+
| `query(sql)` | Execute a SQL query, returns `QueryResult` |
|
|
91
|
+
| `register_table(name, lance_path)` | Register a Lance dataset as a table |
|
|
92
|
+
| `register_rule(name, datalog)` | Register a Datalog rule |
|
|
93
|
+
| `load_model(name, version, path, ...)` | Load an ONNX model |
|
|
94
|
+
| `health()` | Server health check |
|
|
95
|
+
|
|
96
|
+
### `QueryResult`
|
|
97
|
+
|
|
98
|
+
| Field | Type | Description |
|
|
99
|
+
|:---|:---|:---|
|
|
100
|
+
| `row_count` | `int` | Number of rows returned |
|
|
101
|
+
| `reasoning_tree` | `str \| None` | Provenance reasoning trace |
|
|
102
|
+
| `anomalies` | `list[str]` | Semantic anomaly descriptions |
|
|
103
|
+
| `raw_response` | `dict` | Full JSON response from server |
|
|
104
|
+
|
|
105
|
+
### `ServerHealth`
|
|
106
|
+
|
|
107
|
+
| Field | Type | Description |
|
|
108
|
+
|:---|:---|:---|
|
|
109
|
+
| `status` | `str` | `"SERVING"` or `"NOT_SERVING"` |
|
|
110
|
+
| `version` | `str` | AnamDB server version |
|
|
111
|
+
| `table_count` | `int` | Number of registered tables |
|
|
112
|
+
| `model_count` | `int` | Number of loaded models |
|
|
113
|
+
| `rule_count` | `int` | Number of Datalog rules |
|
|
114
|
+
|
|
115
|
+
## License
|
|
116
|
+
|
|
117
|
+
Apache License 2.0
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
anamdb/__init__.py,sha256=4WI3wPGjQZNA_y-EKDqnYo4vQNHCSFgaQjbwmFSzYqA,591
|
|
2
|
+
anamdb/client.py,sha256=t93O9vSJBJ_GCXzifTz8U9399iHlAq1AYeY_1dii30I,8895
|
|
3
|
+
anamdb/exceptions.py,sha256=oC093XHIYyzWg6BpzPyorQGMhy_ve0jiSiPIwwITmyo,668
|
|
4
|
+
anamdb/models.py,sha256=dagiwJ383jvXmp3z1c30cwkIIdt6wdD1ZwmlAIahlGI,1531
|
|
5
|
+
anamdb-1.0.0.dist-info/METADATA,sha256=T1eT2mQAeFiT6rlieJV1P3tC41kL1Jgd4NhLsGD2l4w,3639
|
|
6
|
+
anamdb-1.0.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
7
|
+
anamdb-1.0.0.dist-info/RECORD,,
|