agentex-sdk 0.4.0__py3-none-any.whl → 0.4.2__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.
- agentex/_version.py +1 -1
- agentex/lib/cli/commands/agents.py +41 -32
- agentex/lib/cli/commands/init.py +1 -0
- agentex/lib/cli/handlers/deploy_handlers.py +47 -89
- agentex/lib/cli/handlers/run_handlers.py +11 -7
- agentex/lib/cli/templates/default/environments.yaml.j2 +57 -0
- agentex/lib/cli/templates/default/manifest.yaml.j2 +4 -0
- agentex/lib/cli/templates/sync/environments.yaml.j2 +53 -0
- agentex/lib/cli/templates/temporal/environments.yaml.j2 +64 -0
- agentex/lib/cli/templates/temporal/manifest.yaml.j2 +4 -1
- agentex/lib/cli/utils/auth_utils.py +45 -5
- agentex/lib/core/services/adk/providers/openai.py +1 -1
- agentex/lib/sdk/config/agent_manifest.py +20 -2
- agentex/lib/sdk/config/environment_config.py +188 -0
- agentex/lib/sdk/config/validation.py +249 -0
- {agentex_sdk-0.4.0.dist-info → agentex_sdk-0.4.2.dist-info}/METADATA +2 -2
- {agentex_sdk-0.4.0.dist-info → agentex_sdk-0.4.2.dist-info}/RECORD +20 -20
- agentex/lib/cli/templates/default/deploy/example.yaml.j2 +0 -55
- agentex/lib/cli/templates/deploy/Screenshot 2025-03-19 at 10.36.57/342/200/257AM.png +0 -0
- agentex/lib/cli/templates/deploy/example.yaml.j2 +0 -55
- agentex/lib/cli/templates/sync/deploy/example.yaml.j2 +0 -55
- agentex/lib/cli/templates/temporal/deploy/example.yaml.j2 +0 -55
- {agentex_sdk-0.4.0.dist-info → agentex_sdk-0.4.2.dist-info}/WHEEL +0 -0
- {agentex_sdk-0.4.0.dist-info → agentex_sdk-0.4.2.dist-info}/entry_points.txt +0 -0
- {agentex_sdk-0.4.0.dist-info → agentex_sdk-0.4.2.dist-info}/licenses/LICENSE +0 -0
@@ -1,16 +1,56 @@
|
|
1
1
|
import base64
|
2
2
|
import json
|
3
|
+
from typing import Any, Dict
|
3
4
|
|
4
5
|
from agentex.lib.sdk.config.agent_manifest import AgentManifest
|
6
|
+
from agentex.lib.sdk.config.environment_config import AgentAuthConfig
|
5
7
|
|
8
|
+
# DEPRECATED: Old function for backward compatibility
|
9
|
+
# Will be removed in future version
|
10
|
+
def _encode_principal_context(manifest: AgentManifest) -> str | None:
|
11
|
+
"""
|
12
|
+
DEPRECATED: This function is deprecated as AgentManifest no longer contains auth.
|
13
|
+
Use _encode_principal_context_from_env_config instead.
|
14
|
+
|
15
|
+
This function is kept temporarily for backward compatibility during migration.
|
16
|
+
"""
|
17
|
+
# AgentManifest no longer has auth field - this will always return None
|
18
|
+
return None
|
6
19
|
|
7
|
-
|
8
|
-
def
|
9
|
-
|
20
|
+
|
21
|
+
def _encode_principal_context_from_env_config(auth_config: "AgentAuthConfig | None") -> str | None:
|
22
|
+
"""
|
23
|
+
Encode principal context from environment configuration.
|
24
|
+
|
25
|
+
Args:
|
26
|
+
auth_config: AgentAuthConfig containing principal configuration
|
27
|
+
|
28
|
+
Returns:
|
29
|
+
Base64-encoded JSON string of the principal, or None if no principal
|
30
|
+
"""
|
31
|
+
if auth_config is None:
|
32
|
+
return None
|
33
|
+
|
34
|
+
principal = auth_config.principal
|
35
|
+
if not principal:
|
10
36
|
return None
|
11
37
|
|
12
|
-
|
13
|
-
|
38
|
+
json_str = json.dumps(principal, separators=(',', ':'))
|
39
|
+
encoded_bytes = base64.b64encode(json_str.encode('utf-8'))
|
40
|
+
return encoded_bytes.decode('utf-8')
|
41
|
+
|
42
|
+
|
43
|
+
def _encode_principal_dict(principal: Dict[str, Any]) -> str | None:
|
44
|
+
"""
|
45
|
+
Encode principal dictionary directly.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
principal: Dictionary containing principal configuration
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
Base64-encoded JSON string of the principal, or None if principal is empty
|
52
|
+
"""
|
53
|
+
if not principal:
|
14
54
|
return None
|
15
55
|
|
16
56
|
json_str = json.dumps(principal, separators=(',', ':'))
|
@@ -656,7 +656,7 @@ class OpenAIService:
|
|
656
656
|
reasoning_content = ReasoningContent(
|
657
657
|
author="agent",
|
658
658
|
summary=[summary.text for summary in reasoning_item.summary],
|
659
|
-
content=[content.text for content in reasoning_item.content] if reasoning_item.content else None,
|
659
|
+
content=[content.text for content in reasoning_item.content] if hasattr(reasoning_item, "content") and reasoning_item.content else None,
|
660
660
|
)
|
661
661
|
|
662
662
|
# Create reasoning content using streaming context (immediate completion)
|
@@ -15,7 +15,8 @@ from pydantic import Field
|
|
15
15
|
|
16
16
|
from agentex.lib.sdk.config.agent_config import AgentConfig
|
17
17
|
from agentex.lib.sdk.config.build_config import BuildConfig
|
18
|
-
from agentex.lib.sdk.config.
|
18
|
+
from agentex.lib.sdk.config.environment_config import AgentEnvironmentsConfig
|
19
|
+
from agentex.lib.sdk.config.deployment_config import DeploymentConfig
|
19
20
|
from agentex.lib.sdk.config.local_development_config import LocalDevelopmentConfig
|
20
21
|
from agentex.lib.utils.logging import make_logger
|
21
22
|
from agentex.lib.utils.model_utils import BaseModel
|
@@ -36,7 +37,7 @@ class AgentManifest(BaseModel):
|
|
36
37
|
deployment: DeploymentConfig | None = Field(
|
37
38
|
default=None, description="Deployment configuration for the agent"
|
38
39
|
)
|
39
|
-
|
40
|
+
|
40
41
|
|
41
42
|
def context_manager(self, build_context_root: Path) -> BuildContextManager:
|
42
43
|
"""
|
@@ -45,6 +46,23 @@ class AgentManifest(BaseModel):
|
|
45
46
|
return BuildContextManager(
|
46
47
|
agent_manifest=self, build_context_root=build_context_root
|
47
48
|
)
|
49
|
+
|
50
|
+
def load_environments_config(self, manifest_dir: Path) -> "AgentEnvironmentsConfig | None":
|
51
|
+
"""Load environments.yaml from same directory as manifest.yaml.
|
52
|
+
|
53
|
+
Args:
|
54
|
+
manifest_dir: Directory containing manifest.yaml
|
55
|
+
|
56
|
+
Returns:
|
57
|
+
AgentEnvironmentsConfig if environments.yaml exists, None otherwise
|
58
|
+
|
59
|
+
Raises:
|
60
|
+
ValueError: If environments.yaml exists but is invalid
|
61
|
+
"""
|
62
|
+
# Import here to avoid circular imports
|
63
|
+
from agentex.lib.sdk.config.environment_config import load_environments_config_from_manifest_dir
|
64
|
+
|
65
|
+
return load_environments_config_from_manifest_dir(manifest_dir)
|
48
66
|
|
49
67
|
|
50
68
|
class BuildContextManager:
|
@@ -0,0 +1,188 @@
|
|
1
|
+
"""
|
2
|
+
Environment-specific configuration models for agent deployments.
|
3
|
+
|
4
|
+
This module provides Pydantic models for managing environment-specific
|
5
|
+
configurations that are separate from the main manifest.yaml file.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from __future__ import annotations
|
9
|
+
|
10
|
+
from pathlib import Path
|
11
|
+
from typing import Any, Dict, override
|
12
|
+
|
13
|
+
import yaml
|
14
|
+
from pydantic import BaseModel, Field, field_validator
|
15
|
+
|
16
|
+
from agentex.lib.utils.model_utils import BaseModel as UtilsBaseModel
|
17
|
+
|
18
|
+
|
19
|
+
class AgentAuthConfig(BaseModel):
|
20
|
+
"""Authentication configuration for an agent in a specific environment."""
|
21
|
+
|
22
|
+
principal: Dict[str, Any] = Field(
|
23
|
+
...,
|
24
|
+
description="Principal configuration for agent authorization and registration"
|
25
|
+
)
|
26
|
+
|
27
|
+
@field_validator('principal')
|
28
|
+
@classmethod
|
29
|
+
def validate_principal_required_fields(cls, v: Any) -> Dict[str, Any]:
|
30
|
+
"""Ensure principal has required fields for agent registration."""
|
31
|
+
if not isinstance(v, dict):
|
32
|
+
raise ValueError("Principal must be a dictionary")
|
33
|
+
return v
|
34
|
+
|
35
|
+
|
36
|
+
class AgentKubernetesConfig(BaseModel):
|
37
|
+
"""Kubernetes configuration for an agent in a specific environment."""
|
38
|
+
|
39
|
+
namespace: str = Field(
|
40
|
+
...,
|
41
|
+
description="Kubernetes namespace where the agent will be deployed"
|
42
|
+
)
|
43
|
+
|
44
|
+
@field_validator('namespace')
|
45
|
+
@classmethod
|
46
|
+
def validate_namespace_format(cls, v: str) -> str:
|
47
|
+
"""Ensure namespace follows Kubernetes naming conventions."""
|
48
|
+
if not v or not v.strip():
|
49
|
+
raise ValueError("Namespace cannot be empty")
|
50
|
+
|
51
|
+
# Basic Kubernetes namespace validation
|
52
|
+
namespace = v.strip().lower()
|
53
|
+
if not namespace.replace('-', '').replace('.', '').isalnum():
|
54
|
+
raise ValueError(
|
55
|
+
f"Namespace '{v}' must contain only lowercase letters, numbers, "
|
56
|
+
"hyphens, and periods"
|
57
|
+
)
|
58
|
+
|
59
|
+
if len(namespace) > 63:
|
60
|
+
raise ValueError(f"Namespace '{v}' cannot exceed 63 characters")
|
61
|
+
|
62
|
+
return namespace
|
63
|
+
|
64
|
+
|
65
|
+
class AgentEnvironmentConfig(BaseModel):
|
66
|
+
"""Complete configuration for an agent in a specific environment."""
|
67
|
+
|
68
|
+
kubernetes: AgentKubernetesConfig | None = Field(
|
69
|
+
default=None,
|
70
|
+
description="Kubernetes deployment configuration"
|
71
|
+
)
|
72
|
+
auth: AgentAuthConfig = Field(
|
73
|
+
...,
|
74
|
+
description="Authentication and authorization configuration"
|
75
|
+
)
|
76
|
+
helm_overrides: Dict[str, Any] = Field(
|
77
|
+
default_factory=dict,
|
78
|
+
description="Helm chart value overrides for environment-specific tuning"
|
79
|
+
)
|
80
|
+
|
81
|
+
|
82
|
+
class AgentEnvironmentsConfig(UtilsBaseModel):
|
83
|
+
"""All environment configurations for an agent."""
|
84
|
+
|
85
|
+
schema_version: str = Field(
|
86
|
+
default="v1",
|
87
|
+
description="Schema version for validation and compatibility"
|
88
|
+
)
|
89
|
+
environments: Dict[str, AgentEnvironmentConfig] = Field(
|
90
|
+
...,
|
91
|
+
description="Environment-specific configurations (dev, prod, etc.)"
|
92
|
+
)
|
93
|
+
|
94
|
+
@field_validator('schema_version')
|
95
|
+
@classmethod
|
96
|
+
def validate_schema_version(cls, v: str) -> str:
|
97
|
+
"""Ensure schema version is supported."""
|
98
|
+
supported_versions = ['v1']
|
99
|
+
if v not in supported_versions:
|
100
|
+
raise ValueError(
|
101
|
+
f"Schema version '{v}' not supported. "
|
102
|
+
f"Supported versions: {', '.join(supported_versions)}"
|
103
|
+
)
|
104
|
+
return v
|
105
|
+
|
106
|
+
@field_validator('environments')
|
107
|
+
@classmethod
|
108
|
+
def validate_environments_not_empty(cls, v: Dict[str, AgentEnvironmentConfig]) -> Dict[str, AgentEnvironmentConfig]:
|
109
|
+
"""Ensure at least one environment is defined."""
|
110
|
+
if not v:
|
111
|
+
raise ValueError("At least one environment must be defined")
|
112
|
+
return v
|
113
|
+
|
114
|
+
def get_config_for_env(self, env_name: str) -> AgentEnvironmentConfig:
|
115
|
+
"""Get configuration for a specific environment.
|
116
|
+
|
117
|
+
Args:
|
118
|
+
env_name: Name of the environment (e.g., 'dev', 'prod')
|
119
|
+
|
120
|
+
Returns:
|
121
|
+
AgentEnvironmentConfig for the specified environment
|
122
|
+
|
123
|
+
Raises:
|
124
|
+
ValueError: If environment is not found
|
125
|
+
"""
|
126
|
+
if env_name not in self.environments:
|
127
|
+
available_envs = ', '.join(self.environments.keys())
|
128
|
+
raise ValueError(
|
129
|
+
f"Environment '{env_name}' not found in environments.yaml. "
|
130
|
+
f"Available environments: {available_envs}"
|
131
|
+
)
|
132
|
+
return self.environments[env_name]
|
133
|
+
|
134
|
+
def list_environments(self) -> list[str]:
|
135
|
+
"""Get list of all configured environment names."""
|
136
|
+
return list(self.environments.keys())
|
137
|
+
|
138
|
+
@classmethod
|
139
|
+
@override
|
140
|
+
def from_yaml(cls, file_path: str) -> "AgentEnvironmentsConfig":
|
141
|
+
"""Load configuration from environments.yaml file.
|
142
|
+
|
143
|
+
Args:
|
144
|
+
file_path: Path to environments.yaml file
|
145
|
+
|
146
|
+
Returns:
|
147
|
+
Parsed and validated AgentEnvironmentsConfig
|
148
|
+
|
149
|
+
Raises:
|
150
|
+
FileNotFoundError: If file doesn't exist
|
151
|
+
ValueError: If file is invalid or doesn't validate
|
152
|
+
"""
|
153
|
+
path = Path(file_path)
|
154
|
+
if not path.exists():
|
155
|
+
raise FileNotFoundError(f"environments.yaml not found: {file_path}")
|
156
|
+
|
157
|
+
try:
|
158
|
+
with open(path, 'r') as f:
|
159
|
+
data = yaml.safe_load(f)
|
160
|
+
|
161
|
+
if not data:
|
162
|
+
raise ValueError("environments.yaml file is empty")
|
163
|
+
|
164
|
+
return cls.model_validate(data)
|
165
|
+
|
166
|
+
except yaml.YAMLError as e:
|
167
|
+
raise ValueError(f"Invalid YAML format in {file_path}: {e}") from e
|
168
|
+
except Exception as e:
|
169
|
+
raise ValueError(f"Failed to load environments.yaml from {file_path}: {e}") from e
|
170
|
+
|
171
|
+
|
172
|
+
def load_environments_config_from_manifest_dir(manifest_dir: Path) -> AgentEnvironmentsConfig | None:
|
173
|
+
"""Helper function to load environments.yaml from same directory as manifest.yaml.
|
174
|
+
|
175
|
+
Args:
|
176
|
+
manifest_dir: Directory containing manifest.yaml
|
177
|
+
|
178
|
+
Returns:
|
179
|
+
AgentEnvironmentsConfig if environments.yaml exists, None otherwise
|
180
|
+
|
181
|
+
Raises:
|
182
|
+
ValueError: If environments.yaml exists but is invalid
|
183
|
+
"""
|
184
|
+
environments_file = manifest_dir / "environments.yaml"
|
185
|
+
if not environments_file.exists():
|
186
|
+
return None
|
187
|
+
|
188
|
+
return AgentEnvironmentsConfig.from_yaml(str(environments_file))
|
@@ -0,0 +1,249 @@
|
|
1
|
+
"""
|
2
|
+
Validation framework for agent configuration files.
|
3
|
+
|
4
|
+
This module provides validation functions for agent configurations,
|
5
|
+
with clear error messages and best practices enforcement.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from pathlib import Path
|
9
|
+
from typing import Any, Dict, List, Optional
|
10
|
+
|
11
|
+
from agentex.lib.sdk.config.environment_config import AgentEnvironmentsConfig, AgentEnvironmentConfig
|
12
|
+
from agentex.lib.utils.logging import make_logger
|
13
|
+
|
14
|
+
logger = make_logger(__name__)
|
15
|
+
|
16
|
+
|
17
|
+
class ConfigValidationError(Exception):
|
18
|
+
"""Exception raised when configuration validation fails."""
|
19
|
+
|
20
|
+
def __init__(self, message: str, file_path: Optional[str] = None):
|
21
|
+
self.file_path = file_path
|
22
|
+
super().__init__(message)
|
23
|
+
|
24
|
+
|
25
|
+
class EnvironmentsValidationError(ConfigValidationError):
|
26
|
+
"""Exception raised when environments.yaml validation fails."""
|
27
|
+
pass
|
28
|
+
|
29
|
+
|
30
|
+
def validate_environments_config(
|
31
|
+
environments_config: AgentEnvironmentsConfig,
|
32
|
+
required_environments: Optional[List[str]] = None
|
33
|
+
) -> None:
|
34
|
+
"""
|
35
|
+
Validate environments configuration with comprehensive checks.
|
36
|
+
|
37
|
+
Args:
|
38
|
+
environments_config: The loaded environments configuration
|
39
|
+
required_environments: List of environment names that must be present
|
40
|
+
|
41
|
+
Raises:
|
42
|
+
EnvironmentsValidationError: If validation fails
|
43
|
+
"""
|
44
|
+
# Check for required environments
|
45
|
+
if required_environments:
|
46
|
+
missing_envs: List[str] = []
|
47
|
+
for env_name in required_environments:
|
48
|
+
if env_name not in environments_config.environments:
|
49
|
+
missing_envs.append(env_name)
|
50
|
+
|
51
|
+
if missing_envs:
|
52
|
+
available_envs = list(environments_config.environments.keys())
|
53
|
+
raise EnvironmentsValidationError(
|
54
|
+
f"Missing required environments: {', '.join(missing_envs)}. "
|
55
|
+
f"Available environments: {', '.join(available_envs)}"
|
56
|
+
)
|
57
|
+
|
58
|
+
# Validate each environment configuration
|
59
|
+
for env_name, env_config in environments_config.environments.items():
|
60
|
+
try:
|
61
|
+
_validate_single_environment_config(env_name, env_config)
|
62
|
+
except Exception as e:
|
63
|
+
raise EnvironmentsValidationError(
|
64
|
+
f"Environment '{env_name}' configuration error: {str(e)}"
|
65
|
+
) from e
|
66
|
+
|
67
|
+
|
68
|
+
def _validate_single_environment_config(env_name: str, env_config: AgentEnvironmentConfig) -> None:
|
69
|
+
"""
|
70
|
+
Validate a single environment configuration.
|
71
|
+
|
72
|
+
Args:
|
73
|
+
env_name: Name of the environment
|
74
|
+
env_config: AgentEnvironmentConfig instance
|
75
|
+
|
76
|
+
Raises:
|
77
|
+
ValueError: If validation fails
|
78
|
+
"""
|
79
|
+
# Validate namespace naming conventions if kubernetes config exists
|
80
|
+
if env_config.kubernetes and env_config.kubernetes.namespace:
|
81
|
+
namespace = env_config.kubernetes.namespace
|
82
|
+
|
83
|
+
# Check for common namespace naming issues
|
84
|
+
if namespace != namespace.lower():
|
85
|
+
logger.warning(
|
86
|
+
f"Namespace '{namespace}' contains uppercase letters. "
|
87
|
+
"Kubernetes namespaces should be lowercase."
|
88
|
+
)
|
89
|
+
|
90
|
+
if namespace.startswith('-') or namespace.endswith('-'):
|
91
|
+
raise ValueError(
|
92
|
+
f"Namespace '{namespace}' cannot start or end with hyphens"
|
93
|
+
)
|
94
|
+
|
95
|
+
# Validate auth principal
|
96
|
+
principal = env_config.auth.principal
|
97
|
+
if not principal.get('user_id'):
|
98
|
+
raise ValueError("Auth principal must contain non-empty 'user_id'")
|
99
|
+
|
100
|
+
# Check for environment-specific user_id patterns
|
101
|
+
user_id = principal['user_id']
|
102
|
+
if isinstance(user_id, str):
|
103
|
+
if not any(env_name.lower() in user_id.lower() for env_name in ['dev', 'prod', 'staging', env_name]):
|
104
|
+
logger.warning(
|
105
|
+
f"User ID '{user_id}' doesn't contain environment indicator. "
|
106
|
+
f"Consider including '{env_name}' in the user_id for clarity."
|
107
|
+
)
|
108
|
+
|
109
|
+
# Validate helm overrides if present
|
110
|
+
if env_config.helm_overrides:
|
111
|
+
_validate_helm_overrides(env_config.helm_overrides)
|
112
|
+
|
113
|
+
|
114
|
+
def _validate_helm_overrides(helm_overrides: Dict[str, Any]) -> None:
|
115
|
+
"""
|
116
|
+
Validate helm override configuration.
|
117
|
+
|
118
|
+
Args:
|
119
|
+
helm_overrides: Dictionary of helm overrides
|
120
|
+
|
121
|
+
Raises:
|
122
|
+
ValueError: If validation fails
|
123
|
+
"""
|
124
|
+
# Check for common helm override issues
|
125
|
+
if 'resources' in helm_overrides:
|
126
|
+
resources = helm_overrides['resources']
|
127
|
+
if isinstance(resources, dict):
|
128
|
+
# Validate resource format
|
129
|
+
if 'requests' in resources or 'limits' in resources:
|
130
|
+
for resource_type in ['requests', 'limits']:
|
131
|
+
if resource_type in resources:
|
132
|
+
resource_config: Any = resources[resource_type]
|
133
|
+
if isinstance(resource_config, dict):
|
134
|
+
# Check for valid resource specifications
|
135
|
+
for key, value in resource_config.items():
|
136
|
+
if key in ['cpu', 'memory'] and not isinstance(value, str):
|
137
|
+
logger.warning(
|
138
|
+
f"Resource {key} should be a string (e.g., '500m', '1Gi'), "
|
139
|
+
f"got {type(value).__name__}: {value}"
|
140
|
+
)
|
141
|
+
|
142
|
+
|
143
|
+
def validate_environments_yaml_file(file_path: str) -> AgentEnvironmentsConfig:
|
144
|
+
"""
|
145
|
+
Load and validate environments.yaml file.
|
146
|
+
|
147
|
+
Args:
|
148
|
+
file_path: Path to environments.yaml file
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
Validated AgentEnvironmentsConfig
|
152
|
+
|
153
|
+
Raises:
|
154
|
+
EnvironmentsValidationError: If file is invalid
|
155
|
+
"""
|
156
|
+
try:
|
157
|
+
environments_config = AgentEnvironmentsConfig.from_yaml(file_path)
|
158
|
+
validate_environments_config(environments_config)
|
159
|
+
return environments_config
|
160
|
+
except FileNotFoundError:
|
161
|
+
raise EnvironmentsValidationError(
|
162
|
+
f"environments.yaml not found: {file_path}\n\n"
|
163
|
+
"📋 Why required:\n"
|
164
|
+
" Environment-specific settings (auth, namespace, resources)\n"
|
165
|
+
" must be separated from global manifest for proper isolation.",
|
166
|
+
file_path=file_path
|
167
|
+
) from None
|
168
|
+
except Exception as e:
|
169
|
+
raise EnvironmentsValidationError(
|
170
|
+
f"Invalid environments.yaml file: {str(e)}",
|
171
|
+
file_path=file_path
|
172
|
+
) from e
|
173
|
+
|
174
|
+
|
175
|
+
def validate_manifest_and_environments(
|
176
|
+
manifest_path: str,
|
177
|
+
required_environment: Optional[str] = None
|
178
|
+
) -> tuple[str, AgentEnvironmentsConfig]:
|
179
|
+
"""
|
180
|
+
Validate both manifest.yaml and environments.yaml files together.
|
181
|
+
|
182
|
+
Args:
|
183
|
+
manifest_path: Path to manifest.yaml file
|
184
|
+
required_environment: Specific environment that must be present
|
185
|
+
|
186
|
+
Returns:
|
187
|
+
Tuple of (manifest_path, environments_config)
|
188
|
+
|
189
|
+
Raises:
|
190
|
+
ConfigValidationError: If validation fails
|
191
|
+
"""
|
192
|
+
manifest_file = Path(manifest_path)
|
193
|
+
if not manifest_file.exists():
|
194
|
+
raise ConfigValidationError(f"Manifest file not found: {manifest_path}")
|
195
|
+
|
196
|
+
# Look for environments.yaml in same directory
|
197
|
+
environments_file = manifest_file.parent / "environments.yaml"
|
198
|
+
environments_config = validate_environments_yaml_file(str(environments_file))
|
199
|
+
|
200
|
+
# Validate specific environment if requested
|
201
|
+
if required_environment:
|
202
|
+
validate_environments_config(
|
203
|
+
environments_config,
|
204
|
+
required_environments=[required_environment]
|
205
|
+
)
|
206
|
+
|
207
|
+
return manifest_path, environments_config
|
208
|
+
|
209
|
+
|
210
|
+
def generate_helpful_error_message(error: Exception, context: str = "") -> str:
|
211
|
+
"""
|
212
|
+
Generate helpful error message with troubleshooting tips.
|
213
|
+
|
214
|
+
Args:
|
215
|
+
error: The original exception
|
216
|
+
context: Additional context about where the error occurred
|
217
|
+
|
218
|
+
Returns:
|
219
|
+
Formatted error message with troubleshooting tips
|
220
|
+
"""
|
221
|
+
base_msg = str(error)
|
222
|
+
|
223
|
+
if context:
|
224
|
+
base_msg = f"{context}: {base_msg}"
|
225
|
+
|
226
|
+
# Add troubleshooting tips based on error type
|
227
|
+
if isinstance(error, FileNotFoundError):
|
228
|
+
if "environments.yaml" in base_msg:
|
229
|
+
base_msg += (
|
230
|
+
"\n\n🔧 Troubleshooting:\n"
|
231
|
+
"1. Check file location: should be next to manifest.yaml\n"
|
232
|
+
"2. Verify file permissions"
|
233
|
+
)
|
234
|
+
elif "user_id" in base_msg.lower():
|
235
|
+
base_msg += (
|
236
|
+
"\n\n💡 Auth Principal Tips:\n"
|
237
|
+
"- user_id should be unique per environment\n"
|
238
|
+
"- Include environment name (e.g., 'dev_my_agent')\n"
|
239
|
+
"- Use consistent naming convention across agents"
|
240
|
+
)
|
241
|
+
elif "namespace" in base_msg.lower():
|
242
|
+
base_msg += (
|
243
|
+
"\n\n🏷️ Namespace Tips:\n"
|
244
|
+
"- Use lowercase letters, numbers, and hyphens only\n"
|
245
|
+
"- Include team and environment (e.g., 'team-dev-agent')\n"
|
246
|
+
"- Keep under 63 characters"
|
247
|
+
)
|
248
|
+
|
249
|
+
return base_msg
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: agentex-sdk
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.2
|
4
4
|
Summary: The official Python library for the agentex API
|
5
5
|
Project-URL: Homepage, https://github.com/scaleapi/agentex-python
|
6
6
|
Project-URL: Repository, https://github.com/scaleapi/agentex-python
|
@@ -31,7 +31,7 @@ Requires-Dist: jsonschema<5,>=4.23.0
|
|
31
31
|
Requires-Dist: kubernetes<29.0.0,>=25.0.0
|
32
32
|
Requires-Dist: litellm<2,>=1.66.0
|
33
33
|
Requires-Dist: mcp[cli]>=1.4.1
|
34
|
-
Requires-Dist: openai-agents
|
34
|
+
Requires-Dist: openai-agents==0.2.7
|
35
35
|
Requires-Dist: openai>=1.99.9
|
36
36
|
Requires-Dist: pydantic<3,>=2.0.0
|
37
37
|
Requires-Dist: pytest-asyncio>=1.0.0
|