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,219 @@
1
+ from __future__ import annotations
2
+
3
+ import io
4
+ import shutil
5
+ import subprocess
6
+ import tarfile
7
+ import tempfile
8
+ import time
9
+ from collections.abc import Iterator
10
+ from contextlib import contextmanager
11
+ from pathlib import Path
12
+ from typing import IO, Any
13
+
14
+ from pydantic import Field
15
+
16
+ from agentex.lib.sdk.config.agent_config import AgentConfig
17
+ from agentex.lib.sdk.config.build_config import BuildConfig
18
+ from agentex.lib.sdk.config.deployment_config import DeploymentConfig
19
+ from agentex.lib.sdk.config.local_development_config import LocalDevelopmentConfig
20
+ from agentex.lib.utils.logging import make_logger
21
+ from agentex.lib.utils.model_utils import BaseModel
22
+
23
+ logger = make_logger(__name__)
24
+
25
+
26
+ class AgentManifest(BaseModel):
27
+ """
28
+ Represents a manifest file that describes how to build and deploy an agent.
29
+ """
30
+
31
+ build: BuildConfig
32
+ agent: AgentConfig
33
+ local_development: LocalDevelopmentConfig | None = Field(
34
+ default=None, description="Configuration for local development"
35
+ )
36
+ deployment: DeploymentConfig | None = Field(
37
+ default=None, description="Deployment configuration for the agent"
38
+ )
39
+
40
+ def context_manager(self, build_context_root: Path) -> BuildContextManager:
41
+ """
42
+ Creates a build context manager
43
+ """
44
+ return BuildContextManager(
45
+ agent_manifest=self, build_context_root=build_context_root
46
+ )
47
+
48
+
49
+ class BuildContextManager:
50
+ """
51
+ A gateway used to manage the build context for a docker image
52
+ """
53
+
54
+ def __init__(self, agent_manifest: AgentManifest, build_context_root: Path):
55
+ self.agent_manifest = agent_manifest
56
+ self.build_context_root = build_context_root
57
+ self._temp_dir: tempfile.TemporaryDirectory | None = None
58
+
59
+ self.path: Path | None = None
60
+ self.dockerfile_path = "Dockerfile"
61
+ self.dockerignore_path = ".dockerignore"
62
+ self.directory_paths: list[Path] = []
63
+
64
+ def __enter__(self) -> BuildContextManager:
65
+ self._temp_dir = tempfile.TemporaryDirectory()
66
+ self.path = Path(self._temp_dir.name)
67
+
68
+ dockerfile_path = (
69
+ self.build_context_root / self.agent_manifest.build.context.dockerfile
70
+ )
71
+ self.add_dockerfile(root_path=self.path, dockerfile_path=dockerfile_path)
72
+
73
+ ignore_patterns = []
74
+ if self.agent_manifest.build.context.dockerignore:
75
+ dockerignore_path = (
76
+ self.build_context_root / self.agent_manifest.build.context.dockerignore
77
+ )
78
+ self.add_dockerignore(
79
+ root_path=self.path, dockerignore_path=dockerignore_path
80
+ )
81
+ ignore_patterns = _extract_dockerignore_patterns(dockerignore_path)
82
+
83
+ for directory in self.agent_manifest.build.context.include_paths:
84
+ directory_path = self.build_context_root / directory
85
+ self.add_directory(
86
+ root_path=self.path,
87
+ directory_path=directory_path,
88
+ context_root=self.build_context_root,
89
+ ignore_patterns=ignore_patterns,
90
+ )
91
+
92
+ return self
93
+
94
+ def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
95
+ if self._temp_dir:
96
+ self._temp_dir.cleanup()
97
+
98
+ def add_dockerfile(self, root_path: Path, dockerfile_path: Path) -> None:
99
+ """
100
+ Copies a dockerfile to the temporary context directory root
101
+ """
102
+ shutil.copy2(dockerfile_path, root_path / self.dockerfile_path)
103
+
104
+ def add_dockerignore(self, root_path: Path, dockerignore_path: Path) -> None:
105
+ """
106
+ Copies a dockerignore to the temporary context directory root
107
+ """
108
+ shutil.copy2(str(dockerignore_path), root_path / self.dockerignore_path)
109
+
110
+ def add_directory(
111
+ self,
112
+ root_path: Path,
113
+ directory_path: Path,
114
+ context_root: Path,
115
+ ignore_patterns: list[str] | None = None,
116
+ ) -> None:
117
+ """
118
+ Copies a directory to the temporary context directory root while maintaining its relative
119
+ path to the context root.
120
+ """
121
+ directory_copy_start_time = time.time()
122
+ last_log_time = directory_copy_start_time
123
+
124
+ def copy_function_with_progress(src, dst):
125
+ nonlocal directory_copy_start_time
126
+ nonlocal last_log_time
127
+ logger.info(f"Adding {src} to build context...")
128
+ shutil.copy2(src, dst)
129
+ current_time = time.time()
130
+ time_elapsed = current_time - directory_copy_start_time
131
+
132
+ if time_elapsed > 1 and current_time - last_log_time >= 1:
133
+ logger.info(
134
+ f"Time elapsed copying ({directory_path}): {time_elapsed} "
135
+ f"seconds"
136
+ )
137
+ last_log_time = current_time
138
+ if time_elapsed > 5:
139
+ logger.warning(
140
+ f"This may take a while... "
141
+ f"Consider adding {directory_path} or {src} to your .dockerignore file."
142
+ )
143
+
144
+ directory_path_relative_to_root = directory_path.relative_to(context_root)
145
+ all_ignore_patterns = [f"{root_path}*"]
146
+ if ignore_patterns:
147
+ all_ignore_patterns += ignore_patterns
148
+ shutil.copytree(
149
+ src=directory_path,
150
+ dst=root_path / directory_path_relative_to_root,
151
+ ignore=shutil.ignore_patterns(*all_ignore_patterns),
152
+ dirs_exist_ok=True,
153
+ copy_function=copy_function_with_progress,
154
+ )
155
+ self.directory_paths.append(directory_path_relative_to_root)
156
+
157
+ @contextmanager
158
+ def zip_stream(self, root_path: Path | None = None) -> Iterator[IO[bytes]]:
159
+ """
160
+ Creates a tar archive of the temporary context directory
161
+ and returns a stream of the archive.
162
+ """
163
+ if not root_path:
164
+ raise ValueError("root_path must be provided")
165
+ context = str(root_path.absolute())
166
+ folders_to_include = "."
167
+ tar_command = ["tar", "-C", context, "-cf", "-"]
168
+ tar_command.extend(folders_to_include)
169
+
170
+ logger.info(f"Creating archive: {' '.join(tar_command)}")
171
+
172
+ with subprocess.Popen(
173
+ tar_command,
174
+ stdout=subprocess.PIPE,
175
+ stderr=subprocess.DEVNULL,
176
+ ) as proc:
177
+ assert proc.stdout is not None
178
+ try:
179
+ yield proc.stdout
180
+ finally:
181
+ pass
182
+
183
+ @staticmethod
184
+ @contextmanager
185
+ def zipped(root_path: Path | None = None) -> Iterator[IO[bytes]]:
186
+ """
187
+ Creates a tar.gz archive of the temporary context directory
188
+ and returns a stream of the archive.
189
+ """
190
+ if not root_path:
191
+ raise ValueError("root_path must be provided")
192
+
193
+ tar_buffer = io.BytesIO()
194
+
195
+ with tarfile.open(fileobj=tar_buffer, mode="w:gz") as tar_file:
196
+ for path in Path(root_path).rglob(
197
+ "*"
198
+ ): # Recursively add files to the tar.gz
199
+ if path.is_file(): # Ensure that we're only adding files
200
+ tar_file.add(path, arcname=path.relative_to(root_path))
201
+
202
+ tar_buffer.seek(0) # Reset the buffer position to the beginning
203
+ yield tar_buffer
204
+
205
+
206
+ def _extract_dockerignore_patterns(dockerignore_path: Path) -> list[str]:
207
+ """
208
+ Extracts glob patterns to ignore from the dockerignore into a list of patterns
209
+ :param dockerignore_path: Path to the dockerignore to extract patterns from
210
+ :return: List of glob patterns to ignore
211
+ :rtype: List[str]
212
+ """
213
+ ignore_patterns = []
214
+ with open(dockerignore_path) as file:
215
+ for line in file:
216
+ ignored_filepath = line.split("#", 1)[0].strip()
217
+ if ignored_filepath:
218
+ ignore_patterns.append(ignored_filepath)
219
+ return ignore_patterns
@@ -0,0 +1,35 @@
1
+ from pydantic import Field
2
+
3
+ from agentex.lib.utils.model_utils import BaseModel
4
+
5
+
6
+ class BuildContext(BaseModel):
7
+ """
8
+ Represents the context in which the Docker image should be built.
9
+ """
10
+
11
+ root: str = Field(
12
+ ...,
13
+ description="The root directory of the build context. Should be specified relative to the location of the "
14
+ "build config file.",
15
+ )
16
+ include_paths: list[str] = Field(
17
+ default_factory=list,
18
+ description="The paths to include in the build context. Should be specified relative to the root directory.",
19
+ )
20
+ dockerfile: str = Field(
21
+ ...,
22
+ description="The path to the Dockerfile. Should be specified relative to the root directory.",
23
+ )
24
+ dockerignore: str | None = Field(
25
+ None,
26
+ description="The path to the .dockerignore file. Should be specified relative to the root directory.",
27
+ )
28
+
29
+
30
+ class BuildConfig(BaseModel):
31
+ """
32
+ Represents a configuration for building the action as a Docker image.
33
+ """
34
+
35
+ context: BuildContext
@@ -0,0 +1,117 @@
1
+ from typing import Any
2
+
3
+ from pydantic import Field
4
+
5
+ from agentex.lib.utils.model_utils import BaseModel
6
+
7
+
8
+ class ImageConfig(BaseModel):
9
+ """Configuration for container images"""
10
+
11
+ repository: str = Field(..., description="Container image repository URL")
12
+ tag: str = Field(default="latest", description="Container image tag")
13
+
14
+
15
+ class ImagePullSecretConfig(BaseModel):
16
+ """Configuration for image pull secrets"""
17
+
18
+ name: str = Field(..., description="Name of the image pull secret")
19
+
20
+
21
+ class ResourceRequirements(BaseModel):
22
+ """Resource requirements for containers"""
23
+
24
+ cpu: str = Field(
25
+ default="500m", description="CPU request/limit (e.g., '500m', '1')"
26
+ )
27
+ memory: str = Field(
28
+ default="1Gi", description="Memory request/limit (e.g., '1Gi', '512Mi')"
29
+ )
30
+
31
+
32
+ class ResourceConfig(BaseModel):
33
+ """Resource configuration for containers"""
34
+
35
+ requests: ResourceRequirements = Field(
36
+ default_factory=ResourceRequirements, description="Resource requests"
37
+ )
38
+ limits: ResourceRequirements = Field(
39
+ default_factory=ResourceRequirements, description="Resource limits"
40
+ )
41
+
42
+
43
+ class GlobalDeploymentConfig(BaseModel):
44
+ """Global deployment configuration that applies to all clusters"""
45
+
46
+ agent: dict[str, str] = Field(
47
+ default_factory=dict, description="Agent metadata (name, description)"
48
+ )
49
+ replicaCount: int = Field(default=1, description="Number of replicas to deploy")
50
+ resources: ResourceConfig = Field(
51
+ default_factory=ResourceConfig, description="Resource requirements"
52
+ )
53
+
54
+
55
+ class DeploymentConfig(BaseModel):
56
+ """Main deployment configuration in the manifest"""
57
+
58
+ image: ImageConfig = Field(..., description="Container image configuration")
59
+ imagePullSecrets: list[ImagePullSecretConfig] | None = Field(
60
+ default=None, description="Image pull secrets to use for the deployment"
61
+ )
62
+ global_config: GlobalDeploymentConfig = Field(
63
+ default_factory=GlobalDeploymentConfig,
64
+ description="Global deployment settings",
65
+ alias="global",
66
+ )
67
+
68
+ class Config:
69
+ validate_by_name = True
70
+
71
+
72
+ class ClusterConfig(BaseModel):
73
+ """Per-cluster deployment overrides"""
74
+
75
+ image: ImageConfig | None = Field(
76
+ default=None, description="Cluster-specific image overrides"
77
+ )
78
+ replicaCount: int | None = Field(
79
+ default=None, description="Cluster-specific replica count"
80
+ )
81
+ resources: ResourceConfig | None = Field(
82
+ default=None, description="Cluster-specific resource overrides"
83
+ )
84
+ env: list[dict[str, str]] | None = Field(
85
+ default=None, description="Additional environment variables for this cluster"
86
+ )
87
+ # Allow additional arbitrary overrides for advanced users
88
+ additional_overrides: dict[str, Any] | None = Field(
89
+ default=None, description="Additional helm chart value overrides"
90
+ )
91
+
92
+
93
+ class InjectedImagePullSecretValues(BaseModel):
94
+ """Values for image pull secrets"""
95
+
96
+ registry: str = Field(..., description="Registry of the image pull secret")
97
+ username: str = Field(..., description="Username of the image pull secret")
98
+ password: str = Field(..., description="Password of the image pull secret")
99
+ email: str | None = Field(
100
+ default=None, description="Email of the image pull secret"
101
+ )
102
+
103
+
104
+ class InjectedSecretsValues(BaseModel):
105
+ """Values for injected secrets"""
106
+
107
+ # Defined as a dictionary because the names need to be unique
108
+ credentials: dict[str, Any] = Field(
109
+ default_factory=dict, description="Secrets to inject into the deployment"
110
+ )
111
+ imagePullSecrets: dict[str, InjectedImagePullSecretValues] = Field(
112
+ default_factory=dict,
113
+ description="Image pull secrets to inject into the deployment",
114
+ )
115
+
116
+ class Config:
117
+ validate_by_name = True
@@ -0,0 +1,56 @@
1
+ from pathlib import Path
2
+
3
+ from pydantic import Field, validator
4
+
5
+ from agentex.lib.utils.model_utils import BaseModel
6
+
7
+
8
+ class LocalAgentConfig(BaseModel):
9
+ """Configuration for local agent development"""
10
+
11
+ port: int = Field(
12
+ ...,
13
+ description="The port where the agent's ACP server is running locally",
14
+ gt=0,
15
+ lt=65536,
16
+ )
17
+ host_address: str = Field(
18
+ default="host.docker.internal",
19
+ description="The host address where the agent's ACP server can be reached (e.g., host.docker.internal for Docker, localhost for direct)",
20
+ )
21
+
22
+
23
+ class LocalPathsConfig(BaseModel):
24
+ """Configuration for local file paths"""
25
+
26
+ acp: str = Field(
27
+ default="project/acp.py",
28
+ description="Path to the ACP server file. Can be relative to manifest directory or absolute.",
29
+ )
30
+ worker: str | None = Field(
31
+ default=None,
32
+ description="Path to the temporal worker file. Can be relative to manifest directory or absolute. (only for temporal agents)",
33
+ )
34
+
35
+ @validator("acp", "worker")
36
+ def validate_path_format(cls, v):
37
+ """Validate that the path is a reasonable format"""
38
+ if v is None:
39
+ return v
40
+
41
+ # Convert to Path to validate format
42
+ try:
43
+ Path(v)
44
+ except Exception as e:
45
+ raise ValueError(f"Invalid path format: {v}") from e
46
+
47
+ return v
48
+
49
+
50
+ class LocalDevelopmentConfig(BaseModel):
51
+ """Configuration for local development environment"""
52
+
53
+ agent: LocalAgentConfig = Field(..., description="Local agent configuration")
54
+ paths: LocalPathsConfig | None = Field(
55
+ default=None, description="File paths for local development"
56
+ )
@@ -0,0 +1,103 @@
1
+ import os
2
+ import re
3
+ from pathlib import Path
4
+ from typing import Any, TypeVar
5
+
6
+ import yaml
7
+ from jinja2 import BaseLoader, Environment, StrictUndefined, TemplateError
8
+
9
+ T = TypeVar("T")
10
+
11
+
12
+ class ConfigResolutionError(Exception):
13
+ def __init__(self, message: str) -> None:
14
+ super().__init__(message)
15
+ self.status_code = 400
16
+
17
+
18
+ def _preprocess_template(template_str: str) -> str:
19
+ # Replace $env. and $variables. with unique internal names
20
+ return template_str.replace("{{ $env.", "{{ __special_env__.").replace(
21
+ "{{ $variables.", "{{ __special_variables__."
22
+ )
23
+
24
+
25
+ def _extract_variables_section(raw_config_str: str) -> str:
26
+ # Use regex to extract the variables: ... block (YAML top-level)
27
+ match = re.search(
28
+ r"(^variables:.*?)(^config:|\Z)", raw_config_str, re.DOTALL | re.MULTILINE
29
+ )
30
+ if not match:
31
+ return ""
32
+ return match.group(1)
33
+
34
+
35
+ def ProjectConfigLoader(
36
+ config_path: str, model: type[T] | None = None, env_path: str | None = None
37
+ ) -> dict[str, Any] | T:
38
+ config_path = Path(config_path)
39
+ env_path = Path(env_path) if env_path else config_path.parent / ".env"
40
+ env = _load_env(env_path)
41
+ raw_config_str = _load_file_as_str(config_path)
42
+ raw_config_str = _preprocess_template(raw_config_str)
43
+
44
+ # Extract and render only the variables section
45
+ variables_section_str = _extract_variables_section(raw_config_str)
46
+ env_context = {"__special_env__": env, "__special_variables__": {}}
47
+ try:
48
+ env_only_template = Environment(
49
+ loader=BaseLoader(),
50
+ undefined=StrictUndefined,
51
+ keep_trailing_newline=True,
52
+ autoescape=False,
53
+ ).from_string(variables_section_str)
54
+ rendered_variables_yaml = env_only_template.render(**env_context)
55
+ variables_dict = yaml.safe_load(rendered_variables_yaml).get("variables", {})
56
+ except Exception as e:
57
+ raise ConfigResolutionError(f"Error rendering variables with $env: {e}") from e
58
+ # Second pass: render the whole config with both __special_env__ and resolved __special_variables__
59
+ full_context = {"__special_env__": env, "__special_variables__": variables_dict}
60
+ rendered_config_str = _jinja_render(raw_config_str, full_context)
61
+ try:
62
+ rendered_config = yaml.safe_load(rendered_config_str)
63
+ except Exception as e:
64
+ raise ConfigResolutionError(f"Error loading rendered YAML: {e}") from e
65
+ if "config" not in rendered_config:
66
+ raise ConfigResolutionError("Missing 'config' section in config file.")
67
+ config_section = rendered_config["config"]
68
+ if model is not None:
69
+ return model(**config_section)
70
+ return config_section
71
+
72
+
73
+ def _load_env(env_path: Path) -> dict[str, str]:
74
+ env = dict(os.environ)
75
+ if env_path.exists():
76
+ with open(env_path) as f:
77
+ for line in f:
78
+ line = line.strip()
79
+ if not line or line.startswith("#"):
80
+ continue
81
+ if "=" in line:
82
+ k, v = line.split("=", 1)
83
+ env[k.strip()] = v.strip()
84
+ return env
85
+
86
+
87
+ def _load_file_as_str(path: Path) -> str:
88
+ with open(path) as f:
89
+ return f.read()
90
+
91
+
92
+ def _jinja_render(template_str: str, context: dict) -> str:
93
+ try:
94
+ env = Environment(
95
+ loader=BaseLoader(),
96
+ undefined=StrictUndefined,
97
+ keep_trailing_newline=True,
98
+ autoescape=False,
99
+ )
100
+ template = env.from_string(template_str)
101
+ return template.render(**context)
102
+ except TemplateError as e:
103
+ raise ConfigResolutionError(f"Jinja template error: {e}") from e
@@ -0,0 +1,3 @@
1
+ from agentex.lib.sdk.fastacp.fastacp import FastACP
2
+
3
+ __all__ = ["FastACP"]