simplai-sdk 0.1.0__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 (42) hide show
  1. billing/__init__.py +6 -0
  2. billing/api.py +55 -0
  3. billing/client.py +14 -0
  4. billing/schema.py +15 -0
  5. constants/__init__.py +90 -0
  6. core/__init__.py +53 -0
  7. core/agents/__init__.py +42 -0
  8. core/agents/execution/__init__.py +49 -0
  9. core/agents/execution/api.py +283 -0
  10. core/agents/execution/client.py +1139 -0
  11. core/agents/models.py +99 -0
  12. core/workflows/WORKFLOW_ARCHITECTURE.md +417 -0
  13. core/workflows/__init__.py +31 -0
  14. core/workflows/bulk/__init__.py +14 -0
  15. core/workflows/bulk/api.py +202 -0
  16. core/workflows/bulk/client.py +115 -0
  17. core/workflows/bulk/schema.py +58 -0
  18. core/workflows/models.py +49 -0
  19. core/workflows/scheduling/__init__.py +9 -0
  20. core/workflows/scheduling/api.py +179 -0
  21. core/workflows/scheduling/client.py +128 -0
  22. core/workflows/scheduling/schema.py +74 -0
  23. core/workflows/tool_execution/__init__.py +16 -0
  24. core/workflows/tool_execution/api.py +172 -0
  25. core/workflows/tool_execution/client.py +195 -0
  26. core/workflows/tool_execution/schema.py +40 -0
  27. exceptions/__init__.py +21 -0
  28. simplai_sdk/__init__.py +7 -0
  29. simplai_sdk/simplai.py +239 -0
  30. simplai_sdk-0.1.0.dist-info/METADATA +728 -0
  31. simplai_sdk-0.1.0.dist-info/RECORD +42 -0
  32. simplai_sdk-0.1.0.dist-info/WHEEL +5 -0
  33. simplai_sdk-0.1.0.dist-info/licenses/LICENSE +21 -0
  34. simplai_sdk-0.1.0.dist-info/top_level.txt +7 -0
  35. traces/__init__.py +1 -0
  36. traces/agents/__init__.py +55 -0
  37. traces/agents/api.py +350 -0
  38. traces/agents/client.py +697 -0
  39. traces/agents/models.py +249 -0
  40. traces/workflows/__init__.py +0 -0
  41. utils/__init__.py +0 -0
  42. utils/config.py +117 -0
