ainative-python 2.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.
@@ -0,0 +1,362 @@
1
+ """
2
+ ZeroDB Tables Module
3
+
4
+ Handles NoSQL table operations including CRUD operations for tables and rows.
5
+ """
6
+
7
+ from typing import TYPE_CHECKING, List, Dict, Any, Optional
8
+
9
+ if TYPE_CHECKING:
10
+ from ..client import AINativeClient
11
+
12
+
13
+ class TablesClient:
14
+ """Client for ZeroDB NoSQL table operations."""
15
+
16
+ def __init__(self, client: "AINativeClient"):
17
+ """
18
+ Initialize tables client.
19
+
20
+ Args:
21
+ client: Parent AINative client instance
22
+ """
23
+ self.client = client
24
+ self.base_path = "/zerodb/tables"
25
+
26
+ # Table Management Operations
27
+
28
+ def create_table(
29
+ self,
30
+ table_name: str,
31
+ schema: Dict[str, Any],
32
+ description: Optional[str] = None,
33
+ ) -> Dict[str, Any]:
34
+ """
35
+ Create a new NoSQL table.
36
+
37
+ Args:
38
+ table_name: Unique table name
39
+ schema: Table schema definition with fields and indexes
40
+ Example: {
41
+ "fields": {
42
+ "email": "string",
43
+ "name": "string",
44
+ "age": "number"
45
+ },
46
+ "indexes": ["email"]
47
+ }
48
+ description: Optional table description
49
+
50
+ Returns:
51
+ Created table information including table_id
52
+
53
+ Example:
54
+ >>> client.zerodb.tables.create_table(
55
+ ... "users",
56
+ ... schema={
57
+ ... "fields": {"email": "string", "name": "string", "age": "number"},
58
+ ... "indexes": ["email"]
59
+ ... }
60
+ ... )
61
+ """
62
+ data = {
63
+ "table_name": table_name,
64
+ "schema": schema,
65
+ }
66
+
67
+ if description:
68
+ data["description"] = description
69
+
70
+ return self.client.post(self.base_path, data=data)
71
+
72
+ def list_tables(
73
+ self,
74
+ limit: int = 100,
75
+ offset: int = 0,
76
+ ) -> Dict[str, Any]:
77
+ """
78
+ List all tables in the project.
79
+
80
+ Args:
81
+ limit: Maximum number of results (default: 100)
82
+ offset: Pagination offset (default: 0)
83
+
84
+ Returns:
85
+ Dictionary with 'tables' list and 'total' count
86
+
87
+ Example:
88
+ >>> tables = client.zerodb.tables.list_tables(limit=50)
89
+ >>> for table in tables['tables']:
90
+ ... print(f"{table['table_name']}: {table['row_count']} rows")
91
+ """
92
+ params = {
93
+ "limit": limit,
94
+ "offset": offset,
95
+ }
96
+
97
+ return self.client.get(self.base_path, params=params)
98
+
99
+ def get_table(
100
+ self,
101
+ table_id: str,
102
+ ) -> Dict[str, Any]:
103
+ """
104
+ Get table details and metadata.
105
+
106
+ Args:
107
+ table_id: Table ID or table name
108
+
109
+ Returns:
110
+ Table details including schema, row_count, and statistics
111
+
112
+ Example:
113
+ >>> table = client.zerodb.tables.get_table("users")
114
+ >>> print(f"Schema: {table['schema']}")
115
+ >>> print(f"Rows: {table['row_count']}")
116
+ """
117
+ return self.client.get(f"{self.base_path}/{table_id}")
118
+
119
+ def delete_table(
120
+ self,
121
+ table_id: str,
122
+ confirm: bool = False,
123
+ ) -> Dict[str, Any]:
124
+ """
125
+ Delete a table and all its data.
126
+
127
+ Args:
128
+ table_id: Table ID or table name
129
+ confirm: Must be True to confirm deletion (safety check)
130
+
131
+ Returns:
132
+ Deletion result with number of rows deleted
133
+
134
+ Example:
135
+ >>> result = client.zerodb.tables.delete_table("old_table", confirm=True)
136
+ >>> print(f"Deleted {result['rows_deleted']} rows")
137
+ """
138
+ if not confirm:
139
+ raise ValueError(
140
+ "Table deletion requires confirmation. "
141
+ "Set confirm=True to proceed."
142
+ )
143
+
144
+ data = {"confirm": True}
145
+ return self.client.delete(f"{self.base_path}/{table_id}", data=data)
146
+
147
+ # Row Operations
148
+
149
+ def insert_rows(
150
+ self,
151
+ table_name: str,
152
+ rows: List[Dict[str, Any]],
153
+ return_ids: bool = True,
154
+ ) -> Dict[str, Any]:
155
+ """
156
+ Insert rows into a table.
157
+
158
+ Args:
159
+ table_name: Table name
160
+ rows: List of row objects to insert (max 1000 per request)
161
+ return_ids: Whether to return inserted row IDs
162
+
163
+ Returns:
164
+ Insert result with inserted_count and optional row IDs
165
+
166
+ Example:
167
+ >>> rows = [
168
+ ... {"email": "user@example.com", "name": "John", "age": 30},
169
+ ... {"email": "jane@example.com", "name": "Jane", "age": 25}
170
+ ... ]
171
+ >>> result = client.zerodb.tables.insert_rows("users", rows)
172
+ >>> print(f"Inserted {result['inserted_count']} rows")
173
+ """
174
+ if not rows:
175
+ raise ValueError("rows cannot be empty")
176
+
177
+ if len(rows) > 1000:
178
+ raise ValueError("Maximum 1000 rows per request")
179
+
180
+ data = {
181
+ "rows": rows,
182
+ "return_ids": return_ids,
183
+ }
184
+
185
+ return self.client.post(f"{self.base_path}/{table_name}/rows", data=data)
186
+
187
+ def query_rows(
188
+ self,
189
+ table_name: str,
190
+ filter: Optional[Dict[str, Any]] = None,
191
+ sort: Optional[Dict[str, int]] = None,
192
+ limit: int = 100,
193
+ offset: int = 0,
194
+ projection: Optional[Dict[str, int]] = None,
195
+ ) -> Dict[str, Any]:
196
+ """
197
+ Query rows from a table with filters and sorting.
198
+
199
+ Args:
200
+ table_name: Table name
201
+ filter: MongoDB-style query filter
202
+ Example: {"age": {"$gte": 25}, "status": "active"}
203
+ sort: Sort specification
204
+ Example: {"created_at": -1} for descending
205
+ limit: Maximum results (default: 100)
206
+ offset: Pagination offset (default: 0)
207
+ projection: Field projection (which fields to return)
208
+ Example: {"name": 1, "email": 1, "_id": 0}
209
+
210
+ Returns:
211
+ Query results with rows, total count, and pagination info
212
+
213
+ Example:
214
+ >>> # Query users over 25, sorted by age descending
215
+ >>> results = client.zerodb.tables.query_rows(
216
+ ... "users",
217
+ ... filter={"age": {"$gte": 25}},
218
+ ... sort={"age": -1},
219
+ ... limit=10
220
+ ... )
221
+ >>> for row in results['rows']:
222
+ ... print(f"{row['name']}: {row['age']}")
223
+ """
224
+ data = {
225
+ "limit": limit,
226
+ "offset": offset,
227
+ }
228
+
229
+ if filter:
230
+ data["filter"] = filter
231
+
232
+ if sort:
233
+ data["sort"] = sort
234
+
235
+ if projection:
236
+ data["projection"] = projection
237
+
238
+ return self.client.post(f"{self.base_path}/{table_name}/query", data=data)
239
+
240
+ def update_rows(
241
+ self,
242
+ table_name: str,
243
+ filter: Dict[str, Any],
244
+ update: Dict[str, Any],
245
+ upsert: bool = False,
246
+ ) -> Dict[str, Any]:
247
+ """
248
+ Update rows matching the filter.
249
+
250
+ Args:
251
+ table_name: Table name
252
+ filter: MongoDB-style query filter to match rows
253
+ update: Update operations
254
+ Example: {"$set": {"age": 31}, "$inc": {"login_count": 1}}
255
+ upsert: Insert if not found (default: False)
256
+
257
+ Returns:
258
+ Update result with count of modified rows
259
+
260
+ Example:
261
+ >>> # Update age for specific user
262
+ >>> result = client.zerodb.tables.update_rows(
263
+ ... "users",
264
+ ... filter={"email": "user@example.com"},
265
+ ... update={"$set": {"age": 31}}
266
+ ... )
267
+ >>> print(f"Updated {result['modified_count']} rows")
268
+ """
269
+ data = {
270
+ "filter": filter,
271
+ "update": update,
272
+ "upsert": upsert,
273
+ }
274
+
275
+ return self.client.put(f"{self.base_path}/{table_name}/rows", data=data)
276
+
277
+ def delete_rows(
278
+ self,
279
+ table_name: str,
280
+ filter: Dict[str, Any],
281
+ limit: int = 0,
282
+ ) -> Dict[str, Any]:
283
+ """
284
+ Delete rows matching the filter.
285
+
286
+ Args:
287
+ table_name: Table name
288
+ filter: MongoDB-style query filter to match rows
289
+ limit: Maximum rows to delete (0 = all matching, default)
290
+
291
+ Returns:
292
+ Delete result with count of deleted rows
293
+
294
+ Example:
295
+ >>> # Delete inactive users
296
+ >>> result = client.zerodb.tables.delete_rows(
297
+ ... "users",
298
+ ... filter={"age": {"$lt": 18}}
299
+ ... )
300
+ >>> print(f"Deleted {result['deleted_count']} rows")
301
+ """
302
+ data = {
303
+ "filter": filter,
304
+ "limit": limit,
305
+ }
306
+
307
+ return self.client.request(
308
+ "DELETE",
309
+ f"{self.base_path}/{table_name}/rows",
310
+ data=data
311
+ )
312
+
313
+ # Utility Methods
314
+
315
+ def count_rows(
316
+ self,
317
+ table_name: str,
318
+ filter: Optional[Dict[str, Any]] = None,
319
+ ) -> int:
320
+ """
321
+ Count rows in a table matching optional filter.
322
+
323
+ Args:
324
+ table_name: Table name
325
+ filter: Optional query filter
326
+
327
+ Returns:
328
+ Number of matching rows
329
+
330
+ Example:
331
+ >>> count = client.zerodb.tables.count_rows("users", {"age": {"$gte": 18}})
332
+ >>> print(f"Adult users: {count}")
333
+ """
334
+ result = self.query_rows(
335
+ table_name,
336
+ filter=filter,
337
+ limit=0 # Don't return actual rows, just count
338
+ )
339
+ return result.get("total", 0)
340
+
341
+ def table_exists(
342
+ self,
343
+ table_name: str,
344
+ ) -> bool:
345
+ """
346
+ Check if a table exists.
347
+
348
+ Args:
349
+ table_name: Table name to check
350
+
351
+ Returns:
352
+ True if table exists, False otherwise
353
+
354
+ Example:
355
+ >>> if client.zerodb.tables.table_exists("users"):
356
+ ... print("Users table exists")
357
+ """
358
+ try:
359
+ self.get_table(table_name)
360
+ return True
361
+ except Exception:
362
+ return False
@@ -0,0 +1,231 @@
1
+ """
2
+ ZeroDB Vectors Module
3
+
4
+ Handles vector operations including upsert, search, and management.
5
+ """
6
+
7
+ from typing import TYPE_CHECKING, List, Dict, Any, Optional, Union
8
+ import numpy as np
9
+
10
+ if TYPE_CHECKING:
11
+ from ..client import AINativeClient
12
+
13
+
14
+ class VectorsClient:
15
+ """Client for ZeroDB vector operations."""
16
+
17
+ def __init__(self, client: "AINativeClient"):
18
+ """
19
+ Initialize vectors client.
20
+
21
+ Args:
22
+ client: Parent AINative client instance
23
+ """
24
+ self.client = client
25
+ self.base_path = "/zerodb/vectors"
26
+
27
+ def upsert(
28
+ self,
29
+ project_id: str,
30
+ vectors: List[Union[List[float], np.ndarray]],
31
+ metadata: Optional[List[Dict[str, Any]]] = None,
32
+ ids: Optional[List[str]] = None,
33
+ namespace: str = "default",
34
+ ) -> Dict[str, Any]:
35
+ """
36
+ Upsert vectors into the database.
37
+
38
+ Args:
39
+ project_id: Project ID
40
+ vectors: List of vectors (as lists or numpy arrays)
41
+ metadata: Optional metadata for each vector
42
+ ids: Optional IDs for vectors (auto-generated if not provided)
43
+ namespace: Namespace for vectors
44
+
45
+ Returns:
46
+ Upsert operation result
47
+ """
48
+ # Convert numpy arrays to lists if needed
49
+ vector_data = []
50
+ for i, vector in enumerate(vectors):
51
+ if isinstance(vector, np.ndarray):
52
+ vector = vector.tolist()
53
+
54
+ item = {"vector": vector}
55
+
56
+ if ids and i < len(ids):
57
+ item["id"] = ids[i]
58
+
59
+ if metadata and i < len(metadata):
60
+ item["metadata"] = metadata[i]
61
+
62
+ vector_data.append(item)
63
+
64
+ data = {
65
+ "project_id": project_id,
66
+ "namespace": namespace,
67
+ "items": vector_data,
68
+ }
69
+
70
+ return self.client.put(self.base_path, data=data)
71
+
72
+ def search(
73
+ self,
74
+ project_id: str,
75
+ vector: Union[List[float], np.ndarray],
76
+ top_k: int = 10,
77
+ namespace: str = "default",
78
+ filter: Optional[Dict[str, Any]] = None,
79
+ include_metadata: bool = True,
80
+ include_values: bool = False,
81
+ ) -> List[Dict[str, Any]]:
82
+ """
83
+ Search for similar vectors.
84
+
85
+ Args:
86
+ project_id: Project ID
87
+ vector: Query vector
88
+ top_k: Number of results to return
89
+ namespace: Namespace to search in
90
+ filter: Optional metadata filter
91
+ include_metadata: Include metadata in results
92
+ include_values: Include vector values in results
93
+
94
+ Returns:
95
+ List of search results with scores
96
+ """
97
+ if isinstance(vector, np.ndarray):
98
+ vector = vector.tolist()
99
+
100
+ data = {
101
+ "project_id": project_id,
102
+ "vector": vector,
103
+ "top_k": top_k,
104
+ "namespace": namespace,
105
+ "include_metadata": include_metadata,
106
+ "include_values": include_values,
107
+ }
108
+
109
+ if filter:
110
+ data["filter"] = filter
111
+
112
+ response = self.client.post(f"{self.base_path}/search", data=data)
113
+ return response.get("results", [])
114
+
115
+ def get(
116
+ self,
117
+ project_id: str,
118
+ ids: List[str],
119
+ namespace: str = "default",
120
+ include_metadata: bool = True,
121
+ include_values: bool = True,
122
+ ) -> List[Dict[str, Any]]:
123
+ """
124
+ Get vectors by IDs.
125
+
126
+ Args:
127
+ project_id: Project ID
128
+ ids: List of vector IDs
129
+ namespace: Namespace
130
+ include_metadata: Include metadata
131
+ include_values: Include vector values
132
+
133
+ Returns:
134
+ List of vectors
135
+ """
136
+ params = {
137
+ "project_id": project_id,
138
+ "ids": ",".join(ids),
139
+ "namespace": namespace,
140
+ "include_metadata": include_metadata,
141
+ "include_values": include_values,
142
+ }
143
+
144
+ response = self.client.get(self.base_path, params=params)
145
+ return response.get("vectors", [])
146
+
147
+ def delete(
148
+ self,
149
+ project_id: str,
150
+ ids: Optional[List[str]] = None,
151
+ namespace: str = "default",
152
+ delete_all: bool = False,
153
+ filter: Optional[Dict[str, Any]] = None,
154
+ ) -> Dict[str, Any]:
155
+ """
156
+ Delete vectors.
157
+
158
+ Args:
159
+ project_id: Project ID
160
+ ids: List of vector IDs to delete
161
+ namespace: Namespace
162
+ delete_all: Delete all vectors in namespace
163
+ filter: Delete vectors matching filter
164
+
165
+ Returns:
166
+ Deletion result
167
+ """
168
+ data = {
169
+ "project_id": project_id,
170
+ "namespace": namespace,
171
+ }
172
+
173
+ if delete_all:
174
+ data["delete_all"] = True
175
+ elif ids:
176
+ data["ids"] = ids
177
+ elif filter:
178
+ data["filter"] = filter
179
+ else:
180
+ raise ValueError("Must provide ids, filter, or delete_all=True")
181
+
182
+ return self.client.delete(self.base_path, data=data)
183
+
184
+ def update_metadata(
185
+ self,
186
+ project_id: str,
187
+ id: str,
188
+ metadata: Dict[str, Any],
189
+ namespace: str = "default",
190
+ ) -> Dict[str, Any]:
191
+ """
192
+ Update vector metadata.
193
+
194
+ Args:
195
+ project_id: Project ID
196
+ id: Vector ID
197
+ metadata: New metadata
198
+ namespace: Namespace
199
+
200
+ Returns:
201
+ Update result
202
+ """
203
+ data = {
204
+ "project_id": project_id,
205
+ "id": id,
206
+ "metadata": metadata,
207
+ "namespace": namespace,
208
+ }
209
+
210
+ return self.client.patch(f"{self.base_path}/{id}/metadata", data=data)
211
+
212
+ def describe_index_stats(
213
+ self,
214
+ project_id: str,
215
+ namespace: Optional[str] = None,
216
+ ) -> Dict[str, Any]:
217
+ """
218
+ Get index statistics.
219
+
220
+ Args:
221
+ project_id: Project ID
222
+ namespace: Optional namespace filter
223
+
224
+ Returns:
225
+ Index statistics
226
+ """
227
+ params = {"project_id": project_id}
228
+ if namespace:
229
+ params["namespace"] = namespace
230
+
231
+ return self.client.get(f"{self.base_path}/stats", params=params)