supervaizer 0.9.8__py3-none-any.whl → 0.10.1__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 +378 -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 +52 -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.1.dist-info}/METADATA +105 -34
  49. supervaizer-0.10.1.dist-info/RECORD +76 -0
  50. {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.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.1.dist-info}/entry_points.txt +0 -0
  56. {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.dist-info}/licenses/LICENSE.md +0 -0
supervaizer/routes.py CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  import traceback
8
8
  from functools import wraps
9
+ from pathlib import Path
9
10
  from typing import (
10
11
  TYPE_CHECKING,
11
12
  Any,
@@ -26,11 +27,12 @@ from fastapi import (
26
27
  Depends,
27
28
  HTTPException,
28
29
  Query,
30
+ Request,
29
31
  Security,
30
32
  status as http_status,
31
33
  )
32
- from fastapi.responses import JSONResponse
33
- from rich import inspect
34
+ from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, Response
35
+ from fastapi.templating import Jinja2Templates
34
36
 
35
37
  from supervaizer.agent import (
36
38
  Agent,
@@ -51,8 +53,6 @@ if TYPE_CHECKING:
51
53
 
52
54
  T = TypeVar("T")
53
55
 
54
- insp = inspect
55
-
56
56
 
57
57
  class CaseUpdateRequest(SvBaseModel):
58
58
  """Request model for updating a case with answer to a question."""
@@ -259,10 +259,13 @@ def create_default_routes(server: "Server") -> APIRouter:
259
259
  )
260
260
 
261
261
  # Update the case with the answer
262
- case.update(update)
262
+ # case.update(update) - Redundant, receive_human_input calls update()
263
263
 
264
264
  # Transition the case from AWAITING to IN_PROGRESS
265
- case.receive_human_input()
265
+ case.receive_human_input(update)
266
+
267
+ # TODO CALL CUSTOM HOOKS HERE - AS DEFINED IN THE AGENT CONFIGURATION
268
+ # TODO REDEFINE AGENT TO ADD CUSTOM HOOKS HERE
266
269
 
267
270
  log.info(
268
271
  f"[Case update] Job {job_id}, Case {case_id} - Answer processed successfully"
@@ -383,6 +386,54 @@ def create_agent_route(server: "Server", agent: Agent) -> APIRouter:
383
386
  **agent.registration_info,
384
387
  )
385
388
 
389
+ @router.get(
390
+ f"/{agent.instructions_path}",
391
+ summary=f"Get supervaize instructions page for agent {agent.name}",
392
+ description="HTML page displaying agent registration information and instructions",
393
+ response_class=HTMLResponse,
394
+ tags=tags,
395
+ )
396
+ @handle_route_errors()
397
+ async def supervaize_instructions(
398
+ request: Request, agent: Agent = Depends(get_agent)
399
+ ) -> Response:
400
+ """Serve the supervaize instructions HTML page for this agent."""
401
+ log.info(
402
+ f"📥 GET /{agent.instructions_path} [Supervaize Instructions] for agent{agent.name}"
403
+ )
404
+
405
+ registration_info = agent.registration_info
406
+
407
+ # Convert instructions_path string to Path object
408
+ instructions_path = Path(agent.instructions_path)
409
+
410
+ # Check if file exists - if not, return empty HTML
411
+ if not instructions_path.exists() or not instructions_path.is_file():
412
+ return HTMLResponse(content="", status_code=200)
413
+
414
+ # Serve the file (check if it's a Jinja2 template or static HTML)
415
+ with open(instructions_path, "r", encoding="utf-8") as f:
416
+ content = f.read()
417
+ # Simple check: if it contains Jinja2 syntax, render as template
418
+ if "{{" in content or "{%" in content:
419
+ # Render as Jinja2 template
420
+ custom_templates = Jinja2Templates(
421
+ directory=str(instructions_path.parent)
422
+ )
423
+ return custom_templates.TemplateResponse(
424
+ instructions_path.name,
425
+ {
426
+ "request": request,
427
+ "registration_info": registration_info,
428
+ },
429
+ )
430
+ else:
431
+ # Serve as static HTML file
432
+ return FileResponse(
433
+ str(instructions_path),
434
+ media_type="text/html",
435
+ )
436
+
386
437
  @router.post(
387
438
  "/validate-agent-parameters",
388
439
  summary=f"Validate agent parameters for agent: {agent.name}",
@@ -406,30 +457,62 @@ def create_agent_route(server: "Server", agent: Agent) -> APIRouter:
406
457
  )
407
458
 
408
459
  if not agent.parameters_setup:
409
- return {
460
+ result = {
410
461
  "valid": True,
411
462
  "message": "Agent has no parameter setup defined",
412
463
  "errors": [],
413
464
  "invalid_parameters": {},
414
465
  }
466
+ log.info(f"📤 Agent {agent.name}: No parameter setup defined → {result}")
467
+ return result
468
+
469
+ if body_params is None:
470
+ body_params = {}
415
471
 
416
472
  encrypted_agent_parameters = body_params.get("encrypted_agent_parameters")
417
473
 
418
- # Decrypt agent parameters if provided
419
474
  agent_parameters: Dict[str, Any] = {}
420
475
  if encrypted_agent_parameters:
476
+ # Basic debug trace
477
+ log.info(
478
+ f"📥 Received encrypted_agent_parameters, length: {len(encrypted_agent_parameters)}"
479
+ )
480
+
421
481
  try:
422
- from supervaizer.common import decrypt_value
423
482
  import json
424
483
 
484
+ from supervaizer.common import decrypt_value
485
+
425
486
  agent_parameters_str = decrypt_value(
426
487
  encrypted_agent_parameters, server.private_key
427
488
  )
428
489
  agent_parameters = (
429
490
  json.loads(agent_parameters_str) if agent_parameters_str else {}
430
491
  )
492
+
493
+ # Debug: Log the parsed data type and structure
494
+ log.info(f"🔍 Parsed agent_parameters type: {type(agent_parameters)}")
495
+ if isinstance(agent_parameters, list):
496
+ log.info(
497
+ f"🔍 Converting list to dict with {len(agent_parameters)} items"
498
+ )
499
+ # Convert list to dict if needed (common when frontend sends array)
500
+ agent_parameters = {
501
+ f"param_{i}": param for i, param in enumerate(agent_parameters)
502
+ }
503
+ elif isinstance(agent_parameters, dict):
504
+ log.info(
505
+ f"🔍 Agent parameters keys: {list(agent_parameters.keys())}"
506
+ )
507
+ else:
508
+ log.warning(
509
+ f"🔍 Unexpected type: {type(agent_parameters)}, converting to empty dict"
510
+ )
511
+ agent_parameters = {}
512
+
431
513
  except Exception as e:
432
- return {
514
+ log.error(f"❌ Decryption failed: {type(e).__name__}: {str(e)}")
515
+ result = {
433
516
  "valid": False,
434
517
  "message": f"Failed to decrypt agent parameters: {str(e)}",
435
518
  "errors": [f"Decryption failed: {str(e)}"],
@@ -437,11 +520,18 @@ def create_agent_route(server: "Server", agent: Agent) -> APIRouter:
437
520
  "encrypted_agent_parameters": f"Decryption failed: {str(e)}"
438
521
  },
439
522
  }
523
+ log.info(f"📤 Agent {agent.name}: Decryption failed → {result}")
524
+ return result
525
+
526
+ # Log the incoming request details
527
+ log.info(
528
+ f"🔍 Agent {agent.name}: Incoming request - encrypted_params: {bool(encrypted_agent_parameters)}, parsed_params: {agent_parameters}"
529
+ )
440
530
 
441
531
  # Validate agent parameters
442
532
  validation_result = agent.parameters_setup.validate_parameters(agent_parameters)
443
533
 
444
- return {
534
+ result = {
445
535
  "valid": validation_result["valid"],
446
536
  "message": "Agent parameters validated successfully"
447
537
  if validation_result["valid"]
@@ -450,6 +540,9 @@ def create_agent_route(server: "Server", agent: Agent) -> APIRouter:
450
540
  "invalid_parameters": validation_result["invalid_parameters"],
451
541
  }
452
542
 
543
+ log.info(f"📤 Agent {agent.name}: Validation result → {result}")
544
+ return result
545
+
453
546
  @router.post(
454
547
  "/validate-method-fields",
455
548
  summary=f"Validate method fields for agent: {agent.name}",
@@ -472,40 +565,59 @@ def create_agent_route(server: "Server", agent: Agent) -> APIRouter:
472
565
  f"📥 POST /validate-method-fields [Validate method fields] {agent.name}"
473
566
  )
474
567
 
568
+ if body_params is None:
569
+ body_params = {}
570
+
475
571
  method_name = body_params.get("method_name", "job_start")
476
572
  job_fields = body_params.get("job_fields", {})
477
573
 
574
+ # Log the incoming request details
575
+ log.info(
576
+ f"🔍 Agent {agent.name}: Incoming request - method: {method_name}, fields: {job_fields}"
577
+ )
578
+
478
579
  # Get the method to validate against
479
580
  if not agent.methods:
480
- return {
581
+ result = {
481
582
  "valid": False,
482
583
  "message": "Agent has no methods defined",
483
584
  "errors": ["Agent has no methods defined"],
484
585
  "invalid_fields": {},
485
586
  }
587
+ log.info(f"📤 Agent {agent.name}: No methods defined → {result}")
588
+ return result
486
589
 
487
590
  if method_name == "job_start":
488
591
  method = agent.methods.job_start
489
592
  elif agent.methods.custom and method_name in agent.methods.custom:
490
593
  method = agent.methods.custom[method_name]
491
594
  else:
492
- return {
595
+ result = {
493
596
  "valid": False,
494
597
  "message": f"Method '{method_name}' not found",
495
598
  "errors": [f"Method '{method_name}' not found"],
496
599
  "invalid_fields": {},
497
600
  }
601
+ log.info(
602
+ f"📤 Agent {agent.name}: Method '{method_name}' not found → {result}"
603
+ )
604
+ return result
498
605
 
499
606
  # Validate method fields
500
607
  validation_result = method.validate_method_fields(job_fields)
501
608
 
502
- return {
609
+ result = {
503
610
  "valid": validation_result["valid"],
504
611
  "message": validation_result["message"],
505
612
  "errors": validation_result["errors"],
506
613
  "invalid_fields": validation_result["invalid_fields"],
507
614
  }
508
615
 
616
+ log.info(
617
+ f"📤 Agent {agent.name}: Method '{method_name}' validation result → {result}"
618
+ )
619
+ return result
620
+
509
621
  if not agent.methods:
510
622
  raise ValueError(f"Agent {agent.name} has no methods defined")
511
623
 
@@ -540,8 +652,15 @@ def create_agent_route(server: "Server", agent: Agent) -> APIRouter:
540
652
  """Start a new job for this agent"""
541
653
  log.info(f"📥 POST /jobs [Start job] {agent.name} with params {body_params}")
542
654
 
543
- sv_context: JobContext = JobContext(**body_params["job_context"])
544
- job_fields = body_params["job_fields"]
655
+ if body_params is None:
656
+ body_params = {}
657
+
658
+ job_context_data = body_params.get("job_context")
659
+ if job_context_data is None:
660
+ raise ValueError("job_context is required")
661
+
662
+ sv_context: JobContext = JobContext(**job_context_data)
663
+ job_fields = body_params.get("job_fields", {})
545
664
 
546
665
  # Get job encrypted parameters if available
547
666
  encrypted_agent_parameters = body_params.get("encrypted_agent_parameters")
@@ -649,7 +768,9 @@ def create_agent_route(server: "Server", agent: Agent) -> APIRouter:
649
768
  agent: Agent = Depends(get_agent),
650
769
  ) -> AgentResponse:
651
770
  log.info(f"📥 POST /stop [Stop agent] {agent.name} with params {params}")
652
- result = agent.job_stop(params.get("job_context", {}))
771
+ # Pass job_context as 'context' parameter to match agent method expectations
772
+ job_context = params.get("job_context", {})
773
+ result = agent.job_stop({"context": job_context})
653
774
  res_info = result.registration_info if result else {}
654
775
  return AgentResponse(
655
776
  name=agent.name,
@@ -763,6 +884,9 @@ def create_agent_custom_routes(server: "Server", agent: Agent) -> APIRouter:
763
884
  )
764
885
  log.info(f"body_params: {body_params}")
765
886
 
887
+ if body_params is None:
888
+ raise ValueError("body_params cannot be None")
889
+
766
890
  sv_context: JobContext = body_params.job_context
767
891
  job_fields = body_params.job_fields.to_dict()
768
892
 
supervaizer/server.py CHANGED
@@ -38,7 +38,6 @@ from supervaizer.common import (
38
38
  )
39
39
  from supervaizer.instructions import display_instructions
40
40
  from supervaizer.protocol.a2a import create_routes as create_a2a_routes
41
- from supervaizer.protocol.acp import create_routes as create_acp_routes
42
41
  from supervaizer.routes import (
43
42
  create_agents_routes,
44
43
  create_default_routes,
@@ -82,6 +81,9 @@ def save_server_info_to_storage(server_instance: "Server") -> None:
82
81
  "name": agent.name,
83
82
  "description": agent.description,
84
83
  "version": agent.version,
84
+ "api_path": agent.path,
85
+ "slug": agent.slug,
86
+ "instructions_path": agent.instructions_path,
85
87
  }
86
88
  )
87
89
 
@@ -166,9 +168,6 @@ class ServerAbstract(SvBaseModel):
166
168
  a2a_endpoints: bool = Field(
167
169
  default=True, description="Whether to enable A2A endpoints"
168
170
  )
169
- acp_endpoints: bool = Field(
170
- default=True, description="Whether to enable ACP endpoints"
171
- )
172
171
  private_key: RSAPrivateKey = Field(
173
172
  description="RSA private key for secret parameters encryption - Used in server-to-agent communication - Not needed by user"
174
173
  )
@@ -206,7 +205,6 @@ class ServerAbstract(SvBaseModel):
206
205
  "debug": False,
207
206
  "reload": False,
208
207
  "a2a_endpoints": True,
209
- "acp_endpoints": True,
210
208
  },
211
209
  ]
212
210
  },
@@ -237,7 +235,6 @@ class Server(ServerAbstract):
237
235
  agents: List[Agent],
238
236
  supervisor_account: Optional[Account] = None,
239
237
  a2a_endpoints: bool = True,
240
- acp_endpoints: bool = True,
241
238
  admin_interface: bool = True,
242
239
  scheme: str = "http",
243
240
  environment: str = os.getenv("SUPERVAIZER_ENVIRONMENT", "dev"),
@@ -259,7 +256,6 @@ class Server(ServerAbstract):
259
256
  agents: List of agents to register with the server
260
257
  supervisor_account: Account of the supervisor
261
258
  a2a_endpoints: Whether to enable A2A endpoints
262
- acp_endpoints: Whether to enable ACP endpoints
263
259
  admin_interface: Whether to enable admin interface
264
260
  scheme: URL scheme (http or https)
265
261
  environment: Environment name (e.g., dev, staging, prod)
@@ -350,7 +346,6 @@ class Server(ServerAbstract):
350
346
  reload=reload,
351
347
  supervisor_account=supervisor_account,
352
348
  a2a_endpoints=a2a_endpoints,
353
- acp_endpoints=acp_endpoints,
354
349
  private_key=private_key,
355
350
  public_key=public_key,
356
351
  public_url=public_url,
@@ -371,9 +366,6 @@ class Server(ServerAbstract):
371
366
  if self.a2a_endpoints:
372
367
  log.info("[Server launch] 📢 Deploy A2A routes ")
373
368
  self.app.include_router(create_a2a_routes(self))
374
- if self.acp_endpoints:
375
- log.info("[Server launch] 📢 Deploy ACP routes")
376
- self.app.include_router(create_acp_routes(self))
377
369
 
378
370
  # Deploy admin routes if API key is available
379
371
  if self.api_key and admin_interface:
@@ -519,6 +511,8 @@ class Server(ServerAbstract):
519
511
  server_registration_result: ApiResult = (
520
512
  self.supervisor_account.register_server(server=self)
521
513
  )
514
+ # log.debug(f"[Server launch] Server registration result: {server_registration_result}")
515
+ # inspect(server_registration_result)
522
516
  assert isinstance(
523
517
  server_registration_result, ApiSuccess
524
518
  ) # If ApiError, exception should have been raised before
@@ -0,0 +1,16 @@
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.utils.version_check import (
9
+ check_is_latest_version,
10
+ get_latest_version,
11
+ )
12
+
13
+ __all__ = [
14
+ "check_is_latest_version",
15
+ "get_latest_version",
16
+ ]
@@ -0,0 +1,56 @@
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
+ import httpx
9
+
10
+ try:
11
+ from packaging import version
12
+ except ImportError:
13
+ version = None
14
+
15
+ from supervaizer import __version__
16
+
17
+
18
+ async def get_latest_version() -> str | None:
19
+ """
20
+ Retrieve the latest version number of supervaizer from PyPI.
21
+
22
+ Returns:
23
+ The latest version string (e.g., "0.9.8") if successful, None otherwise.
24
+ """
25
+ try:
26
+ async with httpx.AsyncClient(timeout=10.0) as client:
27
+ response = await client.get("https://pypi.org/pypi/supervaizer/json")
28
+ response.raise_for_status()
29
+ data = response.json()
30
+ return data.get("info", {}).get("version")
31
+ except Exception:
32
+ return None
33
+
34
+
35
+ async def check_is_latest_version() -> tuple[bool, str | None]:
36
+ """
37
+ Check if the currently running supervaizer version is the latest available on PyPI.
38
+
39
+ Returns:
40
+ A tuple of (is_latest: bool, latest_version: str | None).
41
+ - is_latest: True if current version is latest or if check failed
42
+ - latest_version: The latest version string if available, None otherwise
43
+ """
44
+ current_version = __version__.VERSION
45
+ latest_version = await get_latest_version()
46
+
47
+ if latest_version is None:
48
+ return True, None
49
+
50
+ # Compare versions using packaging.version for proper semantic version comparison
51
+ if version is not None:
52
+ is_latest = version.parse(current_version) >= version.parse(latest_version)
53
+ else:
54
+ # Fallback to simple string comparison if packaging is not available
55
+ is_latest = current_version >= latest_version
56
+ return is_latest, latest_version
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: supervaizer
3
- Version: 0.9.8
3
+ Version: 0.10.1
4
4
  Summary: Controller system for Supervaize
5
5
  Project-URL: Homepage, https://supervaize.com
6
6
  Project-URL: Repository, https://github.com/supervaize/supervaizer
@@ -16,52 +16,59 @@ Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Programming Language :: Python :: 3.12
17
17
  Requires-Python: >=3.12
18
18
  Requires-Dist: art>=6.5
19
- Requires-Dist: cryptography>=45.0.6
19
+ Requires-Dist: cryptography>=46.0.2
20
20
  Requires-Dist: demjson3>=3.0.6
21
21
  Requires-Dist: deprecated>=1.2.18
22
- Requires-Dist: fastapi>=0.116.1
22
+ Requires-Dist: fastapi>=0.118.0
23
23
  Requires-Dist: httpx>=0.28.1
24
24
  Requires-Dist: jinja2>=3.1.6
25
25
  Requires-Dist: loguru>=0.7.3
26
- Requires-Dist: orjson>=3.11.1
27
- Requires-Dist: psutil>=6.1.0
28
- Requires-Dist: pydantic>=2.11.7
29
- Requires-Dist: pymongo>=4.14.0
26
+ Requires-Dist: orjson>=3.11.3
27
+ Requires-Dist: packaging>=24.0
28
+ Requires-Dist: psutil>=7.1.0
29
+ Requires-Dist: pydantic>=2.12.0
30
30
  Requires-Dist: python-slugify>=8.0.4
31
+ Requires-Dist: pyyaml>=6.0.3
31
32
  Requires-Dist: rich>=14.1.0
32
33
  Requires-Dist: shortuuid>=1.0.13
33
34
  Requires-Dist: sse-starlette>=3.0.2
34
35
  Requires-Dist: tinydb>=4.8.1
35
- Requires-Dist: typer>=0.16
36
- Requires-Dist: uvicorn>=0.35.0
36
+ Requires-Dist: typer>=0.19.2
37
+ Requires-Dist: uvicorn>=0.36.0
38
+ Provides-Extra: deploy
39
+ Requires-Dist: boto3>=1.34.0; extra == 'deploy'
40
+ Requires-Dist: docker>=7.0.0; extra == 'deploy'
41
+ Requires-Dist: google-cloud-artifact-registry>=1.8.0; extra == 'deploy'
42
+ Requires-Dist: google-cloud-run>=0.10.0; extra == 'deploy'
43
+ Requires-Dist: google-cloud-secret-manager>=2.18.0; extra == 'deploy'
37
44
  Provides-Extra: dev
38
- Requires-Dist: hatch>=1.14.0; extra == 'dev'
39
- Requires-Dist: jsonschema>=4.25.0; extra == 'dev'
40
- Requires-Dist: mypy>=1.17.1; extra == 'dev'
45
+ Requires-Dist: hatch>=1.14.2; extra == 'dev'
46
+ Requires-Dist: jsonschema>=4.25.1; extra == 'dev'
47
+ Requires-Dist: mypy>=1.18.2; extra == 'dev'
41
48
  Requires-Dist: pre-commit>=3.7.0; extra == 'dev'
42
- Requires-Dist: pytest-asyncio>=1.1.0; extra == 'dev'
43
- Requires-Dist: pytest-cov>=6.2.1; extra == 'dev'
44
- Requires-Dist: pytest-mock>=3.14.1; extra == 'dev'
45
- Requires-Dist: pytest-sugar>=1.0.0; extra == 'dev'
46
- Requires-Dist: pytest>=8.4.1; extra == 'dev'
49
+ Requires-Dist: pytest-asyncio>=1.2.0; extra == 'dev'
50
+ Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
51
+ Requires-Dist: pytest-mock>=3.15.1; extra == 'dev'
52
+ Requires-Dist: pytest-sugar>=1.1.1; extra == 'dev'
53
+ Requires-Dist: pytest>=8.4.2; extra == 'dev'
47
54
  Requires-Dist: respx>=0.22.0; extra == 'dev'
48
- Requires-Dist: ruff>=0.12.8; extra == 'dev'
55
+ Requires-Dist: ruff>=0.13.3; extra == 'dev'
49
56
  Requires-Dist: types-deprecated>=1.2.15.20250304; extra == 'dev'
50
57
  Requires-Dist: types-python-slugify>=8.0.2.20240310; extra == 'dev'
51
58
  Description-Content-Type: text/markdown
52
59
 
53
60
  # SUPERVAIZER
54
61
 
55
- [[Operate AI Agents with confidence]]
62
+ [Operate AI Agents with confidence]
56
63
 
57
- A Python toolkit for building, managing, and connecting AI agents with full [Agent-to-Agent (A2A)](https://google.github.io/A2A/#/) and [Agent Communication Protocol (ACP)](https://github.com/i-am-bee/ACP) support.
64
+ A Python toolkit for building, managing, and connecting AI agents with full [Agent-to-Agent (A2A)](https://a2a-protocol.org/) protocol support.
58
65
 
59
66
  [![Python Version](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue.svg)](https://www.python.org/downloads/)
60
- [![Package Version](https://img.shields.io/badge/Supervaizer-0.9.6-yellow.svg)](https://github.com/supervaize/supervaizer)
61
- [![A2A Protocol](https://img.shields.io/badge/A2A-Protocol-orange.svg)](https://google.github.io/A2A/)
62
- [![ACP Protocol](https://img.shields.io/badge/ACP-Protocol-purple.svg)](https://github.com/i-am-bee/ACP)
67
+ [![A2A Protocol](https://img.shields.io/badge/A2A-Protocol-orange.svg)](https://a2a-protocol.org/)
63
68
  [![Test Coverage](https://img.shields.io/badge/Coverage-81%25-brightgreen.svg)](https://github.com/supervaize/supervaizer)
64
69
 
70
+ > **⚠️ Beta Disclaimer**: SUPERVAIZER is currently in beta mode. Not everything works as expected yet. Please report any issues you encounter.
71
+
65
72
  - [SUPERVAIZER](#supervaizer)
66
73
  - [Description](#description)
67
74
  - [Quick Start](#quick-start)
@@ -70,35 +77,45 @@ A Python toolkit for building, managing, and connecting AI agents with full [Age
70
77
  - [3. Scaffold the controller](#3-scaffold-the-controller)
71
78
  - [(Optional) 4. Configure your Supervaize account \& environment](#optional-4-configure-your-supervaize-account--environment)
72
79
  - [5. Start the server 🚀](#5-start-the-server-)
80
+ - [6. Optional parameters](#6-optional-parameters)
73
81
  - [What's next?](#whats-next)
74
82
  - [Features](#features)
75
83
  - [Protocol Support](#protocol-support)
84
+ - [Cloud Deployment](#cloud-deployment)
85
+ - [Quick Start](#quick-start-1)
86
+ - [Deployment Commands](#deployment-commands)
87
+ - [Features](#features-1)
88
+ - [Documentation](#documentation)
76
89
  - [Using the CLI](#using-the-cli)
77
90
  - [API Documentation \& User Interfaces](#api-documentation--user-interfaces)
78
91
  - [Admin Interface (`/admin`)](#admin-interface-admin)
79
- - [Quick Start](#quick-start-1)
92
+ - [Quick Start](#quick-start-2)
80
93
  - [Calculating costs](#calculating-costs)
81
- - [Documentation](#documentation)
94
+ - [Documentation](#documentation-1)
82
95
  - [Contributing](#contributing)
83
96
  - [License](#license)
84
97
 
85
98
  ## Description
86
99
 
87
- SUPERVAIZER is a toolkit built for the age of AI interoperability. At its core, it implements Google's Agent-to-Agent (A2A) protocol and IBM's Agent Communication Protocol (ACP), enabling seamless discovery and interaction between agents across different systems and platforms.
100
+ SUPERVAIZER is a toolkit built for the age of AI interoperability. At its core, it implements the Agent-to-Agent (A2A) protocol, enabling seamless discovery and interaction between agents across different systems and platforms.
88
101
 
89
- With comprehensive support for the A2A/ACP protocols, specification, SUPERVAIZER allows you to:
102
+ With comprehensive support for the A2A protocol specification, SUPERVAIZER allows you to:
90
103
 
91
- - Enhance the capabilities of your agents, making them automatically discoverable by other A2A/ACP compatible systems
104
+ - Enhance the capabilities of your agents, making them automatically discoverable by other A2A compatible systems
92
105
  - Expose standardized agent capabilities through agent cards
93
106
  - Monitor agent health and status through dedicated endpoints
94
107
  - Connect your agents to the growing ecosystem of A2A-compatible tools
95
108
 
96
109
  Beyond A2A interoperability, SUPERVAIZER provides a robust API for agent registration, job control, event handling, telemetry, and more, making it a crucial component for building and managing AI agent systems.
97
110
 
111
+ SUPERVAIZER is the recommended controller to integrate AI Agents into the [supervaize](https://supervaize.com) plateform.
112
+
98
113
  ## Quick Start
99
114
 
100
115
  Kickstart a **Python** agent with the **Supervaizer Controller** so it's discoverable and operable by Supervaize.
101
116
 
117
+ See full our full [documentation](https://doc.supervaize.com/docs/category/supervaizer-controller)
118
+
102
119
  ### What we'll do
103
120
 
104
121
  1. **Install Supervaizer** in that project
@@ -159,28 +176,82 @@ Once the server is running, you'll have:
159
176
  - **A2A discovery**: `/.well-known/agents.json`
160
177
  - **ACP discovery**: `/agents`
161
178
 
179
+ ### 6. Optional parameters
180
+
181
+ Configure retry behavior for HTTP requests to the Supervaize API:
182
+
183
+ - **`SUPERVAIZE_HTTP_MAX_RETRIES`**: Number of retry attempts for failed HTTP requests (default: `2`). The client will automatically retry requests that fail with status codes 429, 500, 502, 503, or 504.
184
+
185
+ ```bash
186
+ export SUPERVAIZE_MAX_HTTP_RETRIES=3 # Will attempt up to 4 times total (1 original + 3 retries)
187
+ ```
188
+
162
189
  ### What's next?
163
190
 
164
191
  - Add more **custom methods** (`chat`, `custom`) to extend control
165
- - Turn on **A2A / ACP** discovery for interoperability
192
+ - Turn on **A2A** discovery for interoperability
166
193
  - Hook your controller into Supervaize to **monitor, audit, and operate** the agent
167
194
 
168
- For detailed instructions on customizing your controller, see the [Controller Setup Guide](https://doc.supervaize.com/docs/supervaizer-controller/controller-setup-guide).
195
+ For detailed instructions on customizing your controller, see the [Controller Setup Guide](https://doc.supervaize.com/docs/supervaizer-controller/controller-setup)
169
196
 
170
197
  ## Features
171
198
 
172
199
  - **Agent Management**: Register, update, and control agents
173
200
  - **Job Control**: Create, track, and manage jobs
174
201
  - **Event Handling**: Process and respond to system events
175
- - Protocol support
176
- - **A2A Protocol **: Integration with Google's Agent-to-Agent protocol for interoperability
177
- - **ACP Protocol **: Integration with IBM/BeeAI's Agent Communication Protocol for standardized agent discovery and interaction
202
+ - **🚀 Cloud Deployment**: Automated deployment to GCP Cloud Run, AWS App Runner, and DigitalOcean App Platform
203
+ - **A2A Protocol Support**: Full integration with the Agent-to-Agent protocol for standardized agent discovery and interaction
178
204
  - **Server Communication**: Interact with SUPERVAIZE servers (see [supervaize.com](https://www.supervaize.com) for more info)
179
205
  - **Web Admin Interface**: Easy to use web-based admin dashboard for managing jobs, cases, and system monitoring
180
206
 
181
207
  ## Protocol Support
182
208
 
183
- SUPERVAIZER provides comprehensive support for multiple agent communication protocols. See [Protocol Documentation](docs/PROTOCOLS.md) for complete details.
209
+ SUPERVAIZER provides comprehensive support for the A2A agent communication protocol. See [Protocol Documentation](docs/PROTOCOLS.md) for complete details.
210
+
211
+ ## Cloud Deployment
212
+
213
+ SUPERVAIZER includes a powerful deployment CLI that automates the entire process of deploying your agents to production cloud platforms.
214
+
215
+ ### Quick Start
216
+
217
+ ```bash
218
+ # Install with deployment dependencies
219
+ pip install supervaizer[deploy]
220
+
221
+ # Test locally with Docker
222
+ supervaizer deploy local --generate-api-key --generate-rsa
223
+
224
+ # Deploy to Google Cloud Run
225
+ supervaizer deploy up --platform cloud-run --region us-central1
226
+
227
+ # Deploy to AWS App Runner
228
+ supervaizer deploy up --platform aws-app-runner --region us-east-1
229
+
230
+ # Deploy to DigitalOcean App Platform
231
+ supervaizer deploy up --platform do-app-platform --region nyc
232
+ ```
233
+
234
+ ### Deployment Commands
235
+
236
+ - **`supervaizer deploy plan`** - Preview deployment actions before applying
237
+ - **`supervaizer deploy up`** - Deploy to cloud platform with automated build, push, and verification
238
+ - **`supervaizer deploy down`** - Tear down deployment and clean up resources
239
+ - **`supervaizer deploy status`** - Check deployment status and health
240
+ - **`supervaizer deploy local`** - Local Docker testing with docker-compose
241
+ - **`supervaizer deploy clean`** - Clean up deployment artifacts and state
242
+
243
+ ### Features
244
+
245
+ - ✅ **Automated Docker Workflow**: Build → Push → Deploy → Verify
246
+ - ✅ **Secret Management**: Secure handling of API keys and RSA keys
247
+ - ✅ **Health Verification**: Automatic health checks at `/.well-known/health`
248
+ - ✅ **Idempotent Deployments**: Safe create/update operations with rollback on failure
249
+ - ✅ **Local Testing**: Full Docker Compose environment for pre-deployment testing
250
+
251
+ ### Documentation
252
+
253
+ - [RFC-001: Cloud Deployment CLI](docs/rfc/001-cloud-deployment-cli.md) - Complete specification
254
+ - [Local Testing Guide](docs/LOCAL_TESTING.md) - Docker testing documentation
184
255
 
185
256
  ## Using the CLI
186
257