supervaizer 0.9.8__py3-none-any.whl → 0.10.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 (56) hide show
  1. supervaizer/__init__.py +11 -2
  2. supervaizer/__version__.py +1 -1
  3. supervaizer/account.py +4 -0
  4. supervaizer/account_service.py +7 -1
  5. supervaizer/admin/routes.py +24 -8
  6. supervaizer/admin/templates/agents.html +74 -0
  7. supervaizer/admin/templates/agents_grid.html +5 -3
  8. supervaizer/admin/templates/navigation.html +11 -1
  9. supervaizer/admin/templates/supervaize_instructions.html +212 -0
  10. supervaizer/agent.py +28 -6
  11. supervaizer/case.py +46 -14
  12. supervaizer/cli.py +247 -7
  13. supervaizer/common.py +45 -4
  14. supervaizer/deploy/__init__.py +16 -0
  15. supervaizer/deploy/cli.py +296 -0
  16. supervaizer/deploy/commands/__init__.py +9 -0
  17. supervaizer/deploy/commands/clean.py +294 -0
  18. supervaizer/deploy/commands/down.py +119 -0
  19. supervaizer/deploy/commands/local.py +460 -0
  20. supervaizer/deploy/commands/plan.py +167 -0
  21. supervaizer/deploy/commands/status.py +169 -0
  22. supervaizer/deploy/commands/up.py +281 -0
  23. supervaizer/deploy/docker.py +370 -0
  24. supervaizer/deploy/driver_factory.py +42 -0
  25. supervaizer/deploy/drivers/__init__.py +39 -0
  26. supervaizer/deploy/drivers/aws_app_runner.py +607 -0
  27. supervaizer/deploy/drivers/base.py +196 -0
  28. supervaizer/deploy/drivers/cloud_run.py +570 -0
  29. supervaizer/deploy/drivers/do_app_platform.py +504 -0
  30. supervaizer/deploy/health.py +404 -0
  31. supervaizer/deploy/state.py +210 -0
  32. supervaizer/deploy/templates/Dockerfile.template +44 -0
  33. supervaizer/deploy/templates/debug_env.py +69 -0
  34. supervaizer/deploy/templates/docker-compose.yml.template +37 -0
  35. supervaizer/deploy/templates/dockerignore.template +66 -0
  36. supervaizer/deploy/templates/entrypoint.sh +20 -0
  37. supervaizer/deploy/utils.py +41 -0
  38. supervaizer/examples/controller_template.py +1 -1
  39. supervaizer/job.py +18 -5
  40. supervaizer/job_service.py +6 -5
  41. supervaizer/parameter.py +13 -1
  42. supervaizer/protocol/__init__.py +2 -2
  43. supervaizer/protocol/a2a/routes.py +1 -1
  44. supervaizer/routes.py +141 -17
  45. supervaizer/server.py +5 -11
  46. supervaizer/utils/__init__.py +16 -0
  47. supervaizer/utils/version_check.py +56 -0
  48. {supervaizer-0.9.8.dist-info → supervaizer-0.10.0.dist-info}/METADATA +105 -34
  49. supervaizer-0.10.0.dist-info/RECORD +76 -0
  50. {supervaizer-0.9.8.dist-info → supervaizer-0.10.0.dist-info}/WHEEL +1 -1
  51. supervaizer/protocol/acp/__init__.py +0 -21
  52. supervaizer/protocol/acp/model.py +0 -198
  53. supervaizer/protocol/acp/routes.py +0 -74
  54. supervaizer-0.9.8.dist-info/RECORD +0 -52
  55. {supervaizer-0.9.8.dist-info → supervaizer-0.10.0.dist-info}/entry_points.txt +0 -0
  56. {supervaizer-0.9.8.dist-info → supervaizer-0.10.0.dist-info}/licenses/LICENSE.md +0 -0
supervaizer/__init__.py CHANGED
@@ -14,7 +14,14 @@ from supervaizer.agent import (
14
14
  AgentMethodParams,
15
15
  AgentMethods,
16
16
  )
17
- from supervaizer.case import Case, CaseNodeUpdate, CaseNoteType, Cases
17
+ from supervaizer.case import (
18
+ Case,
19
+ CaseNodeUpdate,
20
+ CaseNodeType,
21
+ Cases,
22
+ CaseNode,
23
+ CaseNodes,
24
+ )
18
25
  from supervaizer.common import ApiError, ApiResult, ApiSuccess
