select-ai 1.0.0.dev10__tar.gz → 1.0.0rc1__tar.gz
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-1.0.0.dev10/src/select_ai.egg-info → select_ai-1.0.0rc1}/PKG-INFO +5 -2
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/README.md +1 -1
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/pyproject.toml +5 -2
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/vector_index_create.py +0 -1
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/vector_index_rag.py +0 -1
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_explain_sql.py +1 -3
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_narrate.py +1 -3
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_run_sql.py +1 -3
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_show_sql.py +1 -3
- select_ai-1.0.0rc1/src/select_ai/_validations.py +123 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/async_profile.py +14 -6
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/base_profile.py +1 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/profile.py +21 -11
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/provider.py +9 -4
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/synthetic_data.py +3 -2
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/vector_index.py +10 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/version.py +1 -1
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1/src/select_ai.egg-info}/PKG-INFO +5 -2
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai.egg-info/SOURCES.txt +1 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/LICENSE.txt +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/MANIFEST.in +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/conversation_chat_session.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/conversations_list.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/create_ai_credential.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/delete_ai_credential.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/disable_ai_provider.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/enable_ai_provider.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_chat.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_create.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_explain_sql.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_gen_multi_table_synthetic_data.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_gen_single_table_synthetic_data.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_pipeline.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_run_sql.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_show_sql.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_sql_concurrent_tasks.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profiles_list.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/vector_index_delete.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/vector_index_list.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/conversation_chat_session.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/conversation_create.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/conversation_delete.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/conversations_list.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/create_ai_credential.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/delete_ai_credential.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/disable_ai_provider.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/enable_ai_provider.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_chat.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_create.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_delete.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_gen_multi_table_synthetic_data.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_gen_single_table_synthetic_data.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profiles_list.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/vector_index_create.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/vector_index_delete.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/vector_index_list.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/vector_index_rag.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/setup.cfg +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/__init__.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/_abc.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/_enums.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/action.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/conversation.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/credential.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/db.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/errors.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai/sql.py +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai.egg-info/dependency_links.txt +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai.egg-info/requires.txt +0 -0
- {select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/src/select_ai.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: select_ai
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0rc1
|
|
4
4
|
Summary: Select AI for Python
|
|
5
5
|
Author-email: Abhishek Singh <abhishek.o.singh@oracle.com>
|
|
6
6
|
Maintainer-email: Abhishek Singh <abhishek.o.singh@oracle.com>
|
|
@@ -8,8 +8,9 @@ License-Expression: UPL-1.0
|
|
|
8
8
|
Project-URL: Homepage, https://github.com/oracle/python-select-ai
|
|
9
9
|
Project-URL: Repository, https://github.com/oracle/python-select-ai
|
|
10
10
|
Project-URL: Issues, https://github.com/oracle/python-select-ai/issues
|
|
11
|
+
Project-URL: Documentation, https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/pysai/
|
|
11
12
|
Keywords: oracle,select-ai,adbs,autonomous database serverless
|
|
12
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
14
|
Classifier: Intended Audience :: Developers
|
|
14
15
|
Classifier: Natural Language :: English
|
|
15
16
|
Classifier: Operating System :: OS Independent
|
|
@@ -21,6 +22,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
23
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
24
|
Classifier: Topic :: Database
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
27
|
Requires-Python: >=3.9
|
|
25
28
|
Description-Content-Type: text/markdown
|
|
26
29
|
License-File: LICENSE.txt
|
|
@@ -84,4 +84,4 @@ Released under the Universal Permissive License v1.0 as shown at
|
|
|
84
84
|
[ghdiscussions]: https://github.com/oracle/python-select-ai/discussions
|
|
85
85
|
[ghissues]: https://github.com/oracle/python-select-ai/issues
|
|
86
86
|
[samples]: https://github.com/oracle/python-select-ai/tree/main/samples
|
|
87
|
-
[security]: https://github.com/oracle/python-select-ai/blob/main/SECURITY.md
|
|
87
|
+
[security]: https://github.com/oracle/python-select-ai/blob/main/SECURITY.md
|
|
@@ -23,7 +23,7 @@ keywords = [
|
|
|
23
23
|
license = " UPL-1.0"
|
|
24
24
|
license-files = ["LICENSE.txt"]
|
|
25
25
|
classifiers = [
|
|
26
|
-
"Development Status ::
|
|
26
|
+
"Development Status :: 5 - Production/Stable",
|
|
27
27
|
"Intended Audience :: Developers",
|
|
28
28
|
"Natural Language :: English",
|
|
29
29
|
"Operating System :: OS Independent",
|
|
@@ -34,7 +34,9 @@ classifiers = [
|
|
|
34
34
|
"Programming Language :: Python :: 3.12",
|
|
35
35
|
"Programming Language :: Python :: 3.13",
|
|
36
36
|
"Programming Language :: Python :: Implementation :: CPython",
|
|
37
|
-
"Topic :: Database"
|
|
37
|
+
"Topic :: Database",
|
|
38
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
39
|
+
"Topic :: Software Development :: Libraries :: Python Modules"
|
|
38
40
|
]
|
|
39
41
|
dependencies = [
|
|
40
42
|
"oracledb",
|
|
@@ -45,6 +47,7 @@ dependencies = [
|
|
|
45
47
|
Homepage = "https://github.com/oracle/python-select-ai"
|
|
46
48
|
Repository = "https://github.com/oracle/python-select-ai"
|
|
47
49
|
Issues = "https://github.com/oracle/python-select-ai/issues"
|
|
50
|
+
Documentation = "https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/pysai/"
|
|
48
51
|
|
|
49
52
|
[tool.setuptools.packages.find]
|
|
50
53
|
where = ["src"]
|
|
@@ -22,7 +22,5 @@ profile = select_ai.Profile(
|
|
|
22
22
|
profile_name="oci_ai_profile",
|
|
23
23
|
)
|
|
24
24
|
print(profile.description)
|
|
25
|
-
explanation = profile.explain_sql(
|
|
26
|
-
prompt="How many promotions are there in the database?"
|
|
27
|
-
)
|
|
25
|
+
explanation = profile.explain_sql(prompt="How many promotions?")
|
|
28
26
|
print(explanation)
|
|
@@ -23,7 +23,5 @@ select_ai.connect(user=user, password=password, dsn=dsn)
|
|
|
23
23
|
profile = select_ai.Profile(
|
|
24
24
|
profile_name="oci_ai_profile",
|
|
25
25
|
)
|
|
26
|
-
narration = profile.narrate(
|
|
27
|
-
prompt="How many promotions are there in the database?"
|
|
28
|
-
)
|
|
26
|
+
narration = profile.narrate(prompt="How many promotions?")
|
|
29
27
|
print(narration)
|
|
@@ -21,8 +21,6 @@ dsn = os.getenv("SELECT_AI_DB_CONNECT_STRING")
|
|
|
21
21
|
|
|
22
22
|
select_ai.connect(user=user, password=password, dsn=dsn)
|
|
23
23
|
profile = select_ai.Profile(profile_name="oci_ai_profile")
|
|
24
|
-
df = profile.run_sql(
|
|
25
|
-
prompt="How many promotions are there in the database?"
|
|
26
|
-
)
|
|
24
|
+
df = profile.run_sql(prompt="How many promotions ?")
|
|
27
25
|
print(df.columns)
|
|
28
26
|
print(df)
|
|
@@ -21,7 +21,5 @@ dsn = os.getenv("SELECT_AI_DB_CONNECT_STRING")
|
|
|
21
21
|
|
|
22
22
|
select_ai.connect(user=user, password=password, dsn=dsn)
|
|
23
23
|
profile = select_ai.Profile(profile_name="oci_ai_profile")
|
|
24
|
-
sql = profile.show_sql(
|
|
25
|
-
prompt="How many promotions are there in the database?"
|
|
26
|
-
)
|
|
24
|
+
sql = profile.show_sql(prompt="How many promotions ?")
|
|
27
25
|
print(sql)
|
|
@@ -0,0 +1,123 @@
|
|
|
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 inspect
|
|
9
|
+
from collections.abc import Mapping, Sequence, Set
|
|
10
|
+
from functools import wraps
|
|
11
|
+
from typing import Any, get_args, get_origin, get_type_hints
|
|
12
|
+
|
|
13
|
+
NoneType = type(None)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _match(value, annot) -> bool:
|
|
17
|
+
"""Recursively validate value against a typing annotation."""
|
|
18
|
+
if annot is Any:
|
|
19
|
+
return True
|
|
20
|
+
|
|
21
|
+
origin = get_origin(annot)
|
|
22
|
+
args = get_args(annot)
|
|
23
|
+
|
|
24
|
+
# Handle Annotated[T, ...] → treat as T
|
|
25
|
+
if origin is getattr(__import__("typing"), "Annotated", None):
|
|
26
|
+
annot = args[0]
|
|
27
|
+
origin = get_origin(annot)
|
|
28
|
+
args = get_args(annot)
|
|
29
|
+
|
|
30
|
+
# Optional[T] is Union[T, NoneType]
|
|
31
|
+
if origin is getattr(__import__("typing"), "Union", None):
|
|
32
|
+
return any(_match(value, a) for a in args)
|
|
33
|
+
|
|
34
|
+
# Literal[…]
|
|
35
|
+
if origin is getattr(__import__("typing"), "Literal", None):
|
|
36
|
+
return any(value == lit for lit in args)
|
|
37
|
+
|
|
38
|
+
# Tuple cases
|
|
39
|
+
if origin is tuple:
|
|
40
|
+
if not isinstance(value, tuple):
|
|
41
|
+
return False
|
|
42
|
+
if len(args) == 2 and args[1] is Ellipsis:
|
|
43
|
+
# tuple[T, ...]
|
|
44
|
+
return all(_match(v, args[0]) for v in value)
|
|
45
|
+
if len(args) != len(value):
|
|
46
|
+
return False
|
|
47
|
+
return all(_match(v, a) for v, a in zip(value, args))
|
|
48
|
+
|
|
49
|
+
# Mappings (dict-like)
|
|
50
|
+
if origin in (dict, Mapping):
|
|
51
|
+
if not isinstance(value, Mapping):
|
|
52
|
+
return False
|
|
53
|
+
k_annot, v_annot = args if args else (Any, Any)
|
|
54
|
+
return all(
|
|
55
|
+
_match(k, k_annot) and _match(v, v_annot) for k, v in value.items()
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
# Sequences (list, Sequence) – but not str/bytes
|
|
59
|
+
if origin in (list, Sequence):
|
|
60
|
+
if isinstance(value, (str, bytes)):
|
|
61
|
+
return False
|
|
62
|
+
if not isinstance(value, Sequence):
|
|
63
|
+
return False
|
|
64
|
+
elem_annot = args[0] if args else Any
|
|
65
|
+
return all(_match(v, elem_annot) for v in value)
|
|
66
|
+
|
|
67
|
+
# Sets
|
|
68
|
+
if origin in (set, frozenset, Set):
|
|
69
|
+
if not isinstance(value, (set, frozenset)):
|
|
70
|
+
return False
|
|
71
|
+
elem_annot = args[0] if args else Any
|
|
72
|
+
return all(_match(v, elem_annot) for v in value)
|
|
73
|
+
|
|
74
|
+
# Fall back to normal isinstance for non-typing classes
|
|
75
|
+
if isinstance(annot, type):
|
|
76
|
+
return isinstance(value, annot)
|
|
77
|
+
|
|
78
|
+
# If annot is a typing alias like 'list' without args
|
|
79
|
+
if origin is not None:
|
|
80
|
+
# Treat bare containers as accepting anything inside
|
|
81
|
+
return isinstance(value, origin)
|
|
82
|
+
|
|
83
|
+
# Unknown/unsupported typing form: accept conservatively
|
|
84
|
+
return True
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def enforce_types(func):
|
|
88
|
+
# Resolve ForwardRefs using function globals (handles "User" as a string, etc.)
|
|
89
|
+
hints = get_type_hints(
|
|
90
|
+
func, globalns=func.__globals__, include_extras=True
|
|
91
|
+
)
|
|
92
|
+
sig = inspect.signature(func)
|
|
93
|
+
|
|
94
|
+
def _check(bound):
|
|
95
|
+
for name, val in bound.arguments.items():
|
|
96
|
+
if name in hints:
|
|
97
|
+
annot = hints[name]
|
|
98
|
+
if not _match(val, annot):
|
|
99
|
+
raise TypeError(
|
|
100
|
+
f"Argument '{name}' failed type check: expected {annot!r}, "
|
|
101
|
+
f"got {type(val).__name__} -> {val!r}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if inspect.iscoroutinefunction(func):
|
|
105
|
+
|
|
106
|
+
@wraps(func)
|
|
107
|
+
async def aw(*args, **kwargs):
|
|
108
|
+
bound = sig.bind(*args, **kwargs)
|
|
109
|
+
bound.apply_defaults()
|
|
110
|
+
_check(bound)
|
|
111
|
+
return await func(*args, **kwargs)
|
|
112
|
+
|
|
113
|
+
return aw
|
|
114
|
+
else:
|
|
115
|
+
|
|
116
|
+
@wraps(func)
|
|
117
|
+
def w(*args, **kwargs):
|
|
118
|
+
bound = sig.bind(*args, **kwargs)
|
|
119
|
+
bound.apply_defaults()
|
|
120
|
+
_check(bound)
|
|
121
|
+
return func(*args, **kwargs)
|
|
122
|
+
|
|
123
|
+
return w
|
|
@@ -344,8 +344,15 @@ class AsyncProfile(BaseProfile):
|
|
|
344
344
|
keyword_parameters=parameters,
|
|
345
345
|
)
|
|
346
346
|
if data is not None:
|
|
347
|
-
|
|
348
|
-
|
|
347
|
+
result = await data.read()
|
|
348
|
+
else:
|
|
349
|
+
result = None
|
|
350
|
+
if action == Action.RUNSQL and result:
|
|
351
|
+
return pandas.DataFrame(json.loads(result))
|
|
352
|
+
elif action == Action.RUNSQL:
|
|
353
|
+
return pandas.DataFrame()
|
|
354
|
+
else:
|
|
355
|
+
return result
|
|
349
356
|
|
|
350
357
|
async def chat(self, prompt, params: Mapping = None) -> str:
|
|
351
358
|
"""Asynchronously chat with the LLM
|
|
@@ -411,8 +418,7 @@ class AsyncProfile(BaseProfile):
|
|
|
411
418
|
:param params: Parameters to include in the LLM request
|
|
412
419
|
:return: pandas.DataFrame
|
|
413
420
|
"""
|
|
414
|
-
|
|
415
|
-
return pandas.DataFrame(json.loads(data))
|
|
421
|
+
return await self.generate(prompt, action=Action.RUNSQL, params=params)
|
|
416
422
|
|
|
417
423
|
async def show_sql(self, prompt, params: Mapping = None):
|
|
418
424
|
"""Show the generated SQL
|
|
@@ -451,8 +457,10 @@ class AsyncProfile(BaseProfile):
|
|
|
451
457
|
raise ValueError("'synthetic_data_attributes' cannot be None")
|
|
452
458
|
|
|
453
459
|
if not isinstance(synthetic_data_attributes, SyntheticDataAttributes):
|
|
454
|
-
raise TypeError(
|
|
455
|
-
|
|
460
|
+
raise TypeError(
|
|
461
|
+
"'synthetic_data_attributes' must be an object "
|
|
462
|
+
"of type select_ai.SyntheticDataAttributes"
|
|
463
|
+
)
|
|
456
464
|
|
|
457
465
|
keyword_parameters = synthetic_data_attributes.prepare()
|
|
458
466
|
keyword_parameters["profile_name"] = self.profile_name
|
|
@@ -73,6 +73,7 @@ class ProfileAttributes(SelectAIDataClass):
|
|
|
73
73
|
vector_index_name: Optional[str] = None
|
|
74
74
|
|
|
75
75
|
def __post_init__(self):
|
|
76
|
+
super().__post_init__()
|
|
76
77
|
if self.provider and not isinstance(self.provider, Provider):
|
|
77
78
|
raise ValueError(
|
|
78
79
|
f"'provider' must be an object of " f"type select_ai.Provider"
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import json
|
|
9
9
|
from contextlib import contextmanager
|
|
10
10
|
from dataclasses import replace as dataclass_replace
|
|
11
|
-
from typing import Iterator, Mapping, Optional, Union
|
|
11
|
+
from typing import Generator, Iterator, Mapping, Optional, Union
|
|
12
12
|
|
|
13
13
|
import oracledb
|
|
14
14
|
import pandas
|
|
@@ -258,7 +258,9 @@ class Profile(BaseProfile):
|
|
|
258
258
|
raise ProfileNotFoundError(profile_name=profile_name)
|
|
259
259
|
|
|
260
260
|
@classmethod
|
|
261
|
-
def list(
|
|
261
|
+
def list(
|
|
262
|
+
cls, profile_name_pattern: str = ".*"
|
|
263
|
+
) -> Generator["Profile", None, None]:
|
|
262
264
|
"""List AI Profiles saved in the database.
|
|
263
265
|
|
|
264
266
|
:param str profile_name_pattern: Regular expressions can be used
|
|
@@ -314,8 +316,15 @@ class Profile(BaseProfile):
|
|
|
314
316
|
keyword_parameters=parameters,
|
|
315
317
|
)
|
|
316
318
|
if data is not None:
|
|
317
|
-
|
|
318
|
-
|
|
319
|
+
result = data.read()
|
|
320
|
+
else:
|
|
321
|
+
result = None
|
|
322
|
+
if action == Action.RUNSQL and result:
|
|
323
|
+
return pandas.DataFrame(json.loads(result))
|
|
324
|
+
elif action == Action.RUNSQL:
|
|
325
|
+
return pandas.DataFrame()
|
|
326
|
+
else:
|
|
327
|
+
return result
|
|
319
328
|
|
|
320
329
|
def chat(self, prompt: str, params: Mapping = None) -> str:
|
|
321
330
|
"""Chat with the LLM
|
|
@@ -375,10 +384,7 @@ class Profile(BaseProfile):
|
|
|
375
384
|
:param params: Parameters to include in the LLM request
|
|
376
385
|
:return: pandas.DataFrame
|
|
377
386
|
"""
|
|
378
|
-
|
|
379
|
-
self.generate(prompt, action=Action.RUNSQL, params=params)
|
|
380
|
-
)
|
|
381
|
-
return pandas.DataFrame(data)
|
|
387
|
+
return self.generate(prompt, action=Action.RUNSQL, params=params)
|
|
382
388
|
|
|
383
389
|
def show_sql(self, prompt: str, params: Mapping = None) -> str:
|
|
384
390
|
"""Show the generated SQL
|
|
@@ -410,11 +416,15 @@ class Profile(BaseProfile):
|
|
|
410
416
|
|
|
411
417
|
"""
|
|
412
418
|
if synthetic_data_attributes is None:
|
|
413
|
-
raise ValueError(
|
|
419
|
+
raise ValueError(
|
|
420
|
+
"Param 'synthetic_data_attributes' cannot be None"
|
|
421
|
+
)
|
|
414
422
|
|
|
415
423
|
if not isinstance(synthetic_data_attributes, SyntheticDataAttributes):
|
|
416
|
-
raise TypeError(
|
|
417
|
-
|
|
424
|
+
raise TypeError(
|
|
425
|
+
"'synthetic_data_attributes' must be an object "
|
|
426
|
+
"of type select_ai.SyntheticDataAttributes"
|
|
427
|
+
)
|
|
418
428
|
|
|
419
429
|
keyword_parameters = synthetic_data_attributes.prepare()
|
|
420
430
|
keyword_parameters["profile_name"] = self.profile_name
|
|
@@ -9,6 +9,7 @@ from dataclasses import dataclass
|
|
|
9
9
|
from typing import List, Optional, Union
|
|
10
10
|
|
|
11
11
|
from select_ai._abc import SelectAIDataClass
|
|
12
|
+
from select_ai._validations import enforce_types
|
|
12
13
|
|
|
13
14
|
from .db import async_cursor, cursor
|
|
14
15
|
from .sql import (
|
|
@@ -194,6 +195,7 @@ class AnthropicProvider(Provider):
|
|
|
194
195
|
provider_endpoint = "api.anthropic.com"
|
|
195
196
|
|
|
196
197
|
|
|
198
|
+
@enforce_types
|
|
197
199
|
async def async_enable_provider(
|
|
198
200
|
users: Union[str, List[str]], provider_endpoint: str = None
|
|
199
201
|
):
|
|
@@ -210,7 +212,7 @@ async def async_enable_provider(
|
|
|
210
212
|
|
|
211
213
|
async with async_cursor() as cr:
|
|
212
214
|
for user in users:
|
|
213
|
-
await cr.execute(GRANT_PRIVILEGES_TO_USER.format(user))
|
|
215
|
+
await cr.execute(GRANT_PRIVILEGES_TO_USER.format(user.strip()))
|
|
214
216
|
if provider_endpoint:
|
|
215
217
|
await cr.execute(
|
|
216
218
|
ENABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
@@ -219,6 +221,7 @@ async def async_enable_provider(
|
|
|
219
221
|
)
|
|
220
222
|
|
|
221
223
|
|
|
224
|
+
@enforce_types
|
|
222
225
|
async def async_disable_provider(
|
|
223
226
|
users: Union[str, List[str]], provider_endpoint: str = None
|
|
224
227
|
):
|
|
@@ -234,7 +237,7 @@ async def async_disable_provider(
|
|
|
234
237
|
|
|
235
238
|
async with async_cursor() as cr:
|
|
236
239
|
for user in users:
|
|
237
|
-
await cr.execute(REVOKE_PRIVILEGES_FROM_USER.format(user))
|
|
240
|
+
await cr.execute(REVOKE_PRIVILEGES_FROM_USER.format(user.strip()))
|
|
238
241
|
if provider_endpoint:
|
|
239
242
|
await cr.execute(
|
|
240
243
|
DISABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
@@ -243,6 +246,7 @@ async def async_disable_provider(
|
|
|
243
246
|
)
|
|
244
247
|
|
|
245
248
|
|
|
249
|
+
@enforce_types
|
|
246
250
|
def enable_provider(
|
|
247
251
|
users: Union[str, List[str]], provider_endpoint: str = None
|
|
248
252
|
):
|
|
@@ -256,7 +260,7 @@ def enable_provider(
|
|
|
256
260
|
|
|
257
261
|
with cursor() as cr:
|
|
258
262
|
for user in users:
|
|
259
|
-
cr.execute(GRANT_PRIVILEGES_TO_USER.format(user))
|
|
263
|
+
cr.execute(GRANT_PRIVILEGES_TO_USER.format(user.strip()))
|
|
260
264
|
if provider_endpoint:
|
|
261
265
|
cr.execute(
|
|
262
266
|
ENABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
@@ -265,6 +269,7 @@ def enable_provider(
|
|
|
265
269
|
)
|
|
266
270
|
|
|
267
271
|
|
|
272
|
+
@enforce_types
|
|
268
273
|
def disable_provider(
|
|
269
274
|
users: Union[str, List[str]], provider_endpoint: str = None
|
|
270
275
|
):
|
|
@@ -279,7 +284,7 @@ def disable_provider(
|
|
|
279
284
|
|
|
280
285
|
with cursor() as cr:
|
|
281
286
|
for user in users:
|
|
282
|
-
cr.execute(REVOKE_PRIVILEGES_FROM_USER.format(user))
|
|
287
|
+
cr.execute(REVOKE_PRIVILEGES_FROM_USER.format(user.strip()))
|
|
283
288
|
if provider_endpoint:
|
|
284
289
|
cr.execute(
|
|
285
290
|
DISABLE_AI_PROFILE_DOMAIN_FOR_USER,
|
|
@@ -62,8 +62,9 @@ class SyntheticDataAttributes(SelectAIDataClass):
|
|
|
62
62
|
|
|
63
63
|
def __post_init__(self):
|
|
64
64
|
if self.params and not isinstance(self.params, SyntheticDataParams):
|
|
65
|
-
raise TypeError(
|
|
66
|
-
|
|
65
|
+
raise TypeError(
|
|
66
|
+
"'params' must be an object of" " type SyntheticDataParams'"
|
|
67
|
+
)
|
|
67
68
|
|
|
68
69
|
def dict(self, exclude_null=True):
|
|
69
70
|
attributes = {}
|
|
@@ -119,6 +119,16 @@ class _BaseVectorIndex(ABC):
|
|
|
119
119
|
attributes: Optional[VectorIndexAttributes] = None,
|
|
120
120
|
):
|
|
121
121
|
"""Initialize a Vector Index"""
|
|
122
|
+
if attributes and not isinstance(attributes, VectorIndexAttributes):
|
|
123
|
+
raise TypeError(
|
|
124
|
+
"'attributes' must be an object of type "
|
|
125
|
+
"select_ai.VectorIndexAttributes"
|
|
126
|
+
)
|
|
127
|
+
if profile and not isinstance(profile, BaseProfile):
|
|
128
|
+
raise TypeError(
|
|
129
|
+
"'profile' must be an object of type "
|
|
130
|
+
"select_ai.Profile or select_ai.AsyncProfile"
|
|
131
|
+
)
|
|
122
132
|
self.profile = profile
|
|
123
133
|
self.index_name = index_name
|
|
124
134
|
self.attributes = attributes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: select_ai
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.0rc1
|
|
4
4
|
Summary: Select AI for Python
|
|
5
5
|
Author-email: Abhishek Singh <abhishek.o.singh@oracle.com>
|
|
6
6
|
Maintainer-email: Abhishek Singh <abhishek.o.singh@oracle.com>
|
|
@@ -8,8 +8,9 @@ License-Expression: UPL-1.0
|
|
|
8
8
|
Project-URL: Homepage, https://github.com/oracle/python-select-ai
|
|
9
9
|
Project-URL: Repository, https://github.com/oracle/python-select-ai
|
|
10
10
|
Project-URL: Issues, https://github.com/oracle/python-select-ai/issues
|
|
11
|
+
Project-URL: Documentation, https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/pysai/
|
|
11
12
|
Keywords: oracle,select-ai,adbs,autonomous database serverless
|
|
12
|
-
Classifier: Development Status ::
|
|
13
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
14
|
Classifier: Intended Audience :: Developers
|
|
14
15
|
Classifier: Natural Language :: English
|
|
15
16
|
Classifier: Operating System :: OS Independent
|
|
@@ -21,6 +22,8 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
21
22
|
Classifier: Programming Language :: Python :: 3.13
|
|
22
23
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
23
24
|
Classifier: Topic :: Database
|
|
25
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
26
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
27
|
Requires-Python: >=3.9
|
|
25
28
|
Description-Content-Type: text/markdown
|
|
26
29
|
License-File: LICENSE.txt
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/async/profile_gen_multi_table_synthetic_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_gen_multi_table_synthetic_data.py
RENAMED
|
File without changes
|
{select_ai-1.0.0.dev10 → select_ai-1.0.0rc1}/samples/profile_gen_single_table_synthetic_data.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|