select-ai 1.1.0rc1__py3-none-any.whl → 1.2.0rc1__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.

@@ -0,0 +1,280 @@
1
+ # ------------------------------------------------------------------------------
2
+ # Copyright (c) 2025, Oracle and/or its affiliates.
3
+ #
4
+ # Licensed under the Universal Permissive License v 1.0 as shown at
5
+ # http://oss.oracle.com/licenses/upl.
6
+ # -----------------------------------------------------------------------------
7
+
8
+ import json
9
+ from abc import ABC
10
+ from dataclasses import dataclass
11
+ from typing import (
12
+ Any,
13
+ AsyncGenerator,
14
+ Iterator,
15
+ List,
16
+ Mapping,
17
+ Optional,
18
+ Union,
19
+ )
20
+
21
+ import oracledb
22
+
23
+ from select_ai import BaseProfile
24
+ from select_ai._abc import SelectAIDataClass
25
+ from select_ai._enums import StrEnum
26
+ from select_ai.agent.sql import (
27
+ GET_USER_AI_AGENT_TEAM,
28
+ GET_USER_AI_AGENT_TEAM_ATTRIBUTES,
29
+ LIST_USER_AI_AGENT_TEAMS,
30
+ )
31
+ from select_ai.async_profile import AsyncProfile
32
+ from select_ai.db import async_cursor, cursor
33
+ from select_ai.errors import AgentTeamNotFoundError
34
+ from select_ai.profile import Profile
35
+
36
+
37
+ @dataclass
38
+ class TeamAttributes(SelectAIDataClass):
39
+ """
40
+ AI agent team attributes
41
+
42
+ :param List[Mapping] agents: A List of Python dictionaries, each defining
43
+ the agent and the task name. [{"name": "<agent_name>",
44
+ "task": "<task_name>"}]
45
+
46
+ :param str process: Execution order of tasks. Currently only "sequential"
47
+ is supported.
48
+
49
+ """
50
+
51
+ agents: List[Mapping]
52
+ process: str = "sequential"
53
+
54
+
55
+ class BaseTeam(ABC):
56
+
57
+ def __init__(
58
+ self,
59
+ team_name: str,
60
+ attributes: TeamAttributes,
61
+ description: Optional[str] = None,
62
+ ):
63
+ if not isinstance(attributes, TeamAttributes):
64
+ raise TypeError(
65
+ f"attributes must be an object of type "
66
+ f"select_ai.agent.TeamAttributes instance"
67
+ )
68
+ self.team_name = team_name
69
+ self.description = description
70
+ self.attributes = attributes
71
+
72
+ def __repr__(self):
73
+ return (
74
+ f"{self.__class__.__name__}("
75
+ f"team_name={self.team_name}, "
76
+ f"attributes={self.attributes}, description={self.description})"
77
+ )
78
+
79
+
80
+ class Team(BaseTeam):
81
+ """
82
+ A Team of AI agents work together to accomplish tasks
83
+ select_ai.agent.Team class lets you create, delete, enable, disable and
84
+ list AI Tasks.
85
+
86
+ :param str team_name: The name of the AI team
87
+ :param str description: Optional description of the AI team
88
+ :param select_ai.agent.TeamAttributes attributes: AI team attributes
89
+
90
+ """
91
+
92
+ @staticmethod
93
+ def _get_attributes(team_name: str) -> TeamAttributes:
94
+ with cursor() as cr:
95
+ cr.execute(
96
+ GET_USER_AI_AGENT_TEAM_ATTRIBUTES, team_name=team_name.upper()
97
+ )
98
+ attributes = cr.fetchall()
99
+ if attributes:
100
+ post_processed_attributes = {}
101
+ for k, v in attributes:
102
+ if isinstance(v, oracledb.LOB):
103
+ post_processed_attributes[k] = v.read()
104
+ else:
105
+ post_processed_attributes[k] = v
106
+ return TeamAttributes(**post_processed_attributes)
107
+ else:
108
+ raise AgentTeamNotFoundError(team_name=team_name)
109
+
110
+ def create(
111
+ self, enabled: Optional[bool] = True, replace: Optional[bool] = False
112
+ ):
113
+ """
114
+ Create a team of AI agents that work together to accomplish tasks.
115
+
116
+ :param bool enabled: Whether the AI agent team should be enabled.
117
+ Default value is True.
118
+
119
+ :param bool replace: Whether the AI agent team should be replaced.
120
+ Default value is False.
121
+
122
+ """
123
+ if self.team_name is None:
124
+ raise AttributeError("Team must have a name")
125
+ if self.attributes is None:
126
+ raise AttributeError("Team must have attributes")
127
+
128
+ parameters = {
129
+ "team_name": self.team_name,
130
+ "attributes": self.attributes.json(),
131
+ }
132
+ if self.description:
133
+ parameters["description"] = self.description
134
+
135
+ if not enabled:
136
+ parameters["status"] = "disabled"
137
+
138
+ with cursor() as cr:
139
+ try:
140
+ cr.callproc(
141
+ "DBMS_CLOUD_AI_AGENT.CREATE_TEAM",
142
+ keyword_parameters=parameters,
143
+ )
144
+ except oracledb.Error as err:
145
+ (err_obj,) = err.args
146
+ if err_obj.code in (20053, 20052) and replace:
147
+ self.delete(force=True)
148
+ cr.callproc(
149
+ "DBMS_CLOUD_AI_AGENT.CREATE_TEAM",
150
+ keyword_parameters=parameters,
151
+ )
152
+ else:
153
+ raise
154
+
155
+ def delete(self, force: Optional[bool] = False):
156
+ """
157
+ Delete an AI agent team from the database
158
+
159
+ :param bool force: Force the deletion. Default value is False.
160
+ """
161
+ with cursor() as cr:
162
+ cr.callproc(
163
+ "DBMS_CLOUD_AI_AGENT.DROP_TEAM",
164
+ keyword_parameters={
165
+ "team_name": self.team_name,
166
+ "force": force,
167
+ },
168
+ )
169
+
170
+ def disable(self):
171
+ """
172
+ Disable the AI agent team
173
+ """
174
+ pass
175
+
176
+ def enable(self):
177
+ """
178
+ Enable the AI agent team
179
+ """
180
+ pass
181
+
182
+ @classmethod
183
+ def fetch(cls) -> "Team":
184
+ """
185
+ Fetch AI Team attributes from the Database and build a proxy object in
186
+ the Python layer
187
+
188
+ :param str team_name: The name of the AI Team
189
+
190
+ :return: select_ai.agent.Team
191
+
192
+ :raises select_ai.errors.AgentTeamNotFoundError:
193
+ If the AI Team is not found
194
+ """
195
+ pass
196
+
197
+ @classmethod
198
+ def list(cls, team_name_pattern: Optional[str] = ".*") -> Iterator["Team"]:
199
+ """
200
+ List AI Agent Teams
201
+
202
+ :param str team_name_pattern: Regular expressions can be used
203
+ to specify a pattern. Function REGEXP_LIKE is used to perform the
204
+ match. Default value is ".*" i.e. match all teams.
205
+
206
+ :return: Iterator[Team]
207
+
208
+ """
209
+ with cursor() as cr:
210
+ cr.execute(
211
+ LIST_USER_AI_AGENT_TEAMS,
212
+ team_name_pattern=team_name_pattern,
213
+ )
214
+ for row in cr.fetchall():
215
+ team_name = row[0]
216
+ if row[1]:
217
+ description = row[1].read() # Oracle.LOB
218
+ else:
219
+ description = None
220
+ attributes = cls._get_attributes(team_name=team_name)
221
+ yield cls(
222
+ team_name=team_name,
223
+ description=description,
224
+ attributes=attributes,
225
+ )
226
+
227
+ def run(self, prompt: str = None, params: Mapping = None):
228
+ """
229
+ Start a new AI agent team or resume a paused one that is waiting
230
+ for human input. If you provide an existing process ID and the
231
+ associated team process is in the WAITING_FOR_HUMAN state, the
232
+ function resumes the workflow using the input you provide as
233
+ the human response
234
+
235
+ :param str prompt: Optional prompt for the user. If the task is
236
+ in the RUNNING state, the input acts as a placeholder for the
237
+ {query} in the task instruction. If the task is in the
238
+ WAITING_FOR_HUMAN state, the input serves as the human response.
239
+
240
+ :param Mapping[str, str] params: Optional parameters for the task.
241
+ Currently, the following parameters are supported:
242
+
243
+ - conversation_id: Identifies the conversation session associated
244
+ with the agent team
245
+
246
+ - variables: key-value pairs that provide additional input to the agent team.
247
+
248
+ """
249
+ parameters = {
250
+ "team_name": self.team_name,
251
+ }
252
+ if prompt:
253
+ parameters["user_prompt"] = prompt
254
+ if params:
255
+ parameters["params"] = json.dumps(params)
256
+
257
+ with cursor() as cr:
258
+ data = cr.callfunc(
259
+ "DBMS_CLOUD_AI_AGENT.RUN_TEAM",
260
+ oracledb.DB_TYPE_CLOB,
261
+ keyword_parameters=parameters,
262
+ )
263
+ if data is not None:
264
+ result = data.read()
265
+ else:
266
+ result = None
267
+ return result
268
+
269
+ def set_attributes(self, attributes: TeamAttributes) -> None:
270
+ """
271
+ Set the attributes of the AI Agent team
272
+ """
273
+ pass
274
+
275
+ def set_attribute(self, attribute_name: str, attribute_value: Any) -> None:
276
+ """
277
+ Set the attribute of the AI Agent team specified by
278
+ `attribute_name` and `attribute_value`.
279
+ """
280
+ pass