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/profile.py ADDED
@@ -0,0 +1,397 @@
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 contextlib import contextmanager
10
+ from dataclasses import replace as dataclass_replace
11
+ from typing import Iterator, Mapping, Optional, Union
12
+
13
+ import oracledb
14
+ import pandas
15
+
16
+ from select_ai import Conversation
17
+ from select_ai.action import Action
18
+ from select_ai.base_profile import BaseProfile, ProfileAttributes
19
+ from select_ai.db import cursor
20
+ from select_ai.errors import ProfileNotFoundError
21
+ from select_ai.provider import Provider
22
+ from select_ai.sql import (
23
+ GET_USER_AI_PROFILE_ATTRIBUTES,
24
+ LIST_USER_AI_PROFILES,
25
+ )
26
+ from select_ai.synthetic_data import SyntheticDataAttributes
27
+
28
+
29
+ class Profile(BaseProfile):
30
+ """Profile class represents an AI Profile. It defines
31
+ attributes and methods to interact with the underlying
32
+ AI Provider. All methods in this class are synchronous
33
+ or blocking
34
+ """
35
+
36
+ def __init__(self, *args, **kwargs):
37
+ super().__init__(*args, **kwargs)
38
+ self._init_profile()
39
+
40
+ def _init_profile(self) -> None:
41
+ """Initializes AI profile based on the passed attributes
42
+
43
+ :return: None
44
+ :raises: oracledb.DatabaseError
45
+ """
46
+ if self.profile_name is not None:
47
+ profile_exists = False
48
+ try:
49
+ saved_attributes = self._get_attributes(
50
+ profile_name=self.profile_name
51
+ )
52
+ profile_exists = True
53
+ except ProfileNotFoundError:
54
+ if self.attributes is None:
55
+ raise
56
+ else:
57
+ if self.attributes is None:
58
+ self.attributes = saved_attributes
59
+ if self.merge:
60
+ self.replace = True
61
+ if self.attributes is not None:
62
+ self.attributes = dataclass_replace(
63
+ saved_attributes,
64
+ **self.attributes.dict(exclude_null=True),
65
+ )
66
+ if self.replace or not profile_exists:
67
+ self.create(replace=self.replace)
68
+
69
+ @staticmethod
70
+ def _get_attributes(profile_name) -> ProfileAttributes:
71
+ """Get AI profile attributes from the Database
72
+
73
+ :param str profile_name: Name of the profile
74
+ :return: select_ai.ProfileAttributes
75
+ :raises: ProfileNotFoundError
76
+ """
77
+ with cursor() as cr:
78
+ cr.execute(
79
+ GET_USER_AI_PROFILE_ATTRIBUTES,
80
+ profile_name=profile_name.upper(),
81
+ )
82
+ attributes = cr.fetchall()
83
+ if attributes:
84
+ return ProfileAttributes.create(**dict(attributes))
85
+ else:
86
+ raise ProfileNotFoundError(profile_name=profile_name)
87
+
88
+ def get_attributes(self) -> ProfileAttributes:
89
+ """Get AI profile attributes from the Database
90
+
91
+ :return: select_ai.ProfileAttributes
92
+ """
93
+ return self._get_attributes(profile_name=self.profile_name)
94
+
95
+ def _set_attribute(
96
+ self,
97
+ attribute_name: str,
98
+ attribute_value: Union[bool, str, int, float],
99
+ ):
100
+ parameters = {
101
+ "profile_name": self.profile_name,
102
+ "attribute_name": attribute_name,
103
+ "attribute_value": attribute_value,
104
+ }
105
+ with cursor() as cr:
106
+ cr.callproc(
107
+ "DBMS_CLOUD_AI.SET_ATTRIBUTE", keyword_parameters=parameters
108
+ )
109
+
110
+ def set_attribute(
111
+ self,
112
+ attribute_name: str,
113
+ attribute_value: Union[bool, str, int, float, Provider],
114
+ ):
115
+ """Updates AI profile attribute on the Python object and also
116
+ saves it in the database
117
+
118
+ :param str attribute_name: Name of the AI profile attribute
119
+ :param Union[bool, str, int, float, Provider] attribute_value: Value of
120
+ the profile attribute
121
+ :return: None
122
+
123
+ """
124
+ self.attributes.set_attribute(attribute_name, attribute_value)
125
+ if isinstance(attribute_value, Provider):
126
+ for k, v in attribute_value.dict().items():
127
+ self._set_attribute(k, v)
128
+ else:
129
+ self._set_attribute(attribute_name, attribute_value)
130
+
131
+ def set_attributes(self, attributes: ProfileAttributes):
132
+ """Updates AI profile attributes on the Python object and also
133
+ saves it in the database
134
+
135
+ :param ProviderAttributes attributes: Object specifying AI profile
136
+ attributes
137
+ :return: None
138
+ """
139
+ self.attributes = attributes
140
+ parameters = {
141
+ "profile_name": self.profile_name,
142
+ "attributes": self.attributes.json(),
143
+ }
144
+ with cursor() as cr:
145
+ cr.callproc(
146
+ "DBMS_CLOUD_AI.SET_ATTRIBUTES", keyword_parameters=parameters
147
+ )
148
+
149
+ def create(self, replace: Optional[int] = False) -> None:
150
+ """Create an AI Profile in the Database
151
+
152
+ :param bool replace: Set True to replace else False
153
+ :return: None
154
+ :raises: oracledb.DatabaseError
155
+ """
156
+
157
+ parameters = {
158
+ "profile_name": self.profile_name,
159
+ "attributes": self.attributes.json(),
160
+ }
161
+ if self.description:
162
+ parameters["description"] = self.description
163
+
164
+ with cursor() as cr:
165
+ try:
166
+ cr.callproc(
167
+ "DBMS_CLOUD_AI.CREATE_PROFILE",
168
+ keyword_parameters=parameters,
169
+ )
170
+ except oracledb.DatabaseError as e:
171
+ (error,) = e.args
172
+ # If already exists and replace is True then drop and recreate
173
+ if "already exists" in error.message.lower() and replace:
174
+ self.delete(force=True)
175
+ cr.callproc(
176
+ "DBMS_CLOUD_AI.CREATE_PROFILE",
177
+ keyword_parameters=parameters,
178
+ )
179
+ else:
180
+ raise
181
+
182
+ def delete(self, force=False) -> None:
183
+ """Deletes an AI profile from the database
184
+
185
+ :param bool force: Ignores errors if AI profile does not exist.
186
+ :return: None
187
+ :raises: oracledb.DatabaseError
188
+ """
189
+ with cursor() as cr:
190
+ cr.callproc(
191
+ "DBMS_CLOUD_AI.DROP_PROFILE",
192
+ keyword_parameters={
193
+ "profile_name": self.profile_name,
194
+ "force": force,
195
+ },
196
+ )
197
+
198
+ @classmethod
199
+ def _from_db(cls, profile_name: str) -> "Profile":
200
+ """Create a Profile object from attributes saved in the database
201
+
202
+ :param str profile_name:
203
+ :return: select_ai.Profile
204
+ :raises: ProfileNotFoundError
205
+ """
206
+ with cursor() as cr:
207
+ cr.execute(
208
+ GET_USER_AI_PROFILE_ATTRIBUTES, profile_name=profile_name
209
+ )
210
+ attributes = cr.fetchall()
211
+ if attributes:
212
+ attributes = ProfileAttributes.create(**dict(attributes))
213
+ return cls(profile_name=profile_name, attributes=attributes)
214
+ else:
215
+ raise ProfileNotFoundError(profile_name=profile_name)
216
+
217
+ @classmethod
218
+ def list(cls, profile_name_pattern: str) -> Iterator["Profile"]:
219
+ """List AI Profiles saved in the database.
220
+
221
+ :param str profile_name_pattern: Regular expressions can be used
222
+ to specify a pattern. Function REGEXP_LIKE is used to perform the
223
+ match
224
+
225
+ :return: Iterator[Profile]
226
+ """
227
+ with cursor() as cr:
228
+ cr.execute(
229
+ LIST_USER_AI_PROFILES,
230
+ profile_name_pattern=profile_name_pattern,
231
+ )
232
+ for row in cr.fetchall():
233
+ profile_name = row[0]
234
+ description = row[1]
235
+ attributes = cls._get_attributes(profile_name=profile_name)
236
+ yield cls(
237
+ profile_name=profile_name,
238
+ description=description,
239
+ attributes=attributes,
240
+ )
241
+
242
+ def generate(
243
+ self,
244
+ prompt: str,
245
+ action: Optional[Action] = Action.RUNSQL,
246
+ params: Mapping = None,
247
+ ) -> Union[pandas.DataFrame, str, None]:
248
+ """Perform AI translation using this profile
249
+
250
+ :param str prompt: Natural language prompt to translate
251
+ :param select_ai.profile.Action action:
252
+ :param params: Parameters to include in the LLM request. For e.g.
253
+ conversation_id for context-aware chats
254
+ :return: Union[pandas.DataFrame, str]
255
+ """
256
+ parameters = {
257
+ "prompt": prompt,
258
+ "action": action,
259
+ "profile_name": self.profile_name,
260
+ # "attributes": self.attributes.json(),
261
+ }
262
+ if params:
263
+ parameters["params"] = json.dumps(params)
264
+ with cursor() as cr:
265
+ data = cr.callfunc(
266
+ "DBMS_CLOUD_AI.GENERATE",
267
+ oracledb.DB_TYPE_CLOB,
268
+ keyword_parameters=parameters,
269
+ )
270
+ if data is not None:
271
+ return data.read()
272
+ return None
273
+
274
+ def chat(self, prompt: str, params: Mapping = None) -> str:
275
+ """Chat with the LLM
276
+
277
+ :param str prompt: Natural language prompt
278
+ :param params: Parameters to include in the LLM request
279
+ :return: str
280
+ """
281
+ return self.generate(prompt, action=Action.CHAT, params=params)
282
+
283
+ @contextmanager
284
+ def chat_session(self, conversation: Conversation, delete: bool = False):
285
+ """Starts a new chat session for context-aware conversations
286
+
287
+ :param Conversation conversation: Conversation object to use for this
288
+ chat session
289
+ :param bool delete: Delete conversation after session ends
290
+
291
+ :return:
292
+ """
293
+ try:
294
+ if (
295
+ conversation.conversation_id is None
296
+ and conversation.attributes is not None
297
+ ):
298
+ conversation.create()
299
+ params = {"conversation_id": conversation.conversation_id}
300
+ session = Session(profile=self, params=params)
301
+ yield session
302
+ finally:
303
+ if delete:
304
+ conversation.delete()
305
+
306
+ def narrate(self, prompt: str, params: Mapping = None) -> str:
307
+ """Narrate the result of the SQL
308
+
309
+ :param str prompt: Natural language prompt
310
+ :param params: Parameters to include in the LLM request
311
+ :return: str
312
+ """
313
+ return self.generate(prompt, action=Action.NARRATE, params=params)
314
+
315
+ def explain_sql(self, prompt: str, params: Mapping = None) -> str:
316
+ """Explain the generated SQL
317
+
318
+ :param str prompt: Natural language prompt
319
+ :param params: Parameters to include in the LLM request
320
+ :return: str
321
+ """
322
+ return self.generate(prompt, action=Action.EXPLAINSQL, params=params)
323
+
324
+ def run_sql(self, prompt: str, params: Mapping = None) -> pandas.DataFrame:
325
+ """Run the generate SQL statement and return a pandas Dataframe built
326
+ using the result set
327
+
328
+ :param str prompt: Natural language prompt
329
+ :param params: Parameters to include in the LLM request
330
+ :return: pandas.DataFrame
331
+ """
332
+ data = json.loads(
333
+ self.generate(prompt, action=Action.RUNSQL, params=params)
334
+ )
335
+ return pandas.DataFrame(data)
336
+
337
+ def show_sql(self, prompt: str, params: Mapping = None) -> str:
338
+ """Show the generated SQL
339
+
340
+ :param str prompt: Natural language prompt
341
+ :param params: Parameters to include in the LLM request
342
+ :return: str
343
+ """
344
+ return self.generate(prompt, action=Action.SHOWSQL, params=params)
345
+
346
+ def show_prompt(self, prompt: str, params: Mapping = None) -> str:
347
+ """Show the prompt sent to LLM
348
+
349
+ :param str prompt: Natural language prompt
350
+ :param params: Parameters to include in the LLM request
351
+ :return: str
352
+ """
353
+ return self.generate(prompt, action=Action.SHOWPROMPT, params=params)
354
+
355
+ def generate_synthetic_data(
356
+ self, synthetic_data_attributes: SyntheticDataAttributes
357
+ ):
358
+ """Generate synthetic data for a single table, multiple tables or a
359
+ full schema.
360
+
361
+ :param select_ai.SyntheticDataAttributes synthetic_data_attributes:
362
+ :return: None
363
+ :raises: oracledb.DatabaseError
364
+
365
+ """
366
+ keyword_parameters = synthetic_data_attributes.prepare()
367
+ keyword_parameters["profile_name"] = self.profile_name
368
+ with cursor() as cr:
369
+ cr.callproc(
370
+ "DBMS_CLOUD_AI.GENERATE_SYNTHETIC_DATA",
371
+ keyword_parameters=keyword_parameters,
372
+ )
373
+
374
+
375
+ class Session:
376
+ """Session lets you persist request parameters across DBMS_CLOUD_AI
377
+ requests. This is useful in context-aware conversations
378
+ """
379
+
380
+ def __init__(self, profile: Profile, params: Mapping):
381
+ """
382
+
383
+ :param profile: An AI Profile to use in this session
384
+ :param params: Parameters to be persisted across requests
385
+ """
386
+ self.params = params
387
+ self.profile = profile
388
+
389
+ def chat(self, prompt: str):
390
+ # params = {"conversation_id": self.conversation_id}
391
+ return self.profile.chat(prompt=prompt, params=self.params)
392
+
393
+ def __enter__(self):
394
+ return self
395
+
396
+ def __exit__(self, exc_type, exc_val, exc_tb):
397
+ pass
select_ai/provider.py ADDED
@@ -0,0 +1,187 @@
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 dataclasses import dataclass, fields
9
+ from typing import Optional
10
+
11
+ from select_ai._abc import SelectAIDataClass
12
+
13
+ OPENAI = "openai"
14
+ COHERE = "cohere"
15
+ AZURE = "azure"
16
+ OCI = "oci"
17
+ GOOGLE = "google"
18
+ ANTHROPIC = "anthropic"
19
+ HUGGINGFACE = "huggingface"
20
+ AWS = "aws"
21
+
22
+
23
+ @dataclass
24
+ class Provider(SelectAIDataClass):
25
+ """
26
+ Base class for AI Provider
27
+
28
+ To create an object of Provider class, use any one of the concrete AI
29
+ provider implementations
30
+
31
+ :param str embedding_model: The embedding model, also known as a
32
+ transformer. Depending on the AI provider, the supported embedding models
33
+ vary
34
+ :param str model: The name of the LLM being used to generate
35
+ responses
36
+ :param str provider_name: The name of the provider being used
37
+ :param str provider_endpoint: Endpoint URL of the AI provider being used
38
+ :param str region: The cloud region of the Gen AI cluster
39
+
40
+ """
41
+
42
+ embedding_model: Optional[str] = None
43
+ model: Optional[str] = None
44
+ provider_name: Optional[str] = None
45
+ provider_endpoint: Optional[str] = None
46
+ region: Optional[str] = None
47
+
48
+ @classmethod
49
+ def create(cls, *, provider_name: Optional[str] = None, **kwargs):
50
+ for subclass in cls.__subclasses__():
51
+ if subclass.provider_name == provider_name:
52
+ return subclass(**kwargs)
53
+ return cls(**kwargs)
54
+
55
+ @classmethod
56
+ def key_alias(cls, k):
57
+ return {"provider": "provider_name", "provider_name": "provider"}.get(
58
+ k, k
59
+ )
60
+
61
+ @classmethod
62
+ def keys(cls):
63
+ return {
64
+ "provider",
65
+ "provider_name",
66
+ "embedding_model",
67
+ "model",
68
+ "region",
69
+ "provider_endpoint",
70
+ "azure_deployment_name",
71
+ "azure_embedding_deployment_name",
72
+ "azure_resource_name",
73
+ "oci_apiformat",
74
+ "oci_compartment_id",
75
+ "oci_endpoint_id",
76
+ "oci_runtimetype",
77
+ "aws_apiformat",
78
+ }
79
+
80
+
81
+ @dataclass
82
+ class AzureProvider(Provider):
83
+ """
84
+ Azure specific attributes
85
+
86
+ :param str azure_deployment_name: Name of the Azure OpenAI Service
87
+ deployed model.
88
+ :param str azure_embedding_deployment_name: Name of the Azure OpenAI
89
+ deployed embedding model.
90
+ :param str azure_resource_name: Name of the Azure OpenAI Service resource
91
+ """
92
+
93
+ provider_name: str = AZURE
94
+ azure_deployment_name: Optional[str] = None
95
+ azure_embedding_deployment_name: Optional[str] = None
96
+ azure_resource_name: Optional[str] = None
97
+
98
+ def __post_init__(self):
99
+ super().__post_init__()
100
+ self.provider_endpoint = f"{self.azure_resource_name}.openai.azure.com"
101
+
102
+
103
+ @dataclass
104
+ class OpenAIProvider(Provider):
105
+ """
106
+ OpenAI specific attributes
107
+ """
108
+
109
+ provider_name: str = OPENAI
110
+ provider_endpoint: Optional[str] = "api.openai.com"
111
+
112
+
113
+ @dataclass
114
+ class OCIGenAIProvider(Provider):
115
+ """
116
+ OCI Gen AI specific attributes
117
+
118
+ :param str oci_apiformat: Specifies the format in which the API expects
119
+ data to be sent and received. Supported values are 'COHERE' and 'GENERIC'
120
+ :param str oci_compartment_id: Specifies the OCID of the compartment you
121
+ are permitted to access when calling the OCI Generative AI service
122
+ :param str oci_endpoint_id: This attributes indicates the endpoint OCID
123
+ of the Oracle dedicated AI hosting cluster
124
+ :param str oci_runtimetype: This attribute indicates the runtime type of
125
+ the provided model. The supported values are 'COHERE' and 'LLAMA'
126
+ """
127
+
128
+ provider_name: str = OCI
129
+ oci_apiformat: Optional[str] = None
130
+ oci_compartment_id: Optional[str] = None
131
+ oci_endpoint_id: Optional[str] = None
132
+ oci_runtimetype: Optional[str] = None
133
+
134
+
135
+ @dataclass
136
+ class CohereProvider(Provider):
137
+ """
138
+ Cohere AI specific attributes
139
+ """
140
+
141
+ provider_name: str = COHERE
142
+ provider_endpoint = "api.cohere.ai"
143
+
144
+
145
+ @dataclass
146
+ class GoogleProvider(Provider):
147
+ """
148
+ Google AI specific attributes
149
+ """
150
+
151
+ provider_name: str = GOOGLE
152
+ provider_endpoint = "generativelanguage.googleapis.com"
153
+
154
+
155
+ @dataclass
156
+ class HuggingFaceProvider(Provider):
157
+ """
158
+ HuggingFace specific attributes
159
+ """
160
+
161
+ provider_name: str = HUGGINGFACE
162
+ provider_endpoint = "api-inference.huggingface.co"
163
+
164
+
165
+ @dataclass
166
+ class AWSProvider(Provider):
167
+ """
168
+ AWS specific attributes
169
+ """
170
+
171
+ provider_name: str = AWS
172
+ aws_apiformat: Optional[str] = None
173
+
174
+ def __post_init__(self):
175
+ super().__post_init__()
176
+ self.provider_endpoint = f"bedrock-runtime.{self.region}.amazonaws.com"
177
+
178
+
179
+
180
+ @dataclass
181
+ class AnthropicProvider(Provider):
182
+ """
183
+ Anthropic specific attributes
184
+ """
185
+
186
+ provider_name: str = ANTHROPIC
187
+ provider_endpoint = "api.anthropic.com"
select_ai/sql.py ADDED
@@ -0,0 +1,105 @@
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
+ GRANT_PRIVILEGES_TO_USER = """
9
+ DECLARE
10
+ TYPE array_t IS VARRAY(3) OF VARCHAR2(60);
11
+ v_packages array_t;
12
+ BEGIN
13
+ v_packages := array_t(
14
+ 'DBMS_CLOUD', 'DBMS_CLOUD_AI', 'DBMS_CLOUD_PIPELINE'
15
+ );
16
+ FOR i in 1..v_packages.count LOOP
17
+ EXECUTE IMMEDIATE
18
+ 'GRANT EXECUTE ON ' || v_packages(i) || ' TO {0}';
19
+ END LOOP;
20
+ END;
21
+ """
22
+
23
+ REVOKE_PRIVILEGES_FROM_USER = """
24
+ DECLARE
25
+ TYPE array_t IS VARRAY(3) OF VARCHAR2(60);
26
+ v_packages array_t;
27
+ BEGIN
28
+ v_packages := array_t(
29
+ 'DBMS_CLOUD', 'DBMS_CLOUD_AI', 'DBMS_CLOUD_PIPELINE'
30
+ );
31
+ FOR i in 1..v_packages.count LOOP
32
+ EXECUTE IMMEDIATE
33
+ 'REVOKE EXECUTE ON ' || v_packages(i) || ' FROM {0}';
34
+ END LOOP;
35
+ END;
36
+ """
37
+
38
+ ENABLE_AI_PROFILE_DOMAIN_FOR_USER = """
39
+ BEGIN
40
+ DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
41
+ host => :host,
42
+ ace => xs$ace_type(privilege_list => xs$name_list('http'),
43
+ principal_name => :user,
44
+ principal_type => xs_acl.ptype_db)
45
+ );
46
+ END;
47
+ """
48
+
49
+ DISABLE_AI_PROFILE_DOMAIN_FOR_USER = """
50
+ BEGIN
51
+ DBMS_NETWORK_ACL_ADMIN.REMOVE_HOST_ACE(
52
+ host => :host,
53
+ ace => xs$ace_type(privilege_list => xs$name_list('http'),
54
+ principal_name => :user,
55
+ principal_type => xs_acl.ptype_db)
56
+ );
57
+ END;
58
+ """
59
+
60
+ GET_USER_AI_PROFILE_ATTRIBUTES = """
61
+ SELECT attribute_name, attribute_value
62
+ FROM USER_CLOUD_AI_PROFILE_ATTRIBUTES
63
+ WHERE profile_name = :profile_name
64
+ """
65
+
66
+ GET_USER_AI_PROFILE = """
67
+ SELECT count(*)
68
+ FROM USER_CLOUD_AI_PROFILES
69
+ WHERE profile_name = :profile_name
70
+ """
71
+
72
+
73
+ LIST_USER_AI_PROFILES = """
74
+ SELECT profile_name, description
75
+ FROM USER_CLOUD_AI_PROFILES
76
+ WHERE REGEXP_LIKE(profile_name, :profile_name_pattern, 'i')
77
+ """
78
+
79
+ LIST_USER_VECTOR_INDEXES = """
80
+ SELECT v.index_name, v.description
81
+ FROM USER_CLOUD_VECTOR_INDEXES v
82
+ WHERE REGEXP_LIKE(v.index_name, :index_name_pattern, 'i')
83
+ """
84
+
85
+ GET_USER_VECTOR_INDEX_ATTRIBUTES = """
86
+ SELECT attribute_name, attribute_value
87
+ FROM USER_CLOUD_VECTOR_INDEX_ATTRIBUTES
88
+ WHERE INDEX_NAME = :index_name
89
+ """
90
+
91
+ LIST_USER_CONVERSATIONS = """
92
+ SELECT conversation_id,
93
+ conversation_title,
94
+ description,
95
+ retention_days
96
+ from USER_CLOUD_AI_CONVERSATIONS
97
+ """
98
+
99
+ GET_USER_CONVERSATION_ATTRIBUTES = """
100
+ SELECT conversation_title,
101
+ description,
102
+ retention_days
103
+ from USER_CLOUD_AI_CONVERSATIONS
104
+ WHERE conversation_id = :conversation_id
105
+ """