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