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,328 @@
|
|
1
|
+
import builtins
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
import questionary
|
5
|
+
import typer
|
6
|
+
from rich import print_json
|
7
|
+
from rich.console import Console
|
8
|
+
from rich.panel import Panel
|
9
|
+
|
10
|
+
from agentex.lib.cli.handlers.agent_handlers import (
|
11
|
+
build_agent,
|
12
|
+
run_agent,
|
13
|
+
)
|
14
|
+
from agentex.lib.cli.handlers.cleanup_handlers import cleanup_agent_workflows
|
15
|
+
from agentex.lib.cli.handlers.deploy_handlers import (
|
16
|
+
DeploymentError,
|
17
|
+
HelmError,
|
18
|
+
InputDeployOverrides,
|
19
|
+
deploy_agent,
|
20
|
+
)
|
21
|
+
from agentex.lib.cli.utils.cli_utils import handle_questionary_cancellation
|
22
|
+
from agentex.lib.cli.utils.kubectl_utils import (
|
23
|
+
check_and_switch_cluster_context,
|
24
|
+
validate_namespace,
|
25
|
+
)
|
26
|
+
from agentex import Agentex
|
27
|
+
from agentex.lib.sdk.config.agent_manifest import AgentManifest
|
28
|
+
from agentex.lib.utils.logging import make_logger
|
29
|
+
|
30
|
+
logger = make_logger(__name__)
|
31
|
+
console = Console()
|
32
|
+
|
33
|
+
agents = typer.Typer()
|
34
|
+
|
35
|
+
|
36
|
+
@agents.command()
|
37
|
+
def get(
|
38
|
+
agent_id: str = typer.Argument(..., help="ID of the agent to get"),
|
39
|
+
):
|
40
|
+
"""
|
41
|
+
Get the agent with the given name.
|
42
|
+
"""
|
43
|
+
logger.info(f"Getting agent with ID: {agent_id}")
|
44
|
+
client = Agentex()
|
45
|
+
agent = client.agents.retrieve(agent_id=agent_id)
|
46
|
+
logger.info(f"Agent retrieved: {agent}")
|
47
|
+
print_json(data=agent.to_dict())
|
48
|
+
|
49
|
+
|
50
|
+
@agents.command()
|
51
|
+
def list():
|
52
|
+
"""
|
53
|
+
List all agents.
|
54
|
+
"""
|
55
|
+
logger.info("Listing all agents")
|
56
|
+
client = Agentex()
|
57
|
+
agents = client.agents.list()
|
58
|
+
logger.info(f"Agents retrieved: {agents}")
|
59
|
+
print_json(data=[agent.to_dict() for agent in agents])
|
60
|
+
|
61
|
+
|
62
|
+
@agents.command()
|
63
|
+
def delete(
|
64
|
+
agent_name: str = typer.Argument(..., help="Name of the agent to delete"),
|
65
|
+
):
|
66
|
+
"""
|
67
|
+
Delete the agent with the given name.
|
68
|
+
"""
|
69
|
+
logger.info(f"Deleting agent with name: {agent_name}")
|
70
|
+
client = Agentex()
|
71
|
+
client.agents.delete_by_name(agent_name=agent_name)
|
72
|
+
logger.info(f"Agent deleted: {agent_name}")
|
73
|
+
|
74
|
+
|
75
|
+
@agents.command()
|
76
|
+
def cleanup_workflows(
|
77
|
+
agent_name: str = typer.Argument(..., help="Name of the agent to cleanup workflows for"),
|
78
|
+
force: bool = typer.Option(False, help="Force cleanup using direct Temporal termination (bypasses development check)"),
|
79
|
+
):
|
80
|
+
"""
|
81
|
+
Clean up all running workflows for an agent.
|
82
|
+
|
83
|
+
By default, uses graceful cancellation via agent RPC.
|
84
|
+
With --force, directly terminates workflows via Temporal client.
|
85
|
+
This is a convenience command that does the same thing as 'agentex tasks cleanup'.
|
86
|
+
"""
|
87
|
+
try:
|
88
|
+
console.print(f"[blue]Cleaning up workflows for agent '{agent_name}'...[/blue]")
|
89
|
+
|
90
|
+
cleanup_agent_workflows(
|
91
|
+
agent_name=agent_name,
|
92
|
+
force=force,
|
93
|
+
development_only=True
|
94
|
+
)
|
95
|
+
|
96
|
+
console.print(f"[green]✓ Workflow cleanup completed for agent '{agent_name}'[/green]")
|
97
|
+
|
98
|
+
except Exception as e:
|
99
|
+
console.print(f"[red]Cleanup failed: {str(e)}[/red]")
|
100
|
+
logger.exception("Agent workflow cleanup failed")
|
101
|
+
raise typer.Exit(1) from e
|
102
|
+
|
103
|
+
|
104
|
+
@agents.command()
|
105
|
+
def build(
|
106
|
+
manifest: str = typer.Option(..., help="Path to the manifest you want to use"),
|
107
|
+
registry: str | None = typer.Option(
|
108
|
+
None, help="Registry URL for pushing the built image"
|
109
|
+
),
|
110
|
+
repository_name: str | None = typer.Option(
|
111
|
+
None, help="Repository name to use for the built image"
|
112
|
+
),
|
113
|
+
platforms: str | None = typer.Option(
|
114
|
+
None, help="Platform to build the image for. Please enter a comma separated list of platforms."
|
115
|
+
),
|
116
|
+
push: bool = typer.Option(False, help="Whether to push the image to the registry"),
|
117
|
+
secret: str | None = typer.Option(
|
118
|
+
None,
|
119
|
+
help="Docker build secret in the format 'id=secret-id,src=path-to-secret-file'",
|
120
|
+
),
|
121
|
+
tag: str | None = typer.Option(
|
122
|
+
None, help="Image tag to use (defaults to 'latest')"
|
123
|
+
),
|
124
|
+
build_arg: builtins.list[str] | None = typer.Option( # noqa: B008
|
125
|
+
None,
|
126
|
+
help="Docker build argument in the format 'KEY=VALUE' (can be used multiple times)",
|
127
|
+
),
|
128
|
+
):
|
129
|
+
"""
|
130
|
+
Build an agent image locally from the given manifest.
|
131
|
+
"""
|
132
|
+
typer.echo(f"Building agent image from manifest: {manifest}")
|
133
|
+
|
134
|
+
# Validate required parameters for building
|
135
|
+
if push and not registry:
|
136
|
+
typer.echo("Error: --registry is required when --push is enabled", err=True)
|
137
|
+
raise typer.Exit(1)
|
138
|
+
|
139
|
+
# Only proceed with build if we have a registry (for now, to match existing behavior)
|
140
|
+
if not registry:
|
141
|
+
typer.echo("No registry provided, skipping image build")
|
142
|
+
return
|
143
|
+
|
144
|
+
platform_list = platforms.split(",") if platforms else []
|
145
|
+
|
146
|
+
try:
|
147
|
+
image_url = build_agent(
|
148
|
+
manifest_path=manifest,
|
149
|
+
registry_url=registry, # Now guaranteed to be non-None
|
150
|
+
repository_name=repository_name or "default-repo", # Provide default
|
151
|
+
platforms=platform_list,
|
152
|
+
push=push,
|
153
|
+
secret=secret or "", # Provide default empty string
|
154
|
+
tag=tag or "latest", # Provide default
|
155
|
+
build_args=build_arg or [], # Provide default empty list
|
156
|
+
)
|
157
|
+
if image_url:
|
158
|
+
typer.echo(f"Successfully built image: {image_url}")
|
159
|
+
else:
|
160
|
+
typer.echo("Image build completed but no URL returned")
|
161
|
+
except Exception as e:
|
162
|
+
typer.echo(f"Error building agent image: {str(e)}", err=True)
|
163
|
+
logger.exception("Error building agent image")
|
164
|
+
raise typer.Exit(1) from e
|
165
|
+
|
166
|
+
|
167
|
+
@agents.command()
|
168
|
+
def run(
|
169
|
+
manifest: str = typer.Option(..., help="Path to the manifest you want to use"),
|
170
|
+
cleanup_on_start: bool = typer.Option(
|
171
|
+
False,
|
172
|
+
help="Clean up existing workflows for this agent before starting"
|
173
|
+
),
|
174
|
+
):
|
175
|
+
"""
|
176
|
+
Run an agent locally from the given manifest.
|
177
|
+
"""
|
178
|
+
typer.echo(f"Running agent from manifest: {manifest}")
|
179
|
+
|
180
|
+
# Optionally cleanup existing workflows before starting
|
181
|
+
if cleanup_on_start:
|
182
|
+
try:
|
183
|
+
# Parse manifest to get agent name
|
184
|
+
manifest_obj = AgentManifest.from_yaml(file_path=manifest)
|
185
|
+
agent_name = manifest_obj.agent.name
|
186
|
+
|
187
|
+
console.print(f"[yellow]Cleaning up existing workflows for agent '{agent_name}'...[/yellow]")
|
188
|
+
cleanup_agent_workflows(
|
189
|
+
agent_name=agent_name,
|
190
|
+
force=False,
|
191
|
+
development_only=True
|
192
|
+
)
|
193
|
+
console.print("[green]✓ Pre-run cleanup completed[/green]")
|
194
|
+
|
195
|
+
except Exception as e:
|
196
|
+
console.print(f"[yellow]⚠ Pre-run cleanup failed: {str(e)}[/yellow]")
|
197
|
+
logger.warning(f"Pre-run cleanup failed: {e}")
|
198
|
+
|
199
|
+
try:
|
200
|
+
run_agent(manifest_path=manifest)
|
201
|
+
except Exception as e:
|
202
|
+
typer.echo(f"Error running agent: {str(e)}", err=True)
|
203
|
+
logger.exception("Error running agent")
|
204
|
+
raise typer.Exit(1) from e
|
205
|
+
|
206
|
+
|
207
|
+
@agents.command()
|
208
|
+
def deploy(
|
209
|
+
cluster: str = typer.Option(
|
210
|
+
..., help="Target cluster name (must match kubectl context)"
|
211
|
+
),
|
212
|
+
manifest: str = typer.Option("manifest.yaml", help="Path to the manifest file"),
|
213
|
+
namespace: str | None = typer.Option(
|
214
|
+
None,
|
215
|
+
help="Kubernetes namespace to deploy to (required in non-interactive mode)",
|
216
|
+
),
|
217
|
+
tag: str | None = typer.Option(None, help="Override the image tag for deployment"),
|
218
|
+
repository: str | None = typer.Option(
|
219
|
+
None, help="Override the repository for deployment"
|
220
|
+
),
|
221
|
+
override_file: str | None = typer.Option(
|
222
|
+
None, help="Path to override configuration file"
|
223
|
+
),
|
224
|
+
interactive: bool = typer.Option(
|
225
|
+
True, "--interactive/--no-interactive", help="Enable interactive prompts"
|
226
|
+
),
|
227
|
+
):
|
228
|
+
"""Deploy an agent to a Kubernetes cluster using Helm"""
|
229
|
+
|
230
|
+
console.print(
|
231
|
+
Panel.fit("🚀 [bold blue]Deploy Agent[/bold blue]", border_style="blue")
|
232
|
+
)
|
233
|
+
|
234
|
+
try:
|
235
|
+
# Validate manifest exists
|
236
|
+
manifest_path = Path(manifest)
|
237
|
+
if not manifest_path.exists():
|
238
|
+
console.print(f"[red]Error:[/red] Manifest file not found: {manifest}")
|
239
|
+
raise typer.Exit(1)
|
240
|
+
|
241
|
+
# In non-interactive mode, require namespace
|
242
|
+
if not interactive and not namespace:
|
243
|
+
console.print(
|
244
|
+
"[red]Error:[/red] --namespace is required in non-interactive mode"
|
245
|
+
)
|
246
|
+
raise typer.Exit(1)
|
247
|
+
|
248
|
+
# Get namespace if not provided (only in interactive mode)
|
249
|
+
if not namespace:
|
250
|
+
namespace = questionary.text(
|
251
|
+
"Enter Kubernetes namespace:", default="default"
|
252
|
+
).ask()
|
253
|
+
namespace = handle_questionary_cancellation(namespace, "namespace input")
|
254
|
+
|
255
|
+
if not namespace:
|
256
|
+
console.print("Deployment cancelled")
|
257
|
+
raise typer.Exit(0)
|
258
|
+
|
259
|
+
# Validate override file exists if provided
|
260
|
+
if override_file:
|
261
|
+
override_path = Path(override_file)
|
262
|
+
if not override_path.exists():
|
263
|
+
console.print(
|
264
|
+
f"[red]Error:[/red] Override file not found: {override_file}"
|
265
|
+
)
|
266
|
+
raise typer.Exit(1)
|
267
|
+
|
268
|
+
# Load manifest for credential validation
|
269
|
+
manifest_obj = AgentManifest.from_yaml(str(manifest_path))
|
270
|
+
|
271
|
+
# Confirm deployment (only in interactive mode)
|
272
|
+
console.print("\n[bold]Deployment Summary:[/bold]")
|
273
|
+
console.print(f" Manifest: {manifest}")
|
274
|
+
console.print(f" Cluster: {cluster}")
|
275
|
+
console.print(f" Namespace: {namespace}")
|
276
|
+
if tag:
|
277
|
+
console.print(f" Image Tag: {tag}")
|
278
|
+
if override_file:
|
279
|
+
console.print(f" Override File: {override_file}")
|
280
|
+
|
281
|
+
if interactive:
|
282
|
+
proceed = questionary.confirm("Proceed with deployment?").ask()
|
283
|
+
proceed = handle_questionary_cancellation(
|
284
|
+
proceed, "deployment confirmation"
|
285
|
+
)
|
286
|
+
|
287
|
+
if not proceed:
|
288
|
+
console.print("Deployment cancelled")
|
289
|
+
raise typer.Exit(0)
|
290
|
+
else:
|
291
|
+
console.print("Proceeding with deployment (non-interactive mode)")
|
292
|
+
|
293
|
+
check_and_switch_cluster_context(cluster)
|
294
|
+
if not validate_namespace(namespace, cluster):
|
295
|
+
console.print(
|
296
|
+
f"[red]Error:[/red] Namespace '{namespace}' does not exist in cluster '{cluster}'"
|
297
|
+
)
|
298
|
+
raise typer.Exit(1)
|
299
|
+
|
300
|
+
deploy_overrides = InputDeployOverrides(repository=repository, image_tag=tag)
|
301
|
+
|
302
|
+
# Deploy agent
|
303
|
+
deploy_agent(
|
304
|
+
manifest_path=str(manifest_path),
|
305
|
+
cluster_name=cluster,
|
306
|
+
namespace=namespace,
|
307
|
+
deploy_overrides=deploy_overrides,
|
308
|
+
override_file_path=override_file,
|
309
|
+
)
|
310
|
+
|
311
|
+
# Use the already loaded manifest object
|
312
|
+
release_name = f"{manifest_obj.agent.name}-{cluster}"
|
313
|
+
|
314
|
+
console.print(
|
315
|
+
"\n[bold green]🎉 Deployment completed successfully![/bold green]"
|
316
|
+
)
|
317
|
+
console.print("\nTo check deployment status:")
|
318
|
+
console.print(f" kubectl get pods -n {namespace}")
|
319
|
+
console.print(f" helm status {release_name} -n {namespace}")
|
320
|
+
|
321
|
+
except (DeploymentError, HelmError) as e:
|
322
|
+
console.print(f"[red]Deployment failed:[/red] {str(e)}")
|
323
|
+
logger.exception("Deployment failed")
|
324
|
+
raise typer.Exit(1) from e
|
325
|
+
except Exception as e:
|
326
|
+
console.print(f"[red]Unexpected error:[/red] {str(e)}")
|
327
|
+
logger.exception("Unexpected error during deployment")
|
328
|
+
raise typer.Exit(1) from e
|
@@ -0,0 +1,227 @@
|
|
1
|
+
from enum import Enum
|
2
|
+
from pathlib import Path
|
3
|
+
|
4
|
+
import questionary
|
5
|
+
from jinja2 import Environment, FileSystemLoader
|
6
|
+
from rich.console import Console
|
7
|
+
from rich.panel import Panel
|
8
|
+
from rich.table import Table
|
9
|
+
|
10
|
+
from agentex.lib.utils.logging import make_logger
|
11
|
+
|
12
|
+
logger = make_logger(__name__)
|
13
|
+
console = Console()
|
14
|
+
|
15
|
+
# Get the templates directory relative to this file
|
16
|
+
TEMPLATES_DIR = Path(__file__).parent.parent / "templates"
|
17
|
+
|
18
|
+
|
19
|
+
class TemplateType(str, Enum):
|
20
|
+
TEMPORAL = "temporal"
|
21
|
+
DEFAULT = "default"
|
22
|
+
SYNC = "sync"
|
23
|
+
|
24
|
+
|
25
|
+
def render_template(
|
26
|
+
template_path: str, context: dict, template_type: TemplateType
|
27
|
+
) -> str:
|
28
|
+
"""Render a template with the given context"""
|
29
|
+
env = Environment(loader=FileSystemLoader(TEMPLATES_DIR / template_type.value))
|
30
|
+
template = env.get_template(template_path)
|
31
|
+
return template.render(**context)
|
32
|
+
|
33
|
+
|
34
|
+
def create_project_structure(
|
35
|
+
path: Path, context: dict, template_type: TemplateType, use_uv: bool
|
36
|
+
):
|
37
|
+
"""Create the project structure from templates"""
|
38
|
+
# Create project directory
|
39
|
+
project_dir: Path = path / context["project_name"]
|
40
|
+
project_dir.mkdir(parents=True, exist_ok=True)
|
41
|
+
|
42
|
+
# Create project/code directory
|
43
|
+
code_dir: Path = project_dir / "project"
|
44
|
+
code_dir.mkdir(parents=True, exist_ok=True)
|
45
|
+
|
46
|
+
# Create __init__.py
|
47
|
+
(code_dir / "__init__.py").touch()
|
48
|
+
|
49
|
+
# Define project files based on template type
|
50
|
+
project_files = {
|
51
|
+
TemplateType.TEMPORAL: ["acp.py", "workflow.py", "run_worker.py"],
|
52
|
+
TemplateType.DEFAULT: ["acp.py"],
|
53
|
+
TemplateType.SYNC: ["acp.py"],
|
54
|
+
}[template_type]
|
55
|
+
|
56
|
+
# Create project/code files
|
57
|
+
for template in project_files:
|
58
|
+
template_path = f"project/{template}.j2"
|
59
|
+
output_path = code_dir / template
|
60
|
+
output_path.write_text(render_template(template_path, context, template_type))
|
61
|
+
|
62
|
+
# Create root files
|
63
|
+
root_templates = {
|
64
|
+
".dockerignore.j2": ".dockerignore",
|
65
|
+
"manifest.yaml.j2": "manifest.yaml",
|
66
|
+
"README.md.j2": "README.md",
|
67
|
+
}
|
68
|
+
|
69
|
+
# Add package management file based on uv choice
|
70
|
+
if use_uv:
|
71
|
+
root_templates["pyproject.toml.j2"] = "pyproject.toml"
|
72
|
+
root_templates["Dockerfile-uv.j2"] = "Dockerfile"
|
73
|
+
else:
|
74
|
+
root_templates["requirements.txt.j2"] = "requirements.txt"
|
75
|
+
root_templates["Dockerfile.j2"] = "Dockerfile"
|
76
|
+
|
77
|
+
for template, output in root_templates.items():
|
78
|
+
output_path = project_dir / output
|
79
|
+
output_path.write_text(render_template(template, context, template_type))
|
80
|
+
|
81
|
+
console.print(f"\n[green]✓[/green] Created project structure at: {project_dir}")
|
82
|
+
|
83
|
+
|
84
|
+
def get_project_context(answers: dict, project_path: Path, manifest_root: Path) -> dict:
|
85
|
+
"""Get the project context from user answers"""
|
86
|
+
# Use agent_directory_name as project_name
|
87
|
+
project_name = answers["agent_directory_name"].replace("-", "_")
|
88
|
+
|
89
|
+
# Now, this is actually the exact same as the project_name because we changed the build root to be ../
|
90
|
+
project_path_from_build_root = project_name
|
91
|
+
|
92
|
+
return {
|
93
|
+
**answers,
|
94
|
+
"project_name": project_name,
|
95
|
+
"workflow_class": "".join(
|
96
|
+
word.capitalize() for word in answers["agent_name"].split("-")
|
97
|
+
)
|
98
|
+
+ "Workflow",
|
99
|
+
"workflow_name": answers["agent_name"],
|
100
|
+
"queue_name": project_name + "_queue",
|
101
|
+
"project_path_from_build_root": project_path_from_build_root,
|
102
|
+
}
|
103
|
+
|
104
|
+
|
105
|
+
def init():
|
106
|
+
"""Initialize a new agent project"""
|
107
|
+
console.print(
|
108
|
+
Panel.fit(
|
109
|
+
"🤖 [bold blue]Initialize New Agent Project[/bold blue]",
|
110
|
+
border_style="blue",
|
111
|
+
)
|
112
|
+
)
|
113
|
+
|
114
|
+
# Use a Rich table for template descriptions
|
115
|
+
table = Table(show_header=True, header_style="bold magenta")
|
116
|
+
table.add_column("Template", style="cyan", no_wrap=True)
|
117
|
+
table.add_column("Description", style="white")
|
118
|
+
table.add_row(
|
119
|
+
"[bold cyan]Agentic - ACP Only[/bold cyan]",
|
120
|
+
"A simple synchronous agent that handles tasks directly. Best for straightforward agents that don't need long-running operations.",
|
121
|
+
)
|
122
|
+
table.add_row(
|
123
|
+
"[bold cyan]Agentic - Temporal[/bold cyan]",
|
124
|
+
"An asynchronous agent powered by Temporal workflows. Best for agents that need to handle long-running tasks, retries, or complex state management.",
|
125
|
+
)
|
126
|
+
table.add_row(
|
127
|
+
"[bold cyan]Sync ACP[/bold cyan]",
|
128
|
+
"A synchronous agent that handles tasks directly. The difference is that this Sync ACP will be required to respond with the results in the same call as the input.Best for straightforward agents that don't need long-running operations.",
|
129
|
+
)
|
130
|
+
console.print()
|
131
|
+
console.print(table)
|
132
|
+
console.print()
|
133
|
+
|
134
|
+
def validate_agent_name(text: str) -> bool | str:
|
135
|
+
"""Validate agent name follows required format"""
|
136
|
+
is_valid = len(text) >= 1 and text.replace("-", "").isalnum() and text.islower()
|
137
|
+
if not is_valid:
|
138
|
+
return "Invalid name. Use only lowercase letters, numbers, and hyphens. Examples: 'my-agent', 'newsbot'"
|
139
|
+
return True
|
140
|
+
|
141
|
+
# Gather project information
|
142
|
+
template_type = questionary.select(
|
143
|
+
"What type of template would you like to create?",
|
144
|
+
choices=[
|
145
|
+
{"name": "Agentic - ACP Only", "value": TemplateType.DEFAULT},
|
146
|
+
{"name": "Agentic - Temporal", "value": TemplateType.TEMPORAL},
|
147
|
+
{"name": "Sync ACP", "value": TemplateType.SYNC},
|
148
|
+
],
|
149
|
+
).ask()
|
150
|
+
if not template_type:
|
151
|
+
return
|
152
|
+
|
153
|
+
project_path = questionary.path(
|
154
|
+
"Where would you like to create your project?", default="."
|
155
|
+
).ask()
|
156
|
+
if not project_path:
|
157
|
+
return
|
158
|
+
|
159
|
+
agent_name = questionary.text(
|
160
|
+
"What's your agent name? (letters, numbers, and hyphens only)",
|
161
|
+
validate=validate_agent_name,
|
162
|
+
).ask()
|
163
|
+
if not agent_name:
|
164
|
+
return
|
165
|
+
|
166
|
+
agent_directory_name = questionary.text(
|
167
|
+
"What do you want to name the project folder for your agent?",
|
168
|
+
default=agent_name,
|
169
|
+
).ask()
|
170
|
+
if not agent_directory_name:
|
171
|
+
return
|
172
|
+
|
173
|
+
description = questionary.text(
|
174
|
+
"Provide a brief description of your agent:", default="An AgentEx agent"
|
175
|
+
).ask()
|
176
|
+
if not description:
|
177
|
+
return
|
178
|
+
|
179
|
+
use_uv = questionary.select(
|
180
|
+
"Would you like to use uv for package management?",
|
181
|
+
choices=[
|
182
|
+
{"name": "Yes (Recommended)", "value": True},
|
183
|
+
{"name": "No", "value": False},
|
184
|
+
],
|
185
|
+
).ask()
|
186
|
+
|
187
|
+
answers = {
|
188
|
+
"template_type": template_type,
|
189
|
+
"project_path": project_path,
|
190
|
+
"agent_name": agent_name,
|
191
|
+
"agent_directory_name": agent_directory_name,
|
192
|
+
"description": description,
|
193
|
+
"use_uv": use_uv,
|
194
|
+
}
|
195
|
+
|
196
|
+
# Derive all names from agent_directory_name and path
|
197
|
+
project_path = Path(answers["project_path"]).resolve()
|
198
|
+
manifest_root = Path("../../")
|
199
|
+
|
200
|
+
# Get project context
|
201
|
+
context = get_project_context(answers, project_path, manifest_root)
|
202
|
+
context["template_type"] = answers["template_type"].value
|
203
|
+
context["use_uv"] = answers["use_uv"]
|
204
|
+
|
205
|
+
# Create project structure
|
206
|
+
create_project_structure(
|
207
|
+
project_path, context, answers["template_type"], answers["use_uv"]
|
208
|
+
)
|
209
|
+
|
210
|
+
# Show next steps
|
211
|
+
console.print("\n[bold green]✨ Project created successfully![/bold green]")
|
212
|
+
console.print("\n[bold]Next steps:[/bold]")
|
213
|
+
console.print(f"1. cd {project_path}/{context['project_name']}")
|
214
|
+
console.print("2. Review and customize the generated files")
|
215
|
+
console.print("3. Update the container registry in manifest.yaml")
|
216
|
+
|
217
|
+
if answers["template_type"] == TemplateType.TEMPORAL:
|
218
|
+
console.print("4. Run locally:")
|
219
|
+
console.print(" agentex agents run --manifest manifest.yaml")
|
220
|
+
else:
|
221
|
+
console.print("4. Run locally:")
|
222
|
+
console.print(" agentex agents run --manifest manifest.yaml")
|
223
|
+
|
224
|
+
console.print("5. Deploy your agent:")
|
225
|
+
console.print(
|
226
|
+
" agentex agents deploy --cluster your-cluster --namespace your-namespace"
|
227
|
+
)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import typer
|
2
|
+
|
3
|
+
from agentex.lib.cli.commands.agents import agents
|
4
|
+
from agentex.lib.cli.commands.init import init
|
5
|
+
from agentex.lib.cli.commands.secrets import secrets
|
6
|
+
from agentex.lib.cli.commands.tasks import tasks
|
7
|
+
from agentex.lib.cli.commands.uv import uv
|
8
|
+
|
9
|
+
# Create the main Typer application
|
10
|
+
app = typer.Typer(
|
11
|
+
context_settings={"help_option_names": ["-h", "--help"], "max_content_width": 800},
|
12
|
+
pretty_exceptions_show_locals=False,
|
13
|
+
pretty_exceptions_enable=False,
|
14
|
+
add_completion=False,
|
15
|
+
)
|
16
|
+
|
17
|
+
# Add the subcommands
|
18
|
+
app.add_typer(agents, name="agents", help="Get, list, run, build, and deploy agents")
|
19
|
+
app.add_typer(tasks, name="tasks", help="Get, list, and delete tasks")
|
20
|
+
app.add_typer(secrets, name="secrets", help="Sync, get, list, and delete secrets")
|
21
|
+
app.add_typer(
|
22
|
+
uv, name="uv", help="Wrapper for uv command with AgentEx-specific enhancements"
|
23
|
+
)
|
24
|
+
|
25
|
+
# Add init command with documentation
|
26
|
+
app.command(
|
27
|
+
help="Initialize a new agent project with a template",
|
28
|
+
epilog="Example: agentex init --template temporal my-agent",
|
29
|
+
)(init)
|
30
|
+
|
31
|
+
|
32
|
+
if __name__ == "__main__":
|
33
|
+
app()
|