select-ai 1.0.0.dev7__py3-none-any.whl → 1.0.0.dev9__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 +10 -5
- select_ai/async_profile.py +30 -7
- select_ai/base_profile.py +7 -3
- select_ai/conversation.py +16 -4
- select_ai/credential.py +135 -0
- select_ai/db.py +2 -2
- select_ai/profile.py +30 -7
- select_ai/provider.py +104 -2
- select_ai/synthetic_data.py +5 -0
- select_ai/vector_index.py +10 -4
- select_ai/version.py +1 -1
- select_ai-1.0.0.dev9.dist-info/METADATA +114 -0
- select_ai-1.0.0.dev9.dist-info/RECORD +21 -0
- select_ai/admin.py +0 -116
- select_ai-1.0.0.dev7.dist-info/METADATA +0 -25
- select_ai-1.0.0.dev7.dist-info/RECORD +0 -21
- {select_ai-1.0.0.dev7.dist-info → select_ai-1.0.0.dev9.dist-info}/WHEEL +0 -0
- {select_ai-1.0.0.dev7.dist-info → select_ai-1.0.0.dev9.dist-info}/licenses/LICENSE.txt +0 -0
- {select_ai-1.0.0.dev7.dist-info → select_ai-1.0.0.dev9.dist-info}/top_level.txt +0 -0
select_ai/__init__.py
CHANGED
|
@@ -6,11 +6,6 @@
|
|
|
6
6
|
# -----------------------------------------------------------------------------
|
|
7
7
|
|
|
8
8
|
from .action import Action
|
|
9
|
-
from .admin import (
|
|
10
|
-
create_credential,
|
|
11
|
-
disable_provider,
|
|
12
|
-
enable_provider,
|
|
13
|
-
)
|
|
14
9
|
from .async_profile import AsyncProfile
|
|
15
10
|
from .base_profile import BaseProfile, ProfileAttributes
|
|
16
11
|
from .conversation import (
|
|
@@ -18,6 +13,12 @@ from .conversation import (
|
|
|
18
13
|
Conversation,
|
|
19
14
|
ConversationAttributes,
|
|
20
15
|
)
|
|
16
|
+
from .credential import (
|
|
17
|
+
async_create_credential,
|
|
18
|
+
async_delete_credential,
|
|
19
|
+
create_credential,
|
|
20
|
+
delete_credential,
|
|
21
|
+
)
|
|
21
22
|
from .db import (
|
|
22
23
|
async_connect,
|
|
23
24
|
async_cursor,
|
|
@@ -39,6 +40,10 @@ from .provider import (
|
|
|
39
40
|
OCIGenAIProvider,
|
|
40
41
|
OpenAIProvider,
|
|
41
42
|
Provider,
|
|
43
|
+
async_disable_provider,
|
|
44
|
+
async_enable_provider,
|
|
45
|
+
disable_provider,
|
|
46
|
+
enable_provider,
|
|
42
47
|
)
|
|
43
48
|
from .synthetic_data import (
|
|
44
49
|
SyntheticDataAttributes,
|
select_ai/async_profile.py
CHANGED
|
@@ -55,7 +55,7 @@ class AsyncProfile(BaseProfile):
|
|
|
55
55
|
:return: None
|
|
56
56
|
:raises: oracledb.DatabaseError
|
|
57
57
|
"""
|
|
58
|
-
if self.profile_name
|
|
58
|
+
if self.profile_name:
|
|
59
59
|
profile_exists = False
|
|
60
60
|
try:
|
|
61
61
|
saved_attributes = await self._get_attributes(
|
|
@@ -75,7 +75,7 @@ class AsyncProfile(BaseProfile):
|
|
|
75
75
|
profile_name=self.profile_name
|
|
76
76
|
)
|
|
77
77
|
except ProfileNotFoundError:
|
|
78
|
-
if self.attributes is None:
|
|
78
|
+
if self.attributes is None and self.description is None:
|
|
79
79
|
raise
|
|
80
80
|
else:
|
|
81
81
|
if self.attributes is None:
|
|
@@ -91,10 +91,13 @@ class AsyncProfile(BaseProfile):
|
|
|
91
91
|
await self.create(
|
|
92
92
|
replace=self.replace, description=self.description
|
|
93
93
|
)
|
|
94
|
+
else: # profile name is None:
|
|
95
|
+
if self.attributes is not None or self.description is not None:
|
|
96
|
+
raise ValueError("'profile_name' cannot be empty or None")
|
|
94
97
|
return self
|
|
95
98
|
|
|
96
99
|
@staticmethod
|
|
97
|
-
async def _get_profile_description(profile_name) -> str:
|
|
100
|
+
async def _get_profile_description(profile_name) -> Union[str, None]:
|
|
98
101
|
"""Get description of profile from USER_CLOUD_AI_PROFILES
|
|
99
102
|
|
|
100
103
|
:param str profile_name: Name of profile
|
|
@@ -110,7 +113,10 @@ class AsyncProfile(BaseProfile):
|
|
|
110
113
|
)
|
|
111
114
|
profile = await cr.fetchone()
|
|
112
115
|
if profile:
|
|
113
|
-
|
|
116
|
+
if profile[1] is not None:
|
|
117
|
+
return await profile[1].read()
|
|
118
|
+
else:
|
|
119
|
+
return None
|
|
114
120
|
else:
|
|
115
121
|
raise ProfileNotFoundError(profile_name)
|
|
116
122
|
|
|
@@ -186,6 +192,12 @@ class AsyncProfile(BaseProfile):
|
|
|
186
192
|
attributes
|
|
187
193
|
:return: None
|
|
188
194
|
"""
|
|
195
|
+
if not isinstance(attributes, ProfileAttributes):
|
|
196
|
+
raise TypeError(
|
|
197
|
+
"'attributes' must be an object of type "
|
|
198
|
+
"select_ai.ProfileAttributes"
|
|
199
|
+
)
|
|
200
|
+
|
|
189
201
|
self.attributes = attributes
|
|
190
202
|
parameters = {
|
|
191
203
|
"profile_name": self.profile_name,
|
|
@@ -206,13 +218,14 @@ class AsyncProfile(BaseProfile):
|
|
|
206
218
|
:return: None
|
|
207
219
|
:raises: oracledb.DatabaseError
|
|
208
220
|
"""
|
|
221
|
+
if self.attributes is None:
|
|
222
|
+
raise AttributeError("Profile attributes cannot be None")
|
|
209
223
|
parameters = {
|
|
210
224
|
"profile_name": self.profile_name,
|
|
211
225
|
"attributes": self.attributes.json(),
|
|
212
226
|
}
|
|
213
227
|
if description:
|
|
214
228
|
parameters["description"] = description
|
|
215
|
-
|
|
216
229
|
async with async_cursor() as cr:
|
|
217
230
|
try:
|
|
218
231
|
await cr.callproc(
|
|
@@ -222,7 +235,7 @@ class AsyncProfile(BaseProfile):
|
|
|
222
235
|
except oracledb.DatabaseError as e:
|
|
223
236
|
(error,) = e.args
|
|
224
237
|
# If already exists and replace is True then drop and recreate
|
|
225
|
-
if
|
|
238
|
+
if error.code == 20046 and replace:
|
|
226
239
|
await self.delete(force=True)
|
|
227
240
|
await cr.callproc(
|
|
228
241
|
"DBMS_CLOUD_AI.CREATE_PROFILE",
|
|
@@ -302,7 +315,7 @@ class AsyncProfile(BaseProfile):
|
|
|
302
315
|
)
|
|
303
316
|
|
|
304
317
|
async def generate(
|
|
305
|
-
self, prompt, action=Action.SHOWSQL, params: Mapping = None
|
|
318
|
+
self, prompt: str, action=Action.SHOWSQL, params: Mapping = None
|
|
306
319
|
) -> Union[pandas.DataFrame, str, None]:
|
|
307
320
|
"""Asynchronously perform AI translation using this profile
|
|
308
321
|
|
|
@@ -312,6 +325,9 @@ class AsyncProfile(BaseProfile):
|
|
|
312
325
|
conversation_id for context-aware chats
|
|
313
326
|
:return: Union[pandas.DataFrame, str]
|
|
314
327
|
"""
|
|
328
|
+
if not prompt:
|
|
329
|
+
raise ValueError("prompt cannot be empty or None")
|
|
330
|
+
|
|
315
331
|
parameters = {
|
|
316
332
|
"prompt": prompt,
|
|
317
333
|
"action": action,
|
|
@@ -431,6 +447,13 @@ class AsyncProfile(BaseProfile):
|
|
|
431
447
|
:raises: oracledb.DatabaseError
|
|
432
448
|
|
|
433
449
|
"""
|
|
450
|
+
if synthetic_data_attributes is None:
|
|
451
|
+
raise ValueError("'synthetic_data_attributes' cannot be None")
|
|
452
|
+
|
|
453
|
+
if not isinstance(synthetic_data_attributes, SyntheticDataAttributes):
|
|
454
|
+
raise TypeError("'synthetic_data_attributes' must be an object "
|
|
455
|
+
"of type select_ai.SyntheticDataAttributes")
|
|
456
|
+
|
|
434
457
|
keyword_parameters = synthetic_data_attributes.prepare()
|
|
435
458
|
keyword_parameters["profile_name"] = self.profile_name
|
|
436
459
|
async with async_cursor() as cr:
|
select_ai/base_profile.py
CHANGED
|
@@ -73,10 +73,9 @@ class ProfileAttributes(SelectAIDataClass):
|
|
|
73
73
|
vector_index_name: Optional[str] = None
|
|
74
74
|
|
|
75
75
|
def __post_init__(self):
|
|
76
|
-
if not isinstance(self.provider, Provider):
|
|
76
|
+
if self.provider and not isinstance(self.provider, Provider):
|
|
77
77
|
raise ValueError(
|
|
78
|
-
f"
|
|
79
|
-
f"type select_ai.Provider"
|
|
78
|
+
f"'provider' must be an object of " f"type select_ai.Provider"
|
|
80
79
|
)
|
|
81
80
|
|
|
82
81
|
def json(self, exclude_null=True):
|
|
@@ -166,6 +165,11 @@ class BaseProfile(ABC):
|
|
|
166
165
|
):
|
|
167
166
|
"""Initialize a base profile"""
|
|
168
167
|
self.profile_name = profile_name
|
|
168
|
+
if attributes and not isinstance(attributes, ProfileAttributes):
|
|
169
|
+
raise TypeError(
|
|
170
|
+
"'attributes' must be an object of type "
|
|
171
|
+
"select_ai.ProfileAttributes"
|
|
172
|
+
)
|
|
169
173
|
self.attributes = attributes
|
|
170
174
|
self.description = description
|
|
171
175
|
self.merge = merge
|
select_ai/conversation.py
CHANGED
|
@@ -129,7 +129,10 @@ class Conversation(_BaseConversation):
|
|
|
129
129
|
attributes = cr.fetchone()
|
|
130
130
|
if attributes:
|
|
131
131
|
conversation_title = attributes[0]
|
|
132
|
-
|
|
132
|
+
if attributes[1]:
|
|
133
|
+
description = attributes[1].read() # Oracle.LOB
|
|
134
|
+
else:
|
|
135
|
+
description = None
|
|
133
136
|
retention_days = attributes[2]
|
|
134
137
|
return ConversationAttributes(
|
|
135
138
|
title=conversation_title,
|
|
@@ -154,7 +157,10 @@ class Conversation(_BaseConversation):
|
|
|
154
157
|
for row in cr.fetchall():
|
|
155
158
|
conversation_id = row[0]
|
|
156
159
|
conversation_title = row[1]
|
|
157
|
-
|
|
160
|
+
if row[2]:
|
|
161
|
+
description = row[2].read() # Oracle.LOB
|
|
162
|
+
else:
|
|
163
|
+
description = None
|
|
158
164
|
retention_days = row[3]
|
|
159
165
|
attributes = ConversationAttributes(
|
|
160
166
|
title=conversation_title,
|
|
@@ -224,7 +230,10 @@ class AsyncConversation(_BaseConversation):
|
|
|
224
230
|
attributes = await cr.fetchone()
|
|
225
231
|
if attributes:
|
|
226
232
|
conversation_title = attributes[0]
|
|
227
|
-
|
|
233
|
+
if attributes[1]:
|
|
234
|
+
description = await attributes[1].read() # Oracle.AsyncLOB
|
|
235
|
+
else:
|
|
236
|
+
description = None
|
|
228
237
|
retention_days = attributes[2]
|
|
229
238
|
return ConversationAttributes(
|
|
230
239
|
title=conversation_title,
|
|
@@ -250,7 +259,10 @@ class AsyncConversation(_BaseConversation):
|
|
|
250
259
|
for row in rows:
|
|
251
260
|
conversation_id = row[0]
|
|
252
261
|
conversation_title = row[1]
|
|
253
|
-
|
|
262
|
+
if row[2]:
|
|
263
|
+
description = await row[2].read() # Oracle.AsyncLOB
|
|
264
|
+
else:
|
|
265
|
+
description = None
|
|
254
266
|
retention_days = row[3]
|
|
255
267
|
attributes = ConversationAttributes(
|
|
256
268
|
title=conversation_title,
|
select_ai/credential.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
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
|
+
from typing import Mapping
|
|
9
|
+
|
|
10
|
+
import oracledb
|
|
11
|
+
|
|
12
|
+
from .db import async_cursor, cursor
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"async_create_credential",
|
|
16
|
+
"async_delete_credential",
|
|
17
|
+
"create_credential",
|
|
18
|
+
"delete_credential",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _validate_credential(credential: Mapping[str, str]):
|
|
23
|
+
valid_keys = {
|
|
24
|
+
"credential_name",
|
|
25
|
+
"username",
|
|
26
|
+
"password",
|
|
27
|
+
"user_ocid",
|
|
28
|
+
"tenancy_ocid",
|
|
29
|
+
"private_key",
|
|
30
|
+
"fingerprint",
|
|
31
|
+
"comments",
|
|
32
|
+
}
|
|
33
|
+
for k in credential.keys():
|
|
34
|
+
if k.lower() not in valid_keys:
|
|
35
|
+
raise ValueError(
|
|
36
|
+
f"Invalid value {k}: {credential[k]} for credential object"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def async_create_credential(credential: Mapping, replace: bool = False):
|
|
41
|
+
"""
|
|
42
|
+
Async API to create credential.
|
|
43
|
+
|
|
44
|
+
Creates a credential object using DBMS_CLOUD.CREATE_CREDENTIAL. if replace
|
|
45
|
+
is True, credential will be replaced if it already exists
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
_validate_credential(credential)
|
|
49
|
+
async with async_cursor() as cr:
|
|
50
|
+
try:
|
|
51
|
+
await cr.callproc(
|
|
52
|
+
"DBMS_CLOUD.CREATE_CREDENTIAL", keyword_parameters=credential
|
|
53
|
+
)
|
|
54
|
+
except oracledb.DatabaseError as e:
|
|
55
|
+
(error,) = e.args
|
|
56
|
+
# If already exists and replace is True then drop and recreate
|
|
57
|
+
if error.code == 20022 and replace:
|
|
58
|
+
await cr.callproc(
|
|
59
|
+
"DBMS_CLOUD.DROP_CREDENTIAL",
|
|
60
|
+
keyword_parameters={
|
|
61
|
+
"credential_name": credential["credential_name"]
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
await cr.callproc(
|
|
65
|
+
"DBMS_CLOUD.CREATE_CREDENTIAL",
|
|
66
|
+
keyword_parameters=credential,
|
|
67
|
+
)
|
|
68
|
+
else:
|
|
69
|
+
raise
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def async_delete_credential(credential_name: str, force: bool = False):
|
|
73
|
+
"""
|
|
74
|
+
Async API to create credential.
|
|
75
|
+
|
|
76
|
+
Deletes a credential object using DBMS_CLOUD.DROP_CREDENTIAL
|
|
77
|
+
"""
|
|
78
|
+
async with async_cursor() as cr:
|
|
79
|
+
try:
|
|
80
|
+
await cr.callproc(
|
|
81
|
+
"DBMS_CLOUD.DROP_CREDENTIAL",
|
|
82
|
+
keyword_parameters={"credential_name": credential_name},
|
|
83
|
+
)
|
|
84
|
+
except oracledb.DatabaseError as e:
|
|
85
|
+
(error,) = e.args
|
|
86
|
+
if error.code == 20004 and force: # does not exist
|
|
87
|
+
pass
|
|
88
|
+
else:
|
|
89
|
+
raise
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def create_credential(credential: Mapping, replace: bool = False):
|
|
93
|
+
"""
|
|
94
|
+
|
|
95
|
+
Creates a credential object using DBMS_CLOUD.CREATE_CREDENTIAL. if replace
|
|
96
|
+
is True, credential will be replaced if it "already exists"
|
|
97
|
+
|
|
98
|
+
"""
|
|
99
|
+
_validate_credential(credential)
|
|
100
|
+
with cursor() as cr:
|
|
101
|
+
try:
|
|
102
|
+
cr.callproc(
|
|
103
|
+
"DBMS_CLOUD.CREATE_CREDENTIAL", keyword_parameters=credential
|
|
104
|
+
)
|
|
105
|
+
except oracledb.DatabaseError as e:
|
|
106
|
+
(error,) = e.args
|
|
107
|
+
# If already exists and replace is True then drop and recreate
|
|
108
|
+
if error.code == 20022 and replace:
|
|
109
|
+
cr.callproc(
|
|
110
|
+
"DBMS_CLOUD.DROP_CREDENTIAL",
|
|
111
|
+
keyword_parameters={
|
|
112
|
+
"credential_name": credential["credential_name"]
|
|
113
|
+
},
|
|
114
|
+
)
|
|
115
|
+
cr.callproc(
|
|
116
|
+
"DBMS_CLOUD.CREATE_CREDENTIAL",
|
|
117
|
+
keyword_parameters=credential,
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
raise
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def delete_credential(credential_name: str, force: bool = False):
|
|
124
|
+
with cursor() as cr:
|
|
125
|
+
try:
|
|
126
|
+
cr.callproc(
|
|
127
|
+
"DBMS_CLOUD.DROP_CREDENTIAL",
|
|
128
|
+
keyword_parameters={"credential_name": credential_name},
|
|
129
|
+
)
|
|
130
|
+
except oracledb.DatabaseError as e:
|
|
131
|
+
(error,) = e.args
|
|
132
|
+
if error.code == 20004 and force: # does not exist
|
|
133
|
+
pass
|
|
134
|
+
else:
|
|
135
|
+
raise
|
select_ai/db.py
CHANGED
|
@@ -73,7 +73,7 @@ def is_connected() -> bool:
|
|
|
73
73
|
return False
|
|
74
74
|
try:
|
|
75
75
|
return conn.ping() is None
|
|
76
|
-
except oracledb.DatabaseError:
|
|
76
|
+
except (oracledb.DatabaseError, oracledb.InterfaceError):
|
|
77
77
|
return False
|
|
78
78
|
|
|
79
79
|
|
|
@@ -87,7 +87,7 @@ async def async_is_connected() -> bool:
|
|
|
87
87
|
return False
|
|
88
88
|
try:
|
|
89
89
|
return await conn.ping() is None
|
|
90
|
-
except oracledb.DatabaseError:
|
|
90
|
+
except (oracledb.DatabaseError, oracledb.InterfaceError):
|
|
91
91
|
return False
|
|
92
92
|
|
|
93
93
|
|
select_ai/profile.py
CHANGED
|
@@ -44,7 +44,7 @@ class Profile(BaseProfile):
|
|
|
44
44
|
:return: None
|
|
45
45
|
:raises: oracledb.DatabaseError
|
|
46
46
|
"""
|
|
47
|
-
if self.profile_name
|
|
47
|
+
if self.profile_name:
|
|
48
48
|
profile_exists = False
|
|
49
49
|
try:
|
|
50
50
|
saved_attributes = self._get_attributes(
|
|
@@ -64,7 +64,7 @@ class Profile(BaseProfile):
|
|
|
64
64
|
profile_name=self.profile_name
|
|
65
65
|
)
|
|
66
66
|
except ProfileNotFoundError:
|
|
67
|
-
if self.attributes is None:
|
|
67
|
+
if self.attributes is None and self.description is None:
|
|
68
68
|
raise
|
|
69
69
|
else:
|
|
70
70
|
if self.attributes is None:
|
|
@@ -78,20 +78,28 @@ class Profile(BaseProfile):
|
|
|
78
78
|
)
|
|
79
79
|
if self.replace or not profile_exists:
|
|
80
80
|
self.create(replace=self.replace)
|
|
81
|
+
else: # profile name is None
|
|
82
|
+
if self.attributes is not None or self.description is not None:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"Attribute 'profile_name' cannot be empty or None"
|
|
85
|
+
)
|
|
81
86
|
|
|
82
87
|
@staticmethod
|
|
83
|
-
def _get_profile_description(profile_name) -> str:
|
|
88
|
+
def _get_profile_description(profile_name) -> Union[str, None]:
|
|
84
89
|
"""Get description of profile from USER_CLOUD_AI_PROFILES
|
|
85
90
|
|
|
86
91
|
:param str profile_name:
|
|
87
|
-
:return: str
|
|
92
|
+
:return: Union[str, None] profile description
|
|
88
93
|
:raises: ProfileNotFoundError
|
|
89
94
|
"""
|
|
90
95
|
with cursor() as cr:
|
|
91
96
|
cr.execute(GET_USER_AI_PROFILE, profile_name=profile_name.upper())
|
|
92
97
|
profile = cr.fetchone()
|
|
93
98
|
if profile:
|
|
94
|
-
|
|
99
|
+
if profile[1] is not None:
|
|
100
|
+
return profile[1].read()
|
|
101
|
+
else:
|
|
102
|
+
return None
|
|
95
103
|
else:
|
|
96
104
|
raise ProfileNotFoundError(profile_name)
|
|
97
105
|
|
|
@@ -165,6 +173,11 @@ class Profile(BaseProfile):
|
|
|
165
173
|
attributes
|
|
166
174
|
:return: None
|
|
167
175
|
"""
|
|
176
|
+
if not isinstance(attributes, ProfileAttributes):
|
|
177
|
+
raise TypeError(
|
|
178
|
+
"'attributes' must be an object of type"
|
|
179
|
+
" select_ai.ProfileAttributes"
|
|
180
|
+
)
|
|
168
181
|
self.attributes = attributes
|
|
169
182
|
parameters = {
|
|
170
183
|
"profile_name": self.profile_name,
|
|
@@ -182,7 +195,8 @@ class Profile(BaseProfile):
|
|
|
182
195
|
:return: None
|
|
183
196
|
:raises: oracledb.DatabaseError
|
|
184
197
|
"""
|
|
185
|
-
|
|
198
|
+
if self.attributes is None:
|
|
199
|
+
raise AttributeError("Profile attributes cannot be None")
|
|
186
200
|
parameters = {
|
|
187
201
|
"profile_name": self.profile_name,
|
|
188
202
|
"attributes": self.attributes.json(),
|
|
@@ -199,7 +213,7 @@ class Profile(BaseProfile):
|
|
|
199
213
|
except oracledb.DatabaseError as e:
|
|
200
214
|
(error,) = e.args
|
|
201
215
|
# If already exists and replace is True then drop and recreate
|
|
202
|
-
if
|
|
216
|
+
if error.code == 20046 and replace:
|
|
203
217
|
self.delete(force=True)
|
|
204
218
|
cr.callproc(
|
|
205
219
|
"DBMS_CLOUD_AI.CREATE_PROFILE",
|
|
@@ -283,6 +297,8 @@ class Profile(BaseProfile):
|
|
|
283
297
|
conversation_id for context-aware chats
|
|
284
298
|
:return: Union[pandas.DataFrame, str]
|
|
285
299
|
"""
|
|
300
|
+
if not prompt:
|
|
301
|
+
raise ValueError("prompt cannot be empty or None")
|
|
286
302
|
parameters = {
|
|
287
303
|
"prompt": prompt,
|
|
288
304
|
"action": action,
|
|
@@ -393,6 +409,13 @@ class Profile(BaseProfile):
|
|
|
393
409
|
:raises: oracledb.DatabaseError
|
|
394
410
|
|
|
395
411
|
"""
|
|
412
|
+
if synthetic_data_attributes is None:
|
|
413
|
+
raise ValueError("Param 'synthetic_data_attributes' cannot be None")
|
|
414
|
+
|
|
415
|
+
if not isinstance(synthetic_data_attributes, SyntheticDataAttributes):
|
|
416
|
+
raise TypeError("'synthetic_data_attributes' must be an object "
|
|
417
|
+
"of type select_ai.SyntheticDataAttributes")
|
|
418
|
+
|
|
396
419
|
keyword_parameters = synthetic_data_attributes.prepare()
|
|
397
420
|
keyword_parameters["profile_name"] = self.profile_name
|
|
398
421
|
with cursor() as cr:
|
select_ai/provider.py
CHANGED
|
@@ -5,11 +5,19 @@
|
|
|
5
5
|
# http://oss.oracle.com/licenses/upl.
|
|
6
6
|
# -----------------------------------------------------------------------------
|
|
7
7
|
|
|
8
|
-
from dataclasses import dataclass
|
|
9
|
-
from typing import Optional
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import List, Optional, Union
|
|
10
10
|
|
|
11
11
|
from select_ai._abc import SelectAIDataClass
|
|
12
12
|
|
|
13
|
+
from .db import async_cursor, cursor
|
|
14
|
+
from .sql import (
|
|
15
|
+
DISABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
16
|
+
ENABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
17
|
+
GRANT_PRIVILEGES_TO_USER,
|
|
18
|
+
REVOKE_PRIVILEGES_FROM_USER,
|
|
19
|
+
)
|
|
20
|
+
|
|
13
21
|
OPENAI = "openai"
|
|
14
22
|
COHERE = "cohere"
|
|
15
23
|
AZURE = "azure"
|
|
@@ -184,3 +192,97 @@ class AnthropicProvider(Provider):
|
|
|
184
192
|
|
|
185
193
|
provider_name: str = ANTHROPIC
|
|
186
194
|
provider_endpoint = "api.anthropic.com"
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
async def async_enable_provider(
|
|
198
|
+
users: Union[str, List[str]], provider_endpoint: str = None
|
|
199
|
+
):
|
|
200
|
+
"""
|
|
201
|
+
Async API to enable AI profile for database users.
|
|
202
|
+
|
|
203
|
+
This method grants execute privilege on the packages DBMS_CLOUD,
|
|
204
|
+
DBMS_CLOUD_AI and DBMS_CLOUD_PIPELINE. It also enables the database
|
|
205
|
+
user to invoke the AI Provider (LLM) endpoint
|
|
206
|
+
|
|
207
|
+
"""
|
|
208
|
+
if isinstance(users, str):
|
|
209
|
+
users = [users]
|
|
210
|
+
|
|
211
|
+
async with async_cursor() as cr:
|
|
212
|
+
for user in users:
|
|
213
|
+
await cr.execute(GRANT_PRIVILEGES_TO_USER.format(user))
|
|
214
|
+
if provider_endpoint:
|
|
215
|
+
await cr.execute(
|
|
216
|
+
ENABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
217
|
+
user=user,
|
|
218
|
+
host=provider_endpoint,
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
async def async_disable_provider(
|
|
223
|
+
users: Union[str, List[str]], provider_endpoint: str = None
|
|
224
|
+
):
|
|
225
|
+
"""
|
|
226
|
+
Async API to disable AI profile for database users
|
|
227
|
+
|
|
228
|
+
Disables AI provider for the user. This method revokes execute privilege
|
|
229
|
+
on the packages DBMS_CLOUD, DBMS_CLOUD_AI and DBMS_CLOUD_PIPELINE. It
|
|
230
|
+
also disables the user to invoke the AI Provider (LLM) endpoint
|
|
231
|
+
"""
|
|
232
|
+
if isinstance(users, str):
|
|
233
|
+
users = [users]
|
|
234
|
+
|
|
235
|
+
async with async_cursor() as cr:
|
|
236
|
+
for user in users:
|
|
237
|
+
await cr.execute(REVOKE_PRIVILEGES_FROM_USER.format(user))
|
|
238
|
+
if provider_endpoint:
|
|
239
|
+
await cr.execute(
|
|
240
|
+
DISABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
241
|
+
user=user,
|
|
242
|
+
host=provider_endpoint,
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def enable_provider(
|
|
247
|
+
users: Union[str, List[str]], provider_endpoint: str = None
|
|
248
|
+
):
|
|
249
|
+
"""
|
|
250
|
+
Enables AI profile for the user. This method grants execute privilege
|
|
251
|
+
on the packages DBMS_CLOUD, DBMS_CLOUD_AI and DBMS_CLOUD_PIPELINE. It
|
|
252
|
+
also enables the user to invoke the AI Provider (LLM) endpoint
|
|
253
|
+
"""
|
|
254
|
+
if isinstance(users, str):
|
|
255
|
+
users = [users]
|
|
256
|
+
|
|
257
|
+
with cursor() as cr:
|
|
258
|
+
for user in users:
|
|
259
|
+
cr.execute(GRANT_PRIVILEGES_TO_USER.format(user))
|
|
260
|
+
if provider_endpoint:
|
|
261
|
+
cr.execute(
|
|
262
|
+
ENABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
263
|
+
user=user,
|
|
264
|
+
host=provider_endpoint,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def disable_provider(
|
|
269
|
+
users: Union[str, List[str]], provider_endpoint: str = None
|
|
270
|
+
):
|
|
271
|
+
"""
|
|
272
|
+
Disables AI provider for the user. This method revokes execute privilege
|
|
273
|
+
on the packages DBMS_CLOUD, DBMS_CLOUD_AI and DBMS_CLOUD_PIPELINE. It
|
|
274
|
+
also disables the user to invoke the AI(LLM) endpoint
|
|
275
|
+
|
|
276
|
+
"""
|
|
277
|
+
if isinstance(users, str):
|
|
278
|
+
users = [users]
|
|
279
|
+
|
|
280
|
+
with cursor() as cr:
|
|
281
|
+
for user in users:
|
|
282
|
+
cr.execute(REVOKE_PRIVILEGES_FROM_USER.format(user))
|
|
283
|
+
if provider_endpoint:
|
|
284
|
+
cr.execute(
|
|
285
|
+
DISABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
286
|
+
user=user,
|
|
287
|
+
host=provider_endpoint,
|
|
288
|
+
)
|
select_ai/synthetic_data.py
CHANGED
|
@@ -60,6 +60,11 @@ class SyntheticDataAttributes(SelectAIDataClass):
|
|
|
60
60
|
record_count: Optional[int] = None
|
|
61
61
|
user_prompt: Optional[str] = None
|
|
62
62
|
|
|
63
|
+
def __post_init__(self):
|
|
64
|
+
if self.params and not isinstance(self.params, SyntheticDataParams):
|
|
65
|
+
raise TypeError("'params' must be an object of"
|
|
66
|
+
" type SyntheticDataParams'")
|
|
67
|
+
|
|
63
68
|
def dict(self, exclude_null=True):
|
|
64
69
|
attributes = {}
|
|
65
70
|
for k, v in self.__dict__.items():
|
select_ai/vector_index.py
CHANGED
|
@@ -192,7 +192,7 @@ class VectorIndex(_BaseVectorIndex):
|
|
|
192
192
|
except oracledb.DatabaseError as e:
|
|
193
193
|
(error,) = e.args
|
|
194
194
|
# If already exists and replace is True then drop and recreate
|
|
195
|
-
if
|
|
195
|
+
if error.code == 20048 and replace:
|
|
196
196
|
self.delete(force=True)
|
|
197
197
|
cr.callproc(
|
|
198
198
|
"DBMS_CLOUD_AI.CREATE_VECTOR_INDEX",
|
|
@@ -325,7 +325,10 @@ class VectorIndex(_BaseVectorIndex):
|
|
|
325
325
|
)
|
|
326
326
|
for row in cr.fetchall():
|
|
327
327
|
index_name = row[0]
|
|
328
|
-
|
|
328
|
+
if row[1]:
|
|
329
|
+
description = row[1].read() # Oracle.LOB
|
|
330
|
+
else:
|
|
331
|
+
description = None
|
|
329
332
|
attributes = cls._get_attributes(index_name=index_name)
|
|
330
333
|
yield cls(
|
|
331
334
|
index_name=index_name,
|
|
@@ -396,7 +399,7 @@ class AsyncVectorIndex(_BaseVectorIndex):
|
|
|
396
399
|
except oracledb.DatabaseError as e:
|
|
397
400
|
(error,) = e.args
|
|
398
401
|
# If already exists and replace is True then drop and recreate
|
|
399
|
-
if
|
|
402
|
+
if error.code == 20048 and replace:
|
|
400
403
|
await self.delete(force=True)
|
|
401
404
|
await cr.callproc(
|
|
402
405
|
"DBMS_CLOUD_AI.CREATE_VECTOR_INDEX",
|
|
@@ -534,7 +537,10 @@ class AsyncVectorIndex(_BaseVectorIndex):
|
|
|
534
537
|
rows = await cr.fetchall()
|
|
535
538
|
for row in rows:
|
|
536
539
|
index_name = row[0]
|
|
537
|
-
|
|
540
|
+
if row[1]:
|
|
541
|
+
description = await row[1].read() # AsyncLOB
|
|
542
|
+
else:
|
|
543
|
+
description = None
|
|
538
544
|
attributes = await cls._get_attributes(index_name=index_name)
|
|
539
545
|
yield VectorIndex(
|
|
540
546
|
index_name=index_name,
|
select_ai/version.py
CHANGED
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: select_ai
|
|
3
|
+
Version: 1.0.0.dev9
|
|
4
|
+
Summary: Select AI for Python
|
|
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 :: 4 - Beta
|
|
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
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE.txt
|
|
24
|
+
Requires-Dist: oracledb
|
|
25
|
+
Requires-Dist: pandas==2.2.3
|
|
26
|
+
Dynamic: license-file
|
|
27
|
+
|
|
28
|
+
# Select AI for Python
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
Select AI for Python enables you to ask questions of your database data using natural language (text-to-SQL), get generative AI responses using your trusted content (retrieval augmented generation), generate synthetic data using large language models, and other features – all from Python. With the general availability of Select AI Python, Python developers have access to the functionality of Select AI on Oracle Autonomous Database.
|
|
32
|
+
|
|
33
|
+
Select AI for Python enables you to leverage the broader Python ecosystem in combination with generative AI and database functionality - bridging the gap between the DBMS_CLOUD_AI PL/SQL package and Python's rich ecosystem. It provides intuitive objects and methods for AI model interaction.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Run
|
|
39
|
+
```bash
|
|
40
|
+
python3 -m pip install select_ai
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Samples
|
|
44
|
+
|
|
45
|
+
Examples can be found in the [/samples][samples] directory
|
|
46
|
+
|
|
47
|
+
### Basic Example
|
|
48
|
+
|
|
49
|
+
```python
|
|
50
|
+
import select_ai
|
|
51
|
+
|
|
52
|
+
user = "<your_select_ai_user>"
|
|
53
|
+
password = "<your_select_ai_password>"
|
|
54
|
+
dsn = "<your_select_ai_db_connect_string>"
|
|
55
|
+
|
|
56
|
+
select_ai.connect(user=user, password=password, dsn=dsn)
|
|
57
|
+
profile = select_ai.Profile(profile_name="oci_ai_profile")
|
|
58
|
+
# run_sql returns a pandas dataframe
|
|
59
|
+
df = profile.run_sql(prompt="How many promotions?")
|
|
60
|
+
print(df.columns)
|
|
61
|
+
print(df)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Async Example
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
|
|
68
|
+
import asyncio
|
|
69
|
+
|
|
70
|
+
import select_ai
|
|
71
|
+
|
|
72
|
+
user = "<your_select_ai_user>"
|
|
73
|
+
password = "<your_select_ai_password>"
|
|
74
|
+
dsn = "<your_select_ai_db_connect_string>"
|
|
75
|
+
|
|
76
|
+
# This example shows how to asynchronously run sql
|
|
77
|
+
async def main():
|
|
78
|
+
await select_ai.async_connect(user=user, password=password, dsn=dsn)
|
|
79
|
+
async_profile = await select_ai.AsyncProfile(
|
|
80
|
+
profile_name="async_oci_ai_profile",
|
|
81
|
+
)
|
|
82
|
+
# run_sql returns a pandas df
|
|
83
|
+
df = await async_profile.run_sql("How many promotions?")
|
|
84
|
+
print(df)
|
|
85
|
+
|
|
86
|
+
asyncio.run(main())
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
## Help
|
|
90
|
+
|
|
91
|
+
Questions can be asked in [GitHub Discussions][ghdiscussions].
|
|
92
|
+
|
|
93
|
+
Problem reports can be raised in [GitHub Issues][ghissues].
|
|
94
|
+
|
|
95
|
+
## Contributing
|
|
96
|
+
|
|
97
|
+
This project welcomes contributions from the community. Before submitting a pull request, please [review our contribution guide][contributing]
|
|
98
|
+
|
|
99
|
+
## Security
|
|
100
|
+
|
|
101
|
+
Please consult the [security guide][security] for our responsible security vulnerability disclosure process
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
Copyright (c) 2025 Oracle and/or its affiliates.
|
|
106
|
+
|
|
107
|
+
Released under the Universal Permissive License v1.0 as shown at
|
|
108
|
+
<https://oss.oracle.com/licenses/upl/>.
|
|
109
|
+
|
|
110
|
+
[contributing]: https://github.com/oracle/python-select-ai/blob/main/CONTRIBUTING.md
|
|
111
|
+
[ghdiscussions]: https://github.com/oracle/python-select-ai/discussions
|
|
112
|
+
[ghissues]: https://github.com/oracle/python-select-ai/issues
|
|
113
|
+
[samples]: https://github.com/oracle/python-select-ai/tree/main/samples
|
|
114
|
+
[security]: https://github.com/oracle/python-select-ai/blob/main/SECURITY.md
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
select_ai/__init__.py,sha256=5oVcMxHqKPnIqzsPntFrqrMrTQ86bDrJsG8ARXuxN7U,1460
|
|
2
|
+
select_ai/_abc.py,sha256=ccec6yu54w5QMix_EKc_BJ_0JatUDXKjfLW9KshOGak,2692
|
|
3
|
+
select_ai/_enums.py,sha256=U0UavuE4pbRY-0-Qb8z-F7KfxU0YUfOgUahaSjDi0dU,416
|
|
4
|
+
select_ai/action.py,sha256=s78uhi5HHCLbTN2S5tLDSswTLUl40FZ3nESLka8CfZk,592
|
|
5
|
+
select_ai/async_profile.py,sha256=3Ybsl-mKIxJDelIXcmGTBdzgBdkDacomTGMx0X0kSsw,18705
|
|
6
|
+
select_ai/base_profile.py,sha256=hY9thC-7pHvMwh01W0t7OzOAyG8Fi8Lnd0KhZ7ouVNs,7005
|
|
7
|
+
select_ai/conversation.py,sha256=fbYH7ROLwpyf1VzNtcrESG1S0_ZfLJ-uObbvbUATJUY,9622
|
|
8
|
+
select_ai/credential.py,sha256=cDIsiPgjbvckbOfb2LeoztPUsDO-6cJ_S8DzYT3pm4k,4220
|
|
9
|
+
select_ai/db.py,sha256=ipyXt5_wEXAQGl7PLVq82FBuePdA_e6b8ee_vhEAMks,4723
|
|
10
|
+
select_ai/errors.py,sha256=2T5eICWWjj7a907aRh7es5wTcvWm_wJiP8h0b3ipxVQ,2114
|
|
11
|
+
select_ai/profile.py,sha256=UH7aC9B5W2w4WmflX6N5FRWfSLwagXxgz62T9enpOgY,16059
|
|
12
|
+
select_ai/provider.py,sha256=Sv0b-6SQJEoasPsnyHcWg1W8gS8nKldUW-83lL4MDm8,8285
|
|
13
|
+
select_ai/sql.py,sha256=9yCdB1H_sYpjMLs-6SB8xf5-QpTwCAx4cIgIsElvly4,2786
|
|
14
|
+
select_ai/synthetic_data.py,sha256=-l4eHLsTQRYTgJdsscszSWJlo9p75rLFMpH4XIodllE,3303
|
|
15
|
+
select_ai/vector_index.py,sha256=C6a3jgZ6ffyel7fF5owHV_2G4T_E62pmGp1QXl28M4w,20475
|
|
16
|
+
select_ai/version.py,sha256=LekbfzftsNILcAlsLq7T6_vw-Z7AVR2pVAHm9F3rPJU,348
|
|
17
|
+
select_ai-1.0.0.dev9.dist-info/licenses/LICENSE.txt,sha256=_0VqOxSjO1hu6JexZDVzqUXSmzH1A53EOfyiJzXTBKc,1840
|
|
18
|
+
select_ai-1.0.0.dev9.dist-info/METADATA,sha256=kmaLxaa2dvHdv5hSkETxFnBGPEm6v8ScH0huFPVnorQ,3857
|
|
19
|
+
select_ai-1.0.0.dev9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
+
select_ai-1.0.0.dev9.dist-info/top_level.txt,sha256=u_QUAHDibro58Lvi_MU6a9Wc6VfQT8HEQm0cciMTP-c,10
|
|
21
|
+
select_ai-1.0.0.dev9.dist-info/RECORD,,
|
select_ai/admin.py
DELETED
|
@@ -1,116 +0,0 @@
|
|
|
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
|
-
from typing import List, Mapping, Union
|
|
9
|
-
|
|
10
|
-
import oracledb
|
|
11
|
-
|
|
12
|
-
from .db import cursor
|
|
13
|
-
from .sql import (
|
|
14
|
-
DISABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
15
|
-
ENABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
16
|
-
GRANT_PRIVILEGES_TO_USER,
|
|
17
|
-
REVOKE_PRIVILEGES_FROM_USER,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
__all__ = [
|
|
21
|
-
"create_credential",
|
|
22
|
-
"disable_provider",
|
|
23
|
-
"enable_provider",
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
def create_credential(credential: Mapping, replace: bool = False):
|
|
28
|
-
"""
|
|
29
|
-
Creates a credential object using DBMS_CLOUD.CREATE_CREDENTIAL
|
|
30
|
-
|
|
31
|
-
if replace is True, credential will be replaced if it "already exists"
|
|
32
|
-
|
|
33
|
-
"""
|
|
34
|
-
valid_keys = {
|
|
35
|
-
"credential_name",
|
|
36
|
-
"username",
|
|
37
|
-
"password",
|
|
38
|
-
"user_ocid",
|
|
39
|
-
"tenancy_ocid",
|
|
40
|
-
"private_key",
|
|
41
|
-
"fingerprint",
|
|
42
|
-
"comments",
|
|
43
|
-
}
|
|
44
|
-
for k in credential.keys():
|
|
45
|
-
if k.lower() not in valid_keys:
|
|
46
|
-
raise ValueError(
|
|
47
|
-
f"Invalid value {k}: {credential[k]} for credential object"
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
with cursor() as cr:
|
|
51
|
-
try:
|
|
52
|
-
cr.callproc(
|
|
53
|
-
"DBMS_CLOUD.CREATE_CREDENTIAL", keyword_parameters=credential
|
|
54
|
-
)
|
|
55
|
-
except oracledb.DatabaseError as e:
|
|
56
|
-
(error,) = e.args
|
|
57
|
-
# If already exists and replace is True then drop and recreate
|
|
58
|
-
if "already exists" in error.message.lower() and replace:
|
|
59
|
-
cr.callproc(
|
|
60
|
-
"DBMS_CLOUD.DROP_CREDENTIAL",
|
|
61
|
-
keyword_parameters={
|
|
62
|
-
"credential_name": credential["credential_name"]
|
|
63
|
-
},
|
|
64
|
-
)
|
|
65
|
-
cr.callproc(
|
|
66
|
-
"DBMS_CLOUD.CREATE_CREDENTIAL",
|
|
67
|
-
keyword_parameters=credential,
|
|
68
|
-
)
|
|
69
|
-
else:
|
|
70
|
-
raise
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def enable_provider(
|
|
74
|
-
users: Union[str, List[str]], provider_endpoint: str = None
|
|
75
|
-
):
|
|
76
|
-
"""
|
|
77
|
-
Enables AI profile for the user. This method grants execute privilege
|
|
78
|
-
on the packages DBMS_CLOUD, DBMS_CLOUD_AI and DBMS_CLOUD_PIPELINE. It
|
|
79
|
-
also enables the user to invoke the AI(LLM) endpoint hosted at a
|
|
80
|
-
certain domain
|
|
81
|
-
"""
|
|
82
|
-
if isinstance(users, str):
|
|
83
|
-
users = [users]
|
|
84
|
-
|
|
85
|
-
with cursor() as cr:
|
|
86
|
-
for user in users:
|
|
87
|
-
cr.execute(GRANT_PRIVILEGES_TO_USER.format(user))
|
|
88
|
-
if provider_endpoint:
|
|
89
|
-
cr.execute(
|
|
90
|
-
ENABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
91
|
-
user=user,
|
|
92
|
-
host=provider_endpoint,
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
def disable_provider(
|
|
97
|
-
users: Union[str, List[str]], provider_endpoint: str = None
|
|
98
|
-
):
|
|
99
|
-
"""
|
|
100
|
-
Disables AI provider for the user. This method revokes execute privilege
|
|
101
|
-
on the packages DBMS_CLOUD, DBMS_CLOUD_AI and DBMS_CLOUD_PIPELINE. It
|
|
102
|
-
also disables the user to invoke the AI(LLM) endpoint hosted at a
|
|
103
|
-
certain domain
|
|
104
|
-
"""
|
|
105
|
-
if isinstance(users, str):
|
|
106
|
-
users = [users]
|
|
107
|
-
|
|
108
|
-
with cursor() as cr:
|
|
109
|
-
for user in users:
|
|
110
|
-
cr.execute(REVOKE_PRIVILEGES_FROM_USER.format(user))
|
|
111
|
-
if provider_endpoint:
|
|
112
|
-
cr.execute(
|
|
113
|
-
DISABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
114
|
-
user=user,
|
|
115
|
-
host=provider_endpoint,
|
|
116
|
-
)
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: select_ai
|
|
3
|
-
Version: 1.0.0.dev7
|
|
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
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
select_ai/__init__.py,sha256=LVdOh_JnWEXaXs0NSflA1f0YIWCUEpTBZCvWNTvSS74,1319
|
|
2
|
-
select_ai/_abc.py,sha256=ccec6yu54w5QMix_EKc_BJ_0JatUDXKjfLW9KshOGak,2692
|
|
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=MMkXpvVJZUoloty8HiQnndPVPpuZch22Pnk8llUhUCc,3528
|
|
6
|
-
select_ai/async_profile.py,sha256=7fbEW4rRLy3FcT8E7fabXwlLja-echoWEhlQHQaBCOM,17651
|
|
7
|
-
select_ai/base_profile.py,sha256=Iyd5GdBNqEISiWCwYpGSKOGd394f-A5QgHPtuJ547CE,6791
|
|
8
|
-
select_ai/conversation.py,sha256=jeZy7qtbnFTO9xTE3gNZLyxVI0Pnj_rpc0QZb1r-A00,9240
|
|
9
|
-
select_ai/db.py,sha256=Q0MvoIBzE0fHP1d9oFjvnjfgKqdRk6ZCM6bERokcM3E,4669
|
|
10
|
-
select_ai/errors.py,sha256=2T5eICWWjj7a907aRh7es5wTcvWm_wJiP8h0b3ipxVQ,2114
|
|
11
|
-
select_ai/profile.py,sha256=t9TMAqdaVm9DMz-jdP6AZddce6s9XYhlB_HOrfaq5NM,14926
|
|
12
|
-
select_ai/provider.py,sha256=7KNdU2BmUMzjPj2WAwtn0vEYPBYY83MvRTT1q2Ng5tM,5103
|
|
13
|
-
select_ai/sql.py,sha256=9yCdB1H_sYpjMLs-6SB8xf5-QpTwCAx4cIgIsElvly4,2786
|
|
14
|
-
select_ai/synthetic_data.py,sha256=lnjEIcUzr7lr2dO24OgxRhol-tiVOiKK-kEvFVJoiig,3078
|
|
15
|
-
select_ai/vector_index.py,sha256=0-0xkXWALPm6RqIUoY3r-NEisHO3Ya3lKkIIRed0a_4,20335
|
|
16
|
-
select_ai/version.py,sha256=baI0umotUCS0Xec8hkkCm0zLQafNjA_g3EUrE1upAAU,348
|
|
17
|
-
select_ai-1.0.0.dev7.dist-info/licenses/LICENSE.txt,sha256=_0VqOxSjO1hu6JexZDVzqUXSmzH1A53EOfyiJzXTBKc,1840
|
|
18
|
-
select_ai-1.0.0.dev7.dist-info/METADATA,sha256=B0UgNOCBaYZmtLyC1_5O_-ibVST5j_VEkmFCffliiCs,1016
|
|
19
|
-
select_ai-1.0.0.dev7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
20
|
-
select_ai-1.0.0.dev7.dist-info/top_level.txt,sha256=u_QUAHDibro58Lvi_MU6a9Wc6VfQT8HEQm0cciMTP-c,10
|
|
21
|
-
select_ai-1.0.0.dev7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|