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.
Files changed (289) hide show
  1. agentex/__init__.py +103 -0
  2. agentex/_base_client.py +1992 -0
  3. agentex/_client.py +506 -0
  4. agentex/_compat.py +219 -0
  5. agentex/_constants.py +14 -0
  6. agentex/_exceptions.py +108 -0
  7. agentex/_files.py +123 -0
  8. agentex/_models.py +829 -0
  9. agentex/_qs.py +150 -0
  10. agentex/_resource.py +43 -0
  11. agentex/_response.py +830 -0
  12. agentex/_streaming.py +333 -0
  13. agentex/_types.py +219 -0
  14. agentex/_utils/__init__.py +57 -0
  15. agentex/_utils/_logs.py +25 -0
  16. agentex/_utils/_proxy.py +65 -0
  17. agentex/_utils/_reflection.py +42 -0
  18. agentex/_utils/_resources_proxy.py +24 -0
  19. agentex/_utils/_streams.py +12 -0
  20. agentex/_utils/_sync.py +86 -0
  21. agentex/_utils/_transform.py +447 -0
  22. agentex/_utils/_typing.py +151 -0
  23. agentex/_utils/_utils.py +422 -0
  24. agentex/_version.py +4 -0
  25. agentex/lib/.keep +4 -0
  26. agentex/lib/__init__.py +0 -0
  27. agentex/lib/adk/__init__.py +41 -0
  28. agentex/lib/adk/_modules/__init__.py +0 -0
  29. agentex/lib/adk/_modules/acp.py +247 -0
  30. agentex/lib/adk/_modules/agent_task_tracker.py +176 -0
  31. agentex/lib/adk/_modules/agents.py +77 -0
  32. agentex/lib/adk/_modules/events.py +141 -0
  33. agentex/lib/adk/_modules/messages.py +285 -0
  34. agentex/lib/adk/_modules/state.py +291 -0
  35. agentex/lib/adk/_modules/streaming.py +75 -0
  36. agentex/lib/adk/_modules/tasks.py +124 -0
  37. agentex/lib/adk/_modules/tracing.py +194 -0
  38. agentex/lib/adk/providers/__init__.py +9 -0
  39. agentex/lib/adk/providers/_modules/__init__.py +0 -0
  40. agentex/lib/adk/providers/_modules/litellm.py +232 -0
  41. agentex/lib/adk/providers/_modules/openai.py +416 -0
  42. agentex/lib/adk/providers/_modules/sgp.py +85 -0
  43. agentex/lib/adk/utils/__init__.py +5 -0
  44. agentex/lib/adk/utils/_modules/__init__.py +0 -0
  45. agentex/lib/adk/utils/_modules/templating.py +94 -0
  46. agentex/lib/cli/__init__.py +0 -0
  47. agentex/lib/cli/commands/__init__.py +0 -0
  48. agentex/lib/cli/commands/agents.py +328 -0
  49. agentex/lib/cli/commands/init.py +227 -0
  50. agentex/lib/cli/commands/main.py +33 -0
  51. agentex/lib/cli/commands/secrets.py +169 -0
  52. agentex/lib/cli/commands/tasks.py +118 -0
  53. agentex/lib/cli/commands/uv.py +133 -0
  54. agentex/lib/cli/handlers/__init__.py +0 -0
  55. agentex/lib/cli/handlers/agent_handlers.py +160 -0
  56. agentex/lib/cli/handlers/cleanup_handlers.py +186 -0
  57. agentex/lib/cli/handlers/deploy_handlers.py +351 -0
  58. agentex/lib/cli/handlers/run_handlers.py +452 -0
  59. agentex/lib/cli/handlers/secret_handlers.py +670 -0
  60. agentex/lib/cli/templates/default/.dockerignore.j2 +43 -0
  61. agentex/lib/cli/templates/default/Dockerfile-uv.j2 +42 -0
  62. agentex/lib/cli/templates/default/Dockerfile.j2 +42 -0
  63. agentex/lib/cli/templates/default/README.md.j2 +193 -0
  64. agentex/lib/cli/templates/default/deploy/example.yaml.j2 +55 -0
  65. agentex/lib/cli/templates/default/manifest.yaml.j2 +116 -0
  66. agentex/lib/cli/templates/default/project/acp.py.j2 +29 -0
  67. agentex/lib/cli/templates/default/pyproject.toml.j2 +33 -0
  68. agentex/lib/cli/templates/default/requirements.txt.j2 +5 -0
  69. agentex/lib/cli/templates/deploy/Screenshot 2025-03-19 at 10.36.57/342/200/257AM.png +0 -0
  70. agentex/lib/cli/templates/deploy/example.yaml.j2 +55 -0
  71. agentex/lib/cli/templates/sync/.dockerignore.j2 +43 -0
  72. agentex/lib/cli/templates/sync/Dockerfile-uv.j2 +42 -0
  73. agentex/lib/cli/templates/sync/Dockerfile.j2 +42 -0
  74. agentex/lib/cli/templates/sync/README.md.j2 +293 -0
  75. agentex/lib/cli/templates/sync/deploy/example.yaml.j2 +55 -0
  76. agentex/lib/cli/templates/sync/manifest.yaml.j2 +116 -0
  77. agentex/lib/cli/templates/sync/project/acp.py.j2 +26 -0
  78. agentex/lib/cli/templates/sync/pyproject.toml.j2 +33 -0
  79. agentex/lib/cli/templates/sync/requirements.txt.j2 +5 -0
  80. agentex/lib/cli/templates/temporal/.dockerignore.j2 +43 -0
  81. agentex/lib/cli/templates/temporal/Dockerfile-uv.j2 +48 -0
  82. agentex/lib/cli/templates/temporal/Dockerfile.j2 +48 -0
  83. agentex/lib/cli/templates/temporal/README.md.j2 +316 -0
  84. agentex/lib/cli/templates/temporal/deploy/example.yaml.j2 +55 -0
  85. agentex/lib/cli/templates/temporal/manifest.yaml.j2 +137 -0
  86. agentex/lib/cli/templates/temporal/project/acp.py.j2 +30 -0
  87. agentex/lib/cli/templates/temporal/project/run_worker.py.j2 +33 -0
  88. agentex/lib/cli/templates/temporal/project/workflow.py.j2 +66 -0
  89. agentex/lib/cli/templates/temporal/pyproject.toml.j2 +34 -0
  90. agentex/lib/cli/templates/temporal/requirements.txt.j2 +5 -0
  91. agentex/lib/cli/utils/cli_utils.py +14 -0
  92. agentex/lib/cli/utils/credential_utils.py +103 -0
  93. agentex/lib/cli/utils/exceptions.py +6 -0
  94. agentex/lib/cli/utils/kubectl_utils.py +135 -0
  95. agentex/lib/cli/utils/kubernetes_secrets_utils.py +185 -0
  96. agentex/lib/core/__init__.py +0 -0
  97. agentex/lib/core/adapters/__init__.py +0 -0
  98. agentex/lib/core/adapters/llm/__init__.py +1 -0
  99. agentex/lib/core/adapters/llm/adapter_litellm.py +46 -0
  100. agentex/lib/core/adapters/llm/adapter_sgp.py +55 -0
  101. agentex/lib/core/adapters/llm/port.py +24 -0
  102. agentex/lib/core/adapters/streams/adapter_redis.py +128 -0
  103. agentex/lib/core/adapters/streams/port.py +50 -0
  104. agentex/lib/core/clients/__init__.py +1 -0
  105. agentex/lib/core/clients/temporal/__init__.py +0 -0
  106. agentex/lib/core/clients/temporal/temporal_client.py +181 -0
  107. agentex/lib/core/clients/temporal/types.py +47 -0
  108. agentex/lib/core/clients/temporal/utils.py +56 -0
  109. agentex/lib/core/services/__init__.py +0 -0
  110. agentex/lib/core/services/adk/__init__.py +0 -0
  111. agentex/lib/core/services/adk/acp/__init__.py +0 -0
  112. agentex/lib/core/services/adk/acp/acp.py +210 -0
  113. agentex/lib/core/services/adk/agent_task_tracker.py +85 -0
  114. agentex/lib/core/services/adk/agents.py +43 -0
  115. agentex/lib/core/services/adk/events.py +61 -0
  116. agentex/lib/core/services/adk/messages.py +164 -0
  117. agentex/lib/core/services/adk/providers/__init__.py +0 -0
  118. agentex/lib/core/services/adk/providers/litellm.py +256 -0
  119. agentex/lib/core/services/adk/providers/openai.py +723 -0
  120. agentex/lib/core/services/adk/providers/sgp.py +99 -0
  121. agentex/lib/core/services/adk/state.py +120 -0
  122. agentex/lib/core/services/adk/streaming.py +262 -0
  123. agentex/lib/core/services/adk/tasks.py +69 -0
  124. agentex/lib/core/services/adk/tracing.py +36 -0
  125. agentex/lib/core/services/adk/utils/__init__.py +0 -0
  126. agentex/lib/core/services/adk/utils/templating.py +58 -0
  127. agentex/lib/core/temporal/__init__.py +0 -0
  128. agentex/lib/core/temporal/activities/__init__.py +207 -0
  129. agentex/lib/core/temporal/activities/activity_helpers.py +37 -0
  130. agentex/lib/core/temporal/activities/adk/__init__.py +0 -0
  131. agentex/lib/core/temporal/activities/adk/acp/__init__.py +0 -0
  132. agentex/lib/core/temporal/activities/adk/acp/acp_activities.py +86 -0
  133. agentex/lib/core/temporal/activities/adk/agent_task_tracker_activities.py +76 -0
  134. agentex/lib/core/temporal/activities/adk/agents_activities.py +35 -0
  135. agentex/lib/core/temporal/activities/adk/events_activities.py +50 -0
  136. agentex/lib/core/temporal/activities/adk/messages_activities.py +94 -0
  137. agentex/lib/core/temporal/activities/adk/providers/__init__.py +0 -0
  138. agentex/lib/core/temporal/activities/adk/providers/litellm_activities.py +71 -0
  139. agentex/lib/core/temporal/activities/adk/providers/openai_activities.py +210 -0
  140. agentex/lib/core/temporal/activities/adk/providers/sgp_activities.py +42 -0
  141. agentex/lib/core/temporal/activities/adk/state_activities.py +85 -0
  142. agentex/lib/core/temporal/activities/adk/streaming_activities.py +33 -0
  143. agentex/lib/core/temporal/activities/adk/tasks_activities.py +48 -0
  144. agentex/lib/core/temporal/activities/adk/tracing_activities.py +55 -0
  145. agentex/lib/core/temporal/activities/adk/utils/__init__.py +0 -0
  146. agentex/lib/core/temporal/activities/adk/utils/templating_activities.py +41 -0
  147. agentex/lib/core/temporal/services/__init__.py +0 -0
  148. agentex/lib/core/temporal/services/temporal_task_service.py +69 -0
  149. agentex/lib/core/temporal/types/__init__.py +0 -0
  150. agentex/lib/core/temporal/types/workflow.py +5 -0
  151. agentex/lib/core/temporal/workers/__init__.py +0 -0
  152. agentex/lib/core/temporal/workers/worker.py +162 -0
  153. agentex/lib/core/temporal/workflows/workflow.py +26 -0
  154. agentex/lib/core/tracing/__init__.py +5 -0
  155. agentex/lib/core/tracing/processors/agentex_tracing_processor.py +117 -0
  156. agentex/lib/core/tracing/processors/sgp_tracing_processor.py +119 -0
  157. agentex/lib/core/tracing/processors/tracing_processor_interface.py +40 -0
  158. agentex/lib/core/tracing/trace.py +311 -0
  159. agentex/lib/core/tracing/tracer.py +70 -0
  160. agentex/lib/core/tracing/tracing_processor_manager.py +62 -0
  161. agentex/lib/environment_variables.py +87 -0
  162. agentex/lib/py.typed +0 -0
  163. agentex/lib/sdk/__init__.py +0 -0
  164. agentex/lib/sdk/config/__init__.py +0 -0
  165. agentex/lib/sdk/config/agent_config.py +61 -0
  166. agentex/lib/sdk/config/agent_manifest.py +219 -0
  167. agentex/lib/sdk/config/build_config.py +35 -0
  168. agentex/lib/sdk/config/deployment_config.py +117 -0
  169. agentex/lib/sdk/config/local_development_config.py +56 -0
  170. agentex/lib/sdk/config/project_config.py +103 -0
  171. agentex/lib/sdk/fastacp/__init__.py +3 -0
  172. agentex/lib/sdk/fastacp/base/base_acp_server.py +406 -0
  173. agentex/lib/sdk/fastacp/fastacp.py +74 -0
  174. agentex/lib/sdk/fastacp/impl/agentic_base_acp.py +72 -0
  175. agentex/lib/sdk/fastacp/impl/sync_acp.py +109 -0
  176. agentex/lib/sdk/fastacp/impl/temporal_acp.py +97 -0
  177. agentex/lib/sdk/fastacp/tests/README.md +297 -0
  178. agentex/lib/sdk/fastacp/tests/conftest.py +307 -0
  179. agentex/lib/sdk/fastacp/tests/pytest.ini +10 -0
  180. agentex/lib/sdk/fastacp/tests/run_tests.py +227 -0
  181. agentex/lib/sdk/fastacp/tests/test_base_acp_server.py +450 -0
  182. agentex/lib/sdk/fastacp/tests/test_fastacp_factory.py +344 -0
  183. agentex/lib/sdk/fastacp/tests/test_integration.py +477 -0
  184. agentex/lib/sdk/state_machine/__init__.py +6 -0
  185. agentex/lib/sdk/state_machine/noop_workflow.py +21 -0
  186. agentex/lib/sdk/state_machine/state.py +10 -0
  187. agentex/lib/sdk/state_machine/state_machine.py +189 -0
  188. agentex/lib/sdk/state_machine/state_workflow.py +16 -0
  189. agentex/lib/sdk/utils/__init__.py +0 -0
  190. agentex/lib/sdk/utils/messages.py +223 -0
  191. agentex/lib/types/__init__.py +0 -0
  192. agentex/lib/types/acp.py +94 -0
  193. agentex/lib/types/agent_configs.py +79 -0
  194. agentex/lib/types/agent_results.py +29 -0
  195. agentex/lib/types/credentials.py +34 -0
  196. agentex/lib/types/fastacp.py +61 -0
  197. agentex/lib/types/files.py +13 -0
  198. agentex/lib/types/json_rpc.py +49 -0
  199. agentex/lib/types/llm_messages.py +354 -0
  200. agentex/lib/types/task_message_updates.py +171 -0
  201. agentex/lib/types/tracing.py +34 -0
  202. agentex/lib/utils/__init__.py +0 -0
  203. agentex/lib/utils/completions.py +131 -0
  204. agentex/lib/utils/console.py +14 -0
  205. agentex/lib/utils/io.py +29 -0
  206. agentex/lib/utils/iterables.py +14 -0
  207. agentex/lib/utils/json_schema.py +23 -0
  208. agentex/lib/utils/logging.py +31 -0
  209. agentex/lib/utils/mcp.py +17 -0
  210. agentex/lib/utils/model_utils.py +46 -0
  211. agentex/lib/utils/parsing.py +15 -0
  212. agentex/lib/utils/regex.py +6 -0
  213. agentex/lib/utils/temporal.py +13 -0
  214. agentex/py.typed +0 -0
  215. agentex/resources/__init__.py +103 -0
  216. agentex/resources/agents.py +707 -0
  217. agentex/resources/events.py +294 -0
  218. agentex/resources/messages/__init__.py +33 -0
  219. agentex/resources/messages/batch.py +271 -0
  220. agentex/resources/messages/messages.py +492 -0
  221. agentex/resources/spans.py +557 -0
  222. agentex/resources/states.py +544 -0
  223. agentex/resources/tasks.py +615 -0
  224. agentex/resources/tracker.py +384 -0
  225. agentex/types/__init__.py +56 -0
  226. agentex/types/acp_type.py +7 -0
  227. agentex/types/agent.py +29 -0
  228. agentex/types/agent_list_params.py +13 -0
  229. agentex/types/agent_list_response.py +10 -0
  230. agentex/types/agent_rpc_by_name_params.py +21 -0
  231. agentex/types/agent_rpc_params.py +51 -0
  232. agentex/types/agent_rpc_params1.py +21 -0
  233. agentex/types/agent_rpc_response.py +20 -0
  234. agentex/types/agent_rpc_result.py +90 -0
  235. agentex/types/agent_task_tracker.py +34 -0
  236. agentex/types/data_content.py +30 -0
  237. agentex/types/data_content_param.py +31 -0
  238. agentex/types/data_delta.py +14 -0
  239. agentex/types/event.py +29 -0
  240. agentex/types/event_list_params.py +22 -0
  241. agentex/types/event_list_response.py +10 -0
  242. agentex/types/message_author.py +7 -0
  243. agentex/types/message_create_params.py +18 -0
  244. agentex/types/message_list_params.py +14 -0
  245. agentex/types/message_list_response.py +10 -0
  246. agentex/types/message_style.py +7 -0
  247. agentex/types/message_update_params.py +18 -0
  248. agentex/types/messages/__init__.py +8 -0
  249. agentex/types/messages/batch_create_params.py +16 -0
  250. agentex/types/messages/batch_create_response.py +10 -0
  251. agentex/types/messages/batch_update_params.py +16 -0
  252. agentex/types/messages/batch_update_response.py +10 -0
  253. agentex/types/shared/__init__.py +3 -0
  254. agentex/types/shared/task_message_update.py +83 -0
  255. agentex/types/span.py +36 -0
  256. agentex/types/span_create_params.py +40 -0
  257. agentex/types/span_list_params.py +12 -0
  258. agentex/types/span_list_response.py +10 -0
  259. agentex/types/span_update_params.py +37 -0
  260. agentex/types/state.py +25 -0
  261. agentex/types/state_create_params.py +16 -0
  262. agentex/types/state_list_params.py +16 -0
  263. agentex/types/state_list_response.py +10 -0
  264. agentex/types/state_update_params.py +16 -0
  265. agentex/types/task.py +23 -0
  266. agentex/types/task_delete_by_name_response.py +8 -0
  267. agentex/types/task_delete_response.py +8 -0
  268. agentex/types/task_list_response.py +10 -0
  269. agentex/types/task_message.py +33 -0
  270. agentex/types/task_message_content.py +16 -0
  271. agentex/types/task_message_content_param.py +17 -0
  272. agentex/types/task_message_delta.py +16 -0
  273. agentex/types/text_content.py +53 -0
  274. agentex/types/text_content_param.py +54 -0
  275. agentex/types/text_delta.py +14 -0
  276. agentex/types/tool_request_content.py +36 -0
  277. agentex/types/tool_request_content_param.py +37 -0
  278. agentex/types/tool_request_delta.py +18 -0
  279. agentex/types/tool_response_content.py +36 -0
  280. agentex/types/tool_response_content_param.py +36 -0
  281. agentex/types/tool_response_delta.py +18 -0
  282. agentex/types/tracker_list_params.py +16 -0
  283. agentex/types/tracker_list_response.py +10 -0
  284. agentex/types/tracker_update_params.py +19 -0
  285. agentex_sdk-0.1.0a6.dist-info/METADATA +426 -0
  286. agentex_sdk-0.1.0a6.dist-info/RECORD +289 -0
  287. agentex_sdk-0.1.0a6.dist-info/WHEEL +4 -0
  288. agentex_sdk-0.1.0a6.dist-info/entry_points.txt +2 -0
  289. 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()