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,85 @@
1
+ from agentex import AsyncAgentex
2
+ from agentex.lib.core.tracing.tracer import AsyncTracer
3
+ from agentex.lib.utils.logging import make_logger
4
+ from agentex.types.agent_task_tracker import AgentTaskTracker
5
+
6
+ logger = make_logger(__name__)
7
+
8
+
9
+ class AgentTaskTrackerService:
10
+ def __init__(
11
+ self, agentex_client: AsyncAgentex, tracer: AsyncTracer,
12
+ ):
13
+ self._agentex_client = agentex_client
14
+ self._tracer = tracer
15
+
16
+ async def get_agent_task_tracker(
17
+ self,
18
+ tracker_id: str,
19
+ trace_id: str | None = None,
20
+ parent_span_id: str | None = None,
21
+ ) -> AgentTaskTracker:
22
+ trace = self._tracer.trace(trace_id)
23
+ async with trace.span(
24
+ parent_id=parent_span_id,
25
+ name="get_agent_task_tracker",
26
+ input={"tracker_id": tracker_id},
27
+ ) as span:
28
+ tracker = await self._agentex_client.tracker.retrieve(
29
+ tracker_id
30
+ )
31
+ if span:
32
+ span.output = tracker.model_dump()
33
+ return tracker
34
+
35
+ async def get_by_task_and_agent(
36
+ self,
37
+ task_id: str,
38
+ agent_id: str,
39
+ trace_id: str | None = None,
40
+ parent_span_id: str | None = None,
41
+ ) -> AgentTaskTracker | None:
42
+ trace = self._tracer.trace(trace_id)
43
+ async with trace.span(
44
+ parent_id=parent_span_id,
45
+ name="get_by_task_and_agent",
46
+ input={"task_id": task_id, "agent_id": agent_id},
47
+ ) as span:
48
+ trackers = await self._agentex_client.tracker.list(
49
+ task_id=task_id,
50
+ agent_id=agent_id,
51
+ )
52
+ tracker = trackers[0] if trackers else None
53
+ if span:
54
+ span.output = tracker.model_dump() if tracker else None
55
+ return tracker
56
+
57
+ async def update_agent_task_tracker(
58
+ self,
59
+ tracker_id: str,
60
+ last_processed_event_id: str | None = None,
61
+ status: str | None = None,
62
+ status_reason: str | None = None,
63
+ trace_id: str | None = None,
64
+ parent_span_id: str | None = None,
65
+ ) -> AgentTaskTracker:
66
+ trace = self._tracer.trace(trace_id)
67
+ async with trace.span(
68
+ parent_id=parent_span_id,
69
+ name="update_agent_task_tracker",
70
+ input={
71
+ "tracker_id": tracker_id,
72
+ "last_processed_event_id": last_processed_event_id,
73
+ "status": status,
74
+ "status_reason": status_reason,
75
+ },
76
+ ) as span:
77
+ tracker = await self._agentex_client.tracker.update(
78
+ tracker_id=tracker_id,
79
+ last_processed_event_id=last_processed_event_id,
80
+ status=status,
81
+ status_reason=status_reason,
82
+ )
83
+ if span:
84
+ span.output = tracker.model_dump()
85
+ return tracker
@@ -0,0 +1,43 @@
1
+ from typing import Optional
2
+
3
+ from agentex import AsyncAgentex
4
+ from agentex.lib.core.tracing.tracer import AsyncTracer
5
+ from agentex.types.agent import Agent
6
+ from agentex.lib.utils.logging import make_logger
7
+ from agentex.lib.utils.temporal import heartbeat_if_in_workflow
8
+
9
+ logger = make_logger(__name__)
10
+
11
+
12
+ class AgentsService:
13
+ def __init__(
14
+ self,
15
+ agentex_client: AsyncAgentex,
16
+ tracer: AsyncTracer,
17
+ ):
18
+ self._agentex_client = agentex_client
19
+ self._tracer = tracer
20
+
21
+ async def get_agent(
22
+ self,
23
+ agent_id: Optional[str] = None,
24
+ agent_name: Optional[str] = None,
25
+ trace_id: Optional[str] = None,
26
+ parent_span_id: Optional[str] = None,
27
+ ) -> Agent:
28
+ trace = self._tracer.trace(trace_id)
29
+ async with trace.span(
30
+ parent_id=parent_span_id,
31
+ name="get_agent",
32
+ input={"agent_id": agent_id, "agent_name": agent_name},
33
+ ) as span:
34
+ heartbeat_if_in_workflow("get agent")
35
+ if agent_id:
36
+ agent = await self._agentex_client.agents.retrieve(agent_id=agent_id)
37
+ elif agent_name:
38
+ agent = await self._agentex_client.agents.retrieve_by_name(agent_name=agent_name)
39
+ else:
40
+ raise ValueError("Either agent_id or agent_name must be provided")
41
+ if span:
42
+ span.output = agent.model_dump()
43
+ return agent
@@ -0,0 +1,61 @@
1
+ from agentex import AsyncAgentex
2
+ from agentex.lib.core.tracing.tracer import AsyncTracer
3
+ from agentex.types.event import Event
4
+ from agentex.lib.utils.logging import make_logger
5
+
6
+ logger = make_logger(__name__)
7
+
8
+
9
+ class EventsService:
10
+ def __init__(
11
+ self, agentex_client: AsyncAgentex, tracer: AsyncTracer
12
+ ):
13
+ self._agentex_client = agentex_client
14
+ self._tracer = tracer
15
+
16
+ async def get_event(
17
+ self,
18
+ event_id: str,
19
+ trace_id: str | None = None,
20
+ parent_span_id: str | None = None,
21
+ ) -> Event | None:
22
+ trace = self._tracer.trace(trace_id)
23
+ async with trace.span(
24
+ parent_id=parent_span_id,
25
+ name="get_event",
26
+ input={"event_id": event_id},
27
+ ) as span:
28
+ event = await self._agentex_client.events.retrieve(event_id=event_id)
29
+ if span:
30
+ span.output = event.model_dump()
31
+ return event
32
+
33
+ async def list_events(
34
+ self,
35
+ task_id: str,
36
+ agent_id: str,
37
+ last_processed_event_id: str | None = None,
38
+ limit: int | None = None,
39
+ trace_id: str | None = None,
40
+ parent_span_id: str | None = None,
41
+ ) -> list[Event]:
42
+ trace = self._tracer.trace(trace_id)
43
+ async with trace.span(
44
+ parent_id=parent_span_id,
45
+ name="list_events",
46
+ input={
47
+ "task_id": task_id,
48
+ "agent_id": agent_id,
49
+ "last_processed_event_id": last_processed_event_id,
50
+ "limit": limit,
51
+ },
52
+ ) as span:
53
+ events = await self._agentex_client.events.list(
54
+ task_id=task_id,
55
+ agent_id=agent_id,
56
+ last_processed_event_id=last_processed_event_id,
57
+ limit=limit,
58
+ )
59
+ if span:
60
+ span.output = [event.model_dump() for event in events]
61
+ return events
@@ -0,0 +1,164 @@
1
+ import asyncio
2
+ from typing import Any, Coroutine, cast
3
+
4
+ from agentex import AsyncAgentex
5
+ from agentex.lib.core.services.adk.streaming import StreamingService
6
+ from agentex.lib.core.tracing.tracer import AsyncTracer
7
+ from agentex.lib.types.task_message_updates import StreamTaskMessageFull, TaskMessageUpdate
8
+ from agentex.types.task_message import TaskMessage, TaskMessageContent
9
+ from agentex.lib.utils.logging import make_logger
10
+ from agentex.lib.utils.temporal import heartbeat_if_in_workflow
11
+ from agentex.types.task_message_content_param import TaskMessageContentParam
12
+
13
+ logger = make_logger(__name__)
14
+
15
+
16
+ class MessagesService:
17
+ def __init__(
18
+ self,
19
+ agentex_client: AsyncAgentex,
20
+ streaming_service: StreamingService,
21
+ tracer: AsyncTracer,
22
+ ):
23
+ self._agentex_client = agentex_client
24
+ self._streaming_service = streaming_service
25
+ self._tracer = tracer
26
+
27
+ async def create_message(
28
+ self,
29
+ task_id: str,
30
+ content: TaskMessageContent,
31
+ emit_updates: bool = True,
32
+ trace_id: str | None = None,
33
+ parent_span_id: str | None = None,
34
+ ) -> TaskMessage:
35
+ trace = self._tracer.trace(trace_id)
36
+ async with trace.span(
37
+ parent_id=parent_span_id,
38
+ name="create_message",
39
+ input={"task_id": task_id, "message": content},
40
+ ) as span:
41
+ heartbeat_if_in_workflow("create message")
42
+ task_message = await self._agentex_client.messages.create(
43
+ task_id=task_id,
44
+ content=content.model_dump(),
45
+ )
46
+ if emit_updates:
47
+ await self._emit_updates([task_message])
48
+ if span:
49
+ span.output = task_message.model_dump()
50
+ return task_message
51
+
52
+ async def update_message(
53
+ self,
54
+ task_id: str,
55
+ message_id: str,
56
+ content: TaskMessageContent,
57
+ trace_id: str | None = None,
58
+ parent_span_id: str | None = None,
59
+ ) -> TaskMessage:
60
+ trace = self._tracer.trace(trace_id)
61
+ async with trace.span(
62
+ parent_id=parent_span_id,
63
+ name="update_message",
64
+ input={
65
+ "task_id": task_id,
66
+ "message_id": message_id,
67
+ "message": content,
68
+ },
69
+ ) as span:
70
+ heartbeat_if_in_workflow("update message")
71
+ task_message = await self._agentex_client.messages.update(
72
+ task_id=task_id,
73
+ message_id=message_id,
74
+ content=content.model_dump(),
75
+ )
76
+ if span:
77
+ span.output = task_message.model_dump()
78
+ return task_message
79
+
80
+ async def create_messages_batch(
81
+ self,
82
+ task_id: str,
83
+ contents: list[TaskMessageContent],
84
+ emit_updates: bool = True,
85
+ trace_id: str | None = None,
86
+ parent_span_id: str | None = None,
87
+ ) -> list[TaskMessage]:
88
+ trace = self._tracer.trace(trace_id)
89
+ async with trace.span(
90
+ parent_id=parent_span_id,
91
+ name="create_messages_batch",
92
+ input={"task_id": task_id, "messages": contents},
93
+ ) as span:
94
+ heartbeat_if_in_workflow("create messages batch")
95
+ task_messages = await self._agentex_client.messages.batch.create(
96
+ task_id=task_id,
97
+ contents=[content.model_dump() for content in contents],
98
+ )
99
+ if emit_updates:
100
+ await self._emit_updates(task_messages)
101
+ if span:
102
+ span.output = [task_message.model_dump() for task_message in task_messages]
103
+ return task_messages
104
+
105
+ async def update_messages_batch(
106
+ self,
107
+ task_id: str,
108
+ updates: dict[str, TaskMessageContent],
109
+ trace_id: str | None = None,
110
+ parent_span_id: str | None = None,
111
+ ) -> list[TaskMessage]:
112
+ trace = self._tracer.trace(trace_id)
113
+ async with trace.span(
114
+ parent_id=parent_span_id,
115
+ name="update_messages_batch",
116
+ input={"task_id": task_id, "updates": updates},
117
+ ) as span:
118
+ heartbeat_if_in_workflow("update messages batch")
119
+ task_messages = await self._agentex_client.messages.batch.update(
120
+ task_id=task_id,
121
+ updates={
122
+ message_id: content.model_dump()
123
+ for message_id, content in updates.items()
124
+ },
125
+ )
126
+ if span:
127
+ span.output = [task_message.model_dump() for task_message in task_messages]
128
+ return task_messages
129
+
130
+ async def list_messages(
131
+ self,
132
+ task_id: str,
133
+ limit: int | None = None,
134
+ trace_id: str | None = None,
135
+ parent_span_id: str | None = None,
136
+ ) -> list[TaskMessage]:
137
+ trace = self._tracer.trace(trace_id)
138
+ async with trace.span(
139
+ parent_id=parent_span_id,
140
+ name="list_messages",
141
+ input={"task_id": task_id, "limit": limit},
142
+ ) as span:
143
+ heartbeat_if_in_workflow("list messages")
144
+ task_messages = await self._agentex_client.messages.list(
145
+ task_id=task_id,
146
+ limit=limit,
147
+ )
148
+ if span:
149
+ span.output = [task_message.model_dump() for task_message in task_messages]
150
+ return task_messages
151
+
152
+ async def _emit_updates(self, task_messages: list[TaskMessage]) -> None:
153
+ stream_update_handlers: list[Coroutine[Any, Any, TaskMessageUpdate | None]] = []
154
+ for task_message in task_messages:
155
+ stream_update_handler = self._streaming_service.stream_update(
156
+ update=StreamTaskMessageFull(
157
+ type="full",
158
+ parent_task_message=task_message,
159
+ content=task_message.content,
160
+ )
161
+ )
162
+ stream_update_handlers.append(stream_update_handler)
163
+
164
+ await asyncio.gather(*stream_update_handlers)
File without changes
@@ -0,0 +1,256 @@
1
+ from collections.abc import AsyncGenerator
2
+
3
+ from agentex import AsyncAgentex
4
+ from agentex.lib.core.adapters.llm.adapter_litellm import LiteLLMGateway
5
+ from agentex.lib.core.services.adk.streaming import StreamingService
6
+ from agentex.lib.core.tracing.tracer import AsyncTracer
7
+ from agentex.lib.types.llm_messages import (
8
+ Completion,
9
+ LLMConfig,
10
+ )
11
+ from agentex.lib.types.task_message_updates import (
12
+ StreamTaskMessageDelta,
13
+ StreamTaskMessageFull,
14
+ TextDelta,
15
+ )
16
+ from agentex.types.task_message import TaskMessage
17
+ from agentex.types.task_message_content import TextContent
18
+ from agentex.lib.utils import logging
19
+ from agentex.lib.utils.completions import concat_completion_chunks
20
+ from agentex.lib.utils.temporal import heartbeat_if_in_workflow
21
+
22
+ logger = logging.make_logger(__name__)
23
+
24
+
25
+ class LiteLLMService:
26
+ def __init__(
27
+ self,
28
+ agentex_client: AsyncAgentex,
29
+ streaming_service: StreamingService,
30
+ tracer: AsyncTracer,
31
+ llm_gateway: LiteLLMGateway | None = None,
32
+ ):
33
+ self.agentex_client = agentex_client
34
+ self.llm_gateway = llm_gateway
35
+ self.streaming_service = streaming_service
36
+ self.tracer = tracer
37
+
38
+ async def chat_completion(
39
+ self,
40
+ llm_config: LLMConfig,
41
+ trace_id: str | None = None,
42
+ parent_span_id: str | None = None,
43
+ ) -> Completion:
44
+ trace = self.tracer.trace(trace_id)
45
+ async with trace.span(
46
+ parent_id=parent_span_id,
47
+ name="chat_completion",
48
+ input=llm_config.model_dump(),
49
+ ) as span:
50
+ heartbeat_if_in_workflow("chat completion")
51
+ if self.llm_gateway is None:
52
+ raise ValueError("LLM Gateway is not set")
53
+ completion = await self.llm_gateway.acompletion(**llm_config.model_dump())
54
+ if span:
55
+ span.output = completion.model_dump()
56
+ return completion
57
+
58
+ async def chat_completion_auto_send(
59
+ self,
60
+ task_id: str,
61
+ llm_config: LLMConfig,
62
+ trace_id: str | None = None,
63
+ parent_span_id: str | None = None,
64
+ ) -> TaskMessage | None:
65
+ """
66
+ Chat completion with automatic TaskMessage creation. This does not stream the completion. To stream use chat_completion_stream_auto_send.
67
+
68
+ Args:
69
+ task_id (str): The ID of the task to run the agent for.
70
+ llm_config (LLMConfig): The configuration for the LLM (must have stream=True).
71
+
72
+ Returns:
73
+ TaskMessage: A TaskMessage object
74
+ """
75
+
76
+ if llm_config.stream:
77
+ raise ValueError(
78
+ "LLM config must not have stream=True. To stream use `chat_completion_stream` or `chat_completion_stream_auto_send`."
79
+ )
80
+
81
+ if self.llm_gateway is None:
82
+ raise ValueError("LLM Gateway is not set")
83
+
84
+ trace = self.tracer.trace(trace_id)
85
+ async with trace.span(
86
+ parent_id=parent_span_id,
87
+ name="chat_completion_auto_send",
88
+ input=llm_config.model_dump(),
89
+ ) as span:
90
+ heartbeat_if_in_workflow("chat completion auto send")
91
+
92
+ async with self.streaming_service.streaming_task_message_context(
93
+ task_id=task_id,
94
+ initial_content=TextContent(
95
+ author="agent",
96
+ content="",
97
+ format="markdown",
98
+ ),
99
+ ) as streaming_context:
100
+ completion = await self.llm_gateway.acompletion(**llm_config.model_dump())
101
+ if (
102
+ completion.choices
103
+ and len(completion.choices) > 0
104
+ and completion.choices[0].message
105
+ ):
106
+ final_content = TextContent(
107
+ author="agent",
108
+ content=completion.choices[0].message.content or "",
109
+ format="markdown",
110
+ )
111
+ await streaming_context.stream_update(
112
+ update=StreamTaskMessageFull(
113
+ parent_task_message=streaming_context.task_message,
114
+ content=final_content,
115
+ ),
116
+ )
117
+ else:
118
+ raise ValueError("No completion message returned from LLM")
119
+
120
+ if span:
121
+ if streaming_context.task_message:
122
+ span.output = streaming_context.task_message.model_dump()
123
+ return streaming_context.task_message if streaming_context.task_message else None
124
+
125
+ async def chat_completion_stream(
126
+ self,
127
+ llm_config: LLMConfig,
128
+ trace_id: str | None = None,
129
+ parent_span_id: str | None = None,
130
+ ) -> AsyncGenerator[Completion, None]:
131
+ """
132
+ Stream chat completion chunks using LiteLLM.
133
+
134
+ Args:
135
+ llm_config (LLMConfig): The configuration for the LLM (must have stream=True).
136
+ trace_id (Optional[str]): The trace ID for tracing.
137
+ parent_span_id (Optional[str]): The parent span ID for tracing.
138
+
139
+ Returns:
140
+ AsyncGenerator[Completion, None]: Generator yielding completion chunks
141
+
142
+ Raises:
143
+ ValueError: If called from within a Temporal workflow or if stream=False
144
+ """
145
+ if not llm_config.stream:
146
+ raise ValueError("LLM config must have stream=True for streaming")
147
+
148
+ if self.llm_gateway is None:
149
+ raise ValueError("LLM Gateway is not set")
150
+
151
+ trace = self.tracer.trace(trace_id)
152
+ async with trace.span(
153
+ parent_id=parent_span_id,
154
+ name="chat_completion_stream",
155
+ input=llm_config.model_dump(),
156
+ ) as span:
157
+ # Direct streaming outside temporal - yield each chunk as it comes
158
+ chunks: list[Completion] = []
159
+ async for chunk in self.llm_gateway.acompletion_stream(
160
+ **llm_config.model_dump()
161
+ ):
162
+ chunks.append(chunk)
163
+ yield chunk
164
+ if span:
165
+ span.output = concat_completion_chunks(chunks).model_dump()
166
+
167
+ async def chat_completion_stream_auto_send(
168
+ self,
169
+ task_id: str,
170
+ llm_config: LLMConfig,
171
+ trace_id: str | None = None,
172
+ parent_span_id: str | None = None,
173
+ ) -> TaskMessage | None:
174
+ """
175
+ Stream chat completion with automatic TaskMessage creation and streaming.
176
+
177
+ Args:
178
+ task_id (str): The ID of the task to run the agent for.
179
+ llm_config (LLMConfig): The configuration for the LLM (must have stream=True).
180
+
181
+ Returns:
182
+ TaskMessage: A TaskMessage object
183
+ """
184
+ heartbeat_if_in_workflow("chat completion stream")
185
+
186
+ if self.llm_gateway is None:
187
+ raise ValueError("LLM Gateway is not set")
188
+
189
+ if not llm_config.stream:
190
+ llm_config.stream = True
191
+
192
+ trace = self.tracer.trace(trace_id)
193
+ async with trace.span(
194
+ parent_id=parent_span_id,
195
+ name="chat_completion_stream",
196
+ input=llm_config.model_dump(),
197
+ ) as span:
198
+ # Use streaming context manager
199
+ async with self.streaming_service.streaming_task_message_context(
200
+ task_id=task_id,
201
+ initial_content=TextContent(
202
+ author="agent",
203
+ content="",
204
+ format="markdown",
205
+ ),
206
+ ) as streaming_context:
207
+ # Get the streaming response
208
+ chunks = []
209
+ async for response in self.llm_gateway.acompletion_stream(
210
+ **llm_config.model_dump()
211
+ ):
212
+ heartbeat_if_in_workflow("chat completion streaming")
213
+ if (
214
+ response.choices
215
+ and len(response.choices) > 0
216
+ and response.choices[0].delta
217
+ ):
218
+ delta = response.choices[0].delta.content
219
+ if delta:
220
+ # Stream the chunk via the context manager
221
+ await streaming_context.stream_update(
222
+ update=StreamTaskMessageDelta(
223
+ parent_task_message=streaming_context.task_message,
224
+ delta=TextDelta(text_delta=delta),
225
+ ),
226
+ )
227
+ heartbeat_if_in_workflow("content chunk streamed")
228
+
229
+ # Store the chunk for final message assembly
230
+ chunks.append(response)
231
+
232
+ # Update the final message content
233
+ complete_message = concat_completion_chunks(chunks)
234
+ if (
235
+ complete_message
236
+ and complete_message.choices
237
+ and complete_message.choices[0].message
238
+ ):
239
+ final_content = TextContent(
240
+ author="agent",
241
+ content=complete_message.choices[0].message.content or "",
242
+ )
243
+ await streaming_context.stream_update(
244
+ update=StreamTaskMessageFull(
245
+ parent_task_message=streaming_context.task_message,
246
+ content=final_content,
247
+ ),
248
+ )
249
+
250
+ heartbeat_if_in_workflow("chat completion stream complete")
251
+
252
+ if span:
253
+ if streaming_context.task_message:
254
+ span.output = streaming_context.task_message.model_dump()
255
+
256
+ return streaming_context.task_message if streaming_context.task_message else None