select-ai 1.0.0.dev4__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.
Potentially problematic release.
This version of select-ai might be problematic. Click here for more details.
- select_ai/__init__.py +52 -0
- select_ai/_abc.py +74 -0
- select_ai/_enums.py +14 -0
- select_ai/action.py +21 -0
- select_ai/admin.py +108 -0
- select_ai/async_profile.py +468 -0
- select_ai/base_profile.py +166 -0
- select_ai/conversation.py +249 -0
- select_ai/db.py +171 -0
- select_ai/errors.py +49 -0
- select_ai/profile.py +397 -0
- select_ai/provider.py +187 -0
- select_ai/sql.py +105 -0
- select_ai/synthetic_data.py +84 -0
- select_ai/vector_index.py +542 -0
- select_ai/version.py +8 -0
- select_ai-1.0.0.dev4.dist-info/METADATA +25 -0
- select_ai-1.0.0.dev4.dist-info/RECORD +21 -0
- select_ai-1.0.0.dev4.dist-info/WHEEL +5 -0
- select_ai-1.0.0.dev4.dist-info/licenses/LICENSE.txt +35 -0
- select_ai-1.0.0.dev4.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2025, Oracle and/or its affiliates.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Universal Permissive License v 1.0 as shown at
|
|
5
|
+
# http://oss.oracle.com/licenses/upl.
|
|
6
|
+
# -----------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import List, Mapping, Optional
|
|
11
|
+
|
|
12
|
+
from select_ai._abc import SelectAIDataClass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class SyntheticDataParams(SelectAIDataClass):
|
|
17
|
+
"""Optional parameters to control generation of synthetic data
|
|
18
|
+
|
|
19
|
+
:param int sample_rows: number of rows from the table to use as a sample
|
|
20
|
+
to guide the LLM in data generation
|
|
21
|
+
|
|
22
|
+
:param bool table_statistics: Enable or disable the use of table
|
|
23
|
+
statistics information. Default value is False
|
|
24
|
+
|
|
25
|
+
:param str priority: Assign a priority value that defines the number of
|
|
26
|
+
parallel requests sent to the LLM for generating synthetic data.
|
|
27
|
+
Tasks with a higher priority will consume more database resources and
|
|
28
|
+
complete faster. Possible values are: HIGH, MEDIUM, LOW
|
|
29
|
+
|
|
30
|
+
:param bool comments: Enable or disable sending comments to the LLM to
|
|
31
|
+
guide data generation. Default value is False
|
|
32
|
+
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
sample_rows: Optional[int] = None
|
|
36
|
+
table_statistics: Optional[bool] = False
|
|
37
|
+
priority: Optional[str] = "HIGH"
|
|
38
|
+
comments: Optional[bool] = False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class SyntheticDataAttributes(SelectAIDataClass):
|
|
43
|
+
"""Attributes to control generation of synthetic data
|
|
44
|
+
|
|
45
|
+
:param str object_name: Table name to populate synthetic data
|
|
46
|
+
:param List[Mapping] object_list: Use this to generate synthetic data
|
|
47
|
+
on multiple tables
|
|
48
|
+
:param str owner_name: Database user who owns the referenced object.
|
|
49
|
+
Default value is connected user's schema
|
|
50
|
+
:param int record_count: Number of records to generate
|
|
51
|
+
:param str user_prompt: User prompt to guide generation of synthetic data
|
|
52
|
+
For e.g. "the release date for the movies should be in 2019"
|
|
53
|
+
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
object_name: Optional[str] = None
|
|
57
|
+
object_list: Optional[List[Mapping]] = None
|
|
58
|
+
owner_name: Optional[str] = None
|
|
59
|
+
params: Optional[SyntheticDataParams] = None
|
|
60
|
+
record_count: Optional[int] = None
|
|
61
|
+
user_prompt: Optional[str] = None
|
|
62
|
+
|
|
63
|
+
def dict(self, exclude_null=True):
|
|
64
|
+
attributes = {}
|
|
65
|
+
for k, v in self.__dict__.items():
|
|
66
|
+
if v is not None or not exclude_null:
|
|
67
|
+
if isinstance(v, SyntheticDataParams):
|
|
68
|
+
attributes[k] = v.json(exclude_null=exclude_null)
|
|
69
|
+
elif isinstance(v, List):
|
|
70
|
+
attributes[k] = json.dumps(v)
|
|
71
|
+
else:
|
|
72
|
+
attributes[k] = v
|
|
73
|
+
return attributes
|
|
74
|
+
|
|
75
|
+
def prepare(self):
|
|
76
|
+
if self.object_name and self.object_list:
|
|
77
|
+
raise ValueError("Both object_name and object_list cannot be set")
|
|
78
|
+
|
|
79
|
+
if not self.object_name and not self.object_list:
|
|
80
|
+
raise ValueError(
|
|
81
|
+
"One of object_name and object_list should be set"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return self.dict()
|
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2025, Oracle and/or its affiliates.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Universal Permissive License v 1.0 as shown at
|
|
5
|
+
# http://oss.oracle.com/licenses/upl.
|
|
6
|
+
# -----------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from abc import ABC
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import AsyncGenerator, Iterator, Optional, Union
|
|
12
|
+
|
|
13
|
+
import oracledb
|
|
14
|
+
|
|
15
|
+
from select_ai import BaseProfile
|
|
16
|
+
from select_ai._abc import SelectAIDataClass
|
|
17
|
+
from select_ai._enums import StrEnum
|
|
18
|
+
from select_ai.async_profile import AsyncProfile
|
|
19
|
+
from select_ai.db import async_cursor, cursor
|
|
20
|
+
from select_ai.errors import VectorIndexNotFoundError
|
|
21
|
+
from select_ai.profile import Profile
|
|
22
|
+
from select_ai.sql import (
|
|
23
|
+
GET_USER_VECTOR_INDEX_ATTRIBUTES,
|
|
24
|
+
LIST_USER_VECTOR_INDEXES,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class VectorDBProvider(StrEnum):
|
|
29
|
+
ORACLE = "oracle"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class VectorDistanceMetric(StrEnum):
|
|
33
|
+
EUCLIDEAN = "EUCLIDEAN"
|
|
34
|
+
L2_SQUARED = "L2_SQUARED"
|
|
35
|
+
COSINE = "COSINE"
|
|
36
|
+
DOT = "DOT"
|
|
37
|
+
MANHATTAN = "MANHATTAN"
|
|
38
|
+
HAMMING = "HAMMING"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass
|
|
42
|
+
class VectorIndexAttributes(SelectAIDataClass):
|
|
43
|
+
"""
|
|
44
|
+
Attributes of a vector index help to manage and configure the behavior of
|
|
45
|
+
the vector index.
|
|
46
|
+
|
|
47
|
+
:param int chunk_size: Text size of chunking the input data.
|
|
48
|
+
:param int chunk_overlap: Specifies the amount of overlapping
|
|
49
|
+
characters between adjacent chunks of text.
|
|
50
|
+
:param str location: Location of the object store.
|
|
51
|
+
:param int match_limit: Specifies the maximum number of results to return
|
|
52
|
+
in a vector search query
|
|
53
|
+
:param str object_storage_credential_name: Name of the credentials for
|
|
54
|
+
accessing object storage.
|
|
55
|
+
:param str profile_name: Name of the AI profile which is used for
|
|
56
|
+
embedding source data and user prompts.
|
|
57
|
+
:param int refresh_rate: Interval of updating data in the vector store.
|
|
58
|
+
The unit is minutes.
|
|
59
|
+
:param float similarity_threshold: Defines the minimum level of similarity
|
|
60
|
+
required for two items to be considered a match
|
|
61
|
+
:param VectorDistanceMetric vector_distance_metric: Specifies the type of
|
|
62
|
+
distance calculation used to compare vectors in a database
|
|
63
|
+
:param VectorDBProvider vector_db_provider: Name of the Vector database
|
|
64
|
+
provider. Default value is "oracle"
|
|
65
|
+
:param str vector_db_endpoint: Endpoint to access the Vector database
|
|
66
|
+
:param str vector_db_credential_name: Name of the credentials for accessing
|
|
67
|
+
Vector database
|
|
68
|
+
:param int vector_dimension: Specifies the number of elements in each
|
|
69
|
+
vector within the vector store
|
|
70
|
+
:param str vector_table_name: Specifies the name of the table or collection
|
|
71
|
+
to store vector embeddings and chunked data
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
chunk_size: Optional[int] = 1024
|
|
75
|
+
chunk_overlap: Optional[int] = 128
|
|
76
|
+
location: Optional[str] = None
|
|
77
|
+
match_limit: Optional[int] = 5
|
|
78
|
+
object_storage_credential_name: Optional[str] = None
|
|
79
|
+
profile_name: Optional[str] = None
|
|
80
|
+
refresh_rate: Optional[int] = 1440
|
|
81
|
+
similarity_threshold: Optional[float] = 0
|
|
82
|
+
vector_distance_metric: Optional[VectorDistanceMetric] = (
|
|
83
|
+
VectorDistanceMetric.COSINE
|
|
84
|
+
)
|
|
85
|
+
vector_db_endpoint: Optional[str] = None
|
|
86
|
+
vector_db_credential_name: Optional[str] = None
|
|
87
|
+
vector_db_provider: Optional[VectorDBProvider] = None
|
|
88
|
+
vector_dimension: Optional[int] = None
|
|
89
|
+
vector_table_name: Optional[str] = None
|
|
90
|
+
pipeline_name: Optional[str] = None
|
|
91
|
+
|
|
92
|
+
def json(self, exclude_null=True):
|
|
93
|
+
attributes = self.dict(exclude_null=exclude_null)
|
|
94
|
+
attributes.pop("pipeline_name", None)
|
|
95
|
+
return json.dumps(attributes)
|
|
96
|
+
|
|
97
|
+
@classmethod
|
|
98
|
+
def create(cls, *, vector_db_provider: Optional[str] = None, **kwargs):
|
|
99
|
+
for subclass in cls.__subclasses__():
|
|
100
|
+
if subclass.vector_db_provider == vector_db_provider:
|
|
101
|
+
return subclass(**kwargs)
|
|
102
|
+
return cls(**kwargs)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass
|
|
106
|
+
class OracleVectorIndexAttributes(VectorIndexAttributes):
|
|
107
|
+
"""Oracle specific vector index attributes"""
|
|
108
|
+
|
|
109
|
+
vector_db_provider: Optional[VectorDBProvider] = VectorDBProvider.ORACLE
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class _BaseVectorIndex(ABC):
|
|
114
|
+
|
|
115
|
+
def __init__(
|
|
116
|
+
self,
|
|
117
|
+
profile: BaseProfile = None,
|
|
118
|
+
index_name: Optional[str] = None,
|
|
119
|
+
description: Optional[str] = None,
|
|
120
|
+
attributes: Optional[VectorIndexAttributes] = None,
|
|
121
|
+
):
|
|
122
|
+
"""Initialize a Vector Index"""
|
|
123
|
+
self.profile = profile
|
|
124
|
+
self.index_name = index_name
|
|
125
|
+
self.attributes = attributes
|
|
126
|
+
self.description = description
|
|
127
|
+
|
|
128
|
+
def __repr__(self):
|
|
129
|
+
return (
|
|
130
|
+
f"{self.__class__.__name__}(profile={self.profile}, "
|
|
131
|
+
f"index_name={self.index_name}, "
|
|
132
|
+
f"attributes={self.attributes}, description={self.description})"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
class VectorIndex(_BaseVectorIndex):
|
|
137
|
+
"""
|
|
138
|
+
VectorIndex objects let you manage vector indexes
|
|
139
|
+
|
|
140
|
+
:param str index_name: The name of the vector index
|
|
141
|
+
:param str description: The description of the vector index
|
|
142
|
+
:param select_ai.VectorIndexAttributes attributes: The attributes of the vector index
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
@staticmethod
|
|
146
|
+
def _get_attributes(index_name: str) -> VectorIndexAttributes:
|
|
147
|
+
"""Get attributes of a vector index
|
|
148
|
+
|
|
149
|
+
:return: select_ai.VectorIndexAttributes
|
|
150
|
+
:raises: VectorIndexNotFoundError
|
|
151
|
+
"""
|
|
152
|
+
with cursor() as cr:
|
|
153
|
+
cr.execute(GET_USER_VECTOR_INDEX_ATTRIBUTES, index_name=index_name)
|
|
154
|
+
attributes = cr.fetchall()
|
|
155
|
+
if attributes:
|
|
156
|
+
post_processed_attributes = {}
|
|
157
|
+
for k, v in attributes:
|
|
158
|
+
if isinstance(v, oracledb.LOB):
|
|
159
|
+
post_processed_attributes[k] = v.read()
|
|
160
|
+
else:
|
|
161
|
+
post_processed_attributes[k] = v
|
|
162
|
+
return VectorIndexAttributes.create(
|
|
163
|
+
**post_processed_attributes
|
|
164
|
+
)
|
|
165
|
+
else:
|
|
166
|
+
raise VectorIndexNotFoundError(index_name=index_name)
|
|
167
|
+
|
|
168
|
+
def create(self, replace=False):
|
|
169
|
+
"""Create a vector index in the database and populates the index
|
|
170
|
+
with data from an object store bucket using an async scheduler job
|
|
171
|
+
|
|
172
|
+
:param bool replace: Replace vector index if it exists
|
|
173
|
+
:return: None
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
if self.attributes.profile_name is None:
|
|
177
|
+
self.attributes.profile_name = self.profile.profile_name
|
|
178
|
+
|
|
179
|
+
parameters = {
|
|
180
|
+
"index_name": self.index_name,
|
|
181
|
+
"attributes": self.attributes.json(),
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if self.description:
|
|
185
|
+
parameters["description"] = self.description
|
|
186
|
+
|
|
187
|
+
with cursor() as cr:
|
|
188
|
+
try:
|
|
189
|
+
cr.callproc(
|
|
190
|
+
"DBMS_CLOUD_AI.CREATE_VECTOR_INDEX",
|
|
191
|
+
keyword_parameters=parameters,
|
|
192
|
+
)
|
|
193
|
+
except oracledb.DatabaseError as e:
|
|
194
|
+
(error,) = e.args
|
|
195
|
+
# If already exists and replace is True then drop and recreate
|
|
196
|
+
if "already exists" in error.message.lower() and replace:
|
|
197
|
+
self.delete(force=True)
|
|
198
|
+
cr.callproc(
|
|
199
|
+
"DBMS_CLOUD_AI.CREATE_VECTOR_INDEX",
|
|
200
|
+
keyword_parameters=parameters,
|
|
201
|
+
)
|
|
202
|
+
else:
|
|
203
|
+
raise
|
|
204
|
+
self.profile.set_attribute("vector_index_name", self.index_name)
|
|
205
|
+
|
|
206
|
+
def delete(
|
|
207
|
+
self, include_data: Optional[int] = True, force: Optional[int] = False
|
|
208
|
+
):
|
|
209
|
+
"""This procedure removes a vector store index
|
|
210
|
+
|
|
211
|
+
:param bool include_data: Indicates whether to delete
|
|
212
|
+
both the customer's vector store and vector index
|
|
213
|
+
along with the vector index object
|
|
214
|
+
:param bool force: Indicates whether to ignore errors
|
|
215
|
+
that occur if the vector index does not exist
|
|
216
|
+
:return: None
|
|
217
|
+
:raises: oracledb.DatabaseError
|
|
218
|
+
"""
|
|
219
|
+
with cursor() as cr:
|
|
220
|
+
cr.callproc(
|
|
221
|
+
"DBMS_CLOUD_AI.DROP_VECTOR_INDEX",
|
|
222
|
+
keyword_parameters={
|
|
223
|
+
"index_name": self.index_name,
|
|
224
|
+
"include_data": include_data,
|
|
225
|
+
"force": force,
|
|
226
|
+
},
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
def enable(self):
|
|
230
|
+
"""This procedure enables or activates a previously disabled vector
|
|
231
|
+
index object. Generally, when you create a vector index, by default
|
|
232
|
+
it is enabled such that the AI profile can use it to perform indexing
|
|
233
|
+
and searching.
|
|
234
|
+
|
|
235
|
+
:return: None
|
|
236
|
+
:raises: oracledb.DatabaseError
|
|
237
|
+
|
|
238
|
+
"""
|
|
239
|
+
with cursor() as cr:
|
|
240
|
+
cr.callproc(
|
|
241
|
+
"DBMS_CLOUD_AI.ENABLE_VECTOR_INDEX",
|
|
242
|
+
keyword_parameters={"index_name": self.index_name},
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
def disable(self):
|
|
246
|
+
"""This procedure disables a vector index object in the current
|
|
247
|
+
database. When disabled, an AI profile cannot use the vector index,
|
|
248
|
+
and the system does not load data into the vector store as new data
|
|
249
|
+
is added to the object store and does not perform indexing, searching
|
|
250
|
+
or querying based on the index.
|
|
251
|
+
|
|
252
|
+
:return: None
|
|
253
|
+
:raises: oracledb.DatabaseError
|
|
254
|
+
"""
|
|
255
|
+
with cursor() as cr:
|
|
256
|
+
cr.callproc(
|
|
257
|
+
"DBMS_CLOUD_AI.DISABLE_VECTOR_INDEX",
|
|
258
|
+
keyword_parameters={"index_name": self.index_name},
|
|
259
|
+
)
|
|
260
|
+
|
|
261
|
+
def set_attributes(
|
|
262
|
+
self,
|
|
263
|
+
attribute_name: str,
|
|
264
|
+
attribute_value: Union[str, int, float],
|
|
265
|
+
attributes: VectorIndexAttributes = None,
|
|
266
|
+
):
|
|
267
|
+
"""
|
|
268
|
+
This procedure updates an existing vector store index with a specified
|
|
269
|
+
value of the vector index attribute. You can specify a single attribute
|
|
270
|
+
or multiple attributes by passing an object of type
|
|
271
|
+
:class `VectorIndexAttributes`
|
|
272
|
+
|
|
273
|
+
:param str attribute_name: Custom attribute name
|
|
274
|
+
:param Union[str, int, float] attribute_value: Attribute Value
|
|
275
|
+
:param VectorIndexAttributes attributes: Specify multiple attributes
|
|
276
|
+
to update in a single API invocation
|
|
277
|
+
:return: None
|
|
278
|
+
:raises: oracledb.DatabaseError
|
|
279
|
+
"""
|
|
280
|
+
if attribute_name and attribute_value and attributes:
|
|
281
|
+
raise ValueError(
|
|
282
|
+
"Either specify a single attribute using "
|
|
283
|
+
"attribute_name and attribute_value or "
|
|
284
|
+
"pass an object of type VectorIndexAttributes"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
parameters = {"index_name": self.index_name}
|
|
288
|
+
if attributes:
|
|
289
|
+
parameters["attributes"] = attributes.json()
|
|
290
|
+
self.attributes = attributes
|
|
291
|
+
else:
|
|
292
|
+
setattr(self.attributes, attribute_name, attribute_value)
|
|
293
|
+
parameters["attributes_name"] = attribute_name
|
|
294
|
+
parameters["attributes_value"] = attribute_value
|
|
295
|
+
|
|
296
|
+
with cursor() as cr:
|
|
297
|
+
cr.callproc(
|
|
298
|
+
"DBMS_CLOUD_AI.UPDATE_VECTOR_INDEX",
|
|
299
|
+
keyword_parameters=parameters,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
def get_attributes(self) -> VectorIndexAttributes:
|
|
303
|
+
"""Get attributes of this vector index
|
|
304
|
+
|
|
305
|
+
:return: select_ai.VectorIndexAttributes
|
|
306
|
+
:raises: VectorIndexNotFoundError
|
|
307
|
+
"""
|
|
308
|
+
return self._get_attributes(self.index_name)
|
|
309
|
+
|
|
310
|
+
@classmethod
|
|
311
|
+
def list(cls, index_name_pattern: str) -> Iterator["VectorIndex"]:
|
|
312
|
+
"""List Vector Indexes
|
|
313
|
+
|
|
314
|
+
:param str index_name_pattern: Regular expressions can be used
|
|
315
|
+
to specify a pattern. Function REGEXP_LIKE is used to perform the
|
|
316
|
+
match
|
|
317
|
+
|
|
318
|
+
:return: Iterator[VectorIndex]
|
|
319
|
+
"""
|
|
320
|
+
with cursor() as cr:
|
|
321
|
+
cr.execute(
|
|
322
|
+
LIST_USER_VECTOR_INDEXES,
|
|
323
|
+
index_name_pattern=index_name_pattern,
|
|
324
|
+
)
|
|
325
|
+
for row in cr.fetchall():
|
|
326
|
+
index_name = row[0]
|
|
327
|
+
description = row[1].read() # Oracle.LOB
|
|
328
|
+
attributes = cls._get_attributes(index_name=index_name)
|
|
329
|
+
yield cls(
|
|
330
|
+
index_name=index_name,
|
|
331
|
+
description=description,
|
|
332
|
+
attributes=attributes,
|
|
333
|
+
profile=Profile(profile_name=attributes.profile_name),
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
class AsyncVectorIndex(_BaseVectorIndex):
|
|
338
|
+
"""
|
|
339
|
+
AsyncVectorIndex objects let you manage vector indexes
|
|
340
|
+
using async APIs. Use this for non-blocking concurrent
|
|
341
|
+
requests
|
|
342
|
+
|
|
343
|
+
:param str index_name: The name of the vector index
|
|
344
|
+
:param str description: The description of the vector index
|
|
345
|
+
:param VectorIndexAttributes attributes: The attributes of the vector index
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
@staticmethod
|
|
349
|
+
async def _get_attributes(index_name: str) -> VectorIndexAttributes:
|
|
350
|
+
"""Get attributes of a vector index
|
|
351
|
+
|
|
352
|
+
:return: select_ai.VectorIndexAttributes
|
|
353
|
+
:raises: VectorIndexNotFoundError
|
|
354
|
+
"""
|
|
355
|
+
async with async_cursor() as cr:
|
|
356
|
+
await cr.execute(
|
|
357
|
+
GET_USER_VECTOR_INDEX_ATTRIBUTES, index_name=index_name
|
|
358
|
+
)
|
|
359
|
+
attributes = await cr.fetchall()
|
|
360
|
+
if attributes:
|
|
361
|
+
post_processed_attributes = {}
|
|
362
|
+
for k, v in attributes:
|
|
363
|
+
if isinstance(v, oracledb.AsyncLOB):
|
|
364
|
+
post_processed_attributes[k] = await v.read()
|
|
365
|
+
else:
|
|
366
|
+
post_processed_attributes[k] = v
|
|
367
|
+
return VectorIndexAttributes.create(
|
|
368
|
+
**post_processed_attributes
|
|
369
|
+
)
|
|
370
|
+
else:
|
|
371
|
+
raise VectorIndexNotFoundError(index_name=index_name)
|
|
372
|
+
|
|
373
|
+
async def create(self, replace: Optional[int] = False) -> None:
|
|
374
|
+
"""Create a vector index in the database and populates it with data
|
|
375
|
+
from an object store bucket using an async scheduler job
|
|
376
|
+
|
|
377
|
+
:param bool replace: True to replace existing vector index
|
|
378
|
+
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
if self.attributes.profile_name is None:
|
|
382
|
+
self.attributes.profile_name = self.profile.profile_name
|
|
383
|
+
parameters = {
|
|
384
|
+
"index_name": self.index_name,
|
|
385
|
+
"attributes": self.attributes.json(),
|
|
386
|
+
}
|
|
387
|
+
if self.description:
|
|
388
|
+
parameters["description"] = self.description
|
|
389
|
+
async with async_cursor() as cr:
|
|
390
|
+
try:
|
|
391
|
+
await cr.callproc(
|
|
392
|
+
"DBMS_CLOUD_AI.CREATE_VECTOR_INDEX",
|
|
393
|
+
keyword_parameters=parameters,
|
|
394
|
+
)
|
|
395
|
+
except oracledb.DatabaseError as e:
|
|
396
|
+
(error,) = e.args
|
|
397
|
+
# If already exists and replace is True then drop and recreate
|
|
398
|
+
if "already exists" in error.message.lower() and replace:
|
|
399
|
+
await self.delete(force=True)
|
|
400
|
+
await cr.callproc(
|
|
401
|
+
"DBMS_CLOUD_AI.CREATE_VECTOR_INDEX",
|
|
402
|
+
keyword_parameters=parameters,
|
|
403
|
+
)
|
|
404
|
+
else:
|
|
405
|
+
raise
|
|
406
|
+
|
|
407
|
+
await self.profile.set_attribute("vector_index_name", self.index_name)
|
|
408
|
+
|
|
409
|
+
async def delete(
|
|
410
|
+
self, include_data: Optional[int] = True, force: Optional[int] = False
|
|
411
|
+
) -> None:
|
|
412
|
+
"""This procedure removes a vector store index.
|
|
413
|
+
|
|
414
|
+
:param bool include_data: Indicates whether to delete
|
|
415
|
+
both the customer's vector store and vector index
|
|
416
|
+
along with the vector index object.
|
|
417
|
+
:param bool force: Indicates whether to ignore errors
|
|
418
|
+
that occur if the vector index does not exist.
|
|
419
|
+
:return: None
|
|
420
|
+
:raises: oracledb.DatabaseError
|
|
421
|
+
|
|
422
|
+
"""
|
|
423
|
+
async with async_cursor() as cr:
|
|
424
|
+
await cr.callproc(
|
|
425
|
+
"DBMS_CLOUD_AI.DROP_VECTOR_INDEX",
|
|
426
|
+
keyword_parameters={
|
|
427
|
+
"index_name": self.index_name,
|
|
428
|
+
"include_data": include_data,
|
|
429
|
+
"force": force,
|
|
430
|
+
},
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
async def enable(self) -> None:
|
|
434
|
+
"""This procedure enables or activates a previously disabled vector
|
|
435
|
+
index object. Generally, when you create a vector index, by default
|
|
436
|
+
it is enabled such that the AI profile can use it to perform indexing
|
|
437
|
+
and searching.
|
|
438
|
+
|
|
439
|
+
:return: None
|
|
440
|
+
:raises: oracledb.DatabaseError
|
|
441
|
+
|
|
442
|
+
"""
|
|
443
|
+
async with async_cursor() as cr:
|
|
444
|
+
await cr.callproc(
|
|
445
|
+
"DBMS_CLOUD_AI.ENABLE_VECTOR_INDEX",
|
|
446
|
+
keyword_parameters={"index_name": self.index_name},
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
async def disable(self) -> None:
|
|
450
|
+
"""This procedure disables a vector index object in the current
|
|
451
|
+
database. When disabled, an AI profile cannot use the vector index,
|
|
452
|
+
and the system does not load data into the vector store as new data
|
|
453
|
+
is added to the object store and does not perform indexing, searching
|
|
454
|
+
or querying based on the index.
|
|
455
|
+
|
|
456
|
+
:return: None
|
|
457
|
+
:raises: oracledb.DatabaseError
|
|
458
|
+
"""
|
|
459
|
+
async with async_cursor() as cr:
|
|
460
|
+
await cr.callproc(
|
|
461
|
+
"DBMS_CLOUD_AI.DISABLE_VECTOR_INDEX",
|
|
462
|
+
keyword_parameters={"index_name": self.index_name},
|
|
463
|
+
)
|
|
464
|
+
|
|
465
|
+
async def set_attributes(
|
|
466
|
+
self,
|
|
467
|
+
attribute_name: str,
|
|
468
|
+
attribute_value: Union[str, int],
|
|
469
|
+
attributes: VectorIndexAttributes = None,
|
|
470
|
+
) -> None:
|
|
471
|
+
"""
|
|
472
|
+
This procedure updates an existing vector store index with a specified
|
|
473
|
+
value of the vector index attribute. You can specify a single attribute
|
|
474
|
+
or multiple attributes by passing an object of type
|
|
475
|
+
:class `VectorIndexAttributes`
|
|
476
|
+
|
|
477
|
+
:param str attribute_name: Custom attribute name
|
|
478
|
+
:param Union[str, int, float] attribute_value: Attribute Value
|
|
479
|
+
:param VectorIndexAttributes attributes: Specify multiple attributes
|
|
480
|
+
to update in a single API invocation
|
|
481
|
+
:return: None
|
|
482
|
+
:raises: oracledb.DatabaseError
|
|
483
|
+
"""
|
|
484
|
+
if attribute_name and attribute_value and attributes:
|
|
485
|
+
raise ValueError(
|
|
486
|
+
"Either specify a single attribute using "
|
|
487
|
+
"attribute_name and attribute_value or "
|
|
488
|
+
"pass an object of type VectorIndexAttributes"
|
|
489
|
+
)
|
|
490
|
+
parameters = {"index_name": self.index_name}
|
|
491
|
+
if attributes:
|
|
492
|
+
self.attributes = attributes
|
|
493
|
+
parameters["attributes"] = attributes.json()
|
|
494
|
+
else:
|
|
495
|
+
setattr(self.attributes, attribute_name, attribute_value)
|
|
496
|
+
parameters["attributes_name"] = attribute_name
|
|
497
|
+
parameters["attributes_value"] = attribute_value
|
|
498
|
+
|
|
499
|
+
async with async_cursor() as cr:
|
|
500
|
+
await cr.callproc(
|
|
501
|
+
"DBMS_CLOUD_AI.UPDATE_VECTOR_INDEX",
|
|
502
|
+
keyword_parameters=parameters,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
async def get_attributes(self) -> VectorIndexAttributes:
|
|
506
|
+
"""Get attributes of a vector index
|
|
507
|
+
|
|
508
|
+
:return: select_ai.VectorIndexAttributes
|
|
509
|
+
:raises: VectorIndexNotFoundError
|
|
510
|
+
"""
|
|
511
|
+
return await self._get_attributes(index_name=self.index_name)
|
|
512
|
+
|
|
513
|
+
@classmethod
|
|
514
|
+
async def list(
|
|
515
|
+
cls, index_name_pattern: str
|
|
516
|
+
) -> AsyncGenerator[VectorIndex, None]:
|
|
517
|
+
"""List Vector Indexes.
|
|
518
|
+
|
|
519
|
+
:param str index_name_pattern: Regular expressions can be used
|
|
520
|
+
to specify a pattern. Function REGEXP_LIKE is used to perform the
|
|
521
|
+
match
|
|
522
|
+
:return: AsyncGenerator[VectorIndex]
|
|
523
|
+
|
|
524
|
+
"""
|
|
525
|
+
async with async_cursor() as cr:
|
|
526
|
+
await cr.execute(
|
|
527
|
+
LIST_USER_VECTOR_INDEXES,
|
|
528
|
+
index_name_pattern=index_name_pattern,
|
|
529
|
+
)
|
|
530
|
+
rows = await cr.fetchall()
|
|
531
|
+
for row in rows:
|
|
532
|
+
index_name = row[0]
|
|
533
|
+
description = await row[1].read() # AsyncLOB
|
|
534
|
+
attributes = await cls._get_attributes(index_name=index_name)
|
|
535
|
+
yield VectorIndex(
|
|
536
|
+
index_name=index_name,
|
|
537
|
+
description=description,
|
|
538
|
+
attributes=attributes,
|
|
539
|
+
profile=await AsyncProfile(
|
|
540
|
+
profile_name=attributes.profile_name
|
|
541
|
+
),
|
|
542
|
+
)
|
select_ai/version.py
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# -----------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) 2025, Oracle and/or its affiliates.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Universal Permissive License v 1.0 as shown at
|
|
5
|
+
# http://oss.oracle.com/licenses/upl.
|
|
6
|
+
# -----------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
__version__ = "1.0.0.dev4"
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: select_ai
|
|
3
|
+
Version: 1.0.0.dev4
|
|
4
|
+
Summary: Python API for Select AI
|
|
5
|
+
Author-email: Abhishek Singh <abhishek.o.singh@oracle.com>
|
|
6
|
+
Maintainer-email: Abhishek Singh <abhishek.o.singh@oracle.com>
|
|
7
|
+
License-Expression: UPL-1.0
|
|
8
|
+
Keywords: oracle,select-ai,adbs,autonomous database serverless
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Natural Language :: English
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
20
|
+
Classifier: Topic :: Database
|
|
21
|
+
Requires-Python: >=3.9
|
|
22
|
+
License-File: LICENSE.txt
|
|
23
|
+
Requires-Dist: oracledb
|
|
24
|
+
Requires-Dist: pandas==2.2.3
|
|
25
|
+
Dynamic: license-file
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
select_ai/__init__.py,sha256=JPm7f3IjEos8cbYr_hpwQKN6_sxG_gNgZXFqDpqxvrI,1281
|
|
2
|
+
select_ai/_abc.py,sha256=PQZYYz2SKchPujRe7V58rmby6IlZ9bMnNpfOQYQ_EZk,2616
|
|
3
|
+
select_ai/_enums.py,sha256=U0UavuE4pbRY-0-Qb8z-F7KfxU0YUfOgUahaSjDi0dU,416
|
|
4
|
+
select_ai/action.py,sha256=s78uhi5HHCLbTN2S5tLDSswTLUl40FZ3nESLka8CfZk,592
|
|
5
|
+
select_ai/admin.py,sha256=87OQEVmbFs6ftV4phLGHC1NGIWvD6x2Wu4Qic2DisRI,3375
|
|
6
|
+
select_ai/async_profile.py,sha256=STAV1yLxCKJbYjFtW4uViAUa8j20lzQxYoToml435BM,16293
|
|
7
|
+
select_ai/base_profile.py,sha256=YzromcmBdAmO9DX86L8_wwNtHYn8nPrHvUirYoxb5fA,6269
|
|
8
|
+
select_ai/conversation.py,sha256=IulU5BHbBksk1zo4ppaR85-EVd0o7VUptkdHrBjKYeg,8770
|
|
9
|
+
select_ai/db.py,sha256=G7yvE9UtUFpS46yXEv0_56lTqeve5r7mVGW7b8QmbEc,4484
|
|
10
|
+
select_ai/errors.py,sha256=dMWdbsdWCpBPO1OD1zkDGUymJ37iBQDjEo9kb-PwirU,1470
|
|
11
|
+
select_ai/profile.py,sha256=NX5-wP-HXBZJzyX9rlNElo3MZDC3A62r9QnPJge4k34,13705
|
|
12
|
+
select_ai/provider.py,sha256=ycMMGMVix-4Pc54WOJXO6e2NvYZJYrMv7o-3tUKMlEc,5104
|
|
13
|
+
select_ai/sql.py,sha256=_KQeBrHy9IXMI3N7PO064wp72gBzJGPfPMMNRprpxWs,2769
|
|
14
|
+
select_ai/synthetic_data.py,sha256=lnjEIcUzr7lr2dO24OgxRhol-tiVOiKK-kEvFVJoiig,3078
|
|
15
|
+
select_ai/vector_index.py,sha256=WnLP2QaqQUqT8BEs_nEyv02o-JfmuYx4V0pfUlflEz0,20156
|
|
16
|
+
select_ai/version.py,sha256=MtG826c9kTe8me0o5cgLxZWIfgQxkiAmJIa1EyANzEI,348
|
|
17
|
+
select_ai-1.0.0.dev4.dist-info/licenses/LICENSE.txt,sha256=_0VqOxSjO1hu6JexZDVzqUXSmzH1A53EOfyiJzXTBKc,1840
|
|
18
|
+
select_ai-1.0.0.dev4.dist-info/METADATA,sha256=JXkOaJouXVgJT5qqNaBtD7uFQgXNjkINPDAobiiYc3k,1016
|
|
19
|
+
select_ai-1.0.0.dev4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
select_ai-1.0.0.dev4.dist-info/top_level.txt,sha256=u_QUAHDibro58Lvi_MU6a9Wc6VfQT8HEQm0cciMTP-c,10
|
|
21
|
+
select_ai-1.0.0.dev4.dist-info/RECORD,,
|