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,373 @@
1
+ /**
2
+ * JobStartForm - Handles job parameter validation and submission
3
+ *
4
+ * This class provides methods to validate agent parameters and method fields
5
+ * before starting a job, using the new validation endpoints.
6
+ */
7
+
8
+ class JobStartForm {
9
+ constructor(agentPath) {
10
+ this.agentPath = agentPath;
11
+ this.form = null;
12
+ this.errorContainer = null;
13
+ this.initialize();
14
+ }
15
+
16
+ initialize() {
17
+ // Wait for DOM to be ready
18
+ if (document.readyState === 'loading') {
19
+ document.addEventListener('DOMContentLoaded', () => this.setupForm());
20
+ } else {
21
+ this.setupForm();
22
+ }
23
+ }
24
+
25
+ setupForm() {
26
+ // Find the form and error container
27
+ this.form = document.querySelector('form[data-job-start]');
28
+ this.errorContainer = document.getElementById('validation-errors');
29
+
30
+ if (!this.form) {
31
+ console.warn('JobStartForm: No form found with data-job-start attribute');
32
+ return;
33
+ }
34
+
35
+ if (!this.errorContainer) {
36
+ console.warn('JobStartForm: No validation-errors container found');
37
+ return;
38
+ }
39
+
40
+ // Add submit handler
41
+ this.form.addEventListener('submit', (e) => this.handleSubmit(e));
42
+
43
+ // Add validation on field change
44
+ this.form.addEventListener('change', () => this.clearErrors());
45
+
46
+ console.log('JobStartForm initialized for', this.agentPath);
47
+ }
48
+
49
+ async handleSubmit(event) {
50
+ event.preventDefault();
51
+
52
+ // Clear previous errors
53
+ this.clearErrors();
54
+
55
+ // Validate both agent parameters and method fields
56
+ const [agentParamsValid, methodFieldsValid] = await Promise.all([
57
+ this.validateAgentParameters(),
58
+ this.validateMethodFields()
59
+ ]);
60
+
61
+ if (agentParamsValid && methodFieldsValid) {
62
+ // All validation passed, submit the form
63
+ this.submitForm();
64
+ }
65
+ }
66
+
67
+ async validateAgentParameters() {
68
+ const encryptedParams = this.getEncryptedAgentParameters();
69
+
70
+ if (!encryptedParams) {
71
+ return true; // No agent parameters to validate
72
+ }
73
+
74
+ try {
75
+ const response = await fetch(
76
+ `${this.agentPath}/validate-agent-parameters`,
77
+ {
78
+ method: 'POST',
79
+ headers: {
80
+ 'Content-Type': 'application/json',
81
+ 'X-API-Key': this.getApiKey()
82
+ },
83
+ body: JSON.stringify({
84
+ encrypted_agent_parameters: encryptedParams,
85
+ }),
86
+ }
87
+ );
88
+
89
+ const result = await response.json();
90
+
91
+ if (!result.valid) {
92
+ this.displayAgentParameterErrors(result);
93
+ return false;
94
+ }
95
+
96
+ return true;
97
+ } catch (error) {
98
+ console.error('Agent parameter validation failed:', error);
99
+ this.displayError('Agent parameter validation failed due to network error');
100
+ return false;
101
+ }
102
+ }
103
+
104
+ async validateMethodFields() {
105
+ const formData = this.getFormData();
106
+
107
+ try {
108
+ const response = await fetch(
109
+ `${this.agentPath}/validate-method-fields`,
110
+ {
111
+ method: 'POST',
112
+ headers: {
113
+ 'Content-Type': 'application/json',
114
+ 'X-API-Key': this.getApiKey()
115
+ },
116
+ body: JSON.stringify({
117
+ method_name: 'job_start',
118
+ job_fields: formData,
119
+ }),
120
+ }
121
+ );
122
+
123
+ const result = await response.json();
124
+
125
+ if (!result.valid) {
126
+ this.displayMethodFieldErrors(result);
127
+ return false;
128
+ }
129
+
130
+ return true;
131
+ } catch (error) {
132
+ console.error('Method field validation failed:', error);
133
+ this.displayError('Method field validation failed due to network error');
134
+ return false;
135
+ }
136
+ }
137
+
138
+ getFormData() {
139
+ const formData = {};
140
+ const formElements = this.form.elements;
141
+
142
+ for (let element of formElements) {
143
+ if (element.name && element.type !== 'submit') {
144
+ if (element.type === 'checkbox') {
145
+ formData[element.name] = element.checked;
146
+ } else if (element.type === 'radio') {
147
+ if (element.checked) {
148
+ formData[element.name] = element.value;
149
+ }
150
+ } else if (element.type === 'select-multiple') {
151
+ formData[element.name] = Array.from(element.selectedOptions).map(option => option.value);
152
+ } else {
153
+ formData[element.name] = element.value;
154
+ }
155
+ }
156
+ }
157
+
158
+ return formData;
159
+ }
160
+
161
+ getEncryptedAgentParameters() {
162
+ // Look for encrypted agent parameters in hidden fields or data attributes
163
+ const encryptedField = this.form.querySelector('[name="encrypted_agent_parameters"]');
164
+ if (encryptedField) {
165
+ return encryptedField.value;
166
+ }
167
+
168
+ // Check if agent parameters are stored in data attributes
169
+ const agentParamsData = this.form.dataset.agentParameters;
170
+ if (agentParamsData) {
171
+ try {
172
+ return JSON.parse(agentParamsData);
173
+ } catch (e) {
174
+ console.warn('Failed to parse agent parameters from data attribute');
175
+ }
176
+ }
177
+
178
+ return null;
179
+ }
180
+
181
+ getApiKey() {
182
+ // Get API key from form data, meta tag, or global variable
183
+ const apiKeyField = this.form.querySelector('[name="api_key"]');
184
+ if (apiKeyField) {
185
+ return apiKeyField.value;
186
+ }
187
+
188
+ const metaApiKey = document.querySelector('meta[name="api-key"]');
189
+ if (metaApiKey) {
190
+ return metaApiKey.content;
191
+ }
192
+
193
+ // Check for global variable
194
+ if (typeof window.SUPERVAIZER_API_KEY !== 'undefined') {
195
+ return window.SUPERVAIZER_API_KEY;
196
+ }
197
+
198
+ return '';
199
+ }
200
+
201
+ displayAgentParameterErrors(validationResult) {
202
+ const errorSection = document.getElementById('agent-parameter-errors');
203
+ if (!errorSection) {
204
+ // Create error section if it doesn't exist
205
+ const section = document.createElement('div');
206
+ section.id = 'agent-parameter-errors';
207
+ section.className = 'mb-4';
208
+ this.errorContainer.appendChild(section);
209
+ }
210
+
211
+ const section = document.getElementById('agent-parameter-errors');
212
+ section.innerHTML = `
213
+ <div class="alert alert-warning">
214
+ <strong>Agent Configuration Issues:</strong> ${validationResult.message
215
+ }
216
+ <ul>
217
+ ${validationResult.errors
218
+ .map((error) => `<li>${error}</li>`)
219
+ .join('')}
220
+ </ul>
221
+ </div>
222
+ `;
223
+ }
224
+
225
+ displayMethodFieldErrors(validationResult) {
226
+ // Clear any existing field-specific errors
227
+ this.form.querySelectorAll('.is-invalid').forEach((field) => {
228
+ field.classList.remove('is-invalid');
229
+ });
230
+
231
+ // Display general error message
232
+ this.displayError(validationResult.message);
233
+
234
+ // Mark invalid fields and show specific error messages
235
+ Object.entries(validationResult.invalid_fields).forEach(([fieldName, errorMessage]) => {
236
+ const field = this.form.querySelector(`[name="${fieldName}"]`);
237
+ if (field) {
238
+ field.classList.add('is-invalid');
239
+
240
+ // Add error message below the field
241
+ let errorElement = field.parentNode.querySelector('.invalid-feedback');
242
+ if (!errorElement) {
243
+ errorElement = document.createElement('div');
244
+ errorElement.className = 'invalid-feedback';
245
+ field.parentNode.appendChild(errorElement);
246
+ }
247
+ errorElement.textContent = errorMessage;
248
+ }
249
+ });
250
+ }
251
+
252
+ displayError(message) {
253
+ this.errorContainer.innerHTML = `
254
+ <div class="alert alert-danger">
255
+ <strong>Validation Error:</strong> ${message}
256
+ </div>
257
+ `;
258
+ }
259
+
260
+ clearErrors() {
261
+ this.errorContainer.innerHTML = '';
262
+
263
+ // Clear agent parameter errors
264
+ const agentErrorSection = document.getElementById('agent-parameter-errors');
265
+ if (agentErrorSection) {
266
+ agentErrorSection.innerHTML = '';
267
+ }
268
+
269
+ // Clear field errors
270
+ this.form.querySelectorAll('.is-invalid').forEach((field) => {
271
+ field.classList.remove('is-invalid');
272
+ });
273
+
274
+ // Clear invalid feedback messages
275
+ this.form.querySelectorAll('.invalid-feedback').forEach((feedback) => {
276
+ feedback.remove();
277
+ });
278
+ }
279
+
280
+ async submitForm() {
281
+ try {
282
+ const formData = this.getFormData();
283
+ const encryptedParams = this.getEncryptedAgentParameters();
284
+
285
+ const requestBody = {
286
+ job_context: {
287
+ // Add any job context data here
288
+ },
289
+ job_fields: formData
290
+ };
291
+
292
+ if (encryptedParams) {
293
+ requestBody.encrypted_agent_parameters = encryptedParams;
294
+ }
295
+
296
+ const response = await fetch(`${this.agentPath}/jobs`, {
297
+ method: 'POST',
298
+ headers: {
299
+ 'Content-Type': 'application/json',
300
+ 'X-API-Key': this.getApiKey()
301
+ },
302
+ body: JSON.stringify(requestBody)
303
+ });
304
+
305
+ if (response.ok) {
306
+ const result = await response.json();
307
+ this.handleJobStarted(result);
308
+ } else {
309
+ const error = await response.json();
310
+ this.handleJobError(error);
311
+ }
312
+ } catch (error) {
313
+ console.error('Job submission failed:', error);
314
+ this.displayError('Failed to submit job due to network error');
315
+ }
316
+ }
317
+
318
+ handleJobStarted(jobData) {
319
+ // Clear form and show success message
320
+ this.form.reset();
321
+ this.clearErrors();
322
+
323
+ this.errorContainer.innerHTML = `
324
+ <div class="alert alert-success">
325
+ <strong>Job Started Successfully!</strong><br>
326
+ Job ID: ${jobData.id || 'Unknown'}<br>
327
+ Status: ${jobData.status || 'Unknown'}
328
+ </div>
329
+ `;
330
+
331
+ // Trigger any success callbacks
332
+ if (typeof this.onJobStarted === 'function') {
333
+ this.onJobStarted(jobData);
334
+ }
335
+ }
336
+
337
+ handleJobError(error) {
338
+ this.displayError(`Job submission failed: ${error.detail || error.message || 'Unknown error'}`);
339
+
340
+ // Trigger any error callbacks
341
+ if (typeof this.onJobError === 'function') {
342
+ this.onJobError(error);
343
+ }
344
+ }
345
+
346
+ // Public methods for external use
347
+ setOnJobStarted(callback) {
348
+ this.onJobStarted = callback;
349
+ }
350
+
351
+ setOnJobError(callback) {
352
+ this.onJobError = callback;
353
+ }
354
+
355
+ // Method to manually trigger validation
356
+ async validate() {
357
+ const [agentParamsValid, methodFieldsValid] = await Promise.all([
358
+ this.validateAgentParameters(),
359
+ this.validateMethodFields()
360
+ ]);
361
+ return agentParamsValid && methodFieldsValid;
362
+ }
363
+ }
364
+
365
+ // Export for module systems
366
+ if (typeof module !== 'undefined' && module.exports) {
367
+ module.exports = JobStartForm;
368
+ }
369
+
370
+ // Make available globally
371
+ if (typeof window !== 'undefined') {
372
+ window.JobStartForm = JobStartForm;
373
+ }
@@ -0,0 +1,145 @@
1
+ <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4 max-h-[80vh] overflow-y-auto">
2
+ <div class="sm:flex sm:items-start">
3
+ <div class="w-full">
4
+ <!-- Header -->
5
+ <div class="flex items-center justify-between mb-4">
6
+ <h3 class="text-lg leading-6 font-medium text-gray-900">Agent Details: {{ agent.name or agent.slug or "Unknown Agent" }}</h3>
7
+ <button
8
+ @click="open = false"
9
+ class="bg-white rounded-md text-gray-400 hover:text-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
10
+ >
11
+ <span class="sr-only">Close</span>
12
+ <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
13
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
14
+ </svg>
15
+ </button>
16
+ </div>
17
+
18
+ <!-- Debug Info (remove in production) -->
19
+ <div class="mb-4 p-4 bg-yellow-50 border border-yellow-200 rounded-md">
20
+ <h4 class="text-sm font-medium text-yellow-800">Debug Info:</h4>
21
+ <pre class="text-xs text-yellow-700 mt-2">{{ agent | tojson(indent=2) }}</pre>
22
+ </div>
23
+
24
+ <!-- Agent Information -->
25
+ <div class="grid grid-cols-1 gap-6 sm:grid-cols-2">
26
+ <div>
27
+ <h4 class="text-sm font-medium text-gray-900 mb-3">Basic Information</h4>
28
+ <dl class="space-y-2">
29
+ <div>
30
+ <dt class="text-xs font-medium text-gray-500 uppercase tracking-wider">Name</dt>
31
+ <dd class="text-sm text-gray-900">{{ agent.name or "Unnamed Agent" }}</dd>
32
+ </div>
33
+ <div>
34
+ <dt class="text-xs font-medium text-gray-500 uppercase tracking-wider">Type</dt>
35
+ <dd class="text-sm text-gray-900 capitalize">{{ agent.type or "conversational" }}</dd>
36
+ </div>
37
+ <div>
38
+ <dt class="text-xs font-medium text-gray-500 uppercase tracking-wider">Version</dt>
39
+ <dd class="text-sm text-gray-900">{{ agent.version or "1.0.0" }}</dd>
40
+ </div>
41
+ <div>
42
+ <dt class="text-xs font-medium text-gray-500 uppercase tracking-wider">Status</dt>
43
+ <dd>
44
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
45
+ {{ agent.status or "Active" }}
46
+ </span>
47
+ </dd>
48
+ </div>
49
+ </dl>
50
+ </div>
51
+
52
+ <div>
53
+ <h4 class="text-sm font-medium text-gray-900 mb-3">Configuration</h4>
54
+ <dl class="space-y-2">
55
+ <div>
56
+ <dt class="text-xs font-medium text-gray-500 uppercase tracking-wider">Model</dt>
57
+ <dd class="text-sm text-gray-900">{{ agent.model or "-" }}</dd>
58
+ </div>
59
+ <div>
60
+ <dt class="text-xs font-medium text-gray-500 uppercase tracking-wider">Provider</dt>
61
+ <dd class="text-sm text-gray-900">{{ agent.provider or "-" }}</dd>
62
+ </div>
63
+ <div>
64
+ <dt class="text-xs font-medium text-gray-500 uppercase tracking-wider">Max Tokens</dt>
65
+ <dd class="text-sm text-gray-900">{{ agent.max_tokens or "-" }}</dd>
66
+ </div>
67
+ <div>
68
+ <dt class="text-xs font-medium text-gray-500 uppercase tracking-wider">Temperature</dt>
69
+ <dd class="text-sm text-gray-900">{{ agent.temperature or "-" }}</dd>
70
+ </div>
71
+ </dl>
72
+ </div>
73
+ </div>
74
+
75
+ <!-- Description -->
76
+ {% if agent.description %}
77
+ <div class="mt-6">
78
+ <h4 class="text-sm font-medium text-gray-900 mb-3">Description</h4>
79
+ <div class="bg-gray-50 p-4 rounded-lg">
80
+ <p class="text-sm text-gray-700">{{ agent.description }}</p>
81
+ </div>
82
+ </div>
83
+ {% endif %}
84
+
85
+ <!-- System Prompt -->
86
+ {% if agent.system_prompt %}
87
+ <div class="mt-6">
88
+ <h4 class="text-sm font-medium text-gray-900 mb-3">System Prompt</h4>
89
+ <div class="bg-gray-50 p-4 rounded-lg">
90
+ <pre class="text-sm text-gray-700 whitespace-pre-wrap">{{ agent.system_prompt }}</pre>
91
+ </div>
92
+ </div>
93
+ {% endif %}
94
+
95
+ <!-- Tools -->
96
+ {% if agent.tools %}
97
+ <div class="mt-6">
98
+ <h4 class="text-sm font-medium text-gray-900 mb-3">Available Tools ({{ agent.tools | length }})</h4>
99
+ <div class="bg-white overflow-hidden shadow rounded-lg">
100
+ <ul role="list" class="divide-y divide-gray-200">
101
+ {% for tool in agent.tools %}
102
+ <li class="px-4 py-3">
103
+ <div class="flex items-center justify-between">
104
+ <div>
105
+ <p class="text-sm font-medium text-gray-900">{{ tool.name if tool is mapping else tool }}</p>
106
+ {% if tool is mapping and tool.description %}
107
+ <p class="text-sm text-gray-500">{{ tool.description }}</p>
108
+ {% endif %}
109
+ </div>
110
+ {% if tool is mapping and tool.enabled is defined %}
111
+ <span class="text-xs px-2 py-1 rounded-full {{ 'bg-green-100 text-green-800' if tool.enabled else 'bg-gray-100 text-gray-800' }}">
112
+ {{ "Enabled" if tool.enabled else "Disabled" }}
113
+ </span>
114
+ {% endif %}
115
+ </div>
116
+ </li>
117
+ {% endfor %}
118
+ </ul>
119
+ </div>
120
+ </div>
121
+ {% endif %}
122
+
123
+ <!-- Additional Configuration -->
124
+ {% if agent.config %}
125
+ <div class="mt-6">
126
+ <h4 class="text-sm font-medium text-gray-900 mb-3">Additional Configuration</h4>
127
+ <div class="bg-gray-50 p-4 rounded-lg">
128
+ <pre class="text-sm text-gray-700 whitespace-pre-wrap">{{ agent.config | tojson(indent=2) }}</pre>
129
+ </div>
130
+ </div>
131
+ {% endif %}
132
+ </div>
133
+ </div>
134
+ </div>
135
+
136
+ <!-- Modal Footer -->
137
+ <div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
138
+ <button
139
+ @click="open = false"
140
+ type="button"
141
+ class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
142
+ >
143
+ Close
144
+ </button>
145
+ </div>