19
26
  from supervaizer.event import (
20
27
  AgentRegisterEvent,
@@ -51,8 +58,10 @@ __all__ = [
51
58
  "ApiSuccess",
52
59
  "Case",
53
60
  "CaseNodeUpdate",
54
- "CaseNoteType",
61
+ "CaseNodeType",
55
62
  "Cases",
63
+ "CaseNode",
64
+ "CaseNodes",
56
65
  "CaseStartEvent",
57
66
  "CaseUpdateEvent",
58
67
  "create_error_response",
@@ -5,6 +5,6 @@
5
5
  # https://mozilla.org/MPL/2.0/.
6
6
 
7
7
 
8
- VERSION = "0.9.8"
8
+ VERSION = "0.10.0"
9
9
  API_VERSION = "v1"
10
10
  TELEMETRY_VERSION = "v1"
supervaizer/account.py CHANGED
@@ -20,6 +20,7 @@ if TYPE_CHECKING:
20
20
  from supervaizer.event import Event
21
21
  from supervaizer.job import Job
22
22
  from supervaizer.server import Server
23
+ from supervaizer.common import log
23
24
 
24
25
 
25
26
  class AccountAbstract(SvBaseModel):
@@ -268,6 +269,9 @@ class Account(AccountAbstract):
268
269
 
269
270
  def send_update_case(self, case: "Case", update: "CaseNodeUpdate") -> ApiResult:
270
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)}")
271
275
  from supervaizer.event import CaseUpdateEvent
272
276
 
273
277
  event = CaseUpdateEvent(case=case, update=update, account=self)
@@ -9,6 +9,7 @@
9
9
  # https://mozilla.org/MPL/2.0/.
10
10
 
11
11
  import logging
12
+ import os
12
13
  from typing import TYPE_CHECKING, Union
13
14
 
14
15
  import httpx
@@ -19,6 +20,11 @@ logger = logging.getLogger("httpx")
19
20
  # Enable httpx debug logging (optional - uncomment for transport-level debugging)
20
21
  logger.setLevel(logging.DEBUG)
21
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
+
22
28
  if TYPE_CHECKING:
23
29
  from supervaizer.account import Account
24
30
  from supervaizer.agent import Agent
