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.
- supervaizer/__init__.py +11 -2
- supervaizer/__version__.py +1 -1
- supervaizer/account.py +4 -0
- supervaizer/account_service.py +7 -1
- supervaizer/admin/routes.py +24 -8
- supervaizer/admin/templates/agents.html +74 -0
- supervaizer/admin/templates/agents_grid.html +5 -3
- supervaizer/admin/templates/navigation.html +11 -1
- supervaizer/admin/templates/supervaize_instructions.html +212 -0
- supervaizer/agent.py +28 -6
- supervaizer/case.py +46 -14
- supervaizer/cli.py +247 -7
- supervaizer/common.py +45 -4
- supervaizer/deploy/__init__.py +16 -0
- supervaizer/deploy/cli.py +296 -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 +378 -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/examples/controller_template.py +1 -1
- supervaizer/job.py +18 -5
- supervaizer/job_service.py +6 -5
- supervaizer/parameter.py +13 -1
- supervaizer/protocol/__init__.py +2 -2
- supervaizer/protocol/a2a/routes.py +1 -1
- supervaizer/routes.py +141 -17
- supervaizer/server.py +5 -11
- supervaizer/utils/__init__.py +16 -0
- supervaizer/utils/version_check.py +56 -0
- {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.dist-info}/METADATA +105 -34
- supervaizer-0.10.1.dist-info/RECORD +76 -0
- {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.dist-info}/WHEEL +1 -1
- supervaizer/protocol/acp/__init__.py +0 -21
- supervaizer/protocol/acp/model.py +0 -198
- supervaizer/protocol/acp/routes.py +0 -74
- supervaizer-0.9.8.dist-info/RECORD +0 -52
- {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.dist-info}/entry_points.txt +0 -0
- {supervaizer-0.9.8.dist-info → supervaizer-0.10.1.dist-info}/licenses/LICENSE.md +0 -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"
|
|
@@ -173,6 +173,7 @@ agent: Agent = Agent(
|
|
|
173
173
|
custom={"custom1": custom_method, "custom2": custom_method2},
|
|
174
174
|
),
|
|
175
175
|
parameters_setup=agent_parameters,
|
|
176
|
+
instructions_path="supervaize_instructions.html", # Path where instructions page is served
|
|
176
177
|
)
|
|
177
178
|
|
|
178
179
|
# For export purposes, use dummy values if environment variables are not set
|
|
@@ -186,7 +187,6 @@ account: Account = Account(
|
|
|
186
187
|
sv_server: Server = Server(
|
|
187
188
|
agents=[agent],
|
|
188
189
|
a2a_endpoints=True, # Enable A2A endpoints
|
|
189
|
-
acp_endpoints=True, # Enable ACP endpoints
|
|
190
190
|
supervisor_account=account, # Account of the supervisor from Supervaize
|
|
191
191
|
)
|
|
192
192
|
|
supervaizer/job.py
CHANGED
|
@@ -8,7 +8,7 @@ import time
|
|
|
8
8
|
import traceback
|
|
9
9
|
import uuid
|
|
10
10
|
from datetime import datetime
|
|
11
|
-
from typing import TYPE_CHECKING, Any, ClassVar, Dict,
|
|
11
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional
|
|
12
12
|
|
|
13
13
|
from supervaizer.__version__ import VERSION
|
|
14
14
|
from supervaizer.common import SvBaseModel, log, singleton
|
|
@@ -242,8 +242,8 @@ class AbstractJob(SvBaseModel):
|
|
|
242
242
|
responses: list["JobResponse"] = []
|
|
243
243
|
finished_at: datetime | None = None
|
|
244
244
|
created_at: datetime | None = None
|
|
245
|
-
agent_parameters:
|
|
246
|
-
case_ids:
|
|
245
|
+
agent_parameters: list[dict[str, Any]] | None = None
|
|
246
|
+
case_ids: list[str] = [] # Foreign key relationship to cases
|
|
247
247
|
|
|
248
248
|
|
|
249
249
|
class Job(AbstractJob):
|
|
@@ -344,7 +344,7 @@ class Job(AbstractJob):
|
|
|
344
344
|
cls,
|
|
345
345
|
job_context: "JobContext",
|
|
346
346
|
agent_name: str,
|
|
347
|
-
agent_parameters: Optional[
|
|
347
|
+
agent_parameters: Optional[list[dict[str, Any]]] = None,
|
|
348
348
|
name: Optional[str] = None,
|
|
349
349
|
) -> "Job":
|
|
350
350
|
"""Create a new job
|
|
@@ -352,7 +352,7 @@ class Job(AbstractJob):
|
|
|
352
352
|
Args:
|
|
353
353
|
job_context (JobContext): The context of the job
|
|
354
354
|
agent_name (str): The name of the agent
|
|
355
|
-
agent_parameters (dict[str, Any] | None): Optional parameters for the job
|
|
355
|
+
agent_parameters (list[dict[str, Any]] | None): Optional parameters for the job
|
|
356
356
|
name (str | None): Optional name for the job, defaults to mission name if not provided
|
|
357
357
|
|
|
358
358
|
Returns:
|
|
@@ -362,6 +362,19 @@ class Job(AbstractJob):
|
|
|
362
362
|
# Use provided name or fallback to mission name from context
|
|
363
363
|
job_name = name or job_context.mission_name
|
|
364
364
|
|
|
365
|
+
# Ensure agent_parameters is a list of dicts, not nested incorrectly
|
|
366
|
+
if agent_parameters is not None:
|
|
367
|
+
# If it's a list but the first element is also a list, unwrap it
|
|
368
|
+
if isinstance(agent_parameters, list) and len(agent_parameters) > 0:
|
|
369
|
+
if isinstance(agent_parameters[0], list):
|
|
370
|
+
# Unwrap nested list: [[{...}, {...}]] -> [{...}, {...}]
|
|
371
|
+
agent_parameters = agent_parameters[0]
|
|
372
|
+
# Ensure all elements are dicts
|
|
373
|
+
if not all(isinstance(p, dict) for p in agent_parameters):
|
|
374
|
+
raise ValueError(
|
|
375
|
+
f"agent_parameters must be a list of dictionaries, got: {type(agent_parameters)}"
|
|
376
|
+
)
|
|
377
|
+
|
|
365
378
|
job = cls(
|
|
366
379
|
id=job_id,
|
|
367
380
|
name=job_name,
|
supervaizer/job_service.py
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
import json
|
|
8
8
|
from typing import TYPE_CHECKING, Any, Dict, Optional
|
|
9
9
|
|
|
10
|
-
from rich import inspect
|
|
11
10
|
|
|
12
11
|
from supervaizer.common import decrypt_value, log
|
|
13
12
|
from supervaizer.event import JobFinishedEvent
|
|
@@ -44,7 +43,7 @@ async def service_job_start(
|
|
|
44
43
|
Returns:
|
|
45
44
|
The created job
|
|
46
45
|
"""
|
|
47
|
-
agent_parameters
|
|
46
|
+
agent_parameters = None
|
|
48
47
|
# If agent has parameters_setup defined, validate parameters
|
|
49
48
|
if getattr(agent, "parameters_setup") and encrypted_agent_parameters:
|
|
50
49
|
agent_parameters_str = decrypt_value(
|
|
@@ -54,14 +53,16 @@ async def service_job_start(
|
|
|
54
53
|
json.loads(agent_parameters_str) if agent_parameters_str else None
|
|
55
54
|
)
|
|
56
55
|
|
|
57
|
-
inspect(agent)
|
|
58
|
-
log.debug(
|
|
56
|
+
# inspect(agent)
|
|
57
|
+
log.debug(
|
|
58
|
+
f"[service_job_start Decrypted parameters] : parameters = {agent_parameters}"
|
|
59
|
+
)
|
|
59
60
|
|
|
60
61
|
# Create and prepare the job
|
|
61
62
|
new_saas_job = Job.new(
|
|
62
63
|
job_context=sv_context,
|
|
63
64
|
agent_name=agent.name,
|
|
64
|
-
agent_parameters=
|
|
65
|
+
agent_parameters=agent_parameters,
|
|
65
66
|
name=sv_context.job_id,
|
|
66
67
|
)
|
|
67
68
|
|
supervaizer/parameter.py
CHANGED
|
@@ -63,7 +63,7 @@ class Parameter(ParameterAbstract):
|
|
|
63
63
|
"""
|
|
64
64
|
Override the to_dict method to handle the value field.
|
|
65
65
|
"""
|
|
66
|
-
data =
|
|
66
|
+
data = super().to_dict
|
|
67
67
|
if self.is_secret:
|
|
68
68
|
data["value"] = "********"
|
|
69
69
|
return data
|
|
@@ -187,6 +187,18 @@ class ParametersSetup(SvBaseModel):
|
|
|
187
187
|
errors = []
|
|
188
188
|
invalid_parameters = {}
|
|
189
189
|
|
|
190
|
+
# Ensure parameters is a dictionary
|
|
191
|
+
if not isinstance(parameters, dict):
|
|
192
|
+
error_msg = (
|
|
193
|
+
f"Parameters must be a dictionary, got {type(parameters).__name__}"
|
|
194
|
+
)
|
|
195
|
+
errors.append(error_msg)
|
|
196
|
+
return {
|
|
197
|
+
"valid": False,
|
|
198
|
+
"errors": errors,
|
|
199
|
+
"invalid_parameters": {"parameters": error_msg},
|
|
200
|
+
}
|
|
201
|
+
|
|
190
202
|
# First check for missing required parameters
|
|
191
203
|
for param_name, param_def in self.definitions.items():
|
|
192
204
|
if param_def.is_required and param_name not in parameters:
|
supervaizer/protocol/__init__.py
CHANGED
|
@@ -48,7 +48,7 @@ def create_routes(server: "Server") -> APIRouter:
|
|
|
48
48
|
@handle_route_errors()
|
|
49
49
|
async def get_health() -> Dict[str, Any]:
|
|
50
50
|
"""Get health information for the server and all agents."""
|
|
51
|
-
log.debug("[A2A] GET /.well-known/health [Health status]")
|
|
51
|
+
# log.debug("[A2A] GET /.well-known/health [Health status]")
|
|
52
52
|
return create_health_data(server.agents)
|
|
53
53
|
|
|
54
54
|
# Create explicit routes for each agent in the versioned format
|