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,208 @@
1
+ from opentelemetry import trace as trace_api
2
+ from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
3
+ from opentelemetry.sdk import trace as trace_sdk
4
+ from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor , BatchSpanProcessor
5
+ from opentelemetry.sdk.resources import Resource
6
+ from openinference.instrumentation.langchain import LangChainInstrumentor
7
+ # from openinference.instrumentation.autogen_agent import AutogenInstrumentor
8
+ # from opentelemetry.instrumentation.openai import OpenAIInstrumentor
9
+ from openinference.instrumentation.autogen_agentchat import AutogenAgentChatInstrumentor
10
+ from openinference.instrumentation.crewai import CrewAIInstrumentor
11
+ from openinference.instrumentation.openai_agents import OpenAIAgentsInstrumentor
12
+ from openinference.instrumentation.dspy import DSPyInstrumentor
13
+ from openinference.instrumentation.llama_index import LlamaIndexInstrumentor
14
+ from openinference.instrumentation.smolagents import SmolagentsInstrumentor
15
+ # from pydantic import BaseModel
16
+ import os
17
+
18
+
19
+ class Tracer:
20
+ """Helpers to instrument various agent frameworks with OpenTelemetry."""
21
+
22
+ def __init__(self):
23
+ """Initialize exporter endpoint from environment."""
24
+ self.base_url = os.getenv("XAI_API_URL", "https://apiv1.lexsi.ai")
25
+ self.endpoint = f"{self.base_url}"
26
+ def setup_langchain_tracing(self , project: object, session_id : str = None) -> None:
27
+ """
28
+ Sets up OpenTelemetry tracing for a given project with OTLP and console exporters.
29
+
30
+ :param project: Object containing project details; must expose project_name.
31
+ :param session_id: Optional session identifier to annotate spans.
32
+ """
33
+
34
+ # Extract project name or use default
35
+
36
+ project_name = getattr(project, 'project_name')
37
+ # Create resource with service and project details
38
+ resource = Resource(attributes={
39
+ "service.name": "Langgraph",
40
+ "project_name": project_name,
41
+ "session_id": session_id if session_id else "None"
42
+ })
43
+
44
+ # Initialize tracer provider
45
+ tracer_provider = trace_sdk.TracerProvider(resource=resource)
46
+ trace_api.set_tracer_provider(tracer_provider)
47
+
48
+ # Add OTLP and console span processors
49
+ tracer_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter(self.endpoint)))
50
+ # Instrument LangChain
51
+ LangChainInstrumentor().instrument()
52
+
53
+ def setup_autogen_tracing(self ,project: object , session_id : str = None) -> None:
54
+ """
55
+ Sets up OpenTelemetry tracing for a given project with OTLP and console exporters.
56
+
57
+ :param project: Object containing project details; must expose project_name.
58
+ :param session_id: Optional session identifier to annotate spans.
59
+ """
60
+
61
+ # Extract project name or use default
62
+
63
+ project_name = getattr(project, 'project_name')
64
+ # Create resource with service and project details
65
+ resource = Resource(attributes={
66
+ "service.name": "Autogen",
67
+ "project_name": project_name,
68
+ "session_id": session_id if session_id else "None"
69
+ })
70
+
71
+ # Initialize tracer provider
72
+ tracer_provider = trace_sdk.TracerProvider(resource=resource)
73
+ trace_api.set_tracer_provider(tracer_provider)
74
+
75
+ # Add OTLP and console span processors
76
+ tracer_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter(self.endpoint)))
77
+ # Instrument Autogen
78
+ AutogenAgentChatInstrumentor().instrument()
79
+
80
+ def setup_crewai_tracing(self , project: object , session_id : str = None) -> None:
81
+ """
82
+ Sets up OpenTelemetry tracing for a given project with OTLP and console exporters.
83
+
84
+ :param project: Object containing project details; must expose project_name.
85
+ :param session_id: Optional session identifier to annotate spans.
86
+ """
87
+
88
+ # Extract project name or use default
89
+
90
+ project_name = getattr(project, 'project_name')
91
+ # Create resource with service and project details
92
+ resource = Resource(attributes={
93
+ "service.name": "Crewai",
94
+ "project_name": project_name,
95
+ "session_id": session_id if session_id else "None"
96
+ })
97
+
98
+ # Initialize tracer provider
99
+ tracer_provider = trace_sdk.TracerProvider(resource=resource)
100
+ trace_api.set_tracer_provider(tracer_provider)
101
+
102
+ # Add OTLP and console span processors
103
+ tracer_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter(self.endpoint)))
104
+ # tracer_provider.add_span_processor(ConsoleSpanExporter())
105
+ # Instrument CrewAI
106
+ CrewAIInstrumentor().instrument()
107
+
108
+ def setup_agents_tracing(self , project : object , session_id : str = None) -> None:
109
+ """
110
+ Sets up OpenTelemetry tracing for a given project with OTLP and console exporters.
111
+
112
+ :param project: Object containing project details; must expose project_name.
113
+ :param session_id: Optional session identifier to annotate spans.
114
+ """
115
+
116
+ # Extract project name or use default
117
+
118
+ project_name = getattr(project, 'project_name')
119
+ # Create resource with service and project details
120
+ resource = Resource(attributes={
121
+ "service.name": "OpenAI-Agents",
122
+ "project_name": project_name,
123
+ "session_id": session_id if session_id else "None"
124
+ })
125
+
126
+ # Initialize tracer provider
127
+ tracer_provider = trace_sdk.TracerProvider(resource=resource)
128
+ trace_api.set_tracer_provider(tracer_provider)
129
+
130
+ # Add OTLP and console span processors
131
+ tracer_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter(self.endpoint)))
132
+ # tracer_provider.add_span_processor(ConsoleSpanExporter())
133
+ # Instrument OpenAI
134
+ OpenAIAgentsInstrumentor().instrument()
135
+
136
+ def setup_dspy_tracing(self , project : object , session_id : str = None) -> None:
137
+ """
138
+ Sets up OpenTelemetry tracing for a given project with OTLP and console exporters.
139
+
140
+ :param project: Object containing project details; must expose project_name.
141
+ :param session_id: Optional session identifier to annotate spans.
142
+ """
143
+
144
+ # Extract project name or use default
145
+
146
+ project_name = getattr(project, 'project_name')
147
+ # Create resource with service and project details
148
+ resource = Resource(attributes={
149
+ "service.name": "DSPy",
150
+ "project_name": project_name,
151
+ "session_id": session_id if session_id else "None"
152
+ })
153
+
154
+ # Initialize tracer provider
155
+ tracer_provider = trace_sdk.TracerProvider(resource=resource)
156
+ trace_api.set_tracer_provider(tracer_provider)
157
+
158
+ # Add OTLP and console span processors
159
+ tracer_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter(self.endpoint)))
160
+ # tracer_provider.add_span_processor(ConsoleSpanExporter())
161
+ # Instrument DSPy
162
+ DSPyInstrumentor().instrument()
163
+
164
+ def setup_llamaindex_tracing(self , project : object , session_id : str = None) -> None:
165
+ """Enable tracing for LlamaIndex runs."""
166
+
167
+ # Extract project name or use default
168
+
169
+ project_name = getattr(project, 'project_name')
170
+ # Create resource with service and project details
171
+ resource = Resource(attributes={
172
+ "service.name": "Llamaindex",
173
+ "project_name": project_name,
174
+ "session_id": session_id if session_id else "None"
175
+ })
176
+
177
+ # Initialize tracer provider
178
+ tracer_provider = trace_sdk.TracerProvider(resource=resource)
179
+ trace_api.set_tracer_provider(tracer_provider)
180
+
181
+ # Add OTLP and console span processors
182
+ tracer_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter(self.endpoint)))
183
+ # tracer_provider.add_span_processor(ConsoleSpanExporter())
184
+ # Instrument llama
185
+ LlamaIndexInstrumentor().instrument()
186
+
187
+ def setup_smolagents_tracing(self , project : object , session_id : str = None) -> None:
188
+ """Enable tracing for Smolagents runs."""
189
+
190
+ # Extract project name or use default
191
+
192
+ project_name = getattr(project, 'project_name')
193
+ # Create resource with service and project details
194
+ resource = Resource(attributes={
195
+ "service.name": "Smolagents",
196
+ "project_name": project_name,
197
+ "session_id": session_id if session_id else "None"
198
+ })
199
+
200
+ # Initialize tracer provider
201
+ tracer_provider = trace_sdk.TracerProvider(resource=resource)
202
+ trace_api.set_tracer_provider(tracer_provider)
203
+
204
+ # Add OTLP and console span processors
205
+ tracer_provider.add_span_processor(SimpleSpanProcessor(OTLPSpanExporter(self.endpoint)))
206
+ # tracer_provider.add_span_processor(ConsoleSpanExporter())
207
+ # Instrument Smol
208
+ SmolagentsInstrumentor().instrument()
@@ -0,0 +1,36 @@
1
+ from lexsi_sdk.client.client import APIClient
2
+
3
+ def build_url(base_url, data_connector_name, project_name, organization_id):
4
+ """Compose a connector URL using project or organization context.
5
+
6
+ :param base_url: Base API path.
7
+ :param data_connector_name: Link service name.
8
+ :param project_name: Project identifier if available.
9
+ :param organization_id: Organization identifier if available.
10
+ :return: Fully composed URL string.
11
+ """
12
+ url = None
13
+ if project_name:
14
+ url = f"{base_url}?project_name={project_name}&link_service_name={data_connector_name}"
15
+ elif organization_id:
16
+ url = f"{base_url}?organization_id={organization_id}&link_service_name={data_connector_name}"
17
+ return url
18
+
19
+ def build_list_data_connector_url(base_url, project_name, organization_id):
20
+ """Build list URL for data connectors within a project or organization.
21
+
22
+ :param base_url: Base API path.
23
+ :param project_name: Project identifier if available.
24
+ :param organization_id: Organization identifier if available.
25
+ :return: Fully composed URL string.
26
+ """
27
+ url = None
28
+ if project_name and organization_id:
29
+ url = f"{base_url}?project_name={project_name}&organization_id={organization_id}"
30
+ elif project_name:
31
+ url = f"{base_url}?project_name={project_name}"
32
+ elif organization_id:
33
+ url = f"{base_url}?organization_id={organization_id}"
34
+ else:
35
+ url = base_url
36
+ return url
@@ -0,0 +1,325 @@
1
+ import pandas as pd
2
+ from pydantic import BaseModel
3
+ from typing import Optional
4
+ from lexsi_sdk.client.client import APIClient
5
+ from lexsi_sdk.common.enums import UserRole
6
+ from lexsi_sdk.common.validation import Validate
7
+ from lexsi_sdk.common.xai_uris import (
8
+ AVAILABLE_CUSTOM_SERVERS_URI,
9
+ CREATE_PROJECT_URI,
10
+ GET_WORKSPACES_DETAILS_URI,
11
+ START_CUSTOM_SERVER_URI,
12
+ STOP_CUSTOM_SERVER_URI,
13
+ UPDATE_WORKSPACE_URI,
14
+ GET_NOTIFICATIONS_URI,
15
+ CLEAR_NOTIFICATIONS_URI,
16
+ )
17
+ from lexsi_sdk.core.project import Project
18
+ from lexsi_sdk.core.text import TextProject
19
+ from lexsi_sdk.core.agent import AgentProject
20
+
21
+
22
+ class Workspace(BaseModel):
23
+ """Class to work with Lexsi.ai workspaces"""
24
+
25
+ organization_id: Optional[str] = None
26
+ created_by: str
27
+ user_workspace_name: str
28
+ workspace_name: str
29
+ created_at: str
30
+
31
+ api_client: APIClient
32
+
33
+ def __init__(self, **kwargs):
34
+ """Attach API client for workspace operations."""
35
+ super().__init__(**kwargs)
36
+ self.api_client = kwargs.get("api_client")
37
+
38
+ def rename_workspace(self, new_workspace_name: str) -> str:
39
+ """rename the current workspace to new name
40
+
41
+ :param new_workspace_name: name for the workspace to be renamed to
42
+ :return: response
43
+ """
44
+ payload = {
45
+ "workspace_name": self.workspace_name,
46
+ "modify_req": {
47
+ "update_workspace": {
48
+ "workspace_name": new_workspace_name,
49
+ }
50
+ },
51
+ }
52
+ res = self.api_client.post(UPDATE_WORKSPACE_URI, payload)
53
+ self.user_workspace_name = new_workspace_name
54
+ return res.get("details")
55
+
56
+ def delete_workspace(self) -> str:
57
+ """deletes the current workspace
58
+ :return: response
59
+ """
60
+ payload = {
61
+ "workspace_name": self.workspace_name,
62
+ "modify_req": {"delete_workspace": self.user_workspace_name},
63
+ }
64
+ res = self.api_client.post(UPDATE_WORKSPACE_URI, payload)
65
+ return res.get("details")
66
+
67
+ def add_user_to_workspace(self, email: str, role: str) -> str:
68
+ """adds user to current workspace
69
+
70
+ :param email: user email
71
+ :param role: user role ["admin", "manager", "user"]
72
+ :return: response
73
+ """
74
+ payload = {
75
+ "workspace_name": self.workspace_name,
76
+ "modify_req": {
77
+ "add_user_workspace": {
78
+ "email": email,
79
+ "role": role,
80
+ },
81
+ },
82
+ }
83
+ res = self.api_client.post(UPDATE_WORKSPACE_URI, payload)
84
+ return res.get("details")
85
+
86
+ def remove_user_from_workspace(self, email: str) -> str:
87
+ """removes user from the current workspace
88
+
89
+ :param email: user email
90
+ :return: response
91
+ """
92
+ payload = {
93
+ "workspace_name": self.workspace_name,
94
+ "modify_req": {
95
+ "remove_user_workspace": email,
96
+ },
97
+ }
98
+ res = self.api_client.post(UPDATE_WORKSPACE_URI, payload)
99
+ return res.get("details")
100
+
101
+ def update_user_access_for_workspace(self, email: str, role: UserRole) -> str:
102
+ """update the user access for the workspace
103
+
104
+ :param email: user email
105
+ :param role: new user role ["admin", "user"]
106
+ :return: _description_
107
+ """
108
+ payload = {
109
+ "workspace_name": self.workspace_name,
110
+ "modify_req": {
111
+ "update_user_workspace": {
112
+ "email": email,
113
+ "role": role,
114
+ }
115
+ },
116
+ }
117
+ res = self.api_client.post(UPDATE_WORKSPACE_URI, payload)
118
+ return res.get("details")
119
+
120
+ def projects(self) -> pd.DataFrame:
121
+ """get user projects for this Workspace
122
+
123
+ :return: Projects details dataframe
124
+ """
125
+ workspace = self.api_client.get(
126
+ f"{GET_WORKSPACES_DETAILS_URI}?workspace_name={self.workspace_name}"
127
+ )
128
+ projects_df = pd.DataFrame(
129
+ workspace.get("data", {}).get("projects", []),
130
+ columns=[
131
+ "user_project_name",
132
+ "access_type",
133
+ "created_by",
134
+ "created_at",
135
+ "updated_at",
136
+ "instance_type",
137
+ "instance_status",
138
+ ],
139
+ )
140
+ return projects_df
141
+
142
+ def project(self, project_name: str) -> Project:
143
+ """Select specific project
144
+
145
+ :param project_name: Name of the project
146
+ :return: Project
147
+ """
148
+ workspace = self.api_client.get(
149
+ f"{GET_WORKSPACES_DETAILS_URI}?workspace_name={self.workspace_name}"
150
+ )
151
+
152
+ project = next(
153
+ filter(lambda project: project.get("user_project_name") == project_name, workspace.get("data", {}).get("projects", [])),
154
+ None,
155
+ )
156
+
157
+ if project is None:
158
+ raise Exception("Project Not Found")
159
+
160
+ if project.get("metadata",{}).get("modality") == "text": return TextProject(api_client=self.api_client, **project)
161
+ elif project.get("metadata",{}).get("modality") == "agent": return AgentProject(api_client=self.api_client, **project)
162
+
163
+ return Project(api_client=self.api_client, **project)
164
+
165
+ def create_project(
166
+ self,
167
+ project_name: str,
168
+ modality: str,
169
+ project_type: str,
170
+ project_sub_type: Optional[str] = None,
171
+ server_type: Optional[str] = None,
172
+ ) -> Project:
173
+ """creates new project in the current workspace
174
+
175
+ :param project_name: name for the project
176
+ :param modality: modality for the project
177
+ Eg:- tabular, image, text
178
+ :project_type: type for the project
179
+ Eg:- classification, regression
180
+ :return: response
181
+ """
182
+ payload = {
183
+ "project_name": project_name,
184
+ "modality": modality,
185
+ "project_type": project_type,
186
+ "project_sub_type": project_sub_type,
187
+ "workspace_name": self.workspace_name,
188
+ }
189
+
190
+ if self.organization_id:
191
+ payload["organization_id"] = self.organization_id
192
+
193
+ if server_type:
194
+ custom_servers = self.api_client.get(AVAILABLE_CUSTOM_SERVERS_URI)
195
+ Validate.value_against_list(
196
+ "server_type",
197
+ server_type,
198
+ [server["name"] for server in custom_servers],
199
+ )
200
+
201
+ payload["instance_type"] = server_type
202
+ payload["server_config"] = {}
203
+
204
+ res = self.api_client.post(CREATE_PROJECT_URI, payload)
205
+
206
+ if not res["success"]:
207
+ raise Exception(res["details"])
208
+
209
+ if modality == "text":
210
+ project = TextProject(api_client=self.api_client, **res["details"])
211
+ elif modality == "agent":
212
+ project = AgentProject(api_client=self.api_client, **res["details"])
213
+ else:
214
+ project = Project(api_client=self.api_client, **res["details"])
215
+
216
+ return project
217
+
218
+ def get_notifications(self) -> pd.DataFrame:
219
+ """get user workspace notifications
220
+
221
+ :return: DataFrame
222
+ """
223
+ url = f"{GET_NOTIFICATIONS_URI}?workspace_name={self.workspace_name}"
224
+
225
+ res = self.api_client.get(url)
226
+
227
+ if not res["success"]:
228
+ raise Exception("Error while getting workspace notifications.")
229
+
230
+ notifications = res["details"]
231
+
232
+ if not notifications:
233
+ return "No notifications found."
234
+
235
+ return pd.DataFrame(notifications).reindex(
236
+ columns=["project_name", "message", "time"]
237
+ )
238
+
239
+ def clear_notifications(self) -> str:
240
+ """clear user workspace notifications
241
+
242
+ :raises Exception: _description_
243
+ :return: str
244
+ """
245
+ url = f"{CLEAR_NOTIFICATIONS_URI}?workspace_name={self.workspace_name}"
246
+
247
+ res = self.api_client.post(url)
248
+
249
+ if not res["success"]:
250
+ raise Exception("Error while clearing workspace notifications.")
251
+
252
+ return res["details"]
253
+
254
+ def start_server(self) -> str:
255
+ """start dedicated workspace server
256
+
257
+ :return: response
258
+ """
259
+
260
+ res = self.api_client.post(
261
+ f"{START_CUSTOM_SERVER_URI}?workspace_name={self.workspace_name}"
262
+ )
263
+
264
+ if not res["success"]:
265
+ raise Exception(res.get("message"))
266
+
267
+ return res["message"]
268
+
269
+ def stop_server(self) -> str:
270
+ """stop dedicated workspace server
271
+
272
+ :return: response
273
+ """
274
+ res = self.api_client.post(
275
+ f"{STOP_CUSTOM_SERVER_URI}?workspace_name={self.workspace_name}"
276
+ )
277
+
278
+ if not res["success"]:
279
+ raise Exception(res.get("message"))
280
+
281
+ return res["message"]
282
+
283
+ def update_server(self, server_type: str) -> str:
284
+ """update dedicated workspace server
285
+ :param server_type: dedicated instance to run workloads
286
+ for all available instances check xai.available_custom_servers()
287
+
288
+ :return: response
289
+ """
290
+ custom_servers = self.api_client.get(AVAILABLE_CUSTOM_SERVERS_URI)
291
+ Validate.value_against_list(
292
+ "server_type",
293
+ server_type,
294
+ [server["name"] for server in custom_servers],
295
+ )
296
+
297
+ payload = {
298
+ "workspace_name": self.workspace_name,
299
+ "modify_req": {
300
+ "update_workspace": {
301
+ "workspace_name": self.user_workspace_name,
302
+ "instance_type": server_type,
303
+ },
304
+ "update_operational_hours": {},
305
+ },
306
+ }
307
+
308
+ res = self.api_client.post(UPDATE_WORKSPACE_URI, payload)
309
+
310
+ if not res["success"]:
311
+ raise Exception(res.get("details"))
312
+
313
+ return "Server Updated"
314
+
315
+ def __print__(self) -> str:
316
+ """User-friendly string representation."""
317
+ return f"Workspace(user_workspace_name='{self.user_workspace_name}', created_by='{self.created_by}', created_at='{self.created_at}')"
318
+
319
+ def __str__(self) -> str:
320
+ """Return printable representation."""
321
+ return self.__print__()
322
+
323
+ def __repr__(self) -> str:
324
+ """Return developer-friendly representation."""
325
+ return self.__print__()