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.
Files changed (40) hide show
  1. lexsi_sdk/__init__.py +5 -0
  2. lexsi_sdk/client/__init__.py +0 -0
  3. lexsi_sdk/client/client.py +176 -0
  4. lexsi_sdk/common/__init__.py +0 -0
  5. lexsi_sdk/common/config/.env.prod +3 -0
  6. lexsi_sdk/common/constants.py +143 -0
  7. lexsi_sdk/common/enums.py +8 -0
  8. lexsi_sdk/common/environment.py +49 -0
  9. lexsi_sdk/common/monitoring.py +81 -0
  10. lexsi_sdk/common/trigger.py +75 -0
  11. lexsi_sdk/common/types.py +122 -0
  12. lexsi_sdk/common/utils.py +93 -0
  13. lexsi_sdk/common/validation.py +110 -0
  14. lexsi_sdk/common/xai_uris.py +197 -0
  15. lexsi_sdk/core/__init__.py +0 -0
  16. lexsi_sdk/core/agent.py +62 -0
  17. lexsi_sdk/core/alert.py +56 -0
  18. lexsi_sdk/core/case.py +618 -0
  19. lexsi_sdk/core/dashboard.py +131 -0
  20. lexsi_sdk/core/guardrails/__init__.py +0 -0
  21. lexsi_sdk/core/guardrails/guard_template.py +299 -0
  22. lexsi_sdk/core/guardrails/guardrail_autogen.py +554 -0
  23. lexsi_sdk/core/guardrails/guardrails_langgraph.py +525 -0
  24. lexsi_sdk/core/guardrails/guardrails_openai.py +541 -0
  25. lexsi_sdk/core/guardrails/openai_runner.py +1328 -0
  26. lexsi_sdk/core/model_summary.py +110 -0
  27. lexsi_sdk/core/organization.py +549 -0
  28. lexsi_sdk/core/project.py +5131 -0
  29. lexsi_sdk/core/synthetic.py +387 -0
  30. lexsi_sdk/core/text.py +595 -0
  31. lexsi_sdk/core/tracer.py +208 -0
  32. lexsi_sdk/core/utils.py +36 -0
  33. lexsi_sdk/core/workspace.py +325 -0
  34. lexsi_sdk/core/wrapper.py +766 -0
  35. lexsi_sdk/core/xai.py +306 -0
  36. lexsi_sdk/version.py +34 -0
  37. lexsi_sdk-0.1.16.dist-info/METADATA +100 -0
  38. lexsi_sdk-0.1.16.dist-info/RECORD +40 -0
  39. lexsi_sdk-0.1.16.dist-info/WHEEL +5 -0
  40. 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__()