agentex-sdk 0.1.0a6__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/__init__.py +103 -0
- agentex/_base_client.py +1992 -0
- agentex/_client.py +506 -0
- agentex/_compat.py +219 -0
- agentex/_constants.py +14 -0
- agentex/_exceptions.py +108 -0
- agentex/_files.py +123 -0
- agentex/_models.py +829 -0
- agentex/_qs.py +150 -0
- agentex/_resource.py +43 -0
- agentex/_response.py +830 -0
- agentex/_streaming.py +333 -0
- agentex/_types.py +219 -0
- agentex/_utils/__init__.py +57 -0
- agentex/_utils/_logs.py +25 -0
- agentex/_utils/_proxy.py +65 -0
- agentex/_utils/_reflection.py +42 -0
- agentex/_utils/_resources_proxy.py +24 -0
- agentex/_utils/_streams.py +12 -0
- agentex/_utils/_sync.py +86 -0
- agentex/_utils/_transform.py +447 -0
- agentex/_utils/_typing.py +151 -0
- agentex/_utils/_utils.py +422 -0
- agentex/_version.py +4 -0
- agentex/lib/.keep +4 -0
- agentex/lib/__init__.py +0 -0
- agentex/lib/adk/__init__.py +41 -0
- agentex/lib/adk/_modules/__init__.py +0 -0
- agentex/lib/adk/_modules/acp.py +247 -0
- agentex/lib/adk/_modules/agent_task_tracker.py +176 -0
- agentex/lib/adk/_modules/agents.py +77 -0
- agentex/lib/adk/_modules/events.py +141 -0
- agentex/lib/adk/_modules/messages.py +285 -0
- agentex/lib/adk/_modules/state.py +291 -0
- agentex/lib/adk/_modules/streaming.py +75 -0
- agentex/lib/adk/_modules/tasks.py +124 -0
- agentex/lib/adk/_modules/tracing.py +194 -0
- agentex/lib/adk/providers/__init__.py +9 -0
- agentex/lib/adk/providers/_modules/__init__.py +0 -0
- agentex/lib/adk/providers/_modules/litellm.py +232 -0
- agentex/lib/adk/providers/_modules/openai.py +416 -0
- agentex/lib/adk/providers/_modules/sgp.py +85 -0
- agentex/lib/adk/utils/__init__.py +5 -0
- agentex/lib/adk/utils/_modules/__init__.py +0 -0
- agentex/lib/adk/utils/_modules/templating.py +94 -0
- agentex/lib/cli/__init__.py +0 -0
- agentex/lib/cli/commands/__init__.py +0 -0
- agentex/lib/cli/commands/agents.py +328 -0
- agentex/lib/cli/commands/init.py +227 -0
- agentex/lib/cli/commands/main.py +33 -0
- agentex/lib/cli/commands/secrets.py +169 -0
- agentex/lib/cli/commands/tasks.py +118 -0
- agentex/lib/cli/commands/uv.py +133 -0
- agentex/lib/cli/handlers/__init__.py +0 -0
- agentex/lib/cli/handlers/agent_handlers.py +160 -0
- agentex/lib/cli/handlers/cleanup_handlers.py +186 -0
- agentex/lib/cli/handlers/deploy_handlers.py +351 -0
- agentex/lib/cli/handlers/run_handlers.py +452 -0
- agentex/lib/cli/handlers/secret_handlers.py +670 -0
- agentex/lib/cli/templates/default/.dockerignore.j2 +43 -0
- agentex/lib/cli/templates/default/Dockerfile-uv.j2 +42 -0
- agentex/lib/cli/templates/default/Dockerfile.j2 +42 -0
- agentex/lib/cli/templates/default/README.md.j2 +193 -0
- agentex/lib/cli/templates/default/deploy/example.yaml.j2 +55 -0
- agentex/lib/cli/templates/default/manifest.yaml.j2 +116 -0
- agentex/lib/cli/templates/default/project/acp.py.j2 +29 -0
- agentex/lib/cli/templates/default/pyproject.toml.j2 +33 -0
- agentex/lib/cli/templates/default/requirements.txt.j2 +5 -0
- 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 +55 -0
- agentex/lib/cli/templates/sync/.dockerignore.j2 +43 -0
- agentex/lib/cli/templates/sync/Dockerfile-uv.j2 +42 -0
- agentex/lib/cli/templates/sync/Dockerfile.j2 +42 -0
- agentex/lib/cli/templates/sync/README.md.j2 +293 -0
- agentex/lib/cli/templates/sync/deploy/example.yaml.j2 +55 -0
- agentex/lib/cli/templates/sync/manifest.yaml.j2 +116 -0
- agentex/lib/cli/templates/sync/project/acp.py.j2 +26 -0
- agentex/lib/cli/templates/sync/pyproject.toml.j2 +33 -0
- agentex/lib/cli/templates/sync/requirements.txt.j2 +5 -0
- agentex/lib/cli/templates/temporal/.dockerignore.j2 +43 -0
- agentex/lib/cli/templates/temporal/Dockerfile-uv.j2 +48 -0
- agentex/lib/cli/templates/temporal/Dockerfile.j2 +48 -0
- agentex/lib/cli/templates/temporal/README.md.j2 +316 -0
- agentex/lib/cli/templates/temporal/deploy/example.yaml.j2 +55 -0
- agentex/lib/cli/templates/temporal/manifest.yaml.j2 +137 -0
- agentex/lib/cli/templates/temporal/project/acp.py.j2 +30 -0
- agentex/lib/cli/templates/temporal/project/run_worker.py.j2 +33 -0
- agentex/lib/cli/templates/temporal/project/workflow.py.j2 +66 -0
- agentex/lib/cli/templates/temporal/pyproject.toml.j2 +34 -0
- agentex/lib/cli/templates/temporal/requirements.txt.j2 +5 -0
- agentex/lib/cli/utils/cli_utils.py +14 -0
- agentex/lib/cli/utils/credential_utils.py +103 -0
- agentex/lib/cli/utils/exceptions.py +6 -0
- agentex/lib/cli/utils/kubectl_utils.py +135 -0
- agentex/lib/cli/utils/kubernetes_secrets_utils.py +185 -0
- agentex/lib/core/__init__.py +0 -0
- agentex/lib/core/adapters/__init__.py +0 -0
- agentex/lib/core/adapters/llm/__init__.py +1 -0
- agentex/lib/core/adapters/llm/adapter_litellm.py +46 -0
- agentex/lib/core/adapters/llm/adapter_sgp.py +55 -0
- agentex/lib/core/adapters/llm/port.py +24 -0
- agentex/lib/core/adapters/streams/adapter_redis.py +128 -0
- agentex/lib/core/adapters/streams/port.py +50 -0
- agentex/lib/core/clients/__init__.py +1 -0
- agentex/lib/core/clients/temporal/__init__.py +0 -0
- agentex/lib/core/clients/temporal/temporal_client.py +181 -0
- agentex/lib/core/clients/temporal/types.py +47 -0
- agentex/lib/core/clients/temporal/utils.py +56 -0
- agentex/lib/core/services/__init__.py +0 -0
- agentex/lib/core/services/adk/__init__.py +0 -0
- agentex/lib/core/services/adk/acp/__init__.py +0 -0
- agentex/lib/core/services/adk/acp/acp.py +210 -0
- agentex/lib/core/services/adk/agent_task_tracker.py +85 -0
- agentex/lib/core/services/adk/agents.py +43 -0
- agentex/lib/core/services/adk/events.py +61 -0
- agentex/lib/core/services/adk/messages.py +164 -0
- agentex/lib/core/services/adk/providers/__init__.py +0 -0
- agentex/lib/core/services/adk/providers/litellm.py +256 -0
- agentex/lib/core/services/adk/providers/openai.py +723 -0
- agentex/lib/core/services/adk/providers/sgp.py +99 -0
- agentex/lib/core/services/adk/state.py +120 -0
- agentex/lib/core/services/adk/streaming.py +262 -0
- agentex/lib/core/services/adk/tasks.py +69 -0
- agentex/lib/core/services/adk/tracing.py +36 -0
- agentex/lib/core/services/adk/utils/__init__.py +0 -0
- agentex/lib/core/services/adk/utils/templating.py +58 -0
- agentex/lib/core/temporal/__init__.py +0 -0
- agentex/lib/core/temporal/activities/__init__.py +207 -0
- agentex/lib/core/temporal/activities/activity_helpers.py +37 -0
- agentex/lib/core/temporal/activities/adk/__init__.py +0 -0
- agentex/lib/core/temporal/activities/adk/acp/__init__.py +0 -0
- agentex/lib/core/temporal/activities/adk/acp/acp_activities.py +86 -0
- agentex/lib/core/temporal/activities/adk/agent_task_tracker_activities.py +76 -0
- agentex/lib/core/temporal/activities/adk/agents_activities.py +35 -0
- agentex/lib/core/temporal/activities/adk/events_activities.py +50 -0
- agentex/lib/core/temporal/activities/adk/messages_activities.py +94 -0
- agentex/lib/core/temporal/activities/adk/providers/__init__.py +0 -0
- agentex/lib/core/temporal/activities/adk/providers/litellm_activities.py +71 -0
- agentex/lib/core/temporal/activities/adk/providers/openai_activities.py +210 -0
- agentex/lib/core/temporal/activities/adk/providers/sgp_activities.py +42 -0
- agentex/lib/core/temporal/activities/adk/state_activities.py +85 -0
- agentex/lib/core/temporal/activities/adk/streaming_activities.py +33 -0
- agentex/lib/core/temporal/activities/adk/tasks_activities.py +48 -0
- agentex/lib/core/temporal/activities/adk/tracing_activities.py +55 -0
- agentex/lib/core/temporal/activities/adk/utils/__init__.py +0 -0
- agentex/lib/core/temporal/activities/adk/utils/templating_activities.py +41 -0
- agentex/lib/core/temporal/services/__init__.py +0 -0
- agentex/lib/core/temporal/services/temporal_task_service.py +69 -0
- agentex/lib/core/temporal/types/__init__.py +0 -0
- agentex/lib/core/temporal/types/workflow.py +5 -0
- agentex/lib/core/temporal/workers/__init__.py +0 -0
- agentex/lib/core/temporal/workers/worker.py +162 -0
- agentex/lib/core/temporal/workflows/workflow.py +26 -0
- agentex/lib/core/tracing/__init__.py +5 -0
- agentex/lib/core/tracing/processors/agentex_tracing_processor.py +117 -0
- agentex/lib/core/tracing/processors/sgp_tracing_processor.py +119 -0
- agentex/lib/core/tracing/processors/tracing_processor_interface.py +40 -0
- agentex/lib/core/tracing/trace.py +311 -0
- agentex/lib/core/tracing/tracer.py +70 -0
- agentex/lib/core/tracing/tracing_processor_manager.py +62 -0
- agentex/lib/environment_variables.py +87 -0
- agentex/lib/py.typed +0 -0
- agentex/lib/sdk/__init__.py +0 -0
- agentex/lib/sdk/config/__init__.py +0 -0
- agentex/lib/sdk/config/agent_config.py +61 -0
- agentex/lib/sdk/config/agent_manifest.py +219 -0
- agentex/lib/sdk/config/build_config.py +35 -0
- agentex/lib/sdk/config/deployment_config.py +117 -0
- agentex/lib/sdk/config/local_development_config.py +56 -0
- agentex/lib/sdk/config/project_config.py +103 -0
- agentex/lib/sdk/fastacp/__init__.py +3 -0
- agentex/lib/sdk/fastacp/base/base_acp_server.py +406 -0
- agentex/lib/sdk/fastacp/fastacp.py +74 -0
- agentex/lib/sdk/fastacp/impl/agentic_base_acp.py +72 -0
- agentex/lib/sdk/fastacp/impl/sync_acp.py +109 -0
- agentex/lib/sdk/fastacp/impl/temporal_acp.py +97 -0
- agentex/lib/sdk/fastacp/tests/README.md +297 -0
- agentex/lib/sdk/fastacp/tests/conftest.py +307 -0
- agentex/lib/sdk/fastacp/tests/pytest.ini +10 -0
- agentex/lib/sdk/fastacp/tests/run_tests.py +227 -0
- agentex/lib/sdk/fastacp/tests/test_base_acp_server.py +450 -0
- agentex/lib/sdk/fastacp/tests/test_fastacp_factory.py +344 -0
- agentex/lib/sdk/fastacp/tests/test_integration.py +477 -0
- agentex/lib/sdk/state_machine/__init__.py +6 -0
- agentex/lib/sdk/state_machine/noop_workflow.py +21 -0
- agentex/lib/sdk/state_machine/state.py +10 -0
- agentex/lib/sdk/state_machine/state_machine.py +189 -0
- agentex/lib/sdk/state_machine/state_workflow.py +16 -0
- agentex/lib/sdk/utils/__init__.py +0 -0
- agentex/lib/sdk/utils/messages.py +223 -0
- agentex/lib/types/__init__.py +0 -0
- agentex/lib/types/acp.py +94 -0
- agentex/lib/types/agent_configs.py +79 -0
- agentex/lib/types/agent_results.py +29 -0
- agentex/lib/types/credentials.py +34 -0
- agentex/lib/types/fastacp.py +61 -0
- agentex/lib/types/files.py +13 -0
- agentex/lib/types/json_rpc.py +49 -0
- agentex/lib/types/llm_messages.py +354 -0
- agentex/lib/types/task_message_updates.py +171 -0
- agentex/lib/types/tracing.py +34 -0
- agentex/lib/utils/__init__.py +0 -0
- agentex/lib/utils/completions.py +131 -0
- agentex/lib/utils/console.py +14 -0
- agentex/lib/utils/io.py +29 -0
- agentex/lib/utils/iterables.py +14 -0
- agentex/lib/utils/json_schema.py +23 -0
- agentex/lib/utils/logging.py +31 -0
- agentex/lib/utils/mcp.py +17 -0
- agentex/lib/utils/model_utils.py +46 -0
- agentex/lib/utils/parsing.py +15 -0
- agentex/lib/utils/regex.py +6 -0
- agentex/lib/utils/temporal.py +13 -0
- agentex/py.typed +0 -0
- agentex/resources/__init__.py +103 -0
- agentex/resources/agents.py +707 -0
- agentex/resources/events.py +294 -0
- agentex/resources/messages/__init__.py +33 -0
- agentex/resources/messages/batch.py +271 -0
- agentex/resources/messages/messages.py +492 -0
- agentex/resources/spans.py +557 -0
- agentex/resources/states.py +544 -0
- agentex/resources/tasks.py +615 -0
- agentex/resources/tracker.py +384 -0
- agentex/types/__init__.py +56 -0
- agentex/types/acp_type.py +7 -0
- agentex/types/agent.py +29 -0
- agentex/types/agent_list_params.py +13 -0
- agentex/types/agent_list_response.py +10 -0
- agentex/types/agent_rpc_by_name_params.py +21 -0
- agentex/types/agent_rpc_params.py +51 -0
- agentex/types/agent_rpc_params1.py +21 -0
- agentex/types/agent_rpc_response.py +20 -0
- agentex/types/agent_rpc_result.py +90 -0
- agentex/types/agent_task_tracker.py +34 -0
- agentex/types/data_content.py +30 -0
- agentex/types/data_content_param.py +31 -0
- agentex/types/data_delta.py +14 -0
- agentex/types/event.py +29 -0
- agentex/types/event_list_params.py +22 -0
- agentex/types/event_list_response.py +10 -0
- agentex/types/message_author.py +7 -0
- agentex/types/message_create_params.py +18 -0
- agentex/types/message_list_params.py +14 -0
- agentex/types/message_list_response.py +10 -0
- agentex/types/message_style.py +7 -0
- agentex/types/message_update_params.py +18 -0
- agentex/types/messages/__init__.py +8 -0
- agentex/types/messages/batch_create_params.py +16 -0
- agentex/types/messages/batch_create_response.py +10 -0
- agentex/types/messages/batch_update_params.py +16 -0
- agentex/types/messages/batch_update_response.py +10 -0
- agentex/types/shared/__init__.py +3 -0
- agentex/types/shared/task_message_update.py +83 -0
- agentex/types/span.py +36 -0
- agentex/types/span_create_params.py +40 -0
- agentex/types/span_list_params.py +12 -0
- agentex/types/span_list_response.py +10 -0
- agentex/types/span_update_params.py +37 -0
- agentex/types/state.py +25 -0
- agentex/types/state_create_params.py +16 -0
- agentex/types/state_list_params.py +16 -0
- agentex/types/state_list_response.py +10 -0
- agentex/types/state_update_params.py +16 -0
- agentex/types/task.py +23 -0
- agentex/types/task_delete_by_name_response.py +8 -0
- agentex/types/task_delete_response.py +8 -0
- agentex/types/task_list_response.py +10 -0
- agentex/types/task_message.py +33 -0
- agentex/types/task_message_content.py +16 -0
- agentex/types/task_message_content_param.py +17 -0
- agentex/types/task_message_delta.py +16 -0
- agentex/types/text_content.py +53 -0
- agentex/types/text_content_param.py +54 -0
- agentex/types/text_delta.py +14 -0
- agentex/types/tool_request_content.py +36 -0
- agentex/types/tool_request_content_param.py +37 -0
- agentex/types/tool_request_delta.py +18 -0
- agentex/types/tool_response_content.py +36 -0
- agentex/types/tool_response_content_param.py +36 -0
- agentex/types/tool_response_delta.py +18 -0
- agentex/types/tracker_list_params.py +16 -0
- agentex/types/tracker_list_response.py +10 -0
- agentex/types/tracker_update_params.py +19 -0
- agentex_sdk-0.1.0a6.dist-info/METADATA +426 -0
- agentex_sdk-0.1.0a6.dist-info/RECORD +289 -0
- agentex_sdk-0.1.0a6.dist-info/WHEEL +4 -0
- agentex_sdk-0.1.0a6.dist-info/entry_points.txt +2 -0
- agentex_sdk-0.1.0a6.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,169 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
import questionary
|
4
|
+
import typer
|
5
|
+
from rich import print_json
|
6
|
+
from rich.console import Console
|
7
|
+
from rich.panel import Panel
|
8
|
+
|
9
|
+
from agentex.lib.cli.handlers.secret_handlers import (
|
10
|
+
delete_secret,
|
11
|
+
get_kubernetes_secrets_by_type,
|
12
|
+
get_secret,
|
13
|
+
sync_secrets,
|
14
|
+
)
|
15
|
+
from agentex.lib.cli.utils.cli_utils import handle_questionary_cancellation
|
16
|
+
from agentex.lib.cli.utils.kubectl_utils import (
|
17
|
+
check_and_switch_cluster_context,
|
18
|
+
validate_namespace,
|
19
|
+
)
|
20
|
+
from agentex.lib.sdk.config.agent_manifest import AgentManifest
|
21
|
+
from agentex.lib.utils.logging import make_logger
|
22
|
+
|
23
|
+
logger = make_logger(__name__)
|
24
|
+
console = Console()
|
25
|
+
|
26
|
+
secrets = typer.Typer()
|
27
|
+
|
28
|
+
|
29
|
+
@secrets.command()
|
30
|
+
def list(
|
31
|
+
namespace: str = typer.Option(
|
32
|
+
"agentex-agents", help="Kubernetes namespace to list secrets from"
|
33
|
+
),
|
34
|
+
cluster: str | None = typer.Option(
|
35
|
+
None, help="Cluster context to use (defaults to current context)"
|
36
|
+
),
|
37
|
+
):
|
38
|
+
"""List names of available secrets"""
|
39
|
+
logger.info(f"Listing secrets in namespace: {namespace}")
|
40
|
+
|
41
|
+
if cluster:
|
42
|
+
check_and_switch_cluster_context(cluster)
|
43
|
+
if not validate_namespace(namespace, cluster):
|
44
|
+
console.print(
|
45
|
+
f"[red]Error:[/red] Namespace '{namespace}' does not exist in cluster '{cluster}'"
|
46
|
+
)
|
47
|
+
raise typer.Exit(1)
|
48
|
+
|
49
|
+
secrets_list = get_kubernetes_secrets_by_type(namespace=namespace, context=cluster)
|
50
|
+
print_json(data=secrets_list)
|
51
|
+
|
52
|
+
|
53
|
+
@secrets.command()
|
54
|
+
def get(
|
55
|
+
name: str = typer.Argument(..., help="Name of the secret to get"),
|
56
|
+
namespace: str = typer.Option(
|
57
|
+
"agentex-agents", help="Kubernetes namespace for the secret"
|
58
|
+
),
|
59
|
+
cluster: str | None = typer.Option(
|
60
|
+
None, help="Cluster context to use (defaults to current context)"
|
61
|
+
),
|
62
|
+
):
|
63
|
+
"""Get details about a secret"""
|
64
|
+
logger.info(f"Getting secret: {name} from namespace: {namespace}")
|
65
|
+
|
66
|
+
if cluster:
|
67
|
+
check_and_switch_cluster_context(cluster)
|
68
|
+
if not validate_namespace(namespace, cluster):
|
69
|
+
console.print(
|
70
|
+
f"[red]Error:[/red] Namespace '{namespace}' does not exist in cluster '{cluster}'"
|
71
|
+
)
|
72
|
+
raise typer.Exit(1)
|
73
|
+
|
74
|
+
secret = get_secret(name=name, namespace=namespace, context=cluster)
|
75
|
+
print_json(data=secret)
|
76
|
+
|
77
|
+
|
78
|
+
@secrets.command()
|
79
|
+
def delete(
|
80
|
+
name: str = typer.Argument(..., help="Name of the secret to delete"),
|
81
|
+
namespace: str = typer.Option(
|
82
|
+
"agentex-agents", help="Kubernetes namespace for the secret"
|
83
|
+
),
|
84
|
+
cluster: str | None = typer.Option(
|
85
|
+
None, help="Cluster context to use (defaults to current context)"
|
86
|
+
),
|
87
|
+
):
|
88
|
+
"""Delete a secret"""
|
89
|
+
logger.info(f"Deleting secret: {name} from namespace: {namespace}")
|
90
|
+
|
91
|
+
if cluster:
|
92
|
+
check_and_switch_cluster_context(cluster)
|
93
|
+
if not validate_namespace(namespace, cluster):
|
94
|
+
console.print(
|
95
|
+
f"[red]Error:[/red] Namespace '{namespace}' does not exist in cluster '{cluster}'"
|
96
|
+
)
|
97
|
+
raise typer.Exit(1)
|
98
|
+
|
99
|
+
delete_secret(name=name, namespace=namespace, context=cluster)
|
100
|
+
|
101
|
+
|
102
|
+
@secrets.command()
|
103
|
+
def sync(
|
104
|
+
manifest: str = typer.Option(..., help="Path to the manifest file"),
|
105
|
+
# TODO: should cluster be here or be in manifest as well?
|
106
|
+
cluster: str = typer.Option(..., "--cluster", help="Cluster to sync secrets to"),
|
107
|
+
interactive: bool = typer.Option(
|
108
|
+
True, "--interactive/--no-interactive", help="Enable interactive prompts"
|
109
|
+
),
|
110
|
+
namespace: str | None = typer.Option(
|
111
|
+
None,
|
112
|
+
help="Kubernetes namespace to deploy to (required in non-interactive mode)",
|
113
|
+
),
|
114
|
+
values: str = typer.Option(None, "--values", help="Path to the values file"),
|
115
|
+
):
|
116
|
+
"""Sync secrets from the cluster to the local environment"""
|
117
|
+
console.print(
|
118
|
+
Panel.fit("🚀 [bold blue]Sync Secrets[/bold blue]", border_style="blue")
|
119
|
+
)
|
120
|
+
|
121
|
+
manifest_path = Path(manifest)
|
122
|
+
if not manifest_path.exists():
|
123
|
+
console.print(f"[red]Error:[/red] Manifest file not found: {manifest}")
|
124
|
+
raise typer.Exit(1)
|
125
|
+
|
126
|
+
# In non-interactive mode, require namespace
|
127
|
+
if not interactive and not namespace:
|
128
|
+
console.print(
|
129
|
+
"[red]Error:[/red] --namespace is required in non-interactive mode"
|
130
|
+
)
|
131
|
+
raise typer.Exit(1)
|
132
|
+
|
133
|
+
# Get namespace if not provided (only in interactive mode)
|
134
|
+
if not namespace:
|
135
|
+
namespace = questionary.text(
|
136
|
+
"Enter Kubernetes namespace:", default="default"
|
137
|
+
).ask()
|
138
|
+
namespace = handle_questionary_cancellation(namespace, "namespace input")
|
139
|
+
|
140
|
+
if not namespace:
|
141
|
+
console.print("Deployment cancelled")
|
142
|
+
raise typer.Exit(0)
|
143
|
+
|
144
|
+
if values:
|
145
|
+
values_path = Path(values)
|
146
|
+
if not values_path.exists():
|
147
|
+
console.print(f"[red]Error:[/red] Values file not found: {values_path}")
|
148
|
+
raise typer.Exit(1)
|
149
|
+
|
150
|
+
# Validate cluster and namespace
|
151
|
+
check_and_switch_cluster_context(cluster)
|
152
|
+
if not validate_namespace(namespace, cluster):
|
153
|
+
console.print(
|
154
|
+
f"[red]Error:[/red] Namespace '{namespace}' does not exist in cluster '{cluster}'"
|
155
|
+
)
|
156
|
+
raise typer.Exit(1)
|
157
|
+
|
158
|
+
agent_manifest = AgentManifest.from_yaml(file_path=manifest)
|
159
|
+
|
160
|
+
# Always call sync_secrets - it will handle the case of no credentials
|
161
|
+
sync_secrets(
|
162
|
+
manifest_obj=agent_manifest,
|
163
|
+
cluster=cluster,
|
164
|
+
namespace=namespace,
|
165
|
+
interactive=interactive,
|
166
|
+
values_path=str(values) if values else None,
|
167
|
+
)
|
168
|
+
|
169
|
+
console.print("[green]Successfully synced secrets[/green]")
|
@@ -0,0 +1,118 @@
|
|
1
|
+
import typer
|
2
|
+
from rich import print_json
|
3
|
+
from rich.console import Console
|
4
|
+
|
5
|
+
from agentex import Agentex
|
6
|
+
from agentex.lib.cli.handlers.cleanup_handlers import cleanup_agent_workflows
|
7
|
+
from agentex.lib.utils.logging import make_logger
|
8
|
+
|
9
|
+
logger = make_logger(__name__)
|
10
|
+
console = Console()
|
11
|
+
|
12
|
+
tasks = typer.Typer()
|
13
|
+
|
14
|
+
|
15
|
+
@tasks.command()
|
16
|
+
def get(
|
17
|
+
task_id: str = typer.Argument(..., help="ID of the task to get"),
|
18
|
+
):
|
19
|
+
"""
|
20
|
+
Get the task with the given ID.
|
21
|
+
"""
|
22
|
+
logger.info(f"Getting task: {task_id}")
|
23
|
+
client = Agentex()
|
24
|
+
task = client.tasks.retrieve(task_id=task_id)
|
25
|
+
print(f"Full Task {task_id}:")
|
26
|
+
print_json(data=task.to_dict())
|
27
|
+
|
28
|
+
|
29
|
+
@tasks.command()
|
30
|
+
def list():
|
31
|
+
"""
|
32
|
+
List all tasks.
|
33
|
+
"""
|
34
|
+
client = Agentex()
|
35
|
+
tasks = client.tasks.list()
|
36
|
+
print_json(data=[task.to_dict() for task in tasks])
|
37
|
+
|
38
|
+
|
39
|
+
@tasks.command()
|
40
|
+
def list_running(
|
41
|
+
agent_name: str = typer.Option(..., help="Name of the agent to list running tasks for"),
|
42
|
+
):
|
43
|
+
"""
|
44
|
+
List all currently running tasks for a specific agent.
|
45
|
+
"""
|
46
|
+
client = Agentex()
|
47
|
+
all_tasks = client.tasks.list()
|
48
|
+
running_tasks = [task for task in all_tasks if hasattr(task, 'status') and task.status == "RUNNING"]
|
49
|
+
|
50
|
+
if not running_tasks:
|
51
|
+
console.print(f"[yellow]No running tasks found for agent '{agent_name}'[/yellow]")
|
52
|
+
return
|
53
|
+
|
54
|
+
console.print(f"[green]Found {len(running_tasks)} running task(s) for agent '{agent_name}':[/green]")
|
55
|
+
|
56
|
+
# Convert to dict with proper datetime serialization
|
57
|
+
serializable_tasks = []
|
58
|
+
for task in running_tasks:
|
59
|
+
try:
|
60
|
+
# Use model_dump with mode='json' for proper datetime handling
|
61
|
+
if hasattr(task, 'model_dump'):
|
62
|
+
serializable_tasks.append(task.model_dump(mode='json'))
|
63
|
+
else:
|
64
|
+
# Fallback for non-Pydantic objects
|
65
|
+
serializable_tasks.append({
|
66
|
+
"id": getattr(task, 'id', 'unknown'),
|
67
|
+
"status": getattr(task, 'status', 'unknown')
|
68
|
+
})
|
69
|
+
except Exception as e:
|
70
|
+
logger.warning(f"Failed to serialize task: {e}")
|
71
|
+
# Minimal fallback
|
72
|
+
serializable_tasks.append({
|
73
|
+
"id": getattr(task, 'id', 'unknown'),
|
74
|
+
"status": getattr(task, 'status', 'unknown')
|
75
|
+
})
|
76
|
+
|
77
|
+
print_json(data=serializable_tasks)
|
78
|
+
|
79
|
+
|
80
|
+
@tasks.command()
|
81
|
+
def delete(
|
82
|
+
task_id: str = typer.Argument(..., help="ID of the task to delete"),
|
83
|
+
):
|
84
|
+
"""
|
85
|
+
Delete the task with the given ID.
|
86
|
+
"""
|
87
|
+
logger.info(f"Deleting task: {task_id}")
|
88
|
+
client = Agentex()
|
89
|
+
client.tasks.delete(task_id=task_id)
|
90
|
+
logger.info(f"Task deleted: {task_id}")
|
91
|
+
|
92
|
+
|
93
|
+
@tasks.command()
|
94
|
+
def cleanup(
|
95
|
+
agent_name: str = typer.Option(..., help="Name of the agent to cleanup tasks for"),
|
96
|
+
force: bool = typer.Option(False, help="Force cleanup using direct Temporal termination (bypasses development check)"),
|
97
|
+
):
|
98
|
+
"""
|
99
|
+
Clean up all running tasks/workflows for an agent.
|
100
|
+
|
101
|
+
By default, uses graceful cancellation via agent RPC.
|
102
|
+
With --force, directly terminates workflows via Temporal client.
|
103
|
+
"""
|
104
|
+
try:
|
105
|
+
console.print(f"[blue]Starting cleanup for agent '{agent_name}'...[/blue]")
|
106
|
+
|
107
|
+
cleanup_agent_workflows(
|
108
|
+
agent_name=agent_name,
|
109
|
+
force=force,
|
110
|
+
development_only=True
|
111
|
+
)
|
112
|
+
|
113
|
+
console.print(f"[green]✓ Cleanup completed for agent '{agent_name}'[/green]")
|
114
|
+
|
115
|
+
except Exception as e:
|
116
|
+
console.print(f"[red]Cleanup failed: {str(e)}[/red]")
|
117
|
+
logger.exception("Task cleanup failed")
|
118
|
+
raise typer.Exit(1) from e
|
@@ -0,0 +1,133 @@
|
|
1
|
+
import os
|
2
|
+
import subprocess
|
3
|
+
import sys
|
4
|
+
|
5
|
+
import typer
|
6
|
+
|
7
|
+
from agentex.lib.utils.logging import make_logger
|
8
|
+
|
9
|
+
logger = make_logger(__name__)
|
10
|
+
|
11
|
+
|
12
|
+
uv = typer.Typer(
|
13
|
+
help="Wrapper for uv command with AgentEx-specific enhancements",
|
14
|
+
context_settings={"help_option_names": ["-h", "--help"]},
|
15
|
+
)
|
16
|
+
|
17
|
+
sync_args = typer.Argument(None, help="Additional arguments to pass to uv sync")
|
18
|
+
|
19
|
+
|
20
|
+
@uv.command()
|
21
|
+
def sync(
|
22
|
+
ctx: typer.Context,
|
23
|
+
index: str | None = typer.Option(
|
24
|
+
None, "--index", "-i", help="UV index URL to use for sync"
|
25
|
+
),
|
26
|
+
group: str | None = typer.Option(
|
27
|
+
None,
|
28
|
+
"--group",
|
29
|
+
"-g",
|
30
|
+
help="Include dependencies from the specified dependency group",
|
31
|
+
),
|
32
|
+
args: list[str] = sync_args,
|
33
|
+
):
|
34
|
+
"""Sync dependencies with optional UV_INDEX support"""
|
35
|
+
args = args or []
|
36
|
+
|
37
|
+
# Check if help was requested
|
38
|
+
if "--help" in args or "-h" in args:
|
39
|
+
# Show our custom help instead of passing to uv
|
40
|
+
typer.echo(ctx.get_help())
|
41
|
+
return
|
42
|
+
|
43
|
+
if index:
|
44
|
+
os.environ["UV_INDEX_URL"] = index
|
45
|
+
logger.info(f"Using provided UV_INDEX_URL: {index}")
|
46
|
+
|
47
|
+
# Build the uv sync command
|
48
|
+
cmd = ["uv", "sync"]
|
49
|
+
|
50
|
+
# Add group if specified
|
51
|
+
if group:
|
52
|
+
cmd.extend(["--group", group])
|
53
|
+
logger.info(f"Using dependency group: {group}")
|
54
|
+
|
55
|
+
# Add any additional arguments
|
56
|
+
cmd.extend(args)
|
57
|
+
|
58
|
+
try:
|
59
|
+
result = subprocess.run(cmd, check=True)
|
60
|
+
sys.exit(result.returncode)
|
61
|
+
except subprocess.CalledProcessError as e:
|
62
|
+
logger.error(f"uv sync failed with exit code {e.returncode}")
|
63
|
+
sys.exit(e.returncode)
|
64
|
+
except FileNotFoundError:
|
65
|
+
logger.error("uv command not found. Please install uv first.")
|
66
|
+
sys.exit(1)
|
67
|
+
|
68
|
+
|
69
|
+
add_args = typer.Argument(None, help="Additional arguments to pass to uv add")
|
70
|
+
|
71
|
+
|
72
|
+
@uv.command()
|
73
|
+
def add(
|
74
|
+
ctx: typer.Context,
|
75
|
+
index: str | None = typer.Option(
|
76
|
+
None, "--index", "-i", help="UV index URL to use for add"
|
77
|
+
),
|
78
|
+
args: list[str] = add_args,
|
79
|
+
):
|
80
|
+
"""Add dependencies with optional UV_INDEX support"""
|
81
|
+
|
82
|
+
args = args or []
|
83
|
+
|
84
|
+
# Check if help was requested
|
85
|
+
if "--help" in args or "-h" in args:
|
86
|
+
# Show our custom help instead of passing to uv
|
87
|
+
typer.echo(ctx.get_help())
|
88
|
+
return
|
89
|
+
|
90
|
+
if index:
|
91
|
+
os.environ["UV_INDEX_URL"] = index
|
92
|
+
logger.info(f"Using provided UV_INDEX_URL: {index}")
|
93
|
+
|
94
|
+
# Build the uv add command
|
95
|
+
cmd = ["uv", "add"] + (args or [])
|
96
|
+
|
97
|
+
try:
|
98
|
+
result = subprocess.run(cmd, check=True)
|
99
|
+
sys.exit(result.returncode)
|
100
|
+
except subprocess.CalledProcessError as e:
|
101
|
+
logger.error(f"uv add failed with exit code {e.returncode}")
|
102
|
+
sys.exit(e.returncode)
|
103
|
+
except FileNotFoundError:
|
104
|
+
logger.error("uv command not found. Please install uv first.")
|
105
|
+
sys.exit(1)
|
106
|
+
|
107
|
+
|
108
|
+
run_args = typer.Argument(None, help="Arguments to pass to uv")
|
109
|
+
|
110
|
+
|
111
|
+
@uv.command()
|
112
|
+
def run(
|
113
|
+
ctx: typer.Context,
|
114
|
+
args: list[str] = run_args,
|
115
|
+
):
|
116
|
+
"""Run any uv command with arguments"""
|
117
|
+
if not args:
|
118
|
+
# If no arguments provided, show help
|
119
|
+
typer.echo(ctx.get_help())
|
120
|
+
return
|
121
|
+
|
122
|
+
# Build the uv command
|
123
|
+
cmd = ["uv"] + args
|
124
|
+
|
125
|
+
try:
|
126
|
+
result = subprocess.run(cmd, check=True)
|
127
|
+
sys.exit(result.returncode)
|
128
|
+
except subprocess.CalledProcessError as e:
|
129
|
+
logger.error(f"uv command failed with exit code {e.returncode}")
|
130
|
+
sys.exit(e.returncode)
|
131
|
+
except FileNotFoundError:
|
132
|
+
logger.error("uv command not found. Please install uv first.")
|
133
|
+
sys.exit(1)
|
File without changes
|
@@ -0,0 +1,160 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
from python_on_whales import DockerException, docker
|
6
|
+
from rich.console import Console
|
7
|
+
|
8
|
+
from agentex.lib.cli.handlers.run_handlers import RunError
|
9
|
+
from agentex.lib.cli.handlers.run_handlers import run_agent as _run_agent
|
10
|
+
from agentex.lib.sdk.config.agent_manifest import AgentManifest
|
11
|
+
from agentex.lib.utils.logging import make_logger
|
12
|
+
|
13
|
+
logger = make_logger(__name__)
|
14
|
+
console = Console()
|
15
|
+
|
16
|
+
|
17
|
+
class DockerBuildError(Exception):
|
18
|
+
"""An error occurred during docker build"""
|
19
|
+
|
20
|
+
|
21
|
+
def build_agent(
|
22
|
+
manifest_path: str,
|
23
|
+
registry_url: str,
|
24
|
+
repository_name: str,
|
25
|
+
platforms: list[str],
|
26
|
+
push: bool = False,
|
27
|
+
secret: str = None,
|
28
|
+
tag: str = None,
|
29
|
+
build_args: list[str] = None,
|
30
|
+
) -> str:
|
31
|
+
"""Build the agent locally and optionally push to registry
|
32
|
+
|
33
|
+
Args:
|
34
|
+
manifest_path: Path to the agent manifest file
|
35
|
+
registry_url: Registry URL for pushing the image
|
36
|
+
push: Whether to push the image to the registry
|
37
|
+
secret: Docker build secret in format 'id=secret-id,src=path-to-secret-file'
|
38
|
+
tag: Image tag to use (defaults to 'latest')
|
39
|
+
build_args: List of Docker build arguments in format 'KEY=VALUE'
|
40
|
+
|
41
|
+
Returns:
|
42
|
+
The image URL
|
43
|
+
"""
|
44
|
+
agent_manifest = AgentManifest.from_yaml(file_path=manifest_path)
|
45
|
+
build_context_root = (
|
46
|
+
Path(manifest_path).parent / agent_manifest.build.context.root
|
47
|
+
).resolve()
|
48
|
+
|
49
|
+
repository_name = repository_name or agent_manifest.agent.name
|
50
|
+
|
51
|
+
# Prepare image name
|
52
|
+
if registry_url:
|
53
|
+
image_name = f"{registry_url}/{repository_name}"
|
54
|
+
else:
|
55
|
+
image_name = repository_name
|
56
|
+
|
57
|
+
if tag:
|
58
|
+
image_name = f"{image_name}:{tag}"
|
59
|
+
else:
|
60
|
+
image_name = f"{image_name}:latest"
|
61
|
+
|
62
|
+
with agent_manifest.context_manager(build_context_root) as build_context:
|
63
|
+
logger.info(f"Building image {image_name} locally...")
|
64
|
+
|
65
|
+
# Log build context information for debugging
|
66
|
+
logger.info(f"Build context path: {build_context.path}")
|
67
|
+
logger.info(
|
68
|
+
f"Dockerfile path: {build_context.path / build_context.dockerfile_path}"
|
69
|
+
)
|
70
|
+
|
71
|
+
try:
|
72
|
+
# Prepare build arguments
|
73
|
+
docker_build_kwargs = {
|
74
|
+
"context_path": str(build_context.path),
|
75
|
+
"file": str(build_context.path / build_context.dockerfile_path),
|
76
|
+
"tags": [image_name],
|
77
|
+
"platforms": platforms,
|
78
|
+
}
|
79
|
+
|
80
|
+
# Add Docker build args if provided
|
81
|
+
if build_args:
|
82
|
+
docker_build_args = {}
|
83
|
+
for arg in build_args:
|
84
|
+
if "=" in arg:
|
85
|
+
key, value = arg.split("=", 1)
|
86
|
+
docker_build_args[key] = value
|
87
|
+
else:
|
88
|
+
logger.warning(
|
89
|
+
f"Invalid build arg format: {arg}. Expected KEY=VALUE"
|
90
|
+
)
|
91
|
+
|
92
|
+
if docker_build_args:
|
93
|
+
docker_build_kwargs["build_args"] = docker_build_args
|
94
|
+
logger.info(f"Using build args: {list(docker_build_args.keys())}")
|
95
|
+
|
96
|
+
# Add secret if provided
|
97
|
+
if secret:
|
98
|
+
docker_build_kwargs["secrets"] = [secret]
|
99
|
+
|
100
|
+
if push:
|
101
|
+
# Build and push in one step for multi-platform builds
|
102
|
+
logger.info("Building and pushing image...")
|
103
|
+
docker_build_kwargs["push"] = (
|
104
|
+
True # Push directly after build for multi-platform
|
105
|
+
)
|
106
|
+
docker.buildx.build(**docker_build_kwargs)
|
107
|
+
|
108
|
+
logger.info(f"Successfully built and pushed {image_name}")
|
109
|
+
else:
|
110
|
+
# Build only
|
111
|
+
logger.info("Building image...")
|
112
|
+
docker.buildx.build(**docker_build_kwargs)
|
113
|
+
|
114
|
+
logger.info(f"Successfully built {image_name}")
|
115
|
+
|
116
|
+
except DockerException as error:
|
117
|
+
error_msg = error.stderr if error.stderr else str(error)
|
118
|
+
action = "build or push" if push else "build"
|
119
|
+
logger.error(f"{action.capitalize()} failed: {error_msg}", exc_info=True)
|
120
|
+
raise DockerBuildError(
|
121
|
+
f"Docker {action} failed: {error_msg}\n"
|
122
|
+
f"Build context: {build_context.path}\n"
|
123
|
+
f"Dockerfile path: {build_context.dockerfile_path}"
|
124
|
+
) from error
|
125
|
+
|
126
|
+
return image_name
|
127
|
+
|
128
|
+
|
129
|
+
def run_agent(manifest_path: str):
|
130
|
+
"""Run an agent locally from the given manifest"""
|
131
|
+
import asyncio
|
132
|
+
import signal
|
133
|
+
import sys
|
134
|
+
|
135
|
+
# Flag to track if we're shutting down
|
136
|
+
shutting_down = False
|
137
|
+
|
138
|
+
def signal_handler(signum, frame):
|
139
|
+
"""Handle signals by raising KeyboardInterrupt"""
|
140
|
+
nonlocal shutting_down
|
141
|
+
if shutting_down:
|
142
|
+
# If we're already shutting down and get another signal, force exit
|
143
|
+
print(f"\nForce exit on signal {signum}")
|
144
|
+
sys.exit(1)
|
145
|
+
|
146
|
+
shutting_down = True
|
147
|
+
print(f"\nReceived signal {signum}, shutting down...")
|
148
|
+
raise KeyboardInterrupt()
|
149
|
+
|
150
|
+
# Set up signal handling for the main thread
|
151
|
+
signal.signal(signal.SIGINT, signal_handler)
|
152
|
+
signal.signal(signal.SIGTERM, signal_handler)
|
153
|
+
|
154
|
+
try:
|
155
|
+
asyncio.run(_run_agent(manifest_path))
|
156
|
+
except KeyboardInterrupt:
|
157
|
+
print("Shutdown completed.")
|
158
|
+
sys.exit(0)
|
159
|
+
except RunError as e:
|
160
|
+
raise RuntimeError(str(e)) from e
|