Skip to content

Commit 2fdcc1c

Browse files
Merge branch 'main' into fix/linting-issues
Resolved conflicts: - deploy_handlers.py: kept logger.info with plugin support from main - temporal_client.py: merged plugin support with client property validation - utils.py: integrated plugin validation from main - worker.py: combined plugin support with existing code - fastacp.py: merged plugin parameter handling - temporal_acp.py: integrated plugin support in create method - fastacp.py types: added plugin validation All files formatted with rye run format
2 parents d0f5003 + e4b39b5 commit 2fdcc1c

File tree

14 files changed

+172
-106
lines changed

14 files changed

+172
-106
lines changed

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "0.4.16"
2+
".": "0.4.18"
33
}

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 34
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sgp%2Fagentex-sdk-80c50e5d150c0baca970fa26bc298a878e02bf5869a16fd3a812255cca077333.yml
3-
openapi_spec_hash: 41bed3c1c935a8054600e6d177faa396
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/sgp%2Fagentex-sdk-76382de80057321dae48471054ca469af8b5b5b1b0b252e92fd70d7a9998dd6d.yml
3+
openapi_spec_hash: c1e34098e62dee3304ba1d49233e4e9d
44
config_hash: aeabb3a919ad2763f5d0f41961a2520a

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# Changelog
22

