select-ai 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of select-ai might be problematic. Click here for more details.

select_ai/db.py ADDED
@@ -0,0 +1,186 @@
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 contextlib
9
+ import os
10
+ from threading import get_ident
11
+ from typing import Dict, Hashable
12
+
13
+ import oracledb
14
+
15
+ from select_ai.errors import DatabaseNotConnectedError
16
+
17
+ __conn__: Dict[Hashable, oracledb.Connection] = {}
18
+ __async_conn__: Dict[Hashable, oracledb.AsyncConnection] = {}
19
+
20
+ __all__ = [
21
+ "connect",
22
+ "async_connect",
23
+ "is_connected",
24
+ "async_is_connected",
25
+ "get_connection",
26
+ "async_get_connection",
27
+ "cursor",
28
+ "async_cursor",
29
+ "disconnect",
30
+ "async_disconnect",
31
+ ]
32
+
33
+
34
+ def connect(user: str, password: str, dsn: str, *args, **kwargs):
35
+ """Creates an oracledb.Connection object
36
+ and saves it global dictionary __conn__
37
+ The connection object is thread local meaning
38
+ in a multithreaded application, individual
39
+ threads cannot see each other's connection
40
+ object
41
+ """
42
+ conn = oracledb.connect(
43
+ user=user,
44
+ password=password,
45
+ dsn=dsn,
46
+ connection_id_prefix="python-select-ai",
47
+ *args,
48
+ **kwargs,
49
+ )
50
+ _set_connection(conn=conn)
51
+
52
+
53
+ async def async_connect(user: str, password: str, dsn: str, *args, **kwargs):
54
+ """Creates an oracledb.AsyncConnection object
55
+ and saves it global dictionary __async_conn__
56
+ The connection object is thread local meaning
57
+ in a multithreaded application, individual
58
+ threads cannot see each other's connection
59
+ object
60
+ """
61
+ async_conn = await oracledb.connect_async(
62
+ user=user, password=password, dsn=dsn, *args, **kwargs
63
+ )
64
+ _set_connection(async_conn=async_conn)
65
+
66
+
67
+ def is_connected() -> bool:
68
+ """Checks if database connection is open and healthy"""
69
+ global __conn__
70
+ key = (os.getpid(), get_ident())
71
+ conn = __conn__.get(key)
72
+ if conn is None:
73
+ return False
74
+ try:
75
+ return conn.ping() is None
76
+ except (oracledb.DatabaseError, oracledb.InterfaceError):
77
+ return False
78
+
79
+
80
+ async def async_is_connected() -> bool:
81
+ """Asynchronously checks if database connection is open and healthy"""
82
+
83
+ global __async_conn__
84
+ key = (os.getpid(), get_ident())
85
+ conn = __async_conn__.get(key)
86
+ if conn is None:
87
+ return False
88
+ try:
89
+ return await conn.ping() is None
90
+ except (oracledb.DatabaseError, oracledb.InterfaceError):
91
+ return False
92
+
93
+
94
+ def _set_connection(
95
+ conn: oracledb.Connection = None,
96
+ async_conn: oracledb.AsyncConnection = None,
97
+ ):
98
+ """Set existing connection for select_ai Python API to reuse
99
+
100
+ :param conn: python-oracledb Connection object
101
+ :param async_conn: python-oracledb
102
+ :return:
103
+ """
104
+ key = (os.getpid(), get_ident())
105
+ if conn:
106
+ global __conn__
107
+ __conn__[key] = conn
108
+ if async_conn:
109
+ global __async_conn__
110
+ __async_conn__[key] = async_conn
111
+
112
+
113
+ def get_connection() -> oracledb.Connection:
114
+ """Returns the connection object if connection is healthy"""
115
+ if not is_connected():
116
+ raise DatabaseNotConnectedError()
117
+ global __conn__
118
+ key = (os.getpid(), get_ident())
119
+ return __conn__[key]
120
+
121
+
122
+ async def async_get_connection() -> oracledb.AsyncConnection:
123
+ """Returns the AsyncConnection object if connection is healthy"""
124
+ if not await async_is_connected():
125
+ raise DatabaseNotConnectedError()
126
+ global __async_conn__
127
+ key = (os.getpid(), get_ident())
128
+ return __async_conn__[key]
129
+
130
+
131
+ @contextlib.contextmanager
132
+ def cursor():
133
+ """
134
+ Creates a context manager for database cursor
135
+
136
+ Typical usage:
137
+
138
+ with select_ai.cursor() as cr:
139
+ cr.execute(<QUERY>)
140
+
141
+ This ensures that the cursor is closed regardless
142
+ of whether an exception occurred
143
+
144
+ """
145
+ cr = get_connection().cursor()
146
+ try:
147
+ yield cr
148
+ finally:
149
+ cr.close()
150
+
151
+
152
+ @contextlib.asynccontextmanager
153
+ async def async_cursor():
154
+ """
155
+ Creates an async context manager for database cursor
156
+
157
+ Typical usage:
158
+
159
+ async with select_ai.cursor() as cr:
160
+ await cr.execute(<QUERY>)
161
+ :return:
162
+ """
163
+ conn = await async_get_connection()
164
+ cr = conn.cursor()
165
+ try:
166
+ yield cr
167
+ finally:
168
+ cr.close()
169
+
170
+
171
+ def disconnect():
172
+ try:
173
+ conn = get_connection()
174
+ except DatabaseNotConnectedError:
175
+ pass
176
+ else:
177
+ conn.close()
178
+
179
+
180
+ async def async_disconnect():
181
+ try:
182
+ conn = await async_get_connection()
183
+ except DatabaseNotConnectedError:
184
+ pass
185
+ else:
186
+ await conn.close()
select_ai/errors.py ADDED
@@ -0,0 +1,73 @@
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
+
9
+ class SelectAIError(Exception):
10
+ """Base class for any SelectAIErrors"""
11
+
12
+ pass
13
+
14
+
15
+ class DatabaseNotConnectedError(SelectAIError):
16
+ """Raised when a database is not connected"""
17
+
18
+ def __str__(self):
19
+ return (
20
+ "Not connected to the Database. "
21
+ "Use select_ai.connect() or select_ai.async_connect() "
22
+ "to establish connection"
23
+ )
24
+
25
+
26
+ class ConversationNotFoundError(SelectAIError):
27
+ """Conversation not found in the database"""
28
+
29
+ def __init__(self, conversation_id: str):
30
+ self.conversation_id = conversation_id
31
+
32
+ def __str__(self):
33
+ return f"Conversation with id {self.conversation_id} not found"
34
+
35
+
36
+ class ProfileNotFoundError(SelectAIError):
37
+ """Profile not found in the database"""
38
+
39
+ def __init__(self, profile_name: str):
40
+ self.profile_name = profile_name
41
+
42
+ def __str__(self):
43
+ return f"Profile {self.profile_name} not found"
44
+
45
+
46
+ class ProfileExistsError(SelectAIError):
47
+ """Profile already exists in the database"""
48
+
49
+ def __init__(self, profile_name: str):
50
+ self.profile_name = profile_name
51
+
52
+ def __str__(self):
53
+ return (
54
+ f"Profile {self.profile_name} already exists. "
55
+ f"Use either replace=True or merge=True"
56
+ )
57
+
58
+
59
+ class VectorIndexNotFoundError(SelectAIError):
60
+ """VectorIndex not found in the database"""
61
+
62
+ def __init__(self, index_name: str, profile_name: str = None):
63
+ self.index_name = index_name
64
+ self.profile_name = profile_name
65
+
66
+ def __str__(self):
67
+ if self.profile_name:
68
+ return (
69
+ f"VectorIndex {self.index_name} "
70
+ f"not found for profile {self.profile_name}"
71
+ )
72
+ else:
73
+ return f"VectorIndex {self.index_name} not found"