@@ -60,7 +66,7 @@ def send_event(
60
66
  curl_cmd = f"curl -X 'GET' '{account.url_event}' {curl_headers}"
61
67
 
62
68
  try:
63
- response = httpx.post(account.url_event, headers=headers, json=payload)
69
+ response = _httpx_client.post(account.url_event, headers=headers, json=payload)
64
70
 
65
71
  # Log response details before raising for status
66
72
 
@@ -27,7 +27,7 @@ from fastapi.templating import Jinja2Templates
27
27
  from pydantic import BaseModel
28
28
  from sse_starlette.sse import EventSourceResponse
29
29
 
30
- from supervaizer.__version__ import API_VERSION
30
+ from supervaizer.__version__ import API_VERSION, VERSION
31
31
  from supervaizer.common import log
32
32
  from supervaizer.lifecycle import EntityStatus
33
33
  from supervaizer.storage import (
@@ -257,10 +257,11 @@ def create_admin_routes() -> APIRouter:
257
257
  stats = get_dashboard_stats(storage)
258
258
 
259
259
  return templates.TemplateResponse(
260
+ request,
260
261
  "dashboard.html",
261
262
  {
262
263
  "request": request,
263
- "api_version": API_VERSION,
264
+ "api_version": VERSION,
264
265
  "stats": stats,
265
266
  "system_status": "Online",
266
267
  "db_name": "TinyDB",
@@ -276,10 +277,11 @@ def create_admin_routes() -> APIRouter:
276
277
  async def admin_jobs_page(request: Request) -> Response:
277
278
  """Jobs management page."""
278
279
  return templates.TemplateResponse(
280
+ request,
279
281
  "jobs_list.html",
280
282
  {
281
283
  "request": request,
282
- "api_version": API_VERSION,
284
+ "api_version": VERSION,
283
285
  "api_key": os.getenv("SUPERVAIZER_API_KEY"),
284
286
  },
285
287
  )
@@ -288,10 +290,11 @@ def create_admin_routes() -> APIRouter:
288
290
  async def admin_cases_page(request: Request) -> Response:
289
291
  """Cases management page."""
290
292
  return templates.TemplateResponse(
293
+ request,
291
294
  "cases_list.html",
292
295
  {
293
296
  "request": request,
294
- "api_version": API_VERSION,
297
+ "api_version": VERSION,
295
298
  "api_key": os.getenv("SUPERVAIZER_API_KEY"),
296
299
  },
297
300
  )
@@ -305,10 +308,11 @@ def create_admin_routes() -> APIRouter:
305
308
  server_config = get_server_configuration(storage)
306
309
 
307
310
  return templates.TemplateResponse(
311
+ request,
308
312
  "server.html",
309
313
  {
310
314
  "request": request,
311
- "api_version": API_VERSION,
315
+ "api_version": VERSION,
312
316
  "server_status": server_status,
313
317
  "server_config": server_config,
314
318
  "api_key": os.getenv("SUPERVAIZER_API_KEY"),
@@ -331,10 +335,11 @@ def create_admin_routes() -> APIRouter:
331
335
  )
332
336
 
333
337
  return templates.TemplateResponse(
338
+ request,
334
339
  "agents.html",
335
340
  {
336
341
  "request": request,
337
- "api_version": API_VERSION,
342
+ "api_version": VERSION,
338
343
  "agents": server_info.agents,
339
344
  "api_key": os.getenv("SUPERVAIZER_API_KEY"),
340
345
  },
@@ -349,10 +354,11 @@ def create_admin_routes() -> APIRouter:
349
354
  async def admin_job_start_test_page(request: Request) -> Response:
350
355
  """Job start form test page."""
351
356
  return templates.TemplateResponse(
357
+ request,
352
358
  "job_start_test.html",
353
359
  {
354
360
  "request": request,
355
- "api_version": API_VERSION,
361
+ "api_version": VERSION,
356
362
  "api_key": os.getenv("SUPERVAIZER_API_KEY"),
357
363
  },
358
364
  )
@@ -378,7 +384,9 @@ def create_admin_routes() -> APIRouter:
378
384
  console_token = generate_console_token()
379
385
 
380
386
  return templates.TemplateResponse(
381
- "console.html", {"request": request, "console_token": console_token}
387
+ request,
388
+ "console.html",
389
+ {"request": request, "console_token": console_token},
382
390
  )
383
391
 
384
392
  # API Routes
@@ -394,6 +402,7 @@ def create_admin_routes() -> APIRouter:
394
402
  server_status = get_server_status()
395
403
 
396
404
  return templates.TemplateResponse(
405
+ request,
397
406
  "server_status_cards.html",
398
407
  {
399
408
  "request": request,
@@ -466,6 +475,7 @@ def create_admin_routes() -> APIRouter:
466
475
  pass
467
476
 
468
477
  return templates.TemplateResponse(
478
+ request,
469
479
  "agents_grid.html",
470
480
  {
471
481
  "request": request,
@@ -503,6 +513,7 @@ def create_admin_routes() -> APIRouter:
503
513
  raise HTTPException(status_code=404, detail="Agent not found")
504
514
 
505
515
  return templates.TemplateResponse(
516
+ request,
506
517
  "agent_detail.html",
507
518
  {
508
519
  "request": request,
@@ -586,6 +597,7 @@ def create_admin_routes() -> APIRouter:
586
597
  jobs.append(job)
587
598
 
588
599
  return templates.TemplateResponse(
600
+ request,
589
601
  "jobs_table.html",
590
602
  {
591
603
  "request": request,
@@ -614,6 +626,7 @@ def create_admin_routes() -> APIRouter:
614
626
  cases_data = storage.get_cases_for_job(job_id)
615
627
 
616
628
  return templates.TemplateResponse(
629
+ request,
617
630
  "job_detail.html",
618
631
  {
619
632
  "request": request,
@@ -704,6 +717,7 @@ def create_admin_routes() -> APIRouter:
704
717
  cases.append(case)
705
718
 
706
719
  return templates.TemplateResponse(
720
+ request,
707
721
  "cases_table.html",
708
722
  {
709
723
  "request": request,
@@ -734,6 +748,7 @@ def create_admin_routes() -> APIRouter:
734
748
  job_data = storage.get_object_by_id("Job", case_data["job_id"])
735
749
 
736
750
  return templates.TemplateResponse(
751
+ request,
737
752
  "case_detail.html",
738
753
  {
739
754
  "request": request,
@@ -885,6 +900,7 @@ def create_admin_routes() -> APIRouter:
885
900
  activities = activities[:10] # Top 10 recent activities
886
901
 
887
902
  return templates.TemplateResponse(
903
+ request,
888
904
  "recent_activity.html",
889
905
  {
890
906
  "request": request,
@@ -126,6 +126,43 @@
126
126
  </div>
127
127
  </div>
128
128
 
129
+ <!-- Instructions Modal -->
130
+ <div id="instructions-modal"
131
+ class="fixed inset-0 z-50"
132
+ x-data="{ open: false, agentSlug: '', instructionsPath: 'supervaize_instructions.html' }"
133
+ x-show="open"
134
+ x-cloak
135
+ @open-instructions.window="agentSlug = $event.detail.slug; instructionsPath = $event.detail.instructionsPath || 'supervaize_instructions.html'; open = true">
136
+ <div class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0">
137
+ <div class="fixed inset-0 transition-opacity bg-gray-500 bg-opacity-75" @click="open = false"></div>
138
+
139
+ <div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-6xl sm:w-full">
140
+ <div class="bg-white px-4 pt-5 pb-4 sm:p-6">
141
+ <div class="flex items-center justify-between mb-4">
142
+ <h3 class="text-lg leading-6 font-medium text-gray-900">Supervaize Instructions</h3>
143
+ <button
144
+ @click="open = false"
145
+ 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"
146
+ >
147
+ <span class="sr-only">Close</span>
148
+ <svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
149
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
150
+ </svg>
151
+ </button>
152
+ </div>
153
+ <div class="mt-4" style="height: 80vh; overflow-y: auto;">
154
+ <iframe
155
+ id="instructions-iframe"
156
+ x-bind:src="agentSlug && instructionsPath ? `/supervaizer/agents/${agentSlug}/${instructionsPath}` : ''"
157
+ class="w-full h-full border-0"
158
+ style="min-height: 600px;"
159
+ ></iframe>
160
+ </div>
161
+ </div>
162
+ </div>
163
+ </div>
164
+ </div>
165
+
129
166
  <script>
130
167
  // Handle agent modal
131
168
  document.body.addEventListener('htmx:afterRequest', function(e) {
@@ -151,6 +188,43 @@
151
188
  htmx.ajax('GET', `/admin/api/agents/${encodeURIComponent(agentSlug)}`, {target: '#agent-modal-content'});
152
189
  };
153
190
 
191
+ window.showAgentInstructions = function(agentSlug, instructionsPath) {
192
+ console.log('showAgentInstructions called with slug:', agentSlug, 'path:', instructionsPath);
193
+
194
+ if (!agentSlug || agentSlug.trim() === '') {
195
+ console.error('Agent slug is missing or empty');
196
+ return;
197
+ }
198
+
199
+ // Default instructions path if not provided
200
+ instructionsPath = instructionsPath || 'supervaize_instructions.html';
201
+
202
+ const modal = document.getElementById('instructions-modal');
203
+ if (!modal) {
204
+ console.error('Instructions modal not found');
205
+ return;
206
+ }
207
+
208
+ // Dispatch Alpine.js event to open modal (Alpine will handle iframe src reactively)
209
+ const event = new CustomEvent('open-instructions', {
210
+ detail: { slug: agentSlug, instructionsPath: instructionsPath },
211
+ bubbles: true
212
+ });
213
+ modal.dispatchEvent(event);
214
+ console.log('Dispatched open-instructions event with slug:', agentSlug, 'path:', instructionsPath);
215
+
216
+ // Also try direct Alpine.js access as fallback
217
+ if (modal._x_dataStack && modal._x_dataStack[0]) {
218
+ const alpineData = modal._x_dataStack[0];
219
+ alpineData.agentSlug = agentSlug;
220
+ alpineData.instructionsPath = instructionsPath;
221
+ alpineData.open = true;
222
+ console.log('Modal opened via direct Alpine.js access');
223
+ } else {
224
+ console.warn('Alpine.js data not available, modal may not open');
225
+ }
226
+ };
227
+
154
228
  window.editAgent = function(agentId) {
155
229
  console.log('Edit agent:', agentId);
156
230
  // Future: htmx.ajax('GET', `/admin/api/agents/${agentId}/edit`, {target: '#agent-modal-content'});
@@ -33,7 +33,7 @@
33
33
  </div>
34
34
  {% endif %}
35
35
  <div class="ml-3">
36
- <h3 class="text-lg font-medium text-gray-900">{{ agent.name }}</h3>
36
+ <h3 class="text-lg font-medium text-gray-900">{{ agent.name }} </h3>
37
37
  <p class="text-sm text-gray-500 capitalize">{{ agent.type or "conversational" }}</p>
38
38
  </div>
39
39
  </div>
@@ -56,10 +56,12 @@
56
56
  </div>
57
57
  </div>
58
58
  <div class="mt-6 flex space-x-3">
59
- <button onclick="showAgentDetails('{{ agent.slug }}')" class="flex-1 bg-blue-50 text-blue-700 hover:bg-blue-100 px-3 py-2 rounded-md text-sm font-medium transition-colors">
59
+ {% set agent_slug = agent.slug if agent.slug else (agent.api_path.replace('/agents/', '').rstrip('/') if agent.api_path and agent.api_path.startswith('/agents/') else '') %}
60
+ {% set instructions_path = agent.instructions_path if agent.instructions_path else 'supervaize_instructions.html' %}
61
+ <button onclick="showAgentInstructions('{{ agent_slug }}', '{{ instructions_path }}')" class="flex-1 bg-blue-50 text-blue-700 hover:bg-blue-100 px-3 py-2 rounded-md text-sm font-medium transition-colors">
60
62
  View
61
63
  </button>
62
- <button onclick="editAgent('{{ agent.slug }}')" class="flex-1 bg-gray-50 text-gray-700 hover:bg-gray-100 px-3 py-2 rounded-md text-sm font-medium transition-colors">
64
+ <button onclick="showAgentDetails('{{ agent_slug }}')" class="flex-1 bg-gray-50 text-gray-700 hover:bg-gray-100 px-3 py-2 rounded-md text-sm font-medium transition-colors">
63
65
  Configure
64
66
  </button>
65
67
  </div>
@@ -83,6 +83,14 @@
83
83
  class="absolute left-0 mt-2 w-48 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 z-50"
84
84
  x-cloak>
85
85
  <div class="py-1">
86
+ <a href="https://doc.supervaize.com/docs/category/supervaizer-controller" target="_blank" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors">
87
+ Supervaizer Doc
88
+ <svg class="inline ml-1 h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
89
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"></path>
90
+ </svg>
91
+ </a>
92
+ <div class="border-t border-gray-200 my-1"></div>
93
+ <div class="px-4 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wider">API Documentation</div>
86
94
  <a href="/docs" target="_blank" class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-colors">
87
95
  Swagger API
88
96
  <svg class="inline ml-1 h-3 w-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -110,7 +118,7 @@
110
118
 
111
119
  <!-- Right side -->
112
120
  <div class="flex items-center space-x-4">
113
- <span class="text-sm text-gray-500">API {{ api_version }}</span>
121
+ <span class="text-sm text-gray-500">{{ api_version }}</span>
114
122
 
115
123
  <!-- Mobile menu button -->
116
124
  <button @click="mobileMenuOpen = !mobileMenuOpen" class="sm:hidden inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100">
@@ -142,6 +150,8 @@
142
150
  <!-- Documentation section -->
143
151
  <div class="pl-3 pr-4 py-2">
144
152
  <div class="text-base font-medium text-gray-700 mb-2">Documentation</div>
153
+ <a href="https://doc.supervaize.com/docs/category/supervaizer-controller" target="_blank" class="block pl-4 py-1 text-sm text-gray-500 hover:text-gray-700">Supervaizer Controller</a>
154
+ <div class="pl-4 py-1 text-xs font-semibold text-gray-500 uppercase tracking-wider">API Documentation</div>
145
155
  <a href="/docs" target="_blank" class="block pl-4 py-1 text-sm text-gray-500 hover:text-gray-700">Swagger API</a>
146
156
  <a href="/redoc" target="_blank" class="block pl-4 py-1 text-sm text-gray-500 hover:text-gray-700">ReDoc</a>
147
157
  <a href="https://github.com/supervaize/supervaizer-controller" target="_blank" class="block pl-4 py-1 text-sm text-gray-500 hover:text-gray-700">Supervaizer GitHub</a>
@@ -0,0 +1,212 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Supervaize Instructions - {{ registration_info.name }}</title>
7
+
8
+ <!-- Tailwind CSS -->
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+
11
+ <style>
12
+ pre {
13
+ white-space: pre-wrap;
14
+ word-wrap: break-word;
15
+ }
16
+ .json-key {
17
+ color: #0369a1;
18
+ font-weight: 600;
19
+ }
20
+ .json-string {
21
+ color: #059669;
22
+ }
23
+ .json-number {
24
+ color: #dc2626;
25
+ }
26
+ .json-boolean {
27
+ color: #7c3aed;
28
+ }
29
+ .json-null {
30
+ color: #6b7280;
31
+ }
32
+ </style>
33
+ </head>
34
+ <body class="bg-gray-50 min-h-screen">
35
+ <div class="max-w-4xl mx-auto py-8 px-4 sm:px-6 lg:px-8">
36
+ <!-- Header -->
37
+ <div class="bg-white shadow rounded-lg mb-6 p-6">
38
+ <h1 class="text-3xl font-bold text-gray-900 mb-2">{{ registration_info.name }}</h1>
39
+ {% if registration_info.description %}
40
+ <p class="text-lg text-gray-600">{{ registration_info.description }}</p>
41
+ {% endif %}
42
+ {% if registration_info.version %}
43
+ <p class="text-sm text-gray-500 mt-2">Version: {{ registration_info.version }}</p>
44
+ {% endif %}
45
+ </div>
46
+
47
+ <!-- Basic Information -->
48
+ <div class="bg-white shadow rounded-lg mb-6 p-6">
49
+ <h2 class="text-xl font-semibold text-gray-900 mb-4">Agent Information</h2>
50
+ <dl class="grid grid-cols-1 gap-4 sm:grid-cols-2">
51
+ {% if registration_info.id %}
52
+ <div>
53
+ <dt class="text-sm font-medium text-gray-500">ID</dt>
54
+ <dd class="mt-1 text-sm text-gray-900">{{ registration_info.id }}</dd>
55
+ </div>
56
+ {% endif %}
57
+ {% if registration_info.api_path %}
58
+ <div>
59
+ <dt class="text-sm font-medium text-gray-500">API Path</dt>
60
+ <dd class="mt-1 text-sm text-gray-900 font-mono">{{ registration_info.api_path }}</dd>
61
+ </div>
62
+ {% endif %}
63
+ {% if registration_info.author %}
64
+ <div>
65
+ <dt class="text-sm font-medium text-gray-500">Author</dt>
66
+ <dd class="mt-1 text-sm text-gray-900">{{ registration_info.author }}</dd>
67
+ </div>
68
+ {% endif %}
69
+ {% if registration_info.developer %}
70
+ <div>
71
+ <dt class="text-sm font-medium text-gray-500">Developer</dt>
72
+ <dd class="mt-1 text-sm text-gray-900">{{ registration_info.developer }}</dd>
73
+ </div>
74
+ {% endif %}
75
+ {% if registration_info.maintainer %}
76
+ <div>
77
+ <dt class="text-sm font-medium text-gray-500">Maintainer</dt>
78
+ <dd class="mt-1 text-sm text-gray-900">{{ registration_info.maintainer }}</dd>
79
+ </div>
80
+ {% endif %}
81
+ {% if registration_info.editor %}
82
+ <div>
83
+ <dt class="text-sm font-medium text-gray-500">Editor</dt>
84
+ <dd class="mt-1 text-sm text-gray-900">{{ registration_info.editor }}</dd>
85
+ </div>
86
+ {% endif %}
87
+ {% if registration_info.max_execution_time %}
88
+ <div>
89
+ <dt class="text-sm font-medium text-gray-500">Max Execution Time</dt>
90
+ <dd class="mt-1 text-sm text-gray-900">{{ registration_info.max_execution_time }} seconds</dd>
91
+ </div>
92
+ {% endif %}
93
+ </dl>
94
+ </div>
95
+
96
+ <!-- Tags -->
97
+ {% if registration_info.tags %}
98
+ <div class="bg-white shadow rounded-lg mb-6 p-6">
99
+ <h2 class="text-xl font-semibold text-gray-900 mb-4">Tags</h2>
100
+ <div class="flex flex-wrap gap-2">
101
+ {% for tag in registration_info.tags %}
102
+ <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-blue-100 text-blue-800">
103
+ {{ tag }}
104
+ </span>
105
+ {% endfor %}
106
+ </div>
107
+ </div>
108
+ {% endif %}
109
+
110
+ <!-- Methods -->
111
+ {% if registration_info.methods %}
112
+ <div class="bg-white shadow rounded-lg mb-6 p-6">
113
+ <h2 class="text-xl font-semibold text-gray-900 mb-4">Available Methods</h2>
114
+ <div class="space-y-4">
115
+ {% if registration_info.methods.job_start %}
116
+ <div class="border-l-4 border-green-500 pl-4">
117
+ <h3 class="font-medium text-gray-900">Job Start</h3>
118
+ {% if registration_info.methods.job_start.description %}
119
+ <p class="text-sm text-gray-600 mt-1">{{ registration_info.methods.job_start.description }}</p>
120
+ {% endif %}
121
+ </div>
122
+ {% endif %}
123
+ {% if registration_info.methods.job_stop %}
124
+ <div class="border-l-4 border-red-500 pl-4">
125
+ <h3 class="font-medium text-gray-900">Job Stop</h3>
126
+ {% if registration_info.methods.job_stop.description %}
127
+ <p class="text-sm text-gray-600 mt-1">{{ registration_info.methods.job_stop.description }}</p>
128
+ {% endif %}
129
+ </div>
130
+ {% endif %}
131
+ {% if registration_info.methods.job_status %}
132
+ <div class="border-l-4 border-blue-500 pl-4">
133
+ <h3 class="font-medium text-gray-900">Job Status</h3>
134
+ {% if registration_info.methods.job_status.description %}
135
+ <p class="text-sm text-gray-600 mt-1">{{ registration_info.methods.job_status.description }}</p>
136
+ {% endif %}
137
+ </div>
138
+ {% endif %}
139
+ {% if registration_info.methods.chat %}
140
+ <div class="border-l-4 border-purple-500 pl-4">
141
+ <h3 class="font-medium text-gray-900">Chat</h3>
142
+ {% if registration_info.methods.chat.description %}
143
+ <p class="text-sm text-gray-600 mt-1">{{ registration_info.methods.chat.description }}</p>
144
+ {% endif %}
145
+ </div>
146
+ {% endif %}
147
+ {% if registration_info.methods.custom %}
148
+ {% for method_name, method_info in registration_info.methods.custom.items() %}
149
+ <div class="border-l-4 border-yellow-500 pl-4">
150
+ <h3 class="font-medium text-gray-900">{{ method_name }}</h3>
151
+ {% if method_info.description %}
152
+ <p class="text-sm text-gray-600 mt-1">{{ method_info.description }}</p>
153
+ {% endif %}
154
+ </div>
155
+ {% endfor %}
156
+ {% endif %}
157
+ </div>
158
+ </div>
159
+ {% endif %}
160
+ <!-- Parameters Setup -->
161
+ <div class="bg-white shadow rounded-lg mb-6 p-6">
162
+ <h2 class="text-xl font-semibold text-gray-900 mb-4">Parameters</h2>
163
+ {% if registration_info.parameters_setup %}
164
+ <div class="overflow-x-auto">
165
+ <table class="min-w-full divide-y divide-gray-200">
166
+ <thead class="bg-gray-50">
167
+ <tr>
168
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
169
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
170
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Type</th>
171
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Required</th>
172
+ </tr>
173
+ </thead>
174
+ <tbody class="bg-white divide-y divide-gray-200">
175
+ {% for param in registration_info.parameters_setup %}
176
+ <tr>
177
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{{ param.name }}</td>
178
+ <td class="px-6 py-4 text-sm text-gray-500">{{ param.description or "-" }}</td>
179
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
180
+ {% if param.is_secret %}
181
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">Secret</span>
182
+ {% else %}
183
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">Standard</span>
184
+ {% endif %}
185
+ </td>
186
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
187
+ {% if param.is_required %}
188
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">Required</span>
189
+ {% else %}
190
+ <span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Optional</span>
191
+ {% endif %}
192
+ </td>
193
+ </tr>
194
+ {% endfor %}
195
+ </tbody>
196
+ </table>
197
+ </div>
198
+ {% else %}
199
+ <p class="text-sm text-gray-500">No parameters configured.</p>
200
+ {% endif %}
201
+ </div>
202
+
203
+ <!-- Custom instructions -->
204
+ <div class="bg-white shadow rounded-lg mb-6 p-6">
205
+ <h2 class="text-xl font-semibold text-gray-900 mb-4">Setup instructions</h2>
206
+ <div class="overflow-x-auto">
207
+ TBD
208
+ </div>
209
+ </div>
210
+ </div>
211
+ </body>
212
+ </html>