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.
- supervaizer/__init__.py +97 -0
- supervaizer/__version__.py +10 -0
- supervaizer/account.py +308 -0
- supervaizer/account_service.py +93 -0
- supervaizer/admin/routes.py +1293 -0
- supervaizer/admin/static/js/job-start-form.js +373 -0
- supervaizer/admin/templates/agent_detail.html +145 -0
- supervaizer/admin/templates/agents.html +249 -0
- supervaizer/admin/templates/agents_grid.html +82 -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/job_start_test.html +109 -0
- supervaizer/admin/templates/jobs_list.html +180 -0
- supervaizer/admin/templates/jobs_table.html +122 -0
- supervaizer/admin/templates/navigation.html +163 -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/admin/templates/supervaize_instructions.html +212 -0
- supervaizer/agent.py +956 -0
- supervaizer/case.py +432 -0
- supervaizer/cli.py +395 -0
- supervaizer/common.py +324 -0
- supervaizer/deploy/__init__.py +16 -0
- supervaizer/deploy/cli.py +305 -0
- supervaizer/deploy/commands/__init__.py +9 -0
- supervaizer/deploy/commands/clean.py +294 -0
- supervaizer/deploy/commands/down.py +119 -0
- supervaizer/deploy/commands/local.py +460 -0
- supervaizer/deploy/commands/plan.py +167 -0
- supervaizer/deploy/commands/status.py +169 -0
- supervaizer/deploy/commands/up.py +281 -0
- supervaizer/deploy/docker.py +377 -0
- supervaizer/deploy/driver_factory.py +42 -0
- supervaizer/deploy/drivers/__init__.py +39 -0
- supervaizer/deploy/drivers/aws_app_runner.py +607 -0
- supervaizer/deploy/drivers/base.py +196 -0
- supervaizer/deploy/drivers/cloud_run.py +570 -0
- supervaizer/deploy/drivers/do_app_platform.py +504 -0
- supervaizer/deploy/health.py +404 -0
- supervaizer/deploy/state.py +210 -0
- supervaizer/deploy/templates/Dockerfile.template +44 -0
- supervaizer/deploy/templates/debug_env.py +69 -0
- supervaizer/deploy/templates/docker-compose.yml.template +37 -0
- supervaizer/deploy/templates/dockerignore.template +66 -0
- supervaizer/deploy/templates/entrypoint.sh +20 -0
- supervaizer/deploy/utils.py +52 -0
- supervaizer/event.py +181 -0
- supervaizer/examples/controller_template.py +196 -0
- supervaizer/instructions.py +145 -0
- supervaizer/job.py +392 -0
- supervaizer/job_service.py +156 -0
- supervaizer/lifecycle.py +417 -0
- supervaizer/parameter.py +233 -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/py.typed +1 -0
- supervaizer/routes.py +917 -0
- supervaizer/server.py +553 -0
- supervaizer/server_utils.py +54 -0
- supervaizer/storage.py +462 -0
- supervaizer/telemetry.py +81 -0
- supervaizer/utils/__init__.py +16 -0
- supervaizer/utils/version_check.py +56 -0
- supervaizer-0.10.5.dist-info/METADATA +317 -0
- supervaizer-0.10.5.dist-info/RECORD +76 -0
- supervaizer-0.10.5.dist-info/WHEEL +4 -0
- supervaizer-0.10.5.dist-info/entry_points.txt +2 -0
- supervaizer-0.10.5.dist-info/licenses/LICENSE.md +346 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# Copyright (c) 2024-2025 Alain Prasquier - Supervaize.com. All rights reserved.
|
|
3
|
+
#
|
|
4
|
+
# This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
|
5
|
+
# If a copy of the MPL was not distributed with this file, you can obtain one at
|
|
6
|
+
# https://mozilla.org/MPL/2.0/.
|
|
7
|
+
|
|
8
|
+
"""Debug script to check environment variables in container."""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def debug_environment_variables() -> None:
|
|
15
|
+
"""Print all environment variables for debugging."""
|
|
16
|
+
print("=== Environment Variables Debug ===")
|
|
17
|
+
print(f"Python version: {sys.version}")
|
|
18
|
+
print(f"Working directory: {os.getcwd()}")
|
|
19
|
+
print()
|
|
20
|
+
|
|
21
|
+
# Check specific Supervaize environment variables
|
|
22
|
+
supervaize_vars = [
|
|
23
|
+
"SUPERVAIZE_API_KEY",
|
|
24
|
+
"SUPERVAIZE_WORKSPACE_ID",
|
|
25
|
+
"SUPERVAIZE_API_URL",
|
|
26
|
+
"SUPERVAIZER_PORT",
|
|
27
|
+
"SUPERVAIZER_PUBLIC_URL",
|
|
28
|
+
"SUPERVAIZER_ENVIRONMENT",
|
|
29
|
+
"SUPERVAIZER_HOST",
|
|
30
|
+
"SUPERVAIZER_API_KEY",
|
|
31
|
+
"SV_RSA_PRIVATE_KEY",
|
|
32
|
+
"SV_LOG_LEVEL",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
print("=== Supervaize Environment Variables ===")
|
|
36
|
+
for var_name in supervaize_vars:
|
|
37
|
+
value = os.getenv(var_name)
|
|
38
|
+
if value is None:
|
|
39
|
+
print(f"{var_name}: [NOT SET]")
|
|
40
|
+
elif value == "":
|
|
41
|
+
print(f"{var_name}: [EMPTY STRING]")
|
|
42
|
+
else:
|
|
43
|
+
# Mask sensitive values
|
|
44
|
+
if "KEY" in var_name or "SECRET" in var_name:
|
|
45
|
+
masked_value = (
|
|
46
|
+
value[:4] + "*" * (len(value) - 8) + value[-4:]
|
|
47
|
+
if len(value) > 8
|
|
48
|
+
else "*" * len(value)
|
|
49
|
+
)
|
|
50
|
+
print(f"{var_name}: {masked_value}")
|
|
51
|
+
else:
|
|
52
|
+
print(f"{var_name}: {value}")
|
|
53
|
+
|
|
54
|
+
print()
|
|
55
|
+
print("=== All Environment Variables ===")
|
|
56
|
+
for key, value in sorted(os.environ.items()):
|
|
57
|
+
if "KEY" in key or "SECRET" in key or "PASSWORD" in key:
|
|
58
|
+
masked_value = (
|
|
59
|
+
value[:4] + "*" * (len(value) - 8) + value[-4:]
|
|
60
|
+
if len(value) > 8
|
|
61
|
+
else "*" * len(value)
|
|
62
|
+
)
|
|
63
|
+
print(f"{key}: {masked_value}")
|
|
64
|
+
else:
|
|
65
|
+
print(f"{key}: {value}")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
if __name__ == "__main__":
|
|
69
|
+
debug_environment_variables()
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
services:
|
|
2
|
+
{{SERVICE_NAME}}:
|
|
3
|
+
build:
|
|
4
|
+
context: ..
|
|
5
|
+
dockerfile: .deployment/Dockerfile
|
|
6
|
+
args:
|
|
7
|
+
- SUPERVAIZE_API_KEY={{API_KEY}}
|
|
8
|
+
- SUPERVAIZE_WORKSPACE_ID={{WORKSPACE_ID}}
|
|
9
|
+
- SUPERVAIZE_API_URL={{API_URL}}
|
|
10
|
+
- SUPERVAIZER_PORT=8000
|
|
11
|
+
- SUPERVAIZER_PUBLIC_URL={{PUBLIC_URL}}
|
|
12
|
+
ports:
|
|
13
|
+
- "{{PORT}}:8000"
|
|
14
|
+
environment:
|
|
15
|
+
- SUPERVAIZER_ENVIRONMENT={{ENVIRONMENT}}
|
|
16
|
+
- SUPERVAIZER_HOST=0.0.0.0
|
|
17
|
+
- SUPERVAIZER_PORT=8000
|
|
18
|
+
- SV_LOG_LEVEL={{ env.SV_LOG_LEVEL | default('INFO') }}
|
|
19
|
+
- SUPERVAIZER_API_KEY={{API_KEY}}
|
|
20
|
+
- SV_RSA_PRIVATE_KEY={{RSA_KEY}}
|
|
21
|
+
- SUPERVAIZE_API_KEY={{API_KEY}}
|
|
22
|
+
- SUPERVAIZE_WORKSPACE_ID={{WORKSPACE_ID}}
|
|
23
|
+
- SUPERVAIZE_API_URL={{API_URL}}
|
|
24
|
+
- SUPERVAIZER_PUBLIC_URL={{PUBLIC_URL}}
|
|
25
|
+
healthcheck:
|
|
26
|
+
test: ["CMD", "curl", "-f", "http://localhost:8000/.well-known/health"]
|
|
27
|
+
interval: 30s
|
|
28
|
+
timeout: 10s
|
|
29
|
+
retries: 3
|
|
30
|
+
start_period: 40s
|
|
31
|
+
restart: unless-stopped
|
|
32
|
+
networks:
|
|
33
|
+
- supervaizer-network
|
|
34
|
+
|
|
35
|
+
networks:
|
|
36
|
+
supervaizer-network:
|
|
37
|
+
driver: bridge
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
*.egg-info/
|
|
20
|
+
.installed.cfg
|
|
21
|
+
*.egg
|
|
22
|
+
|
|
23
|
+
# Virtual environments
|
|
24
|
+
.venv/
|
|
25
|
+
venv/
|
|
26
|
+
env/
|
|
27
|
+
ENV/
|
|
28
|
+
env.bak/
|
|
29
|
+
venv.bak/
|
|
30
|
+
|
|
31
|
+
# IDE
|
|
32
|
+
.vscode/
|
|
33
|
+
.idea/
|
|
34
|
+
*.swp
|
|
35
|
+
*.swo
|
|
36
|
+
*~
|
|
37
|
+
|
|
38
|
+
# OS
|
|
39
|
+
.DS_Store
|
|
40
|
+
Thumbs.db
|
|
41
|
+
|
|
42
|
+
# Git
|
|
43
|
+
.git/
|
|
44
|
+
.gitignore
|
|
45
|
+
|
|
46
|
+
# Documentation
|
|
47
|
+
docs/
|
|
48
|
+
*.md
|
|
49
|
+
README*
|
|
50
|
+
|
|
51
|
+
# Tests
|
|
52
|
+
tests/
|
|
53
|
+
test_*
|
|
54
|
+
*_test.py
|
|
55
|
+
|
|
56
|
+
# Deployment artifacts
|
|
57
|
+
.deployment/
|
|
58
|
+
!.deployment/debug_env.py
|
|
59
|
+
|
|
60
|
+
# Logs
|
|
61
|
+
*.log
|
|
62
|
+
logs/
|
|
63
|
+
|
|
64
|
+
# Temporary files
|
|
65
|
+
tmp/
|
|
66
|
+
temp/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
# Change to app directory
|
|
5
|
+
cd /app
|
|
6
|
+
|
|
7
|
+
# Remove any stale .venv references that might cause issues
|
|
8
|
+
if [ -d ".venv" ] && [ ! -f ".venv/bin/python3" ]; then
|
|
9
|
+
rm -rf .venv
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
# Install the project if not already installed
|
|
13
|
+
# This ensures the supervaizer command is available
|
|
14
|
+
if ! command -v supervaizer &> /dev/null; then
|
|
15
|
+
echo "Installing supervaizer..."
|
|
16
|
+
uv sync --frozen --no-dev
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Run supervaizer start
|
|
20
|
+
exec uv run supervaizer start
|
|
@@ -0,0 +1,52 @@
|
|
|
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
|
+
Deployment State Management
|
|
9
|
+
|
|
10
|
+
This module handles deployment state persistence and management.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import subprocess
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from supervaizer.common import log
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def create_deployment_directory(project_root: Path) -> Path:
|
|
20
|
+
"""Create deployment directory and add to .gitignore."""
|
|
21
|
+
deployment_dir = project_root / ".deployment"
|
|
22
|
+
deployment_dir.mkdir(exist_ok=True)
|
|
23
|
+
|
|
24
|
+
# Create logs subdirectory
|
|
25
|
+
logs_dir = deployment_dir / "logs"
|
|
26
|
+
logs_dir.mkdir(exist_ok=True)
|
|
27
|
+
|
|
28
|
+
# Add to .gitignore if not already present
|
|
29
|
+
gitignore_path = project_root / ".gitignore"
|
|
30
|
+
gitignore_entry = ".deployment/"
|
|
31
|
+
|
|
32
|
+
if gitignore_path.exists():
|
|
33
|
+
gitignore_content = gitignore_path.read_text()
|
|
34
|
+
if gitignore_entry not in gitignore_content:
|
|
35
|
+
gitignore_path.write_text(gitignore_content + f"\n{gitignore_entry}\n")
|
|
36
|
+
log.info(f"Added {gitignore_entry} to .gitignore")
|
|
37
|
+
else:
|
|
38
|
+
gitignore_path.write_text(f"{gitignore_entry}\n")
|
|
39
|
+
log.info(f"Created .gitignore with {gitignore_entry}")
|
|
40
|
+
|
|
41
|
+
return deployment_dir
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_git_sha() -> str:
|
|
45
|
+
"""Get the current git SHA for tagging."""
|
|
46
|
+
try:
|
|
47
|
+
result = subprocess.run(
|
|
48
|
+
["git", "rev-parse", "HEAD"], capture_output=True, text=True, check=True
|
|
49
|
+
)
|
|
50
|
+
return result.stdout.strip()[:8] # Use short SHA
|
|
51
|
+
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
52
|
+
return "latest"
|
supervaizer/event.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
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
|
+
from enum import Enum
|
|
8
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Dict
|
|
9
|
+
|
|
10
|
+
from supervaizer.__version__ import VERSION
|
|
11
|
+
from supervaizer.common import SvBaseModel
|
|
12
|
+
from supervaizer.lifecycle import EntityStatus
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from supervaizer.agent import Agent
|
|
16
|
+
from supervaizer.case import Case, CaseNodeUpdate
|
|
17
|
+
from supervaizer.job import Job
|
|
18
|
+
from supervaizer.server import Server
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class EventType(str, Enum):
|
|
22
|
+
AGENT_REGISTER = "agent.register"
|
|
23
|
+
SERVER_REGISTER = "server.register"
|
|
24
|
+
AGENT_WAKEUP = "agent.wakeup"
|
|
25
|
+
AGENT_SEND_ANOMALY = "agent.anomaly"
|
|
26
|
+
INTERMEDIARY = "agent.intermediary"
|
|
27
|
+
JOB_START_CONFIRMATION = "agent.job.start.confirmation"
|
|
28
|
+
JOB_END = "agent.job.end"
|
|
29
|
+
JOB_STATUS = "agent.job.status"
|
|
30
|
+
JOB_RESULT = "agent.job.result"
|
|
31
|
+
JOB_ERROR = "agent.job.error"
|
|
32
|
+
CASE_START = "agent.case.start"
|
|
33
|
+
CASE_END = "agent.case.end"
|
|
34
|
+
CASE_STATUS = "agent.case.status"
|
|
35
|
+
CASE_RESULT = "agent.case.result"
|
|
36
|
+
CASE_UPDATE = "agent.case.update"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class AbstractEvent(SvBaseModel):
|
|
40
|
+
supervaizer_VERSION: ClassVar[str] = VERSION
|
|
41
|
+
source: Dict[str, Any]
|
|
42
|
+
account: Any # Use Any to avoid Pydantic type resolution issues
|
|
43
|
+
type: EventType
|
|
44
|
+
object_type: str
|
|
45
|
+
details: Dict[str, Any]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Event(AbstractEvent):
|
|
49
|
+
"""Base class for all events in the Supervaize Control system.
|
|
50
|
+
|
|
51
|
+
Events represent messages sent from agents to the control system to communicate
|
|
52
|
+
status, anomalies, deliverables and other information.
|
|
53
|
+
|
|
54
|
+
Inherits from AbstractEvent which defines the core event attributes:
|
|
55
|
+
- source: The source/origin of the event (e.g. agent/server URI)
|
|
56
|
+
- type: The EventType enum indicating the event category
|
|
57
|
+
- account: The account that the event belongs to
|
|
58
|
+
- details: A dictionary containing event-specific details
|
|
59
|
+
|
|
60
|
+
Tests in tests/test_event.py
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
64
|
+
super().__init__(**kwargs)
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def payload(self) -> Dict[str, Any]:
|
|
68
|
+
"""
|
|
69
|
+
Returns the payload for the event.
|
|
70
|
+
This must be a dictionary that can be serialized to JSON to be sent in the request body.
|
|
71
|
+
"""
|
|
72
|
+
return {
|
|
73
|
+
"source": self.source,
|
|
74
|
+
"workspace": f"{self.account.workspace_id}",
|
|
75
|
+
"event_type": f"{self.type.value}",
|
|
76
|
+
"object_type": self.object_type,
|
|
77
|
+
"details": self.details,
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class AgentRegisterEvent(Event):
|
|
82
|
+
"""Event sent when an agent registers with the control system.
|
|
83
|
+
|
|
84
|
+
Test in tests/test_agent_register_event.py
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def __init__(
|
|
88
|
+
self,
|
|
89
|
+
agent: "Agent",
|
|
90
|
+
account: Any, # Use Any to avoid type resolution issues
|
|
91
|
+
polling: bool = True,
|
|
92
|
+
) -> None:
|
|
93
|
+
super().__init__(
|
|
94
|
+
type=EventType.AGENT_REGISTER,
|
|
95
|
+
account=account,
|
|
96
|
+
source={"agent": agent.slug},
|
|
97
|
+
object_type="agent",
|
|
98
|
+
details=agent.registration_info | {"polling": polling},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class ServerRegisterEvent(Event):
|
|
103
|
+
def __init__(
|
|
104
|
+
self,
|
|
105
|
+
account: Any, # Use Any to avoid type resolution issues
|
|
106
|
+
server: "Server",
|
|
107
|
+
) -> None:
|
|
108
|
+
super().__init__(
|
|
109
|
+
type=EventType.SERVER_REGISTER,
|
|
110
|
+
source={"server": server.uri},
|
|
111
|
+
account=account,
|
|
112
|
+
object_type="server",
|
|
113
|
+
details=server.registration_info,
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class JobStartConfirmationEvent(Event):
|
|
118
|
+
def __init__(
|
|
119
|
+
self,
|
|
120
|
+
job: "Job",
|
|
121
|
+
account: Any, # Use Any to avoid type resolution issues
|
|
122
|
+
) -> None:
|
|
123
|
+
super().__init__(
|
|
124
|
+
type=EventType.JOB_START_CONFIRMATION,
|
|
125
|
+
account=account,
|
|
126
|
+
source={"job": job.id},
|
|
127
|
+
object_type="job",
|
|
128
|
+
details=job.registration_info,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class JobFinishedEvent(Event):
|
|
133
|
+
def __init__(self, job: "Job", account: Any) -> None:
|
|
134
|
+
# Check if job has responses, otherwise use the job's current status
|
|
135
|
+
if job.responses:
|
|
136
|
+
details = job.responses[-1].status
|
|
137
|
+
else:
|
|
138
|
+
details = job.status
|
|
139
|
+
|
|
140
|
+
event_type = (
|
|
141
|
+
EventType.JOB_END
|
|
142
|
+
if details == EntityStatus.COMPLETED
|
|
143
|
+
else EventType.JOB_ERROR
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
super().__init__(
|
|
147
|
+
type=event_type,
|
|
148
|
+
account=account,
|
|
149
|
+
source={"job": job.id},
|
|
150
|
+
object_type="job",
|
|
151
|
+
details=job.registration_info,
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
class CaseStartEvent(Event):
|
|
156
|
+
def __init__(
|
|
157
|
+
self, case: "Case", account: Any
|
|
158
|
+
) -> None: # Use Any to avoid type resolution issues
|
|
159
|
+
super().__init__(
|
|
160
|
+
type=EventType.CASE_START,
|
|
161
|
+
account=account,
|
|
162
|
+
source={"job": case.job_id, "case": case.id},
|
|
163
|
+
object_type="case",
|
|
164
|
+
details=case.registration_info,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
class CaseUpdateEvent(Event):
|
|
169
|
+
def __init__(
|
|
170
|
+
self,
|
|
171
|
+
case: "Case",
|
|
172
|
+
account: Any,
|
|
173
|
+
update: "CaseNodeUpdate",
|
|
174
|
+
) -> None:
|
|
175
|
+
super().__init__(
|
|
176
|
+
type=EventType.CASE_UPDATE,
|
|
177
|
+
account=account,
|
|
178
|
+
source={"job": case.job_id, "case": case.id},
|
|
179
|
+
object_type="case",
|
|
180
|
+
details=update.registration_info,
|
|
181
|
+
)
|
|
@@ -0,0 +1,196 @@
|
|
|
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 is an example file.
|
|
8
|
+
# It must be copied / renamed to supervaizer_control.py
|
|
9
|
+
# and edited to configure your agent(s)
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
import shortuuid
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
|
|
15
|
+
from supervaizer import (
|
|
16
|
+
Agent,
|
|
17
|
+
AgentMethod,
|
|
18
|
+
AgentMethods,
|
|
19
|
+
Parameter,
|
|
20
|
+
ParametersSetup,
|
|
21
|
+
Server,
|
|
22
|
+
)
|
|
23
|
+
from supervaizer.account import Account
|
|
24
|
+
|
|
25
|
+
# Create a console with default style set to yellow
|
|
26
|
+
console = Console(style="yellow")
|
|
27
|
+
|
|
28
|
+
# Public url of your hosted agent (including port if needed)
|
|
29
|
+
# Use loca.lt or ngrok to get a public url during development.
|
|
30
|
+
# This can be setup from environment variables.
|
|
31
|
+
# SUPERVAIZER_HOST and SUPERVAIZER_PORT
|
|
32
|
+
DEV_PUBLIC_URL = "https://myagent-dev.loca.lt"
|
|
33
|
+
# Public url of your hosted agent
|
|
34
|
+
PROD_PUBLIC_URL = "https://myagent.cloud-hosting.net:8001"
|
|
35
|
+
|
|
36
|
+
# Define the parameters and secrets expected by the agent
|
|
37
|
+
agent_parameters: ParametersSetup | None = ParametersSetup.from_list(
|
|
38
|
+
[
|
|
39
|
+
Parameter(
|
|
40
|
+
name="OPEN_API_KEY",
|
|
41
|
+
description="OpenAPI Key",
|
|
42
|
+
is_environment=True,
|
|
43
|
+
is_secret=True,
|
|
44
|
+
),
|
|
45
|
+
Parameter(
|
|
46
|
+
name="SERPER_API",
|
|
47
|
+
description="Server API key updated",
|
|
48
|
+
is_environment=True,
|
|
49
|
+
is_secret=True,
|
|
50
|
+
),
|
|
51
|
+
Parameter(
|
|
52
|
+
name="COMPETITOR_SUMMARY_URL",
|
|
53
|
+
description="Competitor Summary URL",
|
|
54
|
+
is_environment=True,
|
|
55
|
+
is_secret=False,
|
|
56
|
+
),
|
|
57
|
+
]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# Define the method used to start a job
|
|
61
|
+
job_start_method: AgentMethod = AgentMethod(
|
|
62
|
+
name="start", # This is required
|
|
63
|
+
method="example_agent.example_synchronous_job_start", # Path to the main function in dotted notation.
|
|
64
|
+
is_async=False, # Only use sync methods for the moment
|
|
65
|
+
params={"action": "start"}, # If default parameters must be passed to the function.
|
|
66
|
+
fields=[
|
|
67
|
+
{
|
|
68
|
+
"name": "Company to research", # Field name - displayed in the UI
|
|
69
|
+
"type": str, # Python type of the field for pydantic validation - note , ChoiceField and MultipleChoiceField are a list[str]
|
|
70
|
+
"field_type": "CharField", # Field type for persistence.
|
|
71
|
+
"description": "Company to research", # Optional - Description of the field - displayed in the UI
|
|
72
|
+
"default": "Google", # Optional - Default value for the field
|
|
73
|
+
"required": True, # Whether the field is required
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "Max number of results",
|
|
77
|
+
"type": int,
|
|
78
|
+
"field_type": "IntegerField",
|
|
79
|
+
"required": True,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"name": "Subscribe to updates",
|
|
83
|
+
"type": bool,
|
|
84
|
+
"field_type": "BooleanField",
|
|
85
|
+
"required": False,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"name": "Type of research",
|
|
89
|
+
"type": str,
|
|
90
|
+
"field_type": "ChoiceField",
|
|
91
|
+
"choices": [["A", "Advanced"], ["R", "Restricted"]],
|
|
92
|
+
"widget": "RadioSelect",
|
|
93
|
+
"required": True,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
"name": "Details of research",
|
|
97
|
+
"type": str,
|
|
98
|
+
"field_type": "CharField",
|
|
99
|
+
"widget": "Textarea",
|
|
100
|
+
"required": False,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"name": "List of countries",
|
|
104
|
+
"type": list[str],
|
|
105
|
+
"field_type": "MultipleChoiceField",
|
|
106
|
+
"choices": [
|
|
107
|
+
["PA", "Panama"],
|
|
108
|
+
["PG", "Papua New Guinea"],
|
|
109
|
+
["PY", "Paraguay"],
|
|
110
|
+
["PE", "Peru"],
|
|
111
|
+
["PH", "Philippines"],
|
|
112
|
+
["PN", "Pitcairn"],
|
|
113
|
+
["PL", "Poland"],
|
|
114
|
+
],
|
|
115
|
+
"required": True,
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"name": "languages",
|
|
119
|
+
"type": list[str],
|
|
120
|
+
"field_type": "MultipleChoiceField",
|
|
121
|
+
"choices": [["en", "English"], ["fr", "French"], ["es", "Spanish"]],
|
|
122
|
+
"required": False,
|
|
123
|
+
},
|
|
124
|
+
],
|
|
125
|
+
description="Start the collection of new competitor summary",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
job_stop_method: AgentMethod = AgentMethod(
|
|
129
|
+
name="stop",
|
|
130
|
+
method="control.stop",
|
|
131
|
+
params={"action": "stop"},
|
|
132
|
+
description="Stop the agent",
|
|
133
|
+
)
|
|
134
|
+
job_status_method: AgentMethod = AgentMethod(
|
|
135
|
+
name="status",
|
|
136
|
+
method="hello.mystatus",
|
|
137
|
+
params={"status": "statusvalue"},
|
|
138
|
+
description="Get the status of the agent",
|
|
139
|
+
)
|
|
140
|
+
custom_method: AgentMethod = AgentMethod(
|
|
141
|
+
name="custom",
|
|
142
|
+
method="control.custom",
|
|
143
|
+
params={"action": "custom"},
|
|
144
|
+
description="Custom method",
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
custom_method2: AgentMethod = AgentMethod(
|
|
148
|
+
name="custom2",
|
|
149
|
+
method="control.custom2",
|
|
150
|
+
params={"action": "custom2"},
|
|
151
|
+
description="Custom method",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
agent_name = "competitor_summary"
|
|
156
|
+
|
|
157
|
+
# Define the Agent
|
|
158
|
+
agent: Agent = Agent(
|
|
159
|
+
name=agent_name,
|
|
160
|
+
id=shortuuid.uuid(f"{agent_name}"),
|
|
161
|
+
author="John Doe", # Author of the agent
|
|
162
|
+
developer="Developer", # Developer of the controller integration
|
|
163
|
+
maintainer="Ive Maintained", # Maintainer of the integration
|
|
164
|
+
editor="DevAiExperts", # Editor (usually a company)
|
|
165
|
+
version="1.3", # Version string
|
|
166
|
+
description="This is a test agent",
|
|
167
|
+
tags=["testtag", "testtag2"],
|
|
168
|
+
methods=AgentMethods(
|
|
169
|
+
job_start=job_start_method,
|
|
170
|
+
job_stop=job_stop_method,
|
|
171
|
+
job_status=job_status_method,
|
|
172
|
+
chat=None,
|
|
173
|
+
custom={"custom1": custom_method, "custom2": custom_method2},
|
|
174
|
+
),
|
|
175
|
+
parameters_setup=agent_parameters,
|
|
176
|
+
instructions_path="supervaize_instructions.html", # Path where instructions page is served
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# For export purposes, use dummy values if environment variables are not set
|
|
180
|
+
account: Account = Account(
|
|
181
|
+
workspace_id=os.getenv("SUPERVAIZE_WORKSPACE_ID") or "dummy_workspace_id",
|
|
182
|
+
api_key=os.getenv("SUPERVAIZE_API_KEY") or "dummy_api_key",
|
|
183
|
+
api_url=os.getenv("SUPERVAIZE_API_URL") or "https://api.supervaize.com",
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
# Define the supervaizer server capabilities
|
|
187
|
+
sv_server: Server = Server(
|
|
188
|
+
agents=[agent],
|
|
189
|
+
a2a_endpoints=True, # Enable A2A endpoints
|
|
190
|
+
supervisor_account=account, # Account of the supervisor from Supervaize
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
# Start the supervaize server
|
|
196
|
+
sv_server.launch(log_level="DEBUG")
|