lexsi-sdk 0.1.16__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.
- lexsi_sdk/__init__.py +5 -0
- lexsi_sdk/client/__init__.py +0 -0
- lexsi_sdk/client/client.py +176 -0
- lexsi_sdk/common/__init__.py +0 -0
- lexsi_sdk/common/config/.env.prod +3 -0
- lexsi_sdk/common/constants.py +143 -0
- lexsi_sdk/common/enums.py +8 -0
- lexsi_sdk/common/environment.py +49 -0
- lexsi_sdk/common/monitoring.py +81 -0
- lexsi_sdk/common/trigger.py +75 -0
- lexsi_sdk/common/types.py +122 -0
- lexsi_sdk/common/utils.py +93 -0
- lexsi_sdk/common/validation.py +110 -0
- lexsi_sdk/common/xai_uris.py +197 -0
- lexsi_sdk/core/__init__.py +0 -0
- lexsi_sdk/core/agent.py +62 -0
- lexsi_sdk/core/alert.py +56 -0
- lexsi_sdk/core/case.py +618 -0
- lexsi_sdk/core/dashboard.py +131 -0
- lexsi_sdk/core/guardrails/__init__.py +0 -0
- lexsi_sdk/core/guardrails/guard_template.py +299 -0
- lexsi_sdk/core/guardrails/guardrail_autogen.py +554 -0
- lexsi_sdk/core/guardrails/guardrails_langgraph.py +525 -0
- lexsi_sdk/core/guardrails/guardrails_openai.py +541 -0
- lexsi_sdk/core/guardrails/openai_runner.py +1328 -0
- lexsi_sdk/core/model_summary.py +110 -0
- lexsi_sdk/core/organization.py +549 -0
- lexsi_sdk/core/project.py +5131 -0
- lexsi_sdk/core/synthetic.py +387 -0
- lexsi_sdk/core/text.py +595 -0
- lexsi_sdk/core/tracer.py +208 -0
- lexsi_sdk/core/utils.py +36 -0
- lexsi_sdk/core/workspace.py +325 -0
- lexsi_sdk/core/wrapper.py +766 -0
- lexsi_sdk/core/xai.py +306 -0
- lexsi_sdk/version.py +34 -0
- lexsi_sdk-0.1.16.dist-info/METADATA +100 -0
- lexsi_sdk-0.1.16.dist-info/RECORD +40 -0
- lexsi_sdk-0.1.16.dist-info/WHEEL +5 -0
- lexsi_sdk-0.1.16.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
from typing import Any, List, Optional
|
|
2
|
+
from pydantic import BaseModel, ConfigDict
|
|
3
|
+
|
|
4
|
+
import io
|
|
5
|
+
import json
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import plotly.graph_objects as go
|
|
8
|
+
|
|
9
|
+
from lexsi_sdk.client.client import APIClient
|
|
10
|
+
from lexsi_sdk.common.utils import poll_events, pretty_date
|
|
11
|
+
from lexsi_sdk.common.validation import Validate
|
|
12
|
+
from lexsi_sdk.common.xai_uris import (
|
|
13
|
+
AVAILABLE_SYNTHETIC_CUSTOM_SERVERS_URI,
|
|
14
|
+
GENERATE_ANONYMITY_SCORE_URI,
|
|
15
|
+
GENERATE_SYNTHETIC_DATA_URI,
|
|
16
|
+
GET_ANONYMITY_SCORE_URI,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SyntheticDataTag(BaseModel):
|
|
21
|
+
"""Representation of a generated synthetic data tag and its quality."""
|
|
22
|
+
api_client: APIClient
|
|
23
|
+
project_name: str
|
|
24
|
+
project: Any
|
|
25
|
+
|
|
26
|
+
model_name: str
|
|
27
|
+
tag: str
|
|
28
|
+
created_at: str
|
|
29
|
+
|
|
30
|
+
overall_quality_score: float
|
|
31
|
+
column_shapes: float
|
|
32
|
+
column_pair_trends: float
|
|
33
|
+
|
|
34
|
+
metadata: Optional[dict] = {}
|
|
35
|
+
plot_data: Optional[List[dict]] = []
|
|
36
|
+
|
|
37
|
+
model_config = ConfigDict(protected_namespaces=())
|
|
38
|
+
|
|
39
|
+
def __init__(self, **kwargs):
|
|
40
|
+
"""Bind API client reference for follow-up requests."""
|
|
41
|
+
super().__init__(**kwargs)
|
|
42
|
+
self.api_client = kwargs.get("api_client")
|
|
43
|
+
|
|
44
|
+
def get_model_name(self) -> str:
|
|
45
|
+
"""get model type
|
|
46
|
+
|
|
47
|
+
:return: model type
|
|
48
|
+
"""
|
|
49
|
+
return self.model_name
|
|
50
|
+
|
|
51
|
+
def view_metadata(self) -> dict:
|
|
52
|
+
"""print metadata"""
|
|
53
|
+
|
|
54
|
+
print(json.dumps(self.metadata, indent=4))
|
|
55
|
+
|
|
56
|
+
def get_metadata(self) -> dict:
|
|
57
|
+
"""get metadata"""
|
|
58
|
+
|
|
59
|
+
return self.metadata
|
|
60
|
+
|
|
61
|
+
def __print__(self) -> str:
|
|
62
|
+
"""User-friendly string representation."""
|
|
63
|
+
created_at = pretty_date(self.created_at)
|
|
64
|
+
return f"SyntheticDataTag(model_name={self.model_name}, tag={self.tag}, created_at={created_at})"
|
|
65
|
+
|
|
66
|
+
def __str__(self) -> str:
|
|
67
|
+
"""Return printable representation."""
|
|
68
|
+
return self.__print__()
|
|
69
|
+
|
|
70
|
+
def __repr__(self) -> str:
|
|
71
|
+
"""Return developer-friendly representation."""
|
|
72
|
+
return self.__print__()
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class SyntheticModel(BaseModel):
|
|
76
|
+
"""Synthetic Model Class
|
|
77
|
+
|
|
78
|
+
:param BaseModel: _description_
|
|
79
|
+
:return: _description_
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
api_client: APIClient
|
|
83
|
+
project_name: str
|
|
84
|
+
project: Any
|
|
85
|
+
|
|
86
|
+
model_name: str
|
|
87
|
+
status: str
|
|
88
|
+
created_at: str
|
|
89
|
+
created_by: str
|
|
90
|
+
|
|
91
|
+
overall_quality_score: Optional[float] = None
|
|
92
|
+
column_shapes: Optional[float] = None
|
|
93
|
+
column_pair_trends: Optional[float] = None
|
|
94
|
+
|
|
95
|
+
metadata: Optional[dict] = {}
|
|
96
|
+
plot_data: Optional[List[dict]] = []
|
|
97
|
+
|
|
98
|
+
model_config = ConfigDict(protected_namespaces=())
|
|
99
|
+
|
|
100
|
+
def __init__(self, **kwargs):
|
|
101
|
+
"""Bind API client reference for this synthetic model."""
|
|
102
|
+
super().__init__(**kwargs)
|
|
103
|
+
self.api_client = kwargs.get("api_client")
|
|
104
|
+
|
|
105
|
+
def get_model_type(self) -> str:
|
|
106
|
+
"""get model type
|
|
107
|
+
|
|
108
|
+
:return: model type
|
|
109
|
+
"""
|
|
110
|
+
return self.metadata["model_name"]
|
|
111
|
+
|
|
112
|
+
def get_data_quality(self) -> pd.DataFrame:
|
|
113
|
+
"""get data quality
|
|
114
|
+
|
|
115
|
+
:return: data quality metrics
|
|
116
|
+
"""
|
|
117
|
+
quality = {
|
|
118
|
+
"overall_quality_score": self.overall_quality_score,
|
|
119
|
+
"column_shapes": self.column_shapes,
|
|
120
|
+
"column_pair_trends": self.column_pair_trends,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
df = pd.DataFrame(quality, index=[0])
|
|
124
|
+
|
|
125
|
+
return df
|
|
126
|
+
|
|
127
|
+
def quality_plot(self):
|
|
128
|
+
"""plot psi chart"""
|
|
129
|
+
x_data = [item["Column"] for item in self.plot_data]
|
|
130
|
+
y_data = [item["Quality Score"] for item in self.plot_data]
|
|
131
|
+
metric_data = [item["Metric"] for item in self.plot_data]
|
|
132
|
+
|
|
133
|
+
traces = []
|
|
134
|
+
for metric in set(metric_data):
|
|
135
|
+
indices = [i for i, val in enumerate(metric_data) if val == metric]
|
|
136
|
+
traces.append(
|
|
137
|
+
go.Bar(
|
|
138
|
+
x=[x_data[i] for i in indices],
|
|
139
|
+
y=[y_data[i] for i in indices],
|
|
140
|
+
name=metric,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
fig = go.Figure(data=traces)
|
|
145
|
+
|
|
146
|
+
fig.update_layout(
|
|
147
|
+
barmode="relative",
|
|
148
|
+
# xaxis_title="Column Names",
|
|
149
|
+
# yaxis_title="Quality Score",
|
|
150
|
+
height=450,
|
|
151
|
+
bargap=0.01,
|
|
152
|
+
legend_orientation="h",
|
|
153
|
+
legend_x=0.1,
|
|
154
|
+
legend_y=1.1,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
fig.show(config={"displaylogo": False})
|
|
158
|
+
|
|
159
|
+
'''
|
|
160
|
+
def get_training_logs(self) -> str:
|
|
161
|
+
"""get model training logs
|
|
162
|
+
|
|
163
|
+
:return: logs of string type
|
|
164
|
+
"""
|
|
165
|
+
url = f"{GET_SYNTHETIC_TRAINING_LOGS_URI}?project_name={self.project_name}&model_name={self.model_name}"
|
|
166
|
+
|
|
167
|
+
res = self.api_client.get(url)
|
|
168
|
+
|
|
169
|
+
if not res['success']:
|
|
170
|
+
raise Exception('Error while getting training logs.')
|
|
171
|
+
|
|
172
|
+
return res['details']
|
|
173
|
+
'''
|
|
174
|
+
|
|
175
|
+
def generate_synthetic_datapoints(
|
|
176
|
+
self, num_of_datapoints: int, instance_type: Optional[str] = "shared"
|
|
177
|
+
):
|
|
178
|
+
"""generate given number of synthetic datapoints
|
|
179
|
+
|
|
180
|
+
:param num_of_datapoints: total datapoints to generate
|
|
181
|
+
:param instance_type: type of instance to run training
|
|
182
|
+
for all available instances check xai.available_synthetic_custom_servers()
|
|
183
|
+
defaults to shared
|
|
184
|
+
:return: None
|
|
185
|
+
"""
|
|
186
|
+
if instance_type != "shared":
|
|
187
|
+
available_servers = self.api_client.get(
|
|
188
|
+
AVAILABLE_SYNTHETIC_CUSTOM_SERVERS_URI
|
|
189
|
+
)["details"]
|
|
190
|
+
servers = list(
|
|
191
|
+
map(lambda instance: instance["instance_name"], available_servers)
|
|
192
|
+
)
|
|
193
|
+
Validate.value_against_list("instance_type", instance_type, servers)
|
|
194
|
+
|
|
195
|
+
payload = {
|
|
196
|
+
"project_name": self.project_name,
|
|
197
|
+
"model_name": self.model_name,
|
|
198
|
+
"instance_type": instance_type,
|
|
199
|
+
"num_of_datapoints": num_of_datapoints,
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
res = self.api_client.post(GENERATE_SYNTHETIC_DATA_URI, payload)
|
|
203
|
+
|
|
204
|
+
if not res["success"]:
|
|
205
|
+
raise Exception(res["details"])
|
|
206
|
+
|
|
207
|
+
print("Generating synthetic datapoints...")
|
|
208
|
+
poll_events(
|
|
209
|
+
self.api_client,
|
|
210
|
+
self.project_name,
|
|
211
|
+
res["event_id"],
|
|
212
|
+
progress_message="Synthetic Data generation progress",
|
|
213
|
+
)
|
|
214
|
+
print("Synthetic datapoints generated successfully.\n")
|
|
215
|
+
|
|
216
|
+
def generate_anonymity_score(
|
|
217
|
+
self,
|
|
218
|
+
aux_columns: List[str],
|
|
219
|
+
control_tag: str,
|
|
220
|
+
instance_type: Optional[str] = "shared",
|
|
221
|
+
):
|
|
222
|
+
"""generate anonymity score
|
|
223
|
+
|
|
224
|
+
:param aux_columns: list of features
|
|
225
|
+
:param control_tag: tag
|
|
226
|
+
:param instance_type: type of instance to run training
|
|
227
|
+
for all available instances check xai.available_synthetic_custom_servers()
|
|
228
|
+
defaults to shared
|
|
229
|
+
|
|
230
|
+
:return: None
|
|
231
|
+
"""
|
|
232
|
+
if instance_type != "shared":
|
|
233
|
+
available_servers = self.api_client.get(
|
|
234
|
+
AVAILABLE_SYNTHETIC_CUSTOM_SERVERS_URI
|
|
235
|
+
)["details"]
|
|
236
|
+
servers = list(
|
|
237
|
+
map(lambda instance: instance["instance_name"], available_servers)
|
|
238
|
+
)
|
|
239
|
+
Validate.value_against_list("instance_type", instance_type, servers)
|
|
240
|
+
|
|
241
|
+
if len(aux_columns) < 2:
|
|
242
|
+
raise Exception("aux_columns requires minimum 2 columns.")
|
|
243
|
+
|
|
244
|
+
project_config = self.project.config()["metadata"]
|
|
245
|
+
|
|
246
|
+
Validate.value_against_list(
|
|
247
|
+
"feature", aux_columns, project_config["feature_include"]
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
all_tags = self.project.all_tags()
|
|
251
|
+
|
|
252
|
+
Validate.value_against_list("tag", [control_tag], all_tags)
|
|
253
|
+
|
|
254
|
+
payload = {
|
|
255
|
+
"aux_columns": aux_columns,
|
|
256
|
+
"control_tag": control_tag,
|
|
257
|
+
"model_name": self.model_name,
|
|
258
|
+
"project_name": self.project_name,
|
|
259
|
+
"instance_type": instance_type,
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
res = self.api_client.post(GENERATE_ANONYMITY_SCORE_URI, payload)
|
|
263
|
+
|
|
264
|
+
if not res["success"]:
|
|
265
|
+
raise Exception(res["details"])
|
|
266
|
+
|
|
267
|
+
print("Calculating anonymity score...")
|
|
268
|
+
poll_events(self.api_client, self.project_name, res["event_id"])
|
|
269
|
+
print("Anonymity score calculated successfully.\n")
|
|
270
|
+
|
|
271
|
+
def anonymity_score(self):
|
|
272
|
+
"""get anonymity score
|
|
273
|
+
|
|
274
|
+
:raises Exception: _description_
|
|
275
|
+
:return: _description_
|
|
276
|
+
"""
|
|
277
|
+
payload = {
|
|
278
|
+
"project_name": self.project_name,
|
|
279
|
+
"model_name": self.model_name,
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
res = self.api_client.post(GET_ANONYMITY_SCORE_URI, payload)
|
|
283
|
+
|
|
284
|
+
if not res["success"]:
|
|
285
|
+
print(res["details"])
|
|
286
|
+
raise Exception("Error while getting anonymity score.")
|
|
287
|
+
|
|
288
|
+
print("metadata:")
|
|
289
|
+
print(res["details"]["metadata"])
|
|
290
|
+
print("\n")
|
|
291
|
+
|
|
292
|
+
return pd.DataFrame(res["details"]["scores"], index=[0])
|
|
293
|
+
|
|
294
|
+
def __print__(self) -> str:
|
|
295
|
+
"""User-friendly string representation."""
|
|
296
|
+
created_at = pretty_date(self.created_at)
|
|
297
|
+
|
|
298
|
+
return f"SyntheticModel(model_name={self.model_name}, status={self.status}, created_by={self.created_by}, created_at={created_at})"
|
|
299
|
+
|
|
300
|
+
def __str__(self) -> str:
|
|
301
|
+
"""Return printable representation."""
|
|
302
|
+
return self.__print__()
|
|
303
|
+
|
|
304
|
+
def __repr__(self) -> str:
|
|
305
|
+
"""Return developer-friendly representation."""
|
|
306
|
+
return self.__print__()
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
class SyntheticPrompt(BaseModel):
|
|
310
|
+
"""Metadata container for a reusable synthetic prompt definition."""
|
|
311
|
+
api_client: APIClient
|
|
312
|
+
project: Any
|
|
313
|
+
|
|
314
|
+
prompt_name: str
|
|
315
|
+
prompt_id: str
|
|
316
|
+
project_name: str
|
|
317
|
+
status: str
|
|
318
|
+
configuration: List[Any]
|
|
319
|
+
metadata: dict
|
|
320
|
+
created_by: str
|
|
321
|
+
updated_by: str
|
|
322
|
+
created_at: str
|
|
323
|
+
updated_at: str
|
|
324
|
+
|
|
325
|
+
def __init__(self, **kwargs):
|
|
326
|
+
"""Bind API client reference for prompt actions."""
|
|
327
|
+
super().__init__(**kwargs)
|
|
328
|
+
self.api_client = kwargs.get("api_client")
|
|
329
|
+
|
|
330
|
+
def get_expression(self) -> str:
|
|
331
|
+
"""construct prompt expression
|
|
332
|
+
|
|
333
|
+
:return: prompt expression
|
|
334
|
+
"""
|
|
335
|
+
expression_list = []
|
|
336
|
+
|
|
337
|
+
if not self.metadata:
|
|
338
|
+
raise Exception("Expression not found.")
|
|
339
|
+
|
|
340
|
+
for item in self.metadata["expression"]:
|
|
341
|
+
if isinstance(item, dict):
|
|
342
|
+
expression_list.append(
|
|
343
|
+
f"{item['column']} {item['expression']} {item['value']}"
|
|
344
|
+
)
|
|
345
|
+
else:
|
|
346
|
+
expression_list.append(item)
|
|
347
|
+
|
|
348
|
+
return " ".join(expression_list)
|
|
349
|
+
|
|
350
|
+
def get_config(self) -> List[dict]:
|
|
351
|
+
"""get prompt configuration
|
|
352
|
+
|
|
353
|
+
:return: prompt configuration
|
|
354
|
+
"""
|
|
355
|
+
return self.configuration
|
|
356
|
+
|
|
357
|
+
"""
|
|
358
|
+
def delete(self) -> str:
|
|
359
|
+
payload = {
|
|
360
|
+
"delete": True,
|
|
361
|
+
"project_name": self.project_name,
|
|
362
|
+
"prompt_id": self.prompt_id,
|
|
363
|
+
"update_keys": {}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
res = self.api_client.post(UPDATE_SYNTHETIC_PROMPT_URI, payload)
|
|
367
|
+
|
|
368
|
+
if not res['success']:
|
|
369
|
+
raise Exception(res['details'])
|
|
370
|
+
|
|
371
|
+
return res['details']
|
|
372
|
+
"""
|
|
373
|
+
|
|
374
|
+
def __print__(self) -> str:
|
|
375
|
+
"""User-friendly string representation."""
|
|
376
|
+
created_at = pretty_date(self.created_at)
|
|
377
|
+
updated_at = pretty_date(self.updated_at)
|
|
378
|
+
|
|
379
|
+
return f"SyntheticPrompt(prompt_name={self.prompt_name}, prompt_id={self.prompt_id}, status={self.status}, created_by={self.created_by}, created_at={created_at}, updated_at={updated_at})"
|
|
380
|
+
|
|
381
|
+
def __str__(self) -> str:
|
|
382
|
+
"""Return printable representation."""
|
|
383
|
+
return self.__print__()
|
|
384
|
+
|
|
385
|
+
def __repr__(self) -> str:
|
|
386
|
+
"""Return developer-friendly representation."""
|
|
387
|
+
return self.__print__()
|