@@ -0,0 +1,249 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass, field
4
+ from datetime import datetime
5
+ from typing import Any, Dict, List, Optional
6
+
7
+
8
+ @dataclass
9
+ class EvalMetrics:
10
+ """Evaluation metrics for a trace."""
11
+ metric: str
12
+ score: Optional[float] = None
13
+ status: Optional[str] = None
14
+ passing_threshold: Optional[float] = None
15
+ desc: Optional[str] = None
16
+ failed_remark: Optional[str] = None
17
+ evaluated_at: Optional[datetime] = None
18
+
19
+
20
+ @dataclass
21
+ class RagTrace:
22
+ """Request-level trace (RAG trace) representing an agent conversation."""
23
+ id: Optional[str] = None
24
+ request_id: Optional[str] = None
25
+ conversation_id: Optional[str] = None
26
+ user_id: Optional[int] = None
27
+ username: Optional[str] = None
28
+ tenant_id: Optional[int] = None
29
+ workflow_id: Optional[str] = None
30
+ workflow_name: Optional[str] = None
31
+ agent_id: Optional[str] = None
32
+ agent_name: Optional[str] = None
33
+ kb_id: Optional[int] = None
34
+ kb_name: Optional[str] = None
35
+ model_id: Optional[str] = None
36
+ model_name: Optional[str] = None
37
+ model_input_tokens: Optional[int] = None
38
+ model_output_tokens: Optional[int] = None
39
+ kb_query_tokens: Optional[int] = None
40
+ context_tokens: Optional[int] = None
41
+ query: Optional[str] = None
42
+ prompt: Optional[str] = None
43
+ context: Optional[List[str]] = None
44
+ response: Optional[str] = None
45
+ response_time: Optional[int] = None
46
+ model_response_time: Optional[int] = None
47
+ kb_response_time: Optional[int] = None
48
+ user_feedback: Optional[bool] = None
49
+ source: List[str] = field(default_factory=list)
50
+ created_at: Optional[datetime] = None
51
+ updated_at: Optional[datetime] = None
52
+ evaluation_started_by: Optional[int] = None
53
+ evaluation_started_by_name: Optional[str] = None
54
+ eval_metrics: Optional[List[EvalMetrics]] = None
55
+ status: Optional[str] = None
56
+ ground_truth: Optional[str] = None
57
+ payload: Optional[Dict[str, Any]] = None # Full API response
58
+
59
+
60
+ @dataclass
61
+ class TraceData:
62
+ """Data payload for a trace node."""
63
+ input: Optional[Dict[str, Any]] = None
64
+ output: Optional[Dict[str, Any]] = None
65
+ error: Optional[str] = None
66
+ metadata: Optional[Dict[str, Any]] = None
67
+
68
+
69
+ @dataclass
70
+ class ChildInfo:
71
+ """Information about a child node."""
72
+ node_id: str
73
+ name: str
74
+ entity_type: Optional[str] = None
75
+
76
+
77
+ @dataclass
78
+ class TraceNode:
79
+ """Tree-based trace node representing a step in agent/workflow execution."""
80
+ id: Optional[str] = None
81
+ tenant_id: Optional[int] = None
82
+ project_id: Optional[int] = None
83
+ user_id: Optional[int] = None
84
+ source: Optional[str] = None
85
+ is_bulk_run_parent: Optional[bool] = None
86
+ node_id: Optional[str] = None # span id
87
+ name: Optional[str] = None # step name (tool name, agent name, etc.)
88
+ entity_type: Optional[str] = None # agent, tool, step, model, kb
89
+ entity_id: Optional[str] = None
90
+ tree_id: Optional[str] = None
91
+ parent_id: Optional[str] = None
92
+ parent_span_id: Optional[str] = None
93
+ children: Optional[List[TraceNode]] = None
94
+ descendants: Optional[List[ChildInfo]] = None
95
+ path: Optional[str] = None # ancestor chain based on node name
96
+ data: Optional[TraceData] = None
97
+ has_children: Optional[bool] = None
98
+ created_at: Optional[datetime] = None
99
+ execution_time: Optional[datetime] = None
100
+ profile_id: Optional[str] = None
101
+ profile: Optional[str] = None
102
+ is_root: Optional[bool] = None
103
+ sub_entity_id: Optional[str] = None
104
+ completion_tokens: Optional[int] = None
105
+ prompt_tokens: Optional[int] = None
106
+ total_tokens: Optional[int] = None
107
+ latency: Optional[float] = None
108
+ context_window_utilization: Optional[float] = None
109
+ status: Optional[str] = None
110
+ origin: Optional[str] = None
111
+ app_id: Optional[str] = None
112
+ child_count: Optional[int] = None
113
+ payload: Optional[Dict[str, Any]] = None # Full API response
114
+
115
+
116
+ @dataclass
117
+ class FetchTraceFilters:
118
+ """Filters for fetching request-level traces (RAG traces)."""
119
+ tenant_id: Optional[int] = None
120
+ user_id: Optional[int] = None
121
+ request_id: Optional[str] = None
122
+ query: Optional[str] = None
123
+ workflow_id: Optional[str] = None
124
+ agent_id: Optional[str] = None
125
+ is_agent: Optional[bool] = None
126
+ kb_id: Optional[int] = None
127
+ model_id: Optional[str] = None
128
+ search_filter: Optional[str] = None
129
+ created_at_from: Optional[datetime] = None
130
+ created_at_to: Optional[datetime] = None
131
+
132
+ def to_query_params(self) -> Dict[str, Any]:
133
+ """Convert filters to query parameters."""
134
+ params: Dict[str, Any] = {}
135
+ if self.user_id is not None:
136
+ params["userId"] = str(self.user_id)
137
+ if self.request_id:
138
+ params["requestId"] = self.request_id
139
+ if self.query:
140
+ params["query"] = self.query
141
+ if self.workflow_id:
142
+ params["workflowId"] = self.workflow_id
143
+ if self.agent_id:
144
+ params["agentId"] = self.agent_id
145
+ if self.is_agent is not None:
146
+ params["isAgent"] = str(self.is_agent).lower()
147
+ if self.kb_id is not None:
148
+ params["kbId"] = str(self.kb_id)
149
+ if self.model_id:
150
+ params["modelId"] = self.model_id
151
+ if self.search_filter:
152
+ params["searchFilter"] = self.search_filter
153
+ if self.created_at_from:
154
+ params["createdAtFrom"] = self.created_at_from.strftime("%Y-%m-%d %H:%M:%S")
155
+ if self.created_at_to:
156
+ params["createdAtTo"] = self.created_at_to.strftime("%Y-%m-%d %H:%M:%S")
157
+ return params
158
+
159
+
160
+ @dataclass
161
+ class TraceFilters:
162
+ """Filters for fetching tree-based traces."""
163
+ tenant_id: Optional[int] = None
164
+ user_id: Optional[int] = None
165
+ project_id: Optional[int] = None
166
+ path: Optional[str] = None
167
+ name: Optional[str] = None
168
+ source: Optional[List[str]] = None
169
+ is_get_all: bool = False
170
+ profile: Optional[str] = None
171
+ profile_id: Optional[str] = None
172
+ entity_id: Optional[str] = None
173
+ sub_entity_id: Optional[str] = None
174
+ version: Optional[str] = None
175
+ status: Optional[List[str]] = None
176
+ origin: Optional[List[str]] = None
177
+ created_at_from: Optional[datetime] = None
178
+ created_at_to: Optional[datetime] = None
179
+
180
+ def to_query_params(self) -> Dict[str, Any]:
181
+ """Convert filters to query parameters."""
182
+ params: Dict[str, Any] = {}
183
+ if self.user_id is not None:
184
+ params["userId"] = str(self.user_id)
185
+ if self.path:
186
+ params["path"] = self.path
187
+ if self.name:
188
+ params["name"] = self.name
189
+ if self.source:
190
+ params["source"] = ",".join(self.source)
191
+ if self.is_get_all:
192
+ params["isGetAll"] = "true"
193
+ if self.profile:
194
+ params["profile"] = self.profile
195
+ if self.profile_id:
196
+ params["profileId"] = self.profile_id
197
+ if self.entity_id:
198
+ params["entityId"] = self.entity_id
199
+ if self.sub_entity_id:
200
+ params["subEntityId"] = self.sub_entity_id
201
+ if self.version:
202
+ params["version"] = self.version
203
+ if self.status:
204
+ params["status"] = ",".join(self.status)
205
+ if self.origin:
206
+ params["origin"] = ",".join(self.origin)
207
+ if self.created_at_from:
208
+ params["createdAtFrom"] = self.created_at_from.strftime("%Y-%m-%d %H:%M:%S")
209
+ if self.created_at_to:
210
+ params["createdAtTo"] = self.created_at_to.strftime("%Y-%m-%d %H:%M:%S")
211
+ return params
212
+
213
+
214
+ @dataclass
215
+ class MetricsRequestDto:
216
+ """Request DTO for fetching metrics."""
217
+ tenant_id: Optional[int] = None
218
+ project_id: Optional[int] = None
219
+ app_id: Optional[str] = None
220
+ from_date: Optional[datetime] = None
221
+ to_date: Optional[datetime] = None
222
+
223
+ def to_query_params(self) -> Dict[str, Any]:
224
+ """Convert to query parameters."""
225
+ params: Dict[str, Any] = {}
226
+ if self.app_id:
227
+ params["appId"] = self.app_id
228
+ if self.from_date:
229
+ params["fromDate"] = self.from_date.strftime("%Y-%m-%d %H:%M:%S")
230
+ if self.to_date:
231
+ params["toDate"] = self.to_date.strftime("%Y-%m-%d %H:%M:%S")
232
+ return params
233
+
234
+
235
+ @dataclass
236
+ class PageableResponse:
237
+ """Paginated response wrapper."""
238
+ success: bool
239
+ status: str
240
+ data: Any # Can be Page[RagTrace] or Page[TraceNode]
241
+ total_elements: Optional[int] = None
242
+ total_pages: Optional[int] = None
243
+ page_number: Optional[int] = None
244
+ page_size: Optional[int] = None
245
+
246
+
247
+ class TraceError(Exception):
248
+ """Raised when a trace operation fails."""
249
+
File without changes
utils/__init__.py ADDED
File without changes
utils/config.py ADDED
@@ -0,0 +1,117 @@
1
+ """Configuration utilities for loading API keys and base URLs.
2
+
3
+ This module provides utilities to load configuration values with the following priority:
4
+ 1. User-provided parameter
5
+ 2. Environment variable
6
+ 3. .env file (if python-dotenv is available)
7
+ 4. Default value (for base_url) or error (for api_key)
8
+ """
9
+
10
+ import os
11
+ from pathlib import Path
12
+ from typing import Optional
13
+
14
+
15
+ def _load_env_file() -> None:
16
+ """Attempt to load .env file from common locations."""
17
+ try:
18
+ from dotenv import load_dotenv
19
+ except ImportError:
20
+ # python-dotenv not installed, skip loading .env
21
+ return
22
+
23
+ # Try to find .env file in common locations
24
+ current_dir = Path.cwd()
25
+ possible_env_paths = [
26
+ current_dir / ".env",
27
+ current_dir.parent / ".env", # python-sdk/.env
28
+ Path(__file__).parent.parent.parent / ".env", # python-sdk/.env from src/utils
29
+ ]
30
+
31
+ for env_path in possible_env_paths:
32
+ if env_path.exists():
33
+ load_dotenv(dotenv_path=env_path, override=False)
34
+ break
35
+ else:
36
+ # Fallback: try loading from current directory
37
+ load_dotenv(override=False)
38
+
39
+
40
+ def get_api_key(api_key: Optional[str] = None) -> str:
41
+ """
42
+ Get API key with fallback priority:
43
+ 1. User-provided api_key parameter
44
+ 2. API_KEY from environment variable
45
+ 3. API_KEY from .env file
46
+ 4. Raise error if not found
47
+
48
+ Args:
49
+ api_key: Optional API key provided by user
50
+
51
+ Returns:
52
+ API key string
53
+
54
+ Raises:
55
+ ValueError: If API key is not found in any source
56
+ """
57
+ # First priority: user-provided API key
58
+ if api_key:
59
+ return api_key
60
+
61
+ # Second priority: check environment variable (may already be set)
62
+ env_api_key = os.getenv("SIMPAI_API_KEY")
63
+ if env_api_key:
64
+ return env_api_key
65
+
66
+ # Third priority: try to load from .env file
67
+ _load_env_file()
68
+
69
+ # Check environment variable again after loading .env
70
+ env_api_key = os.getenv("SIMPAI_API_KEY")
71
+ if env_api_key:
72
+ return env_api_key
73
+
74
+ # Fourth priority: raise error
75
+ raise ValueError(
76
+ "API key is required. Provide it using one of the following methods:\n"
77
+ " 1. As a parameter: SimplAI(api_key='your-key')\n"
78
+ " 2. As environment variable: export API_KEY=your-key\n"
79
+ " 3. In a .env file: API_KEY=your-key"
80
+ )
81
+
82
+
83
+ def get_base_url(base_url: Optional[str] = None) -> str:
84
+ """
85
+ Get base URL with fallback priority:
86
+ 1. User-provided base_url parameter
87
+ 2. BASE_URL from environment variable
88
+ 3. BASE_URL from .env file
89
+ 4. Default base URL constant
90
+
91
+ Args:
92
+ base_url: Optional base URL provided by user
93
+
94
+ Returns:
95
+ Base URL string
96
+ """
97
+ # First priority: user-provided base URL
98
+ if base_url:
99
+ return base_url.rstrip("/")
100
+
101
+ # Second priority: check environment variable (may already be set)
102
+ env_base_url = os.getenv("SIMPAI_BASE_URL")
103
+ if env_base_url:
104
+ return env_base_url.rstrip("/")
105
+
106
+ # Third priority: try to load from .env file
107
+ _load_env_file()
108
+
109
+ # Check environment variable again after loading .env
110
+ env_base_url = os.getenv("SIMPAI_BASE_URL")
111
+ if env_base_url:
112
+ return env_base_url.rstrip("/")
113
+
114
+ # Fourth priority: default base URL
115
+ from constants import DEFAULT_BASE_URL
116
+
117
+ return DEFAULT_BASE_URL