3+
## 0.4.18 (2025-09-29)
4+
5+
Full Changelog: [v0.4.17...v0.4.18](https://github.com/scaleapi/agentex-python/compare/v0.4.17...v0.4.18)
6+
7+
### Chores
8+
9+
* **internal:** version bump ([eded756](https://github.com/scaleapi/agentex-python/commit/eded756bde2f3b4cfcf02c7a9cf72e70a82dd9aa))
10+
11+
## 0.4.17 (2025-09-29)
12+
13+
Full Changelog: [v0.4.16...v0.4.17](https://github.com/scaleapi/agentex-python/compare/v0.4.16...v0.4.17)
14+
315
## 0.4.16 (2025-09-16)
416

517
Full Changelog: [v0.4.15...v0.4.16](https://github.com/scaleapi/agentex-python/compare/v0.4.15...v0.4.16)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "agentex-sdk"
3-
version = "0.4.16"
3+
version = "0.4.18"
44
description = "The official Python library for the agentex API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"

src/agentex/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

33
__title__ = "agentex"
4-
__version__ = "0.4.16" # x-release-please-version
4+
__version__ = "0.4.18" # x-release-please-version

src/agentex/lib/cli/handlers/deploy_handlers.py

Lines changed: 32 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -25,43 +25,35 @@
2525

2626

2727
class InputDeployOverrides(BaseModel):
28-
repository: str | None = Field(
29-
default=None, description="Override the repository for deployment"
30-
)
31-
image_tag: str | None = Field(
32-
default=None, description="Override the image tag for deployment"
33-
)
28+
repository: str | None = Field(default=None, description="Override the repository for deployment")
29+
image_tag: str | None = Field(default=None, description="Override the image tag for deployment")
3430

3531

3632
def check_helm_installed() -> bool:
3733
"""Check if helm is installed and available"""
3834
try:
39-
result = subprocess.run(
40-
["helm", "version", "--short"], capture_output=True, text=True, check=True
41-
)
35+
result = subprocess.run(["helm", "version", "--short"], capture_output=True, text=True, check=True)
4236
logger.info(f"Helm version: {result.stdout.strip()}")
4337
return True
4438
except (subprocess.CalledProcessError, FileNotFoundError):
4539
return False
4640

4741

48-
def add_helm_repo() -> None:
42+
def add_helm_repo(helm_repository_name: str, helm_repository_url: str) -> None:
4943
"""Add the agentex helm repository if not already added"""
5044
try:
5145
# Check if repo already exists
52-
result = subprocess.run(
53-
["helm", "repo", "list"], capture_output=True, text=True, check=True
54-
)
46+
result = subprocess.run(["helm", "repo", "list"], capture_output=True, text=True, check=True)
5547

56-
if "scale-egp" not in result.stdout:
48+
if helm_repository_name not in result.stdout:
5749
console.print("Adding agentex helm repository...")
5850
subprocess.run(
5951
[
6052
"helm",
6153
"repo",
6254
"add",
63-
"scale-egp",
64-
"https://scale-egp-helm-charts-us-west-2.s3.amazonaws.com/charts",
55+
helm_repository_name,
56+
helm_repository_url,
6557
],
6658
check=True,
6759
)
@@ -75,7 +67,6 @@ def add_helm_repo() -> None:
7567
raise HelmError(f"Failed to add helm repository: {e}") from e
7668

7769

78-
7970
def convert_env_vars_dict_to_list(env_vars: dict[str, str]) -> list[dict[str, str]]:
8071
"""Convert a dictionary of environment variables to a list of dictionaries"""
8172
return [{"name": key, "value": value} for key, value in env_vars.items()]
@@ -169,11 +160,11 @@ def merge_deployment_configs(
169160
# Priority: manifest -> environments.yaml -> secrets (highest)
170161
all_env_vars: dict[str, str] = {}
171162
secret_env_vars: list[dict[str, str]] = []
172-
163+
173164
# Start with agent_config env vars from manifest
174165
if agent_config.env:
175166
all_env_vars.update(agent_config.env)
176-
167+
177168
# Override with environment config env vars if they exist
178169
if agent_env_config and agent_env_config.helm_overrides and "env" in agent_env_config.helm_overrides:
179170
env_overrides = agent_env_config.helm_overrides["env"]
@@ -185,8 +176,6 @@ def merge_deployment_configs(
185176
env_override_dict[str(env_var["name"])] = str(env_var["value"])
186177
all_env_vars.update(env_override_dict)
187178

188-
189-
190179
# Handle credentials and check for conflicts
191180
if agent_config.credentials:
192181
for credential in agent_config.credentials:
@@ -199,7 +188,7 @@ def merge_deployment_configs(
199188
env_var_name = credential.env_var_name
200189
secret_name = credential.secret_name
201190
secret_key = credential.secret_key
202-
191+
203192
# Check if the environment variable name conflicts with existing env vars
204193
if env_var_name in all_env_vars:
205194
logger.warning(
@@ -208,7 +197,7 @@ def merge_deployment_configs(
208197
)
209198
# Remove from regular env vars since secret takes precedence
210199
del all_env_vars[env_var_name]
211-
200+
212201
secret_env_vars.append(
213202
{
214203
"name": env_var_name,
@@ -222,13 +211,14 @@ def merge_deployment_configs(
222211
# Add auth principal env var if environment config is set
223212
if agent_env_config.auth:
224213
from agentex.lib.cli.utils.auth_utils import _encode_principal_context_from_env_config
214+
225215
encoded_principal = _encode_principal_context_from_env_config(agent_env_config.auth)
226216
logger.info(f"Encoding auth principal from {agent_env_config.auth}")
227217
if encoded_principal:
228218
all_env_vars[EnvVarKeys.AUTH_PRINCIPAL_B64.value] = encoded_principal
229219
else:
230220
raise DeploymentError(f"Auth principal unable to be encoded for agent_env_config: {agent_env_config}")
231-
221+
232222
logger.info(f"Defined agent helm overrides: {agent_env_config.helm_overrides}")
233223
logger.info(f"Before-merge helm values: {helm_values}")
234224
if agent_env_config.helm_overrides:
@@ -239,7 +229,7 @@ def merge_deployment_configs(
239229
# Environment variable precedence: manifest -> environments.yaml -> secrets (highest)
240230
if all_env_vars:
241231
helm_values["env"] = convert_env_vars_dict_to_list(all_env_vars)
242-
232+
243233
if secret_env_vars:
244234
helm_values["secretEnvVars"] = secret_env_vars
245235

@@ -252,30 +242,25 @@ def merge_deployment_configs(
252242

253243
# Handle image pull secrets
254244
if manifest.deployment and manifest.deployment.imagePullSecrets:
255-
pull_secrets = [
256-
pull_secret.model_dump()
257-
for pull_secret in manifest.deployment.imagePullSecrets
258-
]
245+
pull_secrets = [pull_secret.model_dump() for pull_secret in manifest.deployment.imagePullSecrets]
259246
helm_values["global"]["imagePullSecrets"] = pull_secrets
260247
helm_values["imagePullSecrets"] = pull_secrets
261248

262249
# Add dynamic ACP command based on manifest configuration if command is not set in helm overrides
263-
helm_overrides_command = agent_env_config and agent_env_config.helm_overrides and "command" in agent_env_config.helm_overrides
250+
helm_overrides_command = (
251+
agent_env_config and agent_env_config.helm_overrides and "command" in agent_env_config.helm_overrides
252+
)
264253
if not helm_overrides_command:
265254
add_acp_command_to_helm_values(helm_values, manifest, manifest_path)
266-
255+
267256
logger.info("Deploying with the following helm values: %s", helm_values)
268257
return helm_values
269258

270259

271260
def _deep_merge(base_dict: dict[str, Any], override_dict: dict[str, Any]) -> None:
272261
"""Deep merge override_dict into base_dict"""
273262
for key, value in override_dict.items():
274-
if (
275-
key in base_dict
276-
and isinstance(base_dict[key], dict)
277-
and isinstance(value, dict)
278-
):
263+
if key in base_dict and isinstance(base_dict[key], dict) and isinstance(value, dict):
279264
_deep_merge(base_dict[key], value)
280265
else:
281266
base_dict[key] = value
@@ -317,8 +302,14 @@ def deploy_agent(
317302
else:
318303
console.print(f"[yellow]⚠[/yellow] No environments.yaml found, skipping environment-specific config")
319304

305+
if agent_env_config:
306+
helm_repository_name = agent_env_config.helm_repository_name
307+
helm_repository_url = agent_env_config.helm_repository_url
308+
else:
309+
helm_repository_name = "scale-egp"
310+
helm_repository_url = "https://scale-egp-helm-charts-us-west-2.s3.amazonaws.com/charts"
320311
# Add helm repository/update
321-
add_helm_repo()
312+
add_helm_repo(helm_repository_name, helm_repository_url)
322313

323314
# Merge configurations
324315
helm_values = merge_deployment_configs(manifest, agent_env_config, deploy_overrides, manifest_path)
@@ -348,7 +339,7 @@ def deploy_agent(
348339
"helm",
349340
"upgrade",
350341
release_name,
351-
"scale-egp/agentex-agent",
342+
f"{helm_repository_name}/agentex-agent",
352343
"--version",
353344
AGENTEX_AGENTS_HELM_CHART_VERSION,
354345
"-f",
@@ -370,7 +361,7 @@ def deploy_agent(
370361
"helm",
371362
"install",
372363
release_name,
373-
"scale-egp/agentex-agent",
364+
f"{helm_repository_name}/agentex-agent",
374365
"--version",
375366
AGENTEX_AGENTS_HELM_CHART_VERSION,
376367
"-f",
@@ -388,12 +379,8 @@ def deploy_agent(
388379

389380
# Show success message with helpful commands
390381
console.print("\n[green]🎉 Deployment completed successfully![/green]")
391-
console.print(
392-
f"[blue]Check deployment status:[/blue] helm status {release_name} -n {namespace}"
393-
)
394-
console.print(
395-
f"[blue]View logs:[/blue] kubectl logs -l app.kubernetes.io/name=agentex-agent -n {namespace}"
396-
)
382+
console.print(f"[blue]Check deployment status:[/blue] helm status {release_name} -n {namespace}")
383+
console.print(f"[blue]View logs:[/blue] kubectl logs -l app.kubernetes.io/name=agentex-agent -n {namespace}")
397384

398385
except subprocess.CalledProcessError as e:
399386
raise HelmError(

src/agentex/lib/core/clients/temporal/temporal_client.py

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
maximum_attempts=1,
2323
initial_interval=timedelta(seconds=1),
2424
backoff_coefficient=2.0,
25-
maximum_interval=timedelta(minutes=10)
25+
maximum_interval=timedelta(minutes=10),
2626
)
2727

2828

@@ -40,8 +40,7 @@
4040
),
4141
WorkflowExecutionStatus.FAILED: WorkflowState(
4242
status=TaskStatus.FAILED,
43-
reason="Task encountered terminal failure. "
44-
"Please contact support if retrying does not resolve the issue.",
43+
reason="Task encountered terminal failure. Please contact support if retrying does not resolve the issue.",
4544
is_terminal=True,
4645
),
4746
WorkflowExecutionStatus.RUNNING: WorkflowState(
@@ -75,8 +74,9 @@
7574

7675

7776
class TemporalClient:
78-
def __init__(self, temporal_client: Client | None = None):
77+
def __init__(self, temporal_client: Client | None = None, plugins: list[Any] = []):
7978
self._client: Client | None = temporal_client
79+
self._plugins = plugins
8080

8181
@property
8282
def client(self) -> Client:
@@ -86,7 +86,7 @@ def client(self) -> Client:
8686
return self._client
8787

8888
@classmethod
89-
async def create(cls, temporal_address: str):
89+
async def create(cls, temporal_address: str, plugins: list[Any] = []):
9090
if temporal_address in [
9191
"false",
9292
"False",
@@ -99,13 +99,11 @@ async def create(cls, temporal_address: str):
9999
]:
100100
_client = None
101101
else:
102-
_client = await get_temporal_client(temporal_address)
103-
return cls(_client)
102+
_client = await get_temporal_client(temporal_address, plugins=plugins)
103+
return cls(_client, plugins)
104104

105105
async def setup(self, temporal_address: str):
106-
self._client = await self._get_temporal_client(
107-
temporal_address=temporal_address
108-
)
106+
self._client = await self._get_temporal_client(temporal_address=temporal_address)
109107

110108
async def _get_temporal_client(self, temporal_address: str) -> Client | None:
111109
if temporal_address in [
@@ -120,7 +118,7 @@ async def _get_temporal_client(self, temporal_address: str) -> Client | None:
120118
]:
121119
return None
122120
else:
123-
return await get_temporal_client(temporal_address)
121+
return await get_temporal_client(temporal_address, plugins=self._plugins)
124122

125123
async def start_workflow(
126124
self,
@@ -131,9 +129,7 @@ async def start_workflow(
131129
execution_timeout: timedelta = timedelta(seconds=86400),
132130
**kwargs: Any,
133131
) -> str:
134-
temporal_retry_policy = TemporalRetryPolicy(
135-
**retry_policy.model_dump(exclude_unset=True)
136-
)
132+
temporal_retry_policy = TemporalRetryPolicy(**retry_policy.model_dump(exclude_unset=True))
137133
workflow_handle = await self.client.start_workflow(
138134
*args,
139135
retry_policy=temporal_retry_policy,

src/agentex/lib/core/clients/temporal/utils.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
from temporalio.client import Client
1+
from typing import Any
2+
3+
from temporalio.client import Client, Plugin as ClientPlugin
24
from temporalio.runtime import Runtime, TelemetryConfig, OpenTelemetryConfig
35
from temporalio.contrib.pydantic import pydantic_data_converter
46

@@ -38,12 +40,46 @@
3840
# )
3941

4042

41-
async def get_temporal_client(temporal_address: str, metrics_url: str | None = None) -> Client:
43+
def validate_client_plugins(plugins: list[Any]) -> None:
44+
"""
45+
Validate that all items in the plugins list are valid Temporal client plugins.
46+
47+
Args:
48+
plugins: List of plugins to validate
49+
50+
Raises:
51+
TypeError: If any plugin is not a valid ClientPlugin instance
52+
"""
53+
for i, plugin in enumerate(plugins):
54+
if not isinstance(plugin, ClientPlugin):
55+
raise TypeError(
56+
f"Plugin at index {i} must be an instance of temporalio.client.Plugin, "
57+
f"got {type(plugin).__name__}. Note: WorkerPlugin is not valid for workflow clients."
58+
)
59+
60+
61+
async def get_temporal_client(temporal_address: str, metrics_url: str | None = None, plugins: list[Any] = []) -> Client:
62+
"""
63+
Create a Temporal client with plugin integration.
64+
65+
Args:
66+
temporal_address: Temporal server address
67+
metrics_url: Optional metrics endpoint URL
68+
plugins: List of Temporal plugins to include
69+
70+
Returns:
71+
Configured Temporal client
72+
"""
73+
# Validate plugins if any are provided
74+
if plugins:
75+
validate_client_plugins(plugins)
76+
4277
if not metrics_url:
4378
client = await Client.connect(
4479
target_host=temporal_address,
4580
# data_converter=custom_data_converter,
4681
data_converter=pydantic_data_converter,
82+
plugins=plugins,
4783
)
4884
else:
4985
runtime = Runtime(telemetry=TelemetryConfig(metrics=OpenTelemetryConfig(url=metrics_url)))
@@ -52,5 +88,6 @@ async def get_temporal_client(temporal_address: str, metrics_url: str | None = N
5288
# data_converter=custom_data_converter,
5389
data_converter=pydantic_data_converter,
5490
runtime=runtime,
91+
plugins=plugins,
5592
)
5693
return client

0 commit comments

Comments
 (0)