supervaizer 0.10.5__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 (76) hide show
  1. supervaizer/__init__.py +97 -0
  2. supervaizer/__version__.py +10 -0
  3. supervaizer/account.py +308 -0
  4. supervaizer/account_service.py +93 -0
  5. supervaizer/admin/routes.py +1293 -0
  6. supervaizer/admin/static/js/job-start-form.js +373 -0
  7. supervaizer/admin/templates/agent_detail.html +145 -0
  8. supervaizer/admin/templates/agents.html +249 -0
  9. supervaizer/admin/templates/agents_grid.html +82 -0
  10. supervaizer/admin/templates/base.html +233 -0
  11. supervaizer/admin/templates/case_detail.html +230 -0
  12. supervaizer/admin/templates/cases_list.html +182 -0
  13. supervaizer/admin/templates/cases_table.html +134 -0
  14. supervaizer/admin/templates/console.html +389 -0
  15. supervaizer/admin/templates/dashboard.html +153 -0
  16. supervaizer/admin/templates/job_detail.html +192 -0
  17. supervaizer/admin/templates/job_start_test.html +109 -0
  18. supervaizer/admin/templates/jobs_list.html +180 -0
  19. supervaizer/admin/templates/jobs_table.html +122 -0
  20. supervaizer/admin/templates/navigation.html +163 -0
  21. supervaizer/admin/templates/recent_activity.html +81 -0
  22. supervaizer/admin/templates/server.html +105 -0
  23. supervaizer/admin/templates/server_status_cards.html +121 -0
  24. supervaizer/admin/templates/supervaize_instructions.html +212 -0
  25. supervaizer/agent.py +956 -0
  26. supervaizer/case.py +432 -0
  27. supervaizer/cli.py +395 -0
  28. supervaizer/common.py +324 -0
  29. supervaizer/deploy/__init__.py +16 -0
  30. supervaizer/deploy/cli.py +305 -0
  31. supervaizer/deploy/commands/__init__.py +9 -0
  32. supervaizer/deploy/commands/clean.py +294 -0
  33. supervaizer/deploy/commands/down.py +119 -0
  34. supervaizer/deploy/commands/local.py +460 -0
  35. supervaizer/deploy/commands/plan.py +167 -0
  36. supervaizer/deploy/commands/status.py +169 -0
  37. supervaizer/deploy/commands/up.py +281 -0
  38. supervaizer/deploy/docker.py +377 -0
  39. supervaizer/deploy/driver_factory.py +42 -0
  40. supervaizer/deploy/drivers/__init__.py +39 -0
  41. supervaizer/deploy/drivers/aws_app_runner.py +607 -0
  42. supervaizer/deploy/drivers/base.py +196 -0
  43. supervaizer/deploy/drivers/cloud_run.py +570 -0
  44. supervaizer/deploy/drivers/do_app_platform.py +504 -0
  45. supervaizer/deploy/health.py +404 -0
  46. supervaizer/deploy/state.py +210 -0
  47. supervaizer/deploy/templates/Dockerfile.template +44 -0
  48. supervaizer/deploy/templates/debug_env.py +69 -0
  49. supervaizer/deploy/templates/docker-compose.yml.template +37 -0
  50. supervaizer/deploy/templates/dockerignore.template +66 -0
  51. supervaizer/deploy/templates/entrypoint.sh +20 -0
  52. supervaizer/deploy/utils.py +52 -0
  53. supervaizer/event.py +181 -0
  54. supervaizer/examples/controller_template.py +196 -0
  55. supervaizer/instructions.py +145 -0
  56. supervaizer/job.py +392 -0
  57. supervaizer/job_service.py +156 -0
  58. supervaizer/lifecycle.py +417 -0
  59. supervaizer/parameter.py +233 -0
  60. supervaizer/protocol/__init__.py +11 -0
  61. supervaizer/protocol/a2a/__init__.py +21 -0
  62. supervaizer/protocol/a2a/model.py +227 -0
  63. supervaizer/protocol/a2a/routes.py +99 -0
  64. supervaizer/py.typed +1 -0
  65. supervaizer/routes.py +917 -0
  66. supervaizer/server.py +553 -0
  67. supervaizer/server_utils.py +54 -0
  68. supervaizer/storage.py +462 -0
  69. supervaizer/telemetry.py +81 -0
  70. supervaizer/utils/__init__.py +16 -0
  71. supervaizer/utils/version_check.py +56 -0
  72. supervaizer-0.10.5.dist-info/METADATA +317 -0
  73. supervaizer-0.10.5.dist-info/RECORD +76 -0
  74. supervaizer-0.10.5.dist-info/WHEEL +4 -0
  75. supervaizer-0.10.5.dist-info/entry_points.txt +2 -0
  76. supervaizer-0.10.5.dist-info/licenses/LICENSE.md +346 -0
