augmem.cortext 1.1.6__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.
- augmem/cortext/__init__.py +1310 -0
- augmem/cortext/native/linux-aarch64/libcortext.so +0 -0
- augmem/cortext/native/linux-x86_64/libcortext.so +0 -0
- augmem/cortext/native/macos-aarch64/libcortext.dylib +0 -0
- augmem/cortext/native/macos-x86_64/libcortext.dylib +0 -0
- augmem/cortext/native/manifest.json +42 -0
- augmem/cortext/native/windows-aarch64/cortext.dll +0 -0
- augmem/cortext/native/windows-x86_64/cortext.dll +0 -0
- augmem/cortext/py.typed +0 -0
- augmem_cortext-1.1.6.dist-info/METADATA +92 -0
- augmem_cortext-1.1.6.dist-info/RECORD +13 -0
- augmem_cortext-1.1.6.dist-info/WHEEL +5 -0
- augmem_cortext-1.1.6.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,1310 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import ctypes
|
|
4
|
+
import ctypes.util
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import platform
|
|
8
|
+
from array import array
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Iterable
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"Config",
|
|
15
|
+
"Cortext",
|
|
16
|
+
"CortextError",
|
|
17
|
+
"DBProvider",
|
|
18
|
+
"Media",
|
|
19
|
+
"ObjectStoreProvider",
|
|
20
|
+
"last_error",
|
|
21
|
+
"load_library",
|
|
22
|
+
"version",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class CortextError(RuntimeError):
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass(slots=True)
|
|
31
|
+
class Config:
|
|
32
|
+
focus: float = 0.5
|
|
33
|
+
sensitivity: float = 0.5
|
|
34
|
+
stability: float = 0.5
|
|
35
|
+
affect_interrupt: bool = True
|
|
36
|
+
affect_retrieval: bool = True
|
|
37
|
+
reinforcement_enabled: bool = True
|
|
38
|
+
procedural_enabled: bool = True
|
|
39
|
+
sequential_edges_enabled: bool = True
|
|
40
|
+
signal_filter_audio_enabled: bool = True
|
|
41
|
+
signal_filter_image_enabled: bool = True
|
|
42
|
+
signal_filter_text_enabled: bool = False
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@dataclass(frozen=True, slots=True)
|
|
46
|
+
class Media:
|
|
47
|
+
data: bytes | bytearray | memoryview
|
|
48
|
+
mimetype: str
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class _NativeConfig(ctypes.Structure):
|
|
52
|
+
_fields_ = [
|
|
53
|
+
("struct_size", ctypes.c_size_t),
|
|
54
|
+
("focus", ctypes.c_double),
|
|
55
|
+
("sensitivity", ctypes.c_double),
|
|
56
|
+
("stability", ctypes.c_double),
|
|
57
|
+
("affect_interrupt", ctypes.c_int),
|
|
58
|
+
("affect_retrieval", ctypes.c_int),
|
|
59
|
+
("reinforcement_enabled", ctypes.c_int),
|
|
60
|
+
("procedural_enabled", ctypes.c_int),
|
|
61
|
+
("sequential_edges_enabled", ctypes.c_int),
|
|
62
|
+
("signal_filter_audio_enabled", ctypes.c_int),
|
|
63
|
+
("signal_filter_image_enabled", ctypes.c_int),
|
|
64
|
+
("signal_filter_text_enabled", ctypes.c_int),
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class _NativeMedia(ctypes.Structure):
|
|
69
|
+
_fields_ = [
|
|
70
|
+
("data", ctypes.POINTER(ctypes.c_uint8)),
|
|
71
|
+
("size", ctypes.c_size_t),
|
|
72
|
+
("mimetype", ctypes.c_char_p),
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class _NativeProcessJsonOptions(ctypes.Structure):
|
|
77
|
+
_fields_ = [
|
|
78
|
+
("struct_size", ctypes.c_size_t),
|
|
79
|
+
("include_embedding", ctypes.c_int),
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
CORTEXT_DB_VALUE_NULL = 0
|
|
84
|
+
CORTEXT_DB_VALUE_INT64 = 1
|
|
85
|
+
CORTEXT_DB_VALUE_DOUBLE = 2
|
|
86
|
+
CORTEXT_DB_VALUE_TEXT = 3
|
|
87
|
+
CORTEXT_DB_VALUE_BLOB = 4
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class _DBValue(ctypes.Structure):
|
|
91
|
+
_fields_ = [
|
|
92
|
+
("type", ctypes.c_int),
|
|
93
|
+
("int64_value", ctypes.c_int64),
|
|
94
|
+
("double_value", ctypes.c_double),
|
|
95
|
+
("text_value", ctypes.c_char_p),
|
|
96
|
+
("blob_data", ctypes.POINTER(ctypes.c_uint8)),
|
|
97
|
+
("blob_size", ctypes.c_size_t),
|
|
98
|
+
]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class _DBCell(ctypes.Structure):
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class _DBRow(ctypes.Structure):
|
|
106
|
+
pass
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class _DBResult(ctypes.Structure):
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
_DBCell._fields_ = [("column_name", ctypes.c_char_p), ("value", _DBValue)]
|
|
114
|
+
_DBRow._fields_ = [
|
|
115
|
+
("cells", ctypes.POINTER(_DBCell)),
|
|
116
|
+
("cell_count", ctypes.c_size_t),
|
|
117
|
+
]
|
|
118
|
+
_DBResult._fields_ = [
|
|
119
|
+
("rows", ctypes.POINTER(_DBRow)),
|
|
120
|
+
("row_count", ctypes.c_size_t),
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
_ExecuteCallback = ctypes.CFUNCTYPE(
|
|
125
|
+
ctypes.c_int,
|
|
126
|
+
ctypes.c_void_p,
|
|
127
|
+
ctypes.c_void_p,
|
|
128
|
+
ctypes.c_char_p,
|
|
129
|
+
ctypes.POINTER(_DBValue),
|
|
130
|
+
ctypes.c_size_t,
|
|
131
|
+
ctypes.POINTER(_DBResult),
|
|
132
|
+
ctypes.POINTER(ctypes.c_char_p),
|
|
133
|
+
)
|
|
134
|
+
_BeginCallback = ctypes.CFUNCTYPE(
|
|
135
|
+
ctypes.c_int,
|
|
136
|
+
ctypes.c_void_p,
|
|
137
|
+
ctypes.c_void_p,
|
|
138
|
+
ctypes.POINTER(ctypes.c_void_p),
|
|
139
|
+
ctypes.POINTER(ctypes.c_char_p),
|
|
140
|
+
)
|
|
141
|
+
_TransactionCallback = ctypes.CFUNCTYPE(
|
|
142
|
+
ctypes.c_int,
|
|
143
|
+
ctypes.c_void_p,
|
|
144
|
+
ctypes.c_void_p,
|
|
145
|
+
ctypes.POINTER(ctypes.c_char_p),
|
|
146
|
+
)
|
|
147
|
+
_ReleaseTransactionCallback = ctypes.CFUNCTYPE(
|
|
148
|
+
None,
|
|
149
|
+
ctypes.c_void_p,
|
|
150
|
+
ctypes.c_void_p,
|
|
151
|
+
)
|
|
152
|
+
_ReleaseResultCallback = ctypes.CFUNCTYPE(
|
|
153
|
+
None,
|
|
154
|
+
ctypes.c_void_p,
|
|
155
|
+
ctypes.POINTER(_DBResult),
|
|
156
|
+
)
|
|
157
|
+
_ObjectBeginCallback = ctypes.CFUNCTYPE(
|
|
158
|
+
ctypes.c_int,
|
|
159
|
+
ctypes.c_void_p,
|
|
160
|
+
ctypes.c_void_p,
|
|
161
|
+
ctypes.POINTER(ctypes.c_void_p),
|
|
162
|
+
ctypes.POINTER(ctypes.c_char_p),
|
|
163
|
+
)
|
|
164
|
+
_ObjectPutCallback = ctypes.CFUNCTYPE(
|
|
165
|
+
ctypes.c_int,
|
|
166
|
+
ctypes.c_void_p,
|
|
167
|
+
ctypes.c_void_p,
|
|
168
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
169
|
+
ctypes.c_size_t,
|
|
170
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
171
|
+
ctypes.c_size_t,
|
|
172
|
+
ctypes.POINTER(ctypes.c_char_p),
|
|
173
|
+
)
|
|
174
|
+
_ObjectGetCallback = ctypes.CFUNCTYPE(
|
|
175
|
+
ctypes.c_int,
|
|
176
|
+
ctypes.c_void_p,
|
|
177
|
+
ctypes.c_void_p,
|
|
178
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
179
|
+
ctypes.c_size_t,
|
|
180
|
+
ctypes.POINTER(ctypes.POINTER(ctypes.c_uint8)),
|
|
181
|
+
ctypes.POINTER(ctypes.c_size_t),
|
|
182
|
+
ctypes.POINTER(ctypes.c_char_p),
|
|
183
|
+
)
|
|
184
|
+
_ObjectExistsCallback = ctypes.CFUNCTYPE(
|
|
185
|
+
ctypes.c_int,
|
|
186
|
+
ctypes.c_void_p,
|
|
187
|
+
ctypes.c_void_p,
|
|
188
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
189
|
+
ctypes.c_size_t,
|
|
190
|
+
ctypes.POINTER(ctypes.c_int),
|
|
191
|
+
ctypes.POINTER(ctypes.c_char_p),
|
|
192
|
+
)
|
|
193
|
+
_ObjectDeleteCallback = ctypes.CFUNCTYPE(
|
|
194
|
+
ctypes.c_int,
|
|
195
|
+
ctypes.c_void_p,
|
|
196
|
+
ctypes.c_void_p,
|
|
197
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
198
|
+
ctypes.c_size_t,
|
|
199
|
+
ctypes.POINTER(ctypes.c_int),
|
|
200
|
+
ctypes.POINTER(ctypes.c_char_p),
|
|
201
|
+
)
|
|
202
|
+
_ObjectTransactionCallback = ctypes.CFUNCTYPE(
|
|
203
|
+
ctypes.c_int,
|
|
204
|
+
ctypes.c_void_p,
|
|
205
|
+
ctypes.c_void_p,
|
|
206
|
+
ctypes.POINTER(ctypes.c_char_p),
|
|
207
|
+
)
|
|
208
|
+
_ObjectReleaseTransactionCallback = ctypes.CFUNCTYPE(
|
|
209
|
+
None,
|
|
210
|
+
ctypes.c_void_p,
|
|
211
|
+
ctypes.c_void_p,
|
|
212
|
+
)
|
|
213
|
+
_ObjectReleaseBlobCallback = ctypes.CFUNCTYPE(
|
|
214
|
+
None,
|
|
215
|
+
ctypes.c_void_p,
|
|
216
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
217
|
+
ctypes.c_size_t,
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class _DBCallbacks(ctypes.Structure):
|
|
222
|
+
_fields_ = [
|
|
223
|
+
("struct_size", ctypes.c_size_t),
|
|
224
|
+
("execute", _ExecuteCallback),
|
|
225
|
+
("begin", _BeginCallback),
|
|
226
|
+
("commit", _TransactionCallback),
|
|
227
|
+
("rollback", _TransactionCallback),
|
|
228
|
+
("release_transaction", _ReleaseTransactionCallback),
|
|
229
|
+
("release_result", _ReleaseResultCallback),
|
|
230
|
+
]
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class _ObjectCallbacks(ctypes.Structure):
|
|
234
|
+
_fields_ = [
|
|
235
|
+
("struct_size", ctypes.c_size_t),
|
|
236
|
+
("begin", _ObjectBeginCallback),
|
|
237
|
+
("put", _ObjectPutCallback),
|
|
238
|
+
("get", _ObjectGetCallback),
|
|
239
|
+
("exists", _ObjectExistsCallback),
|
|
240
|
+
("delete_object", _ObjectDeleteCallback),
|
|
241
|
+
("commit", _ObjectTransactionCallback),
|
|
242
|
+
("rollback", _ObjectTransactionCallback),
|
|
243
|
+
("release_transaction", _ObjectReleaseTransactionCallback),
|
|
244
|
+
("release_blob", _ObjectReleaseBlobCallback),
|
|
245
|
+
]
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class DBProvider:
|
|
249
|
+
def execute(self, query: str, params: list[Any], transaction: Any | None) -> list[dict[str, Any]]:
|
|
250
|
+
raise NotImplementedError
|
|
251
|
+
|
|
252
|
+
def begin(self, parent_transaction: Any | None) -> Any:
|
|
253
|
+
raise NotImplementedError
|
|
254
|
+
|
|
255
|
+
def commit(self, transaction: Any) -> None:
|
|
256
|
+
raise NotImplementedError
|
|
257
|
+
|
|
258
|
+
def rollback(self, transaction: Any) -> None:
|
|
259
|
+
raise NotImplementedError
|
|
260
|
+
|
|
261
|
+
def release_transaction(self, transaction: Any) -> None:
|
|
262
|
+
pass
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class ObjectStoreProvider:
|
|
266
|
+
def begin(self, parent_transaction: Any | None) -> Any:
|
|
267
|
+
raise NotImplementedError
|
|
268
|
+
|
|
269
|
+
def put(self, object_id: bytes, data: bytes, transaction: Any) -> None:
|
|
270
|
+
raise NotImplementedError
|
|
271
|
+
|
|
272
|
+
def get(self, object_id: bytes, transaction: Any) -> bytes | None:
|
|
273
|
+
raise NotImplementedError
|
|
274
|
+
|
|
275
|
+
def exists(self, object_id: bytes, transaction: Any) -> bool:
|
|
276
|
+
raise NotImplementedError
|
|
277
|
+
|
|
278
|
+
def delete(self, object_id: bytes, transaction: Any) -> bool:
|
|
279
|
+
raise NotImplementedError
|
|
280
|
+
|
|
281
|
+
def commit(self, transaction: Any) -> None:
|
|
282
|
+
raise NotImplementedError
|
|
283
|
+
|
|
284
|
+
def rollback(self, transaction: Any) -> None:
|
|
285
|
+
raise NotImplementedError
|
|
286
|
+
|
|
287
|
+
def release_transaction(self, transaction: Any) -> None:
|
|
288
|
+
pass
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
_SUPPORTED_NATIVE_TARGETS = {
|
|
292
|
+
"linux-aarch64",
|
|
293
|
+
"linux-x86_64",
|
|
294
|
+
"macos-aarch64",
|
|
295
|
+
"macos-x86_64",
|
|
296
|
+
"windows-aarch64",
|
|
297
|
+
"windows-x86_64",
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
_DLL_DIRECTORY_HANDLES: list[Any] = []
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _package_dir() -> Path:
|
|
304
|
+
return Path(__file__).resolve().parent
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _platform_tag() -> str:
|
|
308
|
+
system = platform.system().lower()
|
|
309
|
+
if system == "darwin":
|
|
310
|
+
os_tag = "macos"
|
|
311
|
+
elif system == "linux":
|
|
312
|
+
os_tag = "linux"
|
|
313
|
+
elif system == "windows":
|
|
314
|
+
os_tag = "windows"
|
|
315
|
+
else:
|
|
316
|
+
os_tag = system or "unknown"
|
|
317
|
+
|
|
318
|
+
machine = platform.machine().lower()
|
|
319
|
+
if machine in {"amd64", "x64"}:
|
|
320
|
+
arch = "x86_64"
|
|
321
|
+
elif machine == "arm64":
|
|
322
|
+
arch = "aarch64"
|
|
323
|
+
else:
|
|
324
|
+
arch = machine or "unknown"
|
|
325
|
+
|
|
326
|
+
return f"{os_tag}-{arch}"
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def _library_name_for_tag(tag: str) -> str:
|
|
330
|
+
os_tag = tag.split("-", 1)[0]
|
|
331
|
+
if os_tag == "macos":
|
|
332
|
+
return "libcortext.dylib"
|
|
333
|
+
if os_tag == "windows":
|
|
334
|
+
return "cortext.dll"
|
|
335
|
+
return "libcortext.so"
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _repo_root() -> Path | None:
|
|
339
|
+
for parent in (_package_dir(), *_package_dir().parents):
|
|
340
|
+
if (parent / "build.zig").is_file() and (parent / "CMakeLists.txt").is_file():
|
|
341
|
+
return parent
|
|
342
|
+
return None
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
def _candidate_library_paths() -> list[Path]:
|
|
346
|
+
names = ("libcortext.dylib", "libcortext.so", "cortext.dll", "libcortext.dll")
|
|
347
|
+
candidates: list[Path] = []
|
|
348
|
+
|
|
349
|
+
env_path = os.environ.get("CORTEXT_LIBRARY_PATH")
|
|
350
|
+
if env_path:
|
|
351
|
+
candidates.append(Path(env_path).expanduser())
|
|
352
|
+
|
|
353
|
+
tag = _platform_tag()
|
|
354
|
+
if tag in _SUPPORTED_NATIVE_TARGETS:
|
|
355
|
+
candidates.append(_package_dir() / "native" / tag / _library_name_for_tag(tag))
|
|
356
|
+
|
|
357
|
+
for name in names:
|
|
358
|
+
candidates.append(_package_dir() / name)
|
|
359
|
+
|
|
360
|
+
root = _repo_root()
|
|
361
|
+
if root is not None:
|
|
362
|
+
for directory in (
|
|
363
|
+
root / "build" / "ffi-release",
|
|
364
|
+
root / "build" / "ffi-release" / "lib",
|
|
365
|
+
root / "zig-out" / "lib",
|
|
366
|
+
root / "zig-out" / "bin",
|
|
367
|
+
root / "install" / "lib",
|
|
368
|
+
root / "install" / "bin",
|
|
369
|
+
):
|
|
370
|
+
for name in names:
|
|
371
|
+
candidates.append(directory / name)
|
|
372
|
+
|
|
373
|
+
return candidates
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def _load_cdll(path: Path | str) -> ctypes.CDLL:
|
|
377
|
+
library_path = Path(path) if not isinstance(path, str) else Path(path)
|
|
378
|
+
if os.name == "nt" and library_path.parent and hasattr(os, "add_dll_directory"):
|
|
379
|
+
_DLL_DIRECTORY_HANDLES.append(os.add_dll_directory(os.fspath(library_path.parent)))
|
|
380
|
+
return ctypes.CDLL(os.fspath(path))
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def _configure_library(lib: ctypes.CDLL) -> ctypes.CDLL:
|
|
384
|
+
lib.cortext_config_init.argtypes = [ctypes.POINTER(_NativeConfig)]
|
|
385
|
+
lib.cortext_config_init.restype = None
|
|
386
|
+
|
|
387
|
+
lib.cortext_create_with_config.argtypes = [
|
|
388
|
+
ctypes.POINTER(_NativeConfig),
|
|
389
|
+
ctypes.c_char_p,
|
|
390
|
+
ctypes.c_char_p,
|
|
391
|
+
]
|
|
392
|
+
lib.cortext_create_with_config.restype = ctypes.c_void_p
|
|
393
|
+
|
|
394
|
+
lib.cortext_create_with_store_callbacks.argtypes = [
|
|
395
|
+
ctypes.POINTER(_NativeConfig),
|
|
396
|
+
ctypes.POINTER(_DBCallbacks),
|
|
397
|
+
ctypes.c_void_p,
|
|
398
|
+
ctypes.c_char_p,
|
|
399
|
+
]
|
|
400
|
+
lib.cortext_create_with_store_callbacks.restype = ctypes.c_void_p
|
|
401
|
+
|
|
402
|
+
lib.cortext_create_with_store_and_object_callbacks.argtypes = [
|
|
403
|
+
ctypes.POINTER(_NativeConfig),
|
|
404
|
+
ctypes.POINTER(_DBCallbacks),
|
|
405
|
+
ctypes.c_void_p,
|
|
406
|
+
ctypes.POINTER(_ObjectCallbacks),
|
|
407
|
+
ctypes.c_void_p,
|
|
408
|
+
ctypes.c_char_p,
|
|
409
|
+
]
|
|
410
|
+
lib.cortext_create_with_store_and_object_callbacks.restype = ctypes.c_void_p
|
|
411
|
+
|
|
412
|
+
lib.cortext_create_with_config_and_object_callbacks.argtypes = [
|
|
413
|
+
ctypes.POINTER(_NativeConfig),
|
|
414
|
+
ctypes.c_char_p,
|
|
415
|
+
ctypes.POINTER(_ObjectCallbacks),
|
|
416
|
+
ctypes.c_void_p,
|
|
417
|
+
ctypes.c_char_p,
|
|
418
|
+
]
|
|
419
|
+
lib.cortext_create_with_config_and_object_callbacks.restype = ctypes.c_void_p
|
|
420
|
+
|
|
421
|
+
lib.cortext_free.argtypes = [ctypes.c_void_p]
|
|
422
|
+
lib.cortext_free.restype = None
|
|
423
|
+
|
|
424
|
+
lib.cortext_process_text_json.argtypes = [
|
|
425
|
+
ctypes.c_void_p,
|
|
426
|
+
ctypes.c_char_p,
|
|
427
|
+
ctypes.c_char_p,
|
|
428
|
+
]
|
|
429
|
+
lib.cortext_process_text_json.restype = ctypes.c_void_p
|
|
430
|
+
|
|
431
|
+
lib.cortext_process_text_json_with_options.argtypes = [
|
|
432
|
+
ctypes.c_void_p,
|
|
433
|
+
ctypes.c_char_p,
|
|
434
|
+
ctypes.c_char_p,
|
|
435
|
+
ctypes.POINTER(_NativeProcessJsonOptions),
|
|
436
|
+
]
|
|
437
|
+
lib.cortext_process_text_json_with_options.restype = ctypes.c_void_p
|
|
438
|
+
|
|
439
|
+
lib.cortext_process_audio_json.argtypes = [
|
|
440
|
+
ctypes.c_void_p,
|
|
441
|
+
ctypes.POINTER(ctypes.c_float),
|
|
442
|
+
ctypes.c_size_t,
|
|
443
|
+
ctypes.c_char_p,
|
|
444
|
+
]
|
|
445
|
+
lib.cortext_process_audio_json.restype = ctypes.c_void_p
|
|
446
|
+
|
|
447
|
+
lib.cortext_process_audio_json_with_options.argtypes = [
|
|
448
|
+
ctypes.c_void_p,
|
|
449
|
+
ctypes.POINTER(ctypes.c_float),
|
|
450
|
+
ctypes.c_size_t,
|
|
451
|
+
ctypes.c_char_p,
|
|
452
|
+
ctypes.POINTER(_NativeProcessJsonOptions),
|
|
453
|
+
]
|
|
454
|
+
lib.cortext_process_audio_json_with_options.restype = ctypes.c_void_p
|
|
455
|
+
|
|
456
|
+
lib.cortext_process_audio_with_media_json.argtypes = [
|
|
457
|
+
ctypes.c_void_p,
|
|
458
|
+
ctypes.POINTER(ctypes.c_float),
|
|
459
|
+
ctypes.c_size_t,
|
|
460
|
+
ctypes.c_char_p,
|
|
461
|
+
ctypes.POINTER(_NativeMedia),
|
|
462
|
+
]
|
|
463
|
+
lib.cortext_process_audio_with_media_json.restype = ctypes.c_void_p
|
|
464
|
+
|
|
465
|
+
lib.cortext_process_audio_with_media_json_with_options.argtypes = [
|
|
466
|
+
ctypes.c_void_p,
|
|
467
|
+
ctypes.POINTER(ctypes.c_float),
|
|
468
|
+
ctypes.c_size_t,
|
|
469
|
+
ctypes.c_char_p,
|
|
470
|
+
ctypes.POINTER(_NativeMedia),
|
|
471
|
+
ctypes.POINTER(_NativeProcessJsonOptions),
|
|
472
|
+
]
|
|
473
|
+
lib.cortext_process_audio_with_media_json_with_options.restype = ctypes.c_void_p
|
|
474
|
+
|
|
475
|
+
lib.cortext_process_image_json.argtypes = [
|
|
476
|
+
ctypes.c_void_p,
|
|
477
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
478
|
+
ctypes.c_int,
|
|
479
|
+
ctypes.c_int,
|
|
480
|
+
ctypes.c_int,
|
|
481
|
+
ctypes.c_char_p,
|
|
482
|
+
]
|
|
483
|
+
lib.cortext_process_image_json.restype = ctypes.c_void_p
|
|
484
|
+
|
|
485
|
+
lib.cortext_process_image_json_with_options.argtypes = [
|
|
486
|
+
ctypes.c_void_p,
|
|
487
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
488
|
+
ctypes.c_int,
|
|
489
|
+
ctypes.c_int,
|
|
490
|
+
ctypes.c_int,
|
|
491
|
+
ctypes.c_char_p,
|
|
492
|
+
ctypes.POINTER(_NativeProcessJsonOptions),
|
|
493
|
+
]
|
|
494
|
+
lib.cortext_process_image_json_with_options.restype = ctypes.c_void_p
|
|
495
|
+
|
|
496
|
+
lib.cortext_process_image_with_media_json.argtypes = [
|
|
497
|
+
ctypes.c_void_p,
|
|
498
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
499
|
+
ctypes.c_int,
|
|
500
|
+
ctypes.c_int,
|
|
501
|
+
ctypes.c_int,
|
|
502
|
+
ctypes.c_char_p,
|
|
503
|
+
ctypes.POINTER(_NativeMedia),
|
|
504
|
+
]
|
|
505
|
+
lib.cortext_process_image_with_media_json.restype = ctypes.c_void_p
|
|
506
|
+
|
|
507
|
+
lib.cortext_process_image_with_media_json_with_options.argtypes = [
|
|
508
|
+
ctypes.c_void_p,
|
|
509
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
510
|
+
ctypes.c_int,
|
|
511
|
+
ctypes.c_int,
|
|
512
|
+
ctypes.c_int,
|
|
513
|
+
ctypes.c_char_p,
|
|
514
|
+
ctypes.POINTER(_NativeMedia),
|
|
515
|
+
ctypes.POINTER(_NativeProcessJsonOptions),
|
|
516
|
+
]
|
|
517
|
+
lib.cortext_process_image_with_media_json_with_options.restype = ctypes.c_void_p
|
|
518
|
+
|
|
519
|
+
lib.cortext_embed_text_json.argtypes = [
|
|
520
|
+
ctypes.c_void_p,
|
|
521
|
+
ctypes.c_char_p,
|
|
522
|
+
]
|
|
523
|
+
lib.cortext_embed_text_json.restype = ctypes.c_void_p
|
|
524
|
+
|
|
525
|
+
lib.cortext_embed_audio_json.argtypes = [
|
|
526
|
+
ctypes.c_void_p,
|
|
527
|
+
ctypes.POINTER(ctypes.c_float),
|
|
528
|
+
ctypes.c_size_t,
|
|
529
|
+
]
|
|
530
|
+
lib.cortext_embed_audio_json.restype = ctypes.c_void_p
|
|
531
|
+
|
|
532
|
+
lib.cortext_embed_image_json.argtypes = [
|
|
533
|
+
ctypes.c_void_p,
|
|
534
|
+
ctypes.POINTER(ctypes.c_uint8),
|
|
535
|
+
ctypes.c_int,
|
|
536
|
+
ctypes.c_int,
|
|
537
|
+
ctypes.c_int,
|
|
538
|
+
]
|
|
539
|
+
lib.cortext_embed_image_json.restype = ctypes.c_void_p
|
|
540
|
+
|
|
541
|
+
lib.cortext_consolidate_json.argtypes = [ctypes.c_void_p]
|
|
542
|
+
lib.cortext_consolidate_json.restype = ctypes.c_void_p
|
|
543
|
+
|
|
544
|
+
lib.cortext_flush.argtypes = [ctypes.c_void_p]
|
|
545
|
+
lib.cortext_flush.restype = ctypes.c_int
|
|
546
|
+
|
|
547
|
+
lib.cortext_reset.argtypes = [ctypes.c_void_p]
|
|
548
|
+
lib.cortext_reset.restype = ctypes.c_int
|
|
549
|
+
|
|
550
|
+
lib.cortext_version.argtypes = []
|
|
551
|
+
lib.cortext_version.restype = ctypes.c_char_p
|
|
552
|
+
|
|
553
|
+
lib.cortext_last_error.argtypes = []
|
|
554
|
+
lib.cortext_last_error.restype = ctypes.c_char_p
|
|
555
|
+
|
|
556
|
+
lib.cortext_string_free.argtypes = [ctypes.c_void_p]
|
|
557
|
+
lib.cortext_string_free.restype = None
|
|
558
|
+
return lib
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
def load_library(path: str | os.PathLike[str] | None = None) -> ctypes.CDLL:
|
|
562
|
+
if path is not None:
|
|
563
|
+
return _configure_library(_load_cdll(Path(path)))
|
|
564
|
+
|
|
565
|
+
for candidate in _candidate_library_paths():
|
|
566
|
+
if candidate.exists():
|
|
567
|
+
return _configure_library(_load_cdll(candidate))
|
|
568
|
+
|
|
569
|
+
discovered = ctypes.util.find_library("cortext")
|
|
570
|
+
if discovered:
|
|
571
|
+
return _configure_library(ctypes.CDLL(discovered))
|
|
572
|
+
|
|
573
|
+
searched = "\n".join(str(candidate) for candidate in _candidate_library_paths())
|
|
574
|
+
supported = ", ".join(sorted(_SUPPORTED_NATIVE_TARGETS))
|
|
575
|
+
raise CortextError(
|
|
576
|
+
"Could not locate the Cortext shared library. "
|
|
577
|
+
f"Current platform tag is {_platform_tag()}; bundled wheels support: {supported}. "
|
|
578
|
+
"Build a wheel with `python scripts/build_python_package.py`, build a local shared "
|
|
579
|
+
"library with `cmake --preset ffi-release && cmake --build --preset ffi-release --target cortext`, "
|
|
580
|
+
"or set CORTEXT_LIBRARY_PATH.\n"
|
|
581
|
+
f"Searched:\n{searched}"
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
_LIB = load_library()
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
def last_error() -> str:
|
|
589
|
+
raw = _LIB.cortext_last_error()
|
|
590
|
+
return raw.decode("utf-8") if raw else ""
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def version() -> str:
|
|
594
|
+
raw = _LIB.cortext_version()
|
|
595
|
+
return raw.decode("utf-8") if raw else ""
|
|
596
|
+
|
|
597
|
+
|
|
598
|
+
def _raise_last_error(prefix: str) -> None:
|
|
599
|
+
detail = last_error()
|
|
600
|
+
raise CortextError(f"{prefix}: {detail}" if detail else prefix)
|
|
601
|
+
|
|
602
|
+
|
|
603
|
+
def _native_media(
|
|
604
|
+
media: Media | bytes | bytearray | memoryview | None,
|
|
605
|
+
mimetype: str | None,
|
|
606
|
+
) -> tuple[_NativeMedia, Any, bytes | None]:
|
|
607
|
+
if media is None:
|
|
608
|
+
return _NativeMedia(None, 0, None), None, None
|
|
609
|
+
if isinstance(media, Media):
|
|
610
|
+
if mimetype is None:
|
|
611
|
+
mimetype = media.mimetype
|
|
612
|
+
media = media.data
|
|
613
|
+
blob = bytes(media)
|
|
614
|
+
if not blob:
|
|
615
|
+
return _NativeMedia(None, 0, None), None, None
|
|
616
|
+
if not mimetype:
|
|
617
|
+
raise ValueError("media_mimetype is required when media bytes are provided")
|
|
618
|
+
raw = (ctypes.c_uint8 * len(blob)).from_buffer_copy(blob)
|
|
619
|
+
mimetype_bytes = mimetype.encode("utf-8")
|
|
620
|
+
return _NativeMedia(raw, len(blob), mimetype_bytes), raw, mimetype_bytes
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
def _native_process_json_options(include_embedding: bool) -> _NativeProcessJsonOptions:
|
|
624
|
+
return _NativeProcessJsonOptions(
|
|
625
|
+
ctypes.sizeof(_NativeProcessJsonOptions), 1 if include_embedding else 0
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def _bool_to_int(value: bool) -> int:
|
|
630
|
+
return 1 if value else 0
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def _encode_optional_string(value: str | None) -> ctypes.Array[ctypes.c_char] | None:
|
|
634
|
+
if value is None:
|
|
635
|
+
return None
|
|
636
|
+
return ctypes.create_string_buffer(value.encode("utf-8"))
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
class _ProviderBridge:
|
|
640
|
+
def __init__(self, provider: DBProvider) -> None:
|
|
641
|
+
self.provider = provider
|
|
642
|
+
self._transactions: dict[int, Any] = {}
|
|
643
|
+
self._next_transaction = 1
|
|
644
|
+
self._results: dict[int, Any] = {}
|
|
645
|
+
self._errors: list[ctypes.Array[ctypes.c_char]] = []
|
|
646
|
+
|
|
647
|
+
self.execute_cb = _ExecuteCallback(self._execute)
|
|
648
|
+
self.begin_cb = _BeginCallback(self._begin)
|
|
649
|
+
self.commit_cb = _TransactionCallback(self._commit)
|
|
650
|
+
self.rollback_cb = _TransactionCallback(self._rollback)
|
|
651
|
+
self.release_transaction_cb = _ReleaseTransactionCallback(
|
|
652
|
+
self._release_transaction
|
|
653
|
+
)
|
|
654
|
+
self.release_result_cb = _ReleaseResultCallback(self._release_result)
|
|
655
|
+
self.callbacks = _DBCallbacks(
|
|
656
|
+
ctypes.sizeof(_DBCallbacks),
|
|
657
|
+
self.execute_cb,
|
|
658
|
+
self.begin_cb,
|
|
659
|
+
self.commit_cb,
|
|
660
|
+
self.rollback_cb,
|
|
661
|
+
self.release_transaction_cb,
|
|
662
|
+
self.release_result_cb,
|
|
663
|
+
)
|
|
664
|
+
|
|
665
|
+
def _set_error(self, out_error: Any, message: str) -> int:
|
|
666
|
+
encoded = ctypes.create_string_buffer(message.encode("utf-8"))
|
|
667
|
+
self._errors.append(encoded)
|
|
668
|
+
if out_error:
|
|
669
|
+
out_error[0] = ctypes.cast(encoded, ctypes.c_char_p)
|
|
670
|
+
return 1
|
|
671
|
+
|
|
672
|
+
def _transaction(self, handle: int) -> Any | None:
|
|
673
|
+
return self._transactions.get(handle) if handle else None
|
|
674
|
+
|
|
675
|
+
def _value_from_c(self, value: _DBValue) -> Any:
|
|
676
|
+
if value.type == CORTEXT_DB_VALUE_INT64:
|
|
677
|
+
return int(value.int64_value)
|
|
678
|
+
if value.type == CORTEXT_DB_VALUE_DOUBLE:
|
|
679
|
+
return float(value.double_value)
|
|
680
|
+
if value.type == CORTEXT_DB_VALUE_TEXT:
|
|
681
|
+
return value.text_value.decode("utf-8") if value.text_value else ""
|
|
682
|
+
if value.type == CORTEXT_DB_VALUE_BLOB:
|
|
683
|
+
if not value.blob_data or value.blob_size == 0:
|
|
684
|
+
return b""
|
|
685
|
+
return bytes(value.blob_data[: value.blob_size])
|
|
686
|
+
return None
|
|
687
|
+
|
|
688
|
+
def _db_value(self, value: Any, keepalive: list[Any]) -> _DBValue:
|
|
689
|
+
out = _DBValue()
|
|
690
|
+
if value is None:
|
|
691
|
+
out.type = CORTEXT_DB_VALUE_NULL
|
|
692
|
+
elif isinstance(value, bool):
|
|
693
|
+
out.type = CORTEXT_DB_VALUE_INT64
|
|
694
|
+
out.int64_value = int(value)
|
|
695
|
+
elif isinstance(value, int):
|
|
696
|
+
out.type = CORTEXT_DB_VALUE_INT64
|
|
697
|
+
out.int64_value = value
|
|
698
|
+
elif isinstance(value, float):
|
|
699
|
+
out.type = CORTEXT_DB_VALUE_DOUBLE
|
|
700
|
+
out.double_value = value
|
|
701
|
+
elif isinstance(value, str):
|
|
702
|
+
raw = ctypes.create_string_buffer(value.encode("utf-8"))
|
|
703
|
+
keepalive.append(raw)
|
|
704
|
+
out.type = CORTEXT_DB_VALUE_TEXT
|
|
705
|
+
out.text_value = ctypes.cast(raw, ctypes.c_char_p)
|
|
706
|
+
else:
|
|
707
|
+
raw_bytes = bytes(value)
|
|
708
|
+
out.type = CORTEXT_DB_VALUE_BLOB
|
|
709
|
+
out.blob_size = len(raw_bytes)
|
|
710
|
+
if raw_bytes:
|
|
711
|
+
array_type = ctypes.c_uint8 * len(raw_bytes)
|
|
712
|
+
raw = array_type.from_buffer_copy(raw_bytes)
|
|
713
|
+
keepalive.append(raw)
|
|
714
|
+
out.blob_data = ctypes.cast(raw, ctypes.POINTER(ctypes.c_uint8))
|
|
715
|
+
return out
|
|
716
|
+
|
|
717
|
+
def _execute(
|
|
718
|
+
self,
|
|
719
|
+
_user_data: int,
|
|
720
|
+
transaction: int,
|
|
721
|
+
query: bytes,
|
|
722
|
+
params: Any,
|
|
723
|
+
param_count: int,
|
|
724
|
+
out_result: Any,
|
|
725
|
+
out_error: Any,
|
|
726
|
+
) -> int:
|
|
727
|
+
try:
|
|
728
|
+
py_params = [
|
|
729
|
+
self._value_from_c(params[index]) for index in range(param_count)
|
|
730
|
+
]
|
|
731
|
+
rows = self.provider.execute(
|
|
732
|
+
query.decode("utf-8"), py_params, self._transaction(transaction)
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
keepalive: list[Any] = []
|
|
736
|
+
row_array = (_DBRow * len(rows))()
|
|
737
|
+
for row_index, row in enumerate(rows):
|
|
738
|
+
items = list(row.items())
|
|
739
|
+
cell_array = (_DBCell * len(items))()
|
|
740
|
+
keepalive.append(cell_array)
|
|
741
|
+
for cell_index, (name, value) in enumerate(items):
|
|
742
|
+
name_raw = ctypes.create_string_buffer(name.encode("utf-8"))
|
|
743
|
+
keepalive.append(name_raw)
|
|
744
|
+
cell_array[cell_index].column_name = ctypes.cast(
|
|
745
|
+
name_raw, ctypes.c_char_p
|
|
746
|
+
)
|
|
747
|
+
cell_array[cell_index].value = self._db_value(value, keepalive)
|
|
748
|
+
row_array[row_index].cells = cell_array
|
|
749
|
+
row_array[row_index].cell_count = len(items)
|
|
750
|
+
|
|
751
|
+
keepalive.append(row_array)
|
|
752
|
+
out_result[0].rows = row_array
|
|
753
|
+
out_result[0].row_count = len(rows)
|
|
754
|
+
self._results[ctypes.addressof(out_result.contents)] = keepalive
|
|
755
|
+
return 0
|
|
756
|
+
except Exception as exc:
|
|
757
|
+
return self._set_error(out_error, str(exc))
|
|
758
|
+
|
|
759
|
+
def _begin(
|
|
760
|
+
self,
|
|
761
|
+
_user_data: int,
|
|
762
|
+
parent_transaction: int,
|
|
763
|
+
out_transaction: Any,
|
|
764
|
+
out_error: Any,
|
|
765
|
+
) -> int:
|
|
766
|
+
try:
|
|
767
|
+
tx = self.provider.begin(self._transaction(parent_transaction))
|
|
768
|
+
handle = self._next_transaction
|
|
769
|
+
self._next_transaction += 1
|
|
770
|
+
self._transactions[handle] = tx
|
|
771
|
+
out_transaction[0] = handle
|
|
772
|
+
return 0
|
|
773
|
+
except Exception as exc:
|
|
774
|
+
return self._set_error(out_error, str(exc))
|
|
775
|
+
|
|
776
|
+
def _commit(self, _user_data: int, transaction: int, out_error: Any) -> int:
|
|
777
|
+
try:
|
|
778
|
+
self.provider.commit(self._transactions[transaction])
|
|
779
|
+
return 0
|
|
780
|
+
except Exception as exc:
|
|
781
|
+
return self._set_error(out_error, str(exc))
|
|
782
|
+
|
|
783
|
+
def _rollback(self, _user_data: int, transaction: int, out_error: Any) -> int:
|
|
784
|
+
try:
|
|
785
|
+
self.provider.rollback(self._transactions[transaction])
|
|
786
|
+
return 0
|
|
787
|
+
except Exception as exc:
|
|
788
|
+
return self._set_error(out_error, str(exc))
|
|
789
|
+
|
|
790
|
+
def _release_transaction(self, _user_data: int, transaction: int) -> None:
|
|
791
|
+
tx = self._transactions.pop(transaction, None)
|
|
792
|
+
if tx is not None:
|
|
793
|
+
self.provider.release_transaction(tx)
|
|
794
|
+
|
|
795
|
+
def _release_result(self, _user_data: int, result: Any) -> None:
|
|
796
|
+
self._results.pop(ctypes.addressof(result.contents), None)
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
class _ObjectProviderBridge:
|
|
800
|
+
def __init__(self, provider: ObjectStoreProvider) -> None:
|
|
801
|
+
self.provider = provider
|
|
802
|
+
self._transactions: dict[int, Any] = {}
|
|
803
|
+
self._next_transaction = 1
|
|
804
|
+
self._errors: list[ctypes.Array[ctypes.c_char]] = []
|
|
805
|
+
self._blobs: dict[int, Any] = {}
|
|
806
|
+
|
|
807
|
+
self.begin_cb = _ObjectBeginCallback(self._begin)
|
|
808
|
+
self.put_cb = _ObjectPutCallback(self._put)
|
|
809
|
+
self.get_cb = _ObjectGetCallback(self._get)
|
|
810
|
+
self.exists_cb = _ObjectExistsCallback(self._exists)
|
|
811
|
+
self.delete_cb = _ObjectDeleteCallback(self._delete)
|
|
812
|
+
self.commit_cb = _ObjectTransactionCallback(self._commit)
|
|
813
|
+
self.rollback_cb = _ObjectTransactionCallback(self._rollback)
|
|
814
|
+
self.release_transaction_cb = _ObjectReleaseTransactionCallback(
|
|
815
|
+
self._release_transaction
|
|
816
|
+
)
|
|
817
|
+
self.release_blob_cb = _ObjectReleaseBlobCallback(self._release_blob)
|
|
818
|
+
self.callbacks = _ObjectCallbacks(
|
|
819
|
+
ctypes.sizeof(_ObjectCallbacks),
|
|
820
|
+
self.begin_cb,
|
|
821
|
+
self.put_cb,
|
|
822
|
+
self.get_cb,
|
|
823
|
+
self.exists_cb,
|
|
824
|
+
self.delete_cb,
|
|
825
|
+
self.commit_cb,
|
|
826
|
+
self.rollback_cb,
|
|
827
|
+
self.release_transaction_cb,
|
|
828
|
+
self.release_blob_cb,
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
def _set_error(self, out_error: Any, message: str) -> int:
|
|
832
|
+
encoded = ctypes.create_string_buffer(message.encode("utf-8"))
|
|
833
|
+
self._errors.append(encoded)
|
|
834
|
+
if out_error:
|
|
835
|
+
out_error[0] = ctypes.cast(encoded, ctypes.c_char_p)
|
|
836
|
+
return 1
|
|
837
|
+
|
|
838
|
+
def _transaction(self, handle: int) -> Any | None:
|
|
839
|
+
return self._transactions.get(handle) if handle else None
|
|
840
|
+
|
|
841
|
+
def _bytes_from_c(self, data: Any, size: int) -> bytes:
|
|
842
|
+
if not data or size == 0:
|
|
843
|
+
return b""
|
|
844
|
+
return bytes(data[:size])
|
|
845
|
+
|
|
846
|
+
def _begin(
|
|
847
|
+
self,
|
|
848
|
+
_user_data: int,
|
|
849
|
+
parent_transaction: int,
|
|
850
|
+
out_transaction: Any,
|
|
851
|
+
out_error: Any,
|
|
852
|
+
) -> int:
|
|
853
|
+
try:
|
|
854
|
+
tx = self.provider.begin(self._transaction(parent_transaction))
|
|
855
|
+
handle = self._next_transaction
|
|
856
|
+
self._next_transaction += 1
|
|
857
|
+
self._transactions[handle] = tx
|
|
858
|
+
out_transaction[0] = handle
|
|
859
|
+
return 0
|
|
860
|
+
except Exception as exc:
|
|
861
|
+
return self._set_error(out_error, str(exc))
|
|
862
|
+
|
|
863
|
+
def _put(
|
|
864
|
+
self,
|
|
865
|
+
_user_data: int,
|
|
866
|
+
transaction: int,
|
|
867
|
+
object_id: Any,
|
|
868
|
+
object_id_size: int,
|
|
869
|
+
data: Any,
|
|
870
|
+
data_size: int,
|
|
871
|
+
out_error: Any,
|
|
872
|
+
) -> int:
|
|
873
|
+
try:
|
|
874
|
+
self.provider.put(
|
|
875
|
+
self._bytes_from_c(object_id, object_id_size),
|
|
876
|
+
self._bytes_from_c(data, data_size),
|
|
877
|
+
self._transactions[transaction],
|
|
878
|
+
)
|
|
879
|
+
return 0
|
|
880
|
+
except Exception as exc:
|
|
881
|
+
return self._set_error(out_error, str(exc))
|
|
882
|
+
|
|
883
|
+
def _get(
|
|
884
|
+
self,
|
|
885
|
+
_user_data: int,
|
|
886
|
+
transaction: int,
|
|
887
|
+
object_id: Any,
|
|
888
|
+
object_id_size: int,
|
|
889
|
+
out_data: Any,
|
|
890
|
+
out_data_size: Any,
|
|
891
|
+
out_error: Any,
|
|
892
|
+
) -> int:
|
|
893
|
+
try:
|
|
894
|
+
data = self.provider.get(
|
|
895
|
+
self._bytes_from_c(object_id, object_id_size),
|
|
896
|
+
self._transactions[transaction],
|
|
897
|
+
)
|
|
898
|
+
if data is None:
|
|
899
|
+
out_data[0] = ctypes.cast(None, ctypes.POINTER(ctypes.c_uint8))
|
|
900
|
+
out_data_size[0] = 0
|
|
901
|
+
return 0
|
|
902
|
+
raw_bytes = bytes(data)
|
|
903
|
+
array_type = ctypes.c_uint8 * len(raw_bytes)
|
|
904
|
+
raw = array_type.from_buffer_copy(raw_bytes)
|
|
905
|
+
ptr = ctypes.cast(raw, ctypes.POINTER(ctypes.c_uint8))
|
|
906
|
+
self._blobs[ctypes.addressof(raw)] = raw
|
|
907
|
+
out_data[0] = ptr
|
|
908
|
+
out_data_size[0] = len(raw_bytes)
|
|
909
|
+
return 0
|
|
910
|
+
except Exception as exc:
|
|
911
|
+
return self._set_error(out_error, str(exc))
|
|
912
|
+
|
|
913
|
+
def _exists(
|
|
914
|
+
self,
|
|
915
|
+
_user_data: int,
|
|
916
|
+
transaction: int,
|
|
917
|
+
object_id: Any,
|
|
918
|
+
object_id_size: int,
|
|
919
|
+
out_exists: Any,
|
|
920
|
+
out_error: Any,
|
|
921
|
+
) -> int:
|
|
922
|
+
try:
|
|
923
|
+
out_exists[0] = int(
|
|
924
|
+
self.provider.exists(
|
|
925
|
+
self._bytes_from_c(object_id, object_id_size),
|
|
926
|
+
self._transactions[transaction],
|
|
927
|
+
)
|
|
928
|
+
)
|
|
929
|
+
return 0
|
|
930
|
+
except Exception as exc:
|
|
931
|
+
return self._set_error(out_error, str(exc))
|
|
932
|
+
|
|
933
|
+
def _delete(
|
|
934
|
+
self,
|
|
935
|
+
_user_data: int,
|
|
936
|
+
transaction: int,
|
|
937
|
+
object_id: Any,
|
|
938
|
+
object_id_size: int,
|
|
939
|
+
out_deleted: Any,
|
|
940
|
+
out_error: Any,
|
|
941
|
+
) -> int:
|
|
942
|
+
try:
|
|
943
|
+
out_deleted[0] = int(
|
|
944
|
+
self.provider.delete(
|
|
945
|
+
self._bytes_from_c(object_id, object_id_size),
|
|
946
|
+
self._transactions[transaction],
|
|
947
|
+
)
|
|
948
|
+
)
|
|
949
|
+
return 0
|
|
950
|
+
except Exception as exc:
|
|
951
|
+
return self._set_error(out_error, str(exc))
|
|
952
|
+
|
|
953
|
+
def _commit(self, _user_data: int, transaction: int, out_error: Any) -> int:
|
|
954
|
+
try:
|
|
955
|
+
self.provider.commit(self._transactions[transaction])
|
|
956
|
+
return 0
|
|
957
|
+
except Exception as exc:
|
|
958
|
+
return self._set_error(out_error, str(exc))
|
|
959
|
+
|
|
960
|
+
def _rollback(self, _user_data: int, transaction: int, out_error: Any) -> int:
|
|
961
|
+
try:
|
|
962
|
+
self.provider.rollback(self._transactions[transaction])
|
|
963
|
+
return 0
|
|
964
|
+
except Exception as exc:
|
|
965
|
+
return self._set_error(out_error, str(exc))
|
|
966
|
+
|
|
967
|
+
def _release_transaction(self, _user_data: int, transaction: int) -> None:
|
|
968
|
+
tx = self._transactions.pop(transaction, None)
|
|
969
|
+
if tx is not None:
|
|
970
|
+
self.provider.release_transaction(tx)
|
|
971
|
+
|
|
972
|
+
def _release_blob(self, _user_data: int, data: Any, _data_size: int) -> None:
|
|
973
|
+
if data:
|
|
974
|
+
self._blobs.pop(ctypes.addressof(data.contents), None)
|
|
975
|
+
|
|
976
|
+
|
|
977
|
+
class Cortext:
|
|
978
|
+
def __init__(
|
|
979
|
+
self,
|
|
980
|
+
db_path: str = ":memory:",
|
|
981
|
+
*,
|
|
982
|
+
store: DBProvider | None = None,
|
|
983
|
+
object_store: ObjectStoreProvider | None = None,
|
|
984
|
+
models_dir: str | None = None,
|
|
985
|
+
config: Config | None = None,
|
|
986
|
+
library_path: str | os.PathLike[str] | None = None,
|
|
987
|
+
) -> None:
|
|
988
|
+
if store is not None and db_path != ":memory:":
|
|
989
|
+
raise ValueError("db_path and store are mutually exclusive")
|
|
990
|
+
self._lib = _LIB if library_path is None else load_library(library_path)
|
|
991
|
+
native_cfg = _NativeConfig()
|
|
992
|
+
self._lib.cortext_config_init(ctypes.byref(native_cfg))
|
|
993
|
+
|
|
994
|
+
if config is not None:
|
|
995
|
+
native_cfg.focus = config.focus
|
|
996
|
+
native_cfg.sensitivity = config.sensitivity
|
|
997
|
+
native_cfg.stability = config.stability
|
|
998
|
+
native_cfg.affect_interrupt = _bool_to_int(config.affect_interrupt)
|
|
999
|
+
native_cfg.affect_retrieval = _bool_to_int(config.affect_retrieval)
|
|
1000
|
+
native_cfg.reinforcement_enabled = _bool_to_int(config.reinforcement_enabled)
|
|
1001
|
+
native_cfg.procedural_enabled = _bool_to_int(config.procedural_enabled)
|
|
1002
|
+
native_cfg.sequential_edges_enabled = _bool_to_int(config.sequential_edges_enabled)
|
|
1003
|
+
native_cfg.signal_filter_audio_enabled = _bool_to_int(
|
|
1004
|
+
config.signal_filter_audio_enabled
|
|
1005
|
+
)
|
|
1006
|
+
native_cfg.signal_filter_image_enabled = _bool_to_int(
|
|
1007
|
+
config.signal_filter_image_enabled
|
|
1008
|
+
)
|
|
1009
|
+
native_cfg.signal_filter_text_enabled = _bool_to_int(
|
|
1010
|
+
config.signal_filter_text_enabled
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
models_dir_raw = models_dir.encode("utf-8") if models_dir is not None else None
|
|
1014
|
+
self._provider_bridge = _ProviderBridge(store) if store is not None else None
|
|
1015
|
+
self._object_provider_bridge = (
|
|
1016
|
+
_ObjectProviderBridge(object_store) if object_store is not None else None
|
|
1017
|
+
)
|
|
1018
|
+
if self._provider_bridge is not None and self._object_provider_bridge is not None:
|
|
1019
|
+
self._handle = self._lib.cortext_create_with_store_and_object_callbacks(
|
|
1020
|
+
ctypes.byref(native_cfg),
|
|
1021
|
+
ctypes.byref(self._provider_bridge.callbacks),
|
|
1022
|
+
None,
|
|
1023
|
+
ctypes.byref(self._object_provider_bridge.callbacks),
|
|
1024
|
+
None,
|
|
1025
|
+
models_dir_raw,
|
|
1026
|
+
)
|
|
1027
|
+
elif self._provider_bridge is not None:
|
|
1028
|
+
self._handle = self._lib.cortext_create_with_store_callbacks(
|
|
1029
|
+
ctypes.byref(native_cfg),
|
|
1030
|
+
ctypes.byref(self._provider_bridge.callbacks),
|
|
1031
|
+
None,
|
|
1032
|
+
models_dir_raw,
|
|
1033
|
+
)
|
|
1034
|
+
elif self._object_provider_bridge is not None:
|
|
1035
|
+
db_path_raw = db_path.encode("utf-8")
|
|
1036
|
+
self._handle = self._lib.cortext_create_with_config_and_object_callbacks(
|
|
1037
|
+
ctypes.byref(native_cfg),
|
|
1038
|
+
db_path_raw,
|
|
1039
|
+
ctypes.byref(self._object_provider_bridge.callbacks),
|
|
1040
|
+
None,
|
|
1041
|
+
models_dir_raw,
|
|
1042
|
+
)
|
|
1043
|
+
else:
|
|
1044
|
+
db_path_raw = db_path.encode("utf-8")
|
|
1045
|
+
self._handle = self._lib.cortext_create_with_config(
|
|
1046
|
+
ctypes.byref(native_cfg),
|
|
1047
|
+
db_path_raw,
|
|
1048
|
+
models_dir_raw,
|
|
1049
|
+
)
|
|
1050
|
+
if not self._handle:
|
|
1051
|
+
_raise_last_error("cortext create failed")
|
|
1052
|
+
|
|
1053
|
+
def close(self) -> None:
|
|
1054
|
+
if getattr(self, "_handle", None):
|
|
1055
|
+
self._lib.cortext_free(self._handle)
|
|
1056
|
+
self._handle = None
|
|
1057
|
+
|
|
1058
|
+
def __enter__(self) -> "Cortext":
|
|
1059
|
+
return self
|
|
1060
|
+
|
|
1061
|
+
def __exit__(self, exc_type: object, exc: object, tb: object) -> None:
|
|
1062
|
+
self.close()
|
|
1063
|
+
|
|
1064
|
+
def __del__(self) -> None:
|
|
1065
|
+
try:
|
|
1066
|
+
self.close()
|
|
1067
|
+
except Exception:
|
|
1068
|
+
pass
|
|
1069
|
+
|
|
1070
|
+
def flush(self) -> None:
|
|
1071
|
+
status = self._lib.cortext_flush(self._handle)
|
|
1072
|
+
if status != 0:
|
|
1073
|
+
_raise_last_error("cortext_flush failed")
|
|
1074
|
+
|
|
1075
|
+
def reset(self) -> None:
|
|
1076
|
+
status = self._lib.cortext_reset(self._handle)
|
|
1077
|
+
if status != 0:
|
|
1078
|
+
_raise_last_error("cortext_reset failed")
|
|
1079
|
+
|
|
1080
|
+
def process_text_json(
|
|
1081
|
+
self, text: str, source_id: str, include_embedding: bool = True
|
|
1082
|
+
) -> str:
|
|
1083
|
+
options = _native_process_json_options(include_embedding)
|
|
1084
|
+
return self._call_json(
|
|
1085
|
+
self._lib.cortext_process_text_json_with_options,
|
|
1086
|
+
text.encode("utf-8"),
|
|
1087
|
+
source_id.encode("utf-8"),
|
|
1088
|
+
ctypes.byref(options),
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
def process_text(
|
|
1092
|
+
self, text: str, source_id: str, include_embedding: bool = True
|
|
1093
|
+
) -> dict[str, Any]:
|
|
1094
|
+
return json.loads(self.process_text_json(text, source_id, include_embedding))
|
|
1095
|
+
|
|
1096
|
+
def embed_text_json(self, text: str) -> str:
|
|
1097
|
+
return self._call_json(
|
|
1098
|
+
self._lib.cortext_embed_text_json,
|
|
1099
|
+
text.encode("utf-8"),
|
|
1100
|
+
)
|
|
1101
|
+
|
|
1102
|
+
def embed_text(self, text: str) -> list[float]:
|
|
1103
|
+
return list(json.loads(self.embed_text_json(text))["embedding"])
|
|
1104
|
+
|
|
1105
|
+
def process_audio_json(
|
|
1106
|
+
self,
|
|
1107
|
+
pcm: Iterable[float],
|
|
1108
|
+
source_id: str,
|
|
1109
|
+
include_embedding: bool = True,
|
|
1110
|
+
) -> str:
|
|
1111
|
+
samples = array("f", pcm)
|
|
1112
|
+
raw = (ctypes.c_float * len(samples)).from_buffer(samples)
|
|
1113
|
+
options = _native_process_json_options(include_embedding)
|
|
1114
|
+
return self._call_json(
|
|
1115
|
+
self._lib.cortext_process_audio_json_with_options,
|
|
1116
|
+
raw,
|
|
1117
|
+
len(samples),
|
|
1118
|
+
source_id.encode("utf-8"),
|
|
1119
|
+
ctypes.byref(options),
|
|
1120
|
+
)
|
|
1121
|
+
|
|
1122
|
+
def process_audio_with_media_json(
|
|
1123
|
+
self,
|
|
1124
|
+
pcm: Iterable[float],
|
|
1125
|
+
source_id: str,
|
|
1126
|
+
media: Media | bytes | bytearray | memoryview | None = None,
|
|
1127
|
+
media_mimetype: str | None = None,
|
|
1128
|
+
include_embedding: bool = True,
|
|
1129
|
+
) -> str:
|
|
1130
|
+
samples = array("f", pcm)
|
|
1131
|
+
raw = (ctypes.c_float * len(samples)).from_buffer(samples)
|
|
1132
|
+
native_media, media_buffer, mimetype_buffer = _native_media(
|
|
1133
|
+
media, media_mimetype
|
|
1134
|
+
)
|
|
1135
|
+
options = _native_process_json_options(include_embedding)
|
|
1136
|
+
_ = (media_buffer, mimetype_buffer)
|
|
1137
|
+
return self._call_json(
|
|
1138
|
+
self._lib.cortext_process_audio_with_media_json_with_options,
|
|
1139
|
+
raw,
|
|
1140
|
+
len(samples),
|
|
1141
|
+
source_id.encode("utf-8"),
|
|
1142
|
+
ctypes.byref(native_media),
|
|
1143
|
+
ctypes.byref(options),
|
|
1144
|
+
)
|
|
1145
|
+
|
|
1146
|
+
def process_audio(
|
|
1147
|
+
self,
|
|
1148
|
+
pcm: Iterable[float],
|
|
1149
|
+
source_id: str,
|
|
1150
|
+
include_embedding: bool = True,
|
|
1151
|
+
) -> dict[str, Any]:
|
|
1152
|
+
return json.loads(self.process_audio_json(pcm, source_id, include_embedding))
|
|
1153
|
+
|
|
1154
|
+
def process_audio_with_media(
|
|
1155
|
+
self,
|
|
1156
|
+
pcm: Iterable[float],
|
|
1157
|
+
source_id: str,
|
|
1158
|
+
media: Media | bytes | bytearray | memoryview | None = None,
|
|
1159
|
+
media_mimetype: str | None = None,
|
|
1160
|
+
include_embedding: bool = True,
|
|
1161
|
+
) -> dict[str, Any]:
|
|
1162
|
+
return json.loads(
|
|
1163
|
+
self.process_audio_with_media_json(
|
|
1164
|
+
pcm, source_id, media, media_mimetype, include_embedding
|
|
1165
|
+
)
|
|
1166
|
+
)
|
|
1167
|
+
|
|
1168
|
+
def embed_audio_json(self, pcm: Iterable[float]) -> str:
|
|
1169
|
+
samples = array("f", pcm)
|
|
1170
|
+
raw = (ctypes.c_float * len(samples)).from_buffer(samples)
|
|
1171
|
+
return self._call_json(
|
|
1172
|
+
self._lib.cortext_embed_audio_json,
|
|
1173
|
+
raw,
|
|
1174
|
+
len(samples),
|
|
1175
|
+
)
|
|
1176
|
+
|
|
1177
|
+
def embed_audio(self, pcm: Iterable[float]) -> list[float]:
|
|
1178
|
+
return list(json.loads(self.embed_audio_json(pcm))["embedding"])
|
|
1179
|
+
|
|
1180
|
+
def process_image_json(
|
|
1181
|
+
self,
|
|
1182
|
+
data: bytes | bytearray | memoryview,
|
|
1183
|
+
width: int,
|
|
1184
|
+
height: int,
|
|
1185
|
+
channels: int,
|
|
1186
|
+
source_id: str,
|
|
1187
|
+
include_embedding: bool = True,
|
|
1188
|
+
) -> str:
|
|
1189
|
+
blob = bytes(data)
|
|
1190
|
+
raw = (ctypes.c_uint8 * len(blob)).from_buffer_copy(blob)
|
|
1191
|
+
options = _native_process_json_options(include_embedding)
|
|
1192
|
+
return self._call_json(
|
|
1193
|
+
self._lib.cortext_process_image_json_with_options,
|
|
1194
|
+
raw,
|
|
1195
|
+
width,
|
|
1196
|
+
height,
|
|
1197
|
+
channels,
|
|
1198
|
+
source_id.encode("utf-8"),
|
|
1199
|
+
ctypes.byref(options),
|
|
1200
|
+
)
|
|
1201
|
+
|
|
1202
|
+
def process_image_with_media_json(
|
|
1203
|
+
self,
|
|
1204
|
+
data: bytes | bytearray | memoryview,
|
|
1205
|
+
width: int,
|
|
1206
|
+
height: int,
|
|
1207
|
+
channels: int,
|
|
1208
|
+
source_id: str,
|
|
1209
|
+
media: Media | bytes | bytearray | memoryview | None = None,
|
|
1210
|
+
media_mimetype: str | None = None,
|
|
1211
|
+
include_embedding: bool = True,
|
|
1212
|
+
) -> str:
|
|
1213
|
+
blob = bytes(data)
|
|
1214
|
+
raw = (ctypes.c_uint8 * len(blob)).from_buffer_copy(blob)
|
|
1215
|
+
native_media, media_buffer, mimetype_buffer = _native_media(
|
|
1216
|
+
media, media_mimetype
|
|
1217
|
+
)
|
|
1218
|
+
options = _native_process_json_options(include_embedding)
|
|
1219
|
+
_ = (media_buffer, mimetype_buffer)
|
|
1220
|
+
return self._call_json(
|
|
1221
|
+
self._lib.cortext_process_image_with_media_json_with_options,
|
|
1222
|
+
raw,
|
|
1223
|
+
width,
|
|
1224
|
+
height,
|
|
1225
|
+
channels,
|
|
1226
|
+
source_id.encode("utf-8"),
|
|
1227
|
+
ctypes.byref(native_media),
|
|
1228
|
+
ctypes.byref(options),
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
def process_image(
|
|
1232
|
+
self,
|
|
1233
|
+
data: bytes | bytearray | memoryview,
|
|
1234
|
+
width: int,
|
|
1235
|
+
height: int,
|
|
1236
|
+
channels: int,
|
|
1237
|
+
source_id: str,
|
|
1238
|
+
include_embedding: bool = True,
|
|
1239
|
+
) -> dict[str, Any]:
|
|
1240
|
+
return json.loads(
|
|
1241
|
+
self.process_image_json(
|
|
1242
|
+
data, width, height, channels, source_id, include_embedding
|
|
1243
|
+
)
|
|
1244
|
+
)
|
|
1245
|
+
|
|
1246
|
+
def process_image_with_media(
|
|
1247
|
+
self,
|
|
1248
|
+
data: bytes | bytearray | memoryview,
|
|
1249
|
+
width: int,
|
|
1250
|
+
height: int,
|
|
1251
|
+
channels: int,
|
|
1252
|
+
source_id: str,
|
|
1253
|
+
media: Media | bytes | bytearray | memoryview | None = None,
|
|
1254
|
+
media_mimetype: str | None = None,
|
|
1255
|
+
include_embedding: bool = True,
|
|
1256
|
+
) -> dict[str, Any]:
|
|
1257
|
+
return json.loads(
|
|
1258
|
+
self.process_image_with_media_json(
|
|
1259
|
+
data,
|
|
1260
|
+
width,
|
|
1261
|
+
height,
|
|
1262
|
+
channels,
|
|
1263
|
+
source_id,
|
|
1264
|
+
media,
|
|
1265
|
+
media_mimetype,
|
|
1266
|
+
include_embedding,
|
|
1267
|
+
)
|
|
1268
|
+
)
|
|
1269
|
+
|
|
1270
|
+
def embed_image_json(
|
|
1271
|
+
self,
|
|
1272
|
+
data: bytes | bytearray | memoryview,
|
|
1273
|
+
width: int,
|
|
1274
|
+
height: int,
|
|
1275
|
+
channels: int,
|
|
1276
|
+
) -> str:
|
|
1277
|
+
blob = bytes(data)
|
|
1278
|
+
raw = (ctypes.c_uint8 * len(blob)).from_buffer_copy(blob)
|
|
1279
|
+
return self._call_json(
|
|
1280
|
+
self._lib.cortext_embed_image_json,
|
|
1281
|
+
raw,
|
|
1282
|
+
width,
|
|
1283
|
+
height,
|
|
1284
|
+
channels,
|
|
1285
|
+
)
|
|
1286
|
+
|
|
1287
|
+
def embed_image(
|
|
1288
|
+
self,
|
|
1289
|
+
data: bytes | bytearray | memoryview,
|
|
1290
|
+
width: int,
|
|
1291
|
+
height: int,
|
|
1292
|
+
channels: int,
|
|
1293
|
+
) -> list[float]:
|
|
1294
|
+
payload = self.embed_image_json(data, width, height, channels)
|
|
1295
|
+
return list(json.loads(payload)["embedding"])
|
|
1296
|
+
|
|
1297
|
+
def consolidate_json(self) -> str:
|
|
1298
|
+
return self._call_json(self._lib.cortext_consolidate_json)
|
|
1299
|
+
|
|
1300
|
+
def consolidate(self) -> dict[str, Any]:
|
|
1301
|
+
return json.loads(self.consolidate_json())
|
|
1302
|
+
|
|
1303
|
+
def _call_json(self, fn: Any, *args: Any) -> str:
|
|
1304
|
+
result = fn(self._handle, *args)
|
|
1305
|
+
if not result:
|
|
1306
|
+
_raise_last_error(f"{fn.__name__} failed")
|
|
1307
|
+
try:
|
|
1308
|
+
return ctypes.string_at(result).decode("utf-8")
|
|
1309
|
+
finally:
|
|
1310
|
+
self._lib.cortext_string_free(result)
|