@@ -0,0 +1,97 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+
8
+ from supervaizer import protocol
9
+ from supervaizer.account import Account
10
+ from supervaizer.agent import (
11
+ Agent,
12
+ AgentCustomMethodParams,
13
+ AgentMethod,
14
+ AgentMethodParams,
15
+ AgentMethods,
16
+ )
17
+ from supervaizer.case import (
18
+ Case,
19
+ CaseNodeUpdate,
20
+ CaseNodeType,
21
+ Cases,
22
+ CaseNode,
23
+ CaseNodes,
24
+ )
25
+ from supervaizer.common import ApiError, ApiResult, ApiSuccess
26
+ from supervaizer.event import (
27
+ AgentRegisterEvent,
28
+ CaseStartEvent,
29
+ CaseUpdateEvent,
30
+ Event,
31
+ EventType,
32
+ JobFinishedEvent,
33
+ JobStartConfirmationEvent,
34
+ ServerRegisterEvent,
35
+ )
36
+ from supervaizer.job import Job, JobContext, JobInstructions, JobResponse, Jobs
37
+ from supervaizer.lifecycle import EntityEvents, EntityLifecycle, EntityStatus
38
+ from supervaizer.parameter import Parameter, ParametersSetup
39
+ from supervaizer.server import Server
40
+ from supervaizer.server_utils import ErrorResponse, ErrorType, create_error_response
41
+ from supervaizer.telemetry import (
42
+ Telemetry,
43
+ TelemetryCategory,
44
+ TelemetrySeverity,
45
+ TelemetryType,
46
+ )
47
+
48
+ __all__ = [
49
+ "Account",
50
+ "Agent",
51
+ "AgentCustomMethodParams",
52
+ "AgentMethod",
53
+ "AgentMethodParams",
54
+ "AgentMethods",
55
+ "AgentRegisterEvent",
56
+ "ApiError",
57
+ "ApiResult",
58
+ "ApiSuccess",
59
+ "Case",
60
+ "CaseNodeUpdate",
61
+ "CaseNodeType",
62
+ "Cases",
63
+ "CaseNode",
64
+ "CaseNodes",
65
+ "CaseStartEvent",
66
+ "CaseUpdateEvent",
67
+ "create_error_response",
68
+ "EntityEvents",
69
+ "EntityEvents",
70
+ "EntityLifecycle",
71
+ "EntityLifecycle",
72
+ "EntityStatus",
73
+ "EntityStatus",
74
+ "ErrorResponse",
75
+ "ErrorType",
76
+ "Event",
77
+ "EventType",
78
+ "Job",
79
+ "JobContext",
80
+ "JobFinishedEvent",
81
+ "JobInstructions",
82
+ "JobResponse",
83
+ "Jobs",
84
+ "JobStartConfirmationEvent",
85
+ "Parameter",
86
+ "ParametersSetup",
87
+ "protocol",
88
+ "Server",
89
+ "ServerRegisterEvent",
90
+ "Telemetry",
91
+ "TelemetryCategory",
92
+ "TelemetrySeverity",
93
+ "TelemetryType",
94
+ ]
95
+
96
+ # Rebuild models to resolve forward references after all imports are done
97
+ Case.model_rebuild()
@@ -0,0 +1,10 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+
8
+ VERSION = "0.10.5"
9
+ API_VERSION = "v1"
10
+ TELEMETRY_VERSION = "v1"
supervaizer/account.py ADDED
@@ -0,0 +1,308 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+
8
+ from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Union
9
+
10
+ import httpx
11
+ from pydantic import Field
12
+
13
+ from supervaizer.__version__ import VERSION
14
+ from supervaizer.common import ApiError, ApiResult, ApiSuccess, SvBaseModel
15
+ from supervaizer.telemetry import Telemetry
16
+
17
+ if TYPE_CHECKING:
18
+ from supervaizer.agent import Agent
19
+ from supervaizer.case import Case, CaseNodeUpdate
20
+ from supervaizer.event import Event
21
+ from supervaizer.job import Job
22
+ from supervaizer.server import Server
23
+ from supervaizer.common import log
24
+
25
+
26
+ class AccountAbstract(SvBaseModel):
27
+ """
28
+ Account model for the Supervaize Control API.
29
+
30
+ This represents an account that can be used to authenticate and communicate
31
+ with the Supervaize SaaS API. Account parameters are provided by Supervaize.com.
32
+ Create an account at [https://supervaize.com/](https://supervaize.com/)
33
+
34
+ The account ID is generated by Supervaize.com. The account API key is provided
35
+ by Supervaize.com and is used to authenticate requests to the Supervaize Control API.
36
+ The API URL is provided by Supervaize.com and is the URL of the Supervaize SaaS API.
37
+
38
+ The account provides methods for registering servers and agents, sending events,
39
+ and communicating with the Supervaize platform.
40
+
41
+
42
+
43
+ Attributes:
44
+ workspace_id (str): The workspace ID provided by Supervaize.com
45
+ api_key (str): The API key provided by Supervaize.com for authentication
46
+ api_url (str): The URL of the Supervaize SaaS API provided by Supervaize.com
47
+ """
48
+
49
+ supervaizer_VERSION: ClassVar[str] = VERSION
50
+ workspace_id: str = Field(description="The workspace ID provided by Supervaize.com")
51
+ api_key: str = Field(
52
+ description="The API key provided by Supervaize.com for authentication"
53
+ )
54
+ api_url: str = Field(
55
+ description="The URL of the Supervaize SaaS API provided by Supervaize.com"
56
+ )
57
+
58
+ model_config = {
59
+ "reference_group": "Core",
60
+ "json_schema_extra": {
61
+ "examples": [
62
+ {
63
+ "workspace_id": "ws_1234567890abcdef",
64
+ "api_key": "sk_1234567890abcdef",
65
+ "api_url": "https://api.supervaize.com",
66
+ }
67
+ ]
68
+ },
69
+ }
70
+
71
+
72
+ class Account(AccountAbstract):
73
+ # URL patterns dictionary
74
+ _URL_PATTERNS = {
75
+ "team": "{api_url}/w/{workspace_id}",
76
+ "event": "{api_url}/w/{workspace_id}/api/v1/ctrl-events/",
77
+ "agent_by_id": "{api_url}/w/{workspace_id}/api/v1/agents/{agent_id}",
78
+ "agent_by_slug": "{api_url}/w/{workspace_id}/api/v1/agents/by-slug/{agent_slug}",
79
+ "telemetry": "{api_url}/{telemetry_version}/telemetry",
80
+ }
81
+
82
+ def __init__(self, **kwargs: Any) -> None:
83
+ """Account parameters are provided by Supervaize.com.
84
+ The account ID is generated by Supervaize.com. The account API key is provided by Supervaize.com and is used
85
+ to authenticate requests to the Supervaize Control API.
86
+ The API URL is provided by Supervaize.com and is the URL of the Supervaize
87
+ SaaS API.
88
+
89
+ Raises:
90
+ ValueError: If the account ID does not match the account name
91
+
92
+ Tested in tests/test_account.py
93
+ """
94
+ super().__init__(**kwargs)
95
+
96
+ @property
97
+ def api_url_w_v1(self) -> str:
98
+ """URL for the Supervaize Control API.
99
+ Tested in tests/test_account.py
100
+ """
101
+ return f"{self.api_url}/w/{self.workspace_id}/api/v1"
102
+
103
+ @property
104
+ def api_headers(self) -> Dict[str, str]:
105
+ """Headers for the Supervaize Control API.
106
+ Tested in tests/test_account.py
107
+ """
108
+ return {
109
+ "Authorization": f"Api-Key {self.api_key}",
110
+ "accept": "application/json",
111
+ "workspace": self.workspace_id,
112
+ }
113
+
114
+ @property
115
+ def api_url_team(self) -> str:
116
+ """URL for the Supervaize workspace team."""
117
+ return self.get_url("team")
118
+
119
+ @property
120
+ def url_event(self) -> str:
121
+ """URL for the Supervaize Control API.
122
+ Tested in tests/test_account.py
123
+ """
124
+ return f"{self.api_url_w_v1}/ctrl-events/"
125
+
126
+ def get_url(self, pattern_name: str, **kwargs: Any) -> str:
127
+ """Generate a URL using the predefined patterns.
128
+
129
+ Args:
130
+ pattern_name (str): The name of the URL pattern to use
131
+ **kwargs: Additional parameters for URL formatting
132
+
133
+ Returns:
134
+ str: The formatted URL
135
+
136
+ Raises:
137
+ KeyError: If the pattern_name is not found in _URL_PATTERNS
138
+ """
139
+ if pattern_name not in self._URL_PATTERNS:
140
+ raise KeyError(f"URL pattern '{pattern_name}' not found")
141
+
142
+ pattern = self._URL_PATTERNS[pattern_name]
143
+
144
+ # Default values from the account
145
+ url_params = {
146
+ "api_url": self.api_url,
147
+ "workspace_id": self.workspace_id,
148
+ "telemetry_version": "v1", # Default telemetry version
149
+ }
150
+
151
+ # Override with any provided kwargs
152
+ url_params.update(kwargs)
153
+
154
+ return pattern.format(**url_params)
155
+
156
+ def send_event(
157
+ self,
158
+ sender: Union["Agent", "Job", "Server", "Case", "CaseNodeUpdate"],
159
+ event: "Event",
160
+ ) -> ApiResult:
161
+ """Send an event to the Supervaize SaaS API.
162
+
163
+ Args:
164
+ sender (Union[Agent, Server, Case, CaseNodeUpdate]): The sender of the event
165
+ event (Event): The event to be sent
166
+
167
+ Returns:
168
+ ApiResult: ApiSuccess with response details if successful,
169
+ Raises:
170
+ Request exception if the request fails.
171
+ """
172
+ # Import here to avoid circular imports
173
+ from supervaizer.account_service import send_event as service_send_event
174
+
175
+ return service_send_event(self, sender, event)
176
+
177
+ def register_server(self, server: "Server") -> ApiResult:
178
+ """Register a server with the Supervaize Control API.
179
+
180
+ Args:
181
+ server (Server): The server to register.
182
+
183
+ Returns:
184
+ ApiResult: ApiSuccess with response details if successful,
185
+ ApiError with error details if request fails
186
+
187
+ Side effects:
188
+ - Sends a ServerRegisterEvent to the Supervaize Control API
189
+ """
190
+ # Import here to avoid circular imports
191
+ from supervaizer.event import ServerRegisterEvent
192
+
193
+ event = ServerRegisterEvent(server=server, account=self)
194
+ return self.send_event(sender=server, event=event)
195
+
196
+ def _create_api_result(
197
+ self,
198
+ success: bool,
199
+ message: str,
200
+ detail: Any = None,
201
+ url: str = "",
202
+ exception: Optional[Exception] = None,
203
+ ) -> ApiResult:
204
+ if success:
205
+ return ApiSuccess(message=message, detail=detail)
206
+ return ApiError(message=message, url=url, payload=None, exception=exception)
207
+
208
+ def get_agent_by(
209
+ self, agent_id: str | None = None, agent_slug: str | None = None
210
+ ) -> ApiSuccess | ApiError:
211
+ """Get an agent from the Supervaize Control API.
212
+
213
+ Args:
214
+ agent_id (str): The ID of the agent to get.
215
+ agent_slut (str): The name of the agent to get. (unique per workspace)
216
+
217
+ Returns:
218
+ ApiResult: ApiSuccess with response details if successful,ApiError with error details if request fails
219
+
220
+ """
221
+ url = ""
222
+ if agent_id:
223
+ url = f"{self.api_url_w_v1}/agents/{agent_id}"
224
+ elif agent_slug:
225
+ url = f"{self.api_url_w_v1}/agents/by-slug/{agent_slug}"
226
+ else:
227
+ raise ValueError("No agent ID or slug provided")
228
+
229
+ headers = self.api_headers
230
+
231
+ try:
232
+ response = httpx.get(url, headers=headers, follow_redirects=True)
233
+ response.raise_for_status()
234
+ return ApiSuccess(
235
+ message=f"GET Agent <{agent_slug or agent_id}>",
236
+ detail=response.json(),
237
+ )
238
+ except httpx.HTTPError as e:
239
+ return ApiError(
240
+ message=f"Error GET Agent <{agent_slug or agent_id}>",
241
+ url=url,
242
+ payload=None,
243
+ exception=e,
244
+ )
245
+
246
+ def register_agent(self, agent: "Agent", polling: bool = True) -> ApiResult:
247
+ """Send a registration event to the Supervaize Control API.
248
+ This will be used for polling, when the agent is registered without a server.
249
+ Args:
250
+ agent (Agent): The agent sending the registration event
251
+ polling (bool): If server is not defined, polling will be used.
252
+
253
+ Returns:
254
+ ApiResult: ApiSuccess with response details if successful,
255
+ ApiError with error details if request fails
256
+ """
257
+ # Import here to avoid circular imports
258
+ from supervaizer.event import AgentRegisterEvent
259
+
260
+ event = AgentRegisterEvent(agent=agent, account=self, polling=polling)
261
+ return self.send_event(agent, event)
262
+
263
+ def send_start_case(self, case: "Case") -> ApiResult:
264
+ # Import here to avoid circular imports
265
+ from supervaizer.event import CaseStartEvent
266
+
267
+ event = CaseStartEvent(case=case, account=self)
268
+ return self.send_event(case, event)
269
+
270
+ def send_update_case(self, case: "Case", update: "CaseNodeUpdate") -> ApiResult:
271
+ # Import here to avoid circular imports
272
+ log.debug(f"[send_update_case] CaseRef {case} with update {update}")
273
+ log.debug(f"[send_update_case] {type(case)}")
274
+ log.debug(f"[send_update_case] {type(update)}")
275
+ from supervaizer.event import CaseUpdateEvent
276
+
277
+ event = CaseUpdateEvent(case=case, update=update, account=self)
278
+ return self.send_event(update, event)
279
+
280
+ def send_telemetry(self, telemetry: Telemetry) -> ApiResult:
281
+ """Send telemetry data to the Supervaize Control API.
282
+
283
+ Args:
284
+ telemetry (Telemetry): The telemetry object to be sent
285
+
286
+ Returns:
287
+ ApiResult: ApiSuccess with response details if successful,
288
+ ApiError with error details if request fails
289
+ """
290
+ url = self.get_url("telemetry")
291
+ headers = self.api_headers
292
+ payload = {"workspace_id": self.workspace_id} | telemetry.payload
293
+ result: ApiSuccess | ApiError
294
+ try:
295
+ response = httpx.post(url, headers=headers, json=payload)
296
+ response.raise_for_status()
297
+ result = ApiSuccess(
298
+ message=f"Telemetry sent {telemetry.type.name}", detail=response.text
299
+ )
300
+ except httpx.HTTPError as e:
301
+ result = ApiError(
302
+ message=f"Error sending telemetry {telemetry.type.name}",
303
+ url=url,
304
+ payload=payload,
305
+ exception=e,
306
+ )
307
+
308
+ return result
@@ -0,0 +1,93 @@
1
+ # Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
2
+ #
3
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
4
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
5
+ # https://mozilla.org/MPL/2.0/.
6
+
7
+ # This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
8
+ # If a copy of the MPL was not distributed with this file, you can obtain one at
9
+ # https://mozilla.org/MPL/2.0/.
10
+
11
+ import logging
12
+ import os
13
+ from typing import TYPE_CHECKING, Union
14
+
15
+ import httpx
16
+
17
+ from supervaizer.common import ApiError, ApiResult, ApiSuccess, log
18
+
19
+ logger = logging.getLogger("httpx")
20
+ # Enable httpx debug logging (optional - uncomment for transport-level debugging)
21
+ logger.setLevel(logging.DEBUG)
22
+
23
+ _httpx_transport = httpx.HTTPTransport(
24
+ retries=int(os.getenv("SUPERVAIZE_HTTP_MAX_RETRIES", 2))
25
+ )
26
+ _httpx_client = httpx.Client(transport=_httpx_transport)
27
+
28
+ if TYPE_CHECKING:
29
+ from supervaizer.account import Account
30
+ from supervaizer.agent import Agent
31
+ from supervaizer.case import Case, CaseNodeUpdate
32
+ from supervaizer.event import Event
33
+ from supervaizer.job import Job
34
+ from supervaizer.server import Server
35
+
36
+
37
+ def send_event(
38
+ account: "Account",
39
+ sender: Union["Agent", "Server", "Job", "Case", "CaseNodeUpdate"],
40
+ event: "Event",
41
+ ) -> ApiResult:
42
+ """Send an event to the Supervaize SaaS API.
43
+
44
+ Args:
45
+ account (Account): The account used to authenticate the request
46
+ sender (Union[Agent, Server, Case, CaseNodeUpdate]): The sender of the event
47
+ event (Event): The event to be sent
48
+
49
+ Returns:
50
+ ApiResult: ApiSuccess with response details if successful,
51
+ Raises:
52
+ Request exception if the request fails.
53
+
54
+ Side effects:
55
+ - Sends an event to the Supervaize Control API
56
+
57
+ Tested in tests/test_account_service.py
58
+ """
59
+
60
+ headers = account.api_headers
61
+ payload = event.payload
62
+
63
+ # Generate curl equivalent for debugging
64
+
65
+ curl_headers = " ".join([f'-H "{k}: {v}"' for k, v in headers.items()])
66
+ curl_cmd = f"curl -X 'GET' '{account.url_event}' {curl_headers}"
67
+
68
+ try:
69
+ response = _httpx_client.post(account.url_event, headers=headers, json=payload)
70
+
71
+ # Log response details before raising for status
72
+
73
+ response.raise_for_status()
74
+ result = ApiSuccess(
75
+ message=f"POST Event {event.type.name} sent", detail=response.text
76
+ )
77
+
78
+ log.success(result.log_message)
79
+ except httpx.HTTPError as e:
80
+ # Enhanced error logging
81
+ log.error("[Send event] HTTP Error occurred")
82
+ log.warning(f"⚠️ Try to connect via curl:\n{curl_cmd}")
83
+
84
+ error_result = ApiError(
85
+ message=f"Error sending event {event.type.name}",
86
+ url=account.url_event,
87
+ payload=event.payload,
88
+ exception=e,
89
+ )
90
+ log.error(f"[Send event] Error details: {error_result.dict}")
91
+ log.error(error_result.log_message)
92
+ raise e
93
+ return result