agentex-sdk 0.4.10__py3-none-any.whl → 0.4.12__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- agentex/_base_client.py +3 -3
- agentex/_compat.py +48 -48
- agentex/_models.py +41 -41
- agentex/_types.py +35 -1
- agentex/_utils/__init__.py +9 -2
- agentex/_utils/_compat.py +45 -0
- agentex/_utils/_datetime_parse.py +136 -0
- agentex/_utils/_transform.py +11 -1
- agentex/_utils/_typing.py +6 -1
- agentex/_utils/_utils.py +0 -1
- agentex/_version.py +1 -1
- agentex/lib/adk/_modules/acp.py +15 -3
- agentex/lib/adk/providers/_modules/openai.py +57 -0
- agentex/lib/cli/handlers/deploy_handlers.py +4 -1
- agentex/lib/cli/templates/temporal/README.md.j2 +18 -2
- agentex/lib/cli/templates/temporal/environments.yaml.j2 +1 -1
- agentex/lib/cli/templates/temporal/project/activities.py.j2 +77 -0
- agentex/lib/cli/templates/temporal/project/run_worker.py.j2 +3 -1
- agentex/lib/core/services/adk/acp/acp.py +27 -12
- agentex/lib/core/services/adk/providers/openai.py +272 -29
- agentex/lib/core/temporal/activities/adk/acp/acp_activities.py +6 -0
- agentex/lib/core/temporal/activities/adk/providers/openai_activities.py +451 -69
- agentex/types/reasoning_content_param.py +4 -3
- {agentex_sdk-0.4.10.dist-info → agentex_sdk-0.4.12.dist-info}/METADATA +1 -1
- {agentex_sdk-0.4.10.dist-info → agentex_sdk-0.4.12.dist-info}/RECORD +28 -25
- {agentex_sdk-0.4.10.dist-info → agentex_sdk-0.4.12.dist-info}/WHEEL +0 -0
- {agentex_sdk-0.4.10.dist-info → agentex_sdk-0.4.12.dist-info}/entry_points.txt +0 -0
- {agentex_sdk-0.4.10.dist-info → agentex_sdk-0.4.12.dist-info}/licenses/LICENSE +0 -0
agentex/_utils/_transform.py
CHANGED
@@ -16,18 +16,20 @@ from ._utils import (
|
|
16
16
|
lru_cache,
|
17
17
|
is_mapping,
|
18
18
|
is_iterable,
|
19
|
+
is_sequence,
|
19
20
|
)
|
20
21
|
from .._files import is_base64_file_input
|
22
|
+
from ._compat import get_origin, is_typeddict
|
21
23
|
from ._typing import (
|
22
24
|
is_list_type,
|
23
25
|
is_union_type,
|
24
26
|
extract_type_arg,
|
25
27
|
is_iterable_type,
|
26
28
|
is_required_type,
|
29
|
+
is_sequence_type,
|
27
30
|
is_annotated_type,
|
28
31
|
strip_annotated_type,
|
29
32
|
)
|
30
|
-
from .._compat import get_origin, model_dump, is_typeddict
|
31
33
|
|
32
34
|
_T = TypeVar("_T")
|
33
35
|
|
@@ -167,6 +169,8 @@ def _transform_recursive(
|
|
167
169
|
|
168
170
|
Defaults to the same value as the `annotation` argument.
|
169
171
|
"""
|
172
|
+
from .._compat import model_dump
|
173
|
+
|
170
174
|
if inner_type is None:
|
171
175
|
inner_type = annotation
|
172
176
|
|
@@ -184,6 +188,8 @@ def _transform_recursive(
|
|
184
188
|
(is_list_type(stripped_type) and is_list(data))
|
185
189
|
# Iterable[T]
|
186
190
|
or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
|
191
|
+
# Sequence[T]
|
192
|
+
or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
|
187
193
|
):
|
188
194
|
# dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
|
189
195
|
# intended as an iterable, so we don't transform it.
|
@@ -329,6 +335,8 @@ async def _async_transform_recursive(
|
|
329
335
|
|
330
336
|
Defaults to the same value as the `annotation` argument.
|
331
337
|
"""
|
338
|
+
from .._compat import model_dump
|
339
|
+
|
332
340
|
if inner_type is None:
|
333
341
|
inner_type = annotation
|
334
342
|
|
@@ -346,6 +354,8 @@ async def _async_transform_recursive(
|
|
346
354
|
(is_list_type(stripped_type) and is_list(data))
|
347
355
|
# Iterable[T]
|
348
356
|
or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
|
357
|
+
# Sequence[T]
|
358
|
+
or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
|
349
359
|
):
|
350
360
|
# dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
|
351
361
|
# intended as an iterable, so we don't transform it.
|
agentex/_utils/_typing.py
CHANGED
@@ -15,7 +15,7 @@ from typing_extensions import (
|
|
15
15
|
|
16
16
|
from ._utils import lru_cache
|
17
17
|
from .._types import InheritsGeneric
|
18
|
-
from
|
18
|
+
from ._compat import is_union as _is_union
|
19
19
|
|
20
20
|
|
21
21
|
def is_annotated_type(typ: type) -> bool:
|
@@ -26,6 +26,11 @@ def is_list_type(typ: type) -> bool:
|
|
26
26
|
return (get_origin(typ) or typ) == list
|
27
27
|
|
28
28
|
|
29
|
+
def is_sequence_type(typ: type) -> bool:
|
30
|
+
origin = get_origin(typ) or typ
|
31
|
+
return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence
|
32
|
+
|
33
|
+
|
29
34
|
def is_iterable_type(typ: type) -> bool:
|
30
35
|
"""If the given type is `typing.Iterable[T]`"""
|
31
36
|
origin = get_origin(typ) or typ
|
agentex/_utils/_utils.py
CHANGED
@@ -22,7 +22,6 @@ from typing_extensions import TypeGuard
|
|
22
22
|
import sniffio
|
23
23
|
|
24
24
|
from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike
|
25
|
-
from .._compat import parse_date as parse_date, parse_datetime as parse_datetime
|
26
25
|
|
27
26
|
_T = TypeVar("_T")
|
28
27
|
_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...])
|
agentex/_version.py
CHANGED
agentex/lib/adk/_modules/acp.py
CHANGED
@@ -205,6 +205,8 @@ class ACPModule:
|
|
205
205
|
self,
|
206
206
|
task_id: str | None = None,
|
207
207
|
task_name: str | None = None,
|
208
|
+
agent_id: str | None = None,
|
209
|
+
agent_name: str | None = None,
|
208
210
|
trace_id: str | None = None,
|
209
211
|
parent_span_id: str | None = None,
|
210
212
|
start_to_close_timeout: timedelta = timedelta(seconds=5),
|
@@ -212,11 +214,13 @@ class ACPModule:
|
|
212
214
|
retry_policy: RetryPolicy = DEFAULT_RETRY_POLICY,
|
213
215
|
) -> Task:
|
214
216
|
"""
|
215
|
-
Cancel a task.
|
217
|
+
Cancel a task by sending cancel request to the agent that owns the task.
|
216
218
|
|
217
219
|
Args:
|
218
|
-
task_id:
|
219
|
-
task_name:
|
220
|
+
task_id: ID of the task to cancel.
|
221
|
+
task_name: Name of the task to cancel.
|
222
|
+
agent_id: ID of the agent that owns the task.
|
223
|
+
agent_name: Name of the agent that owns the task.
|
220
224
|
trace_id: The trace ID for the task.
|
221
225
|
parent_span_id: The parent span ID for the task.
|
222
226
|
start_to_close_timeout: The start to close timeout for the task.
|
@@ -225,6 +229,10 @@ class ACPModule:
|
|
225
229
|
|
226
230
|
Returns:
|
227
231
|
The task entry.
|
232
|
+
|
233
|
+
Raises:
|
234
|
+
ValueError: If neither agent_name nor agent_id is provided,
|
235
|
+
or if neither task_name nor task_id is provided
|
228
236
|
"""
|
229
237
|
if in_temporal_workflow():
|
230
238
|
return await ActivityHelpers.execute_activity(
|
@@ -232,6 +240,8 @@ class ACPModule:
|
|
232
240
|
request=TaskCancelParams(
|
233
241
|
task_id=task_id,
|
234
242
|
task_name=task_name,
|
243
|
+
agent_id=agent_id,
|
244
|
+
agent_name=agent_name,
|
235
245
|
trace_id=trace_id,
|
236
246
|
parent_span_id=parent_span_id,
|
237
247
|
),
|
@@ -244,6 +254,8 @@ class ACPModule:
|
|
244
254
|
return await self._acp_service.task_cancel(
|
245
255
|
task_id=task_id,
|
246
256
|
task_name=task_name,
|
257
|
+
agent_id=agent_id,
|
258
|
+
agent_name=agent_name,
|
247
259
|
trace_id=trace_id,
|
248
260
|
parent_span_id=parent_span_id,
|
249
261
|
)
|
@@ -4,6 +4,7 @@ from typing import Any, Literal
|
|
4
4
|
from agentex.lib.adk.utils._modules.client import create_async_agentex_client
|
5
5
|
from agents import Agent, RunResult, RunResultStreaming
|
6
6
|
from agents.agent import StopAtTools, ToolsToFinalOutputFunction
|
7
|
+
from agents.guardrail import InputGuardrail, OutputGuardrail
|
7
8
|
from agents.agent_output import AgentOutputSchemaBase
|
8
9
|
from agents.model_settings import ModelSettings
|
9
10
|
from agents.tool import Tool
|
@@ -84,6 +85,10 @@ class OpenAIModule:
|
|
84
85
|
| StopAtTools
|
85
86
|
| ToolsToFinalOutputFunction
|
86
87
|
) = "run_llm_again",
|
88
|
+
mcp_timeout_seconds: int | None = None,
|
89
|
+
input_guardrails: list[InputGuardrail] | None = None,
|
90
|
+
output_guardrails: list[OutputGuardrail] | None = None,
|
91
|
+
max_turns: int | None = None,
|
87
92
|
) -> SerializableRunResult | RunResult:
|
88
93
|
"""
|
89
94
|
Run an agent without streaming or TaskMessage creation.
|
@@ -107,6 +112,10 @@ class OpenAIModule:
|
|
107
112
|
tools: Optional list of tools.
|
108
113
|
output_type: Optional output type.
|
109
114
|
tool_use_behavior: Optional tool use behavior.
|
115
|
+
mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
|
116
|
+
input_guardrails: Optional list of input guardrails to run on initial user input.
|
117
|
+
output_guardrails: Optional list of output guardrails to run on final agent output.
|
118
|
+
max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
|
110
119
|
|
111
120
|
Returns:
|
112
121
|
Union[SerializableRunResult, RunResult]: SerializableRunResult when in Temporal, RunResult otherwise.
|
@@ -126,6 +135,10 @@ class OpenAIModule:
|
|
126
135
|
tools=tools,
|
127
136
|
output_type=output_type,
|
128
137
|
tool_use_behavior=tool_use_behavior,
|
138
|
+
mcp_timeout_seconds=mcp_timeout_seconds,
|
139
|
+
input_guardrails=input_guardrails,
|
140
|
+
output_guardrails=output_guardrails,
|
141
|
+
max_turns=max_turns,
|
129
142
|
)
|
130
143
|
return await ActivityHelpers.execute_activity(
|
131
144
|
activity_name=OpenAIActivityName.RUN_AGENT,
|
@@ -150,6 +163,10 @@ class OpenAIModule:
|
|
150
163
|
tools=tools,
|
151
164
|
output_type=output_type,
|
152
165
|
tool_use_behavior=tool_use_behavior,
|
166
|
+
mcp_timeout_seconds=mcp_timeout_seconds,
|
167
|
+
input_guardrails=input_guardrails,
|
168
|
+
output_guardrails=output_guardrails,
|
169
|
+
max_turns=max_turns,
|
153
170
|
)
|
154
171
|
|
155
172
|
async def run_agent_auto_send(
|
@@ -175,6 +192,10 @@ class OpenAIModule:
|
|
175
192
|
| StopAtTools
|
176
193
|
| ToolsToFinalOutputFunction
|
177
194
|
) = "run_llm_again",
|
195
|
+
mcp_timeout_seconds: int | None = None,
|
196
|
+
input_guardrails: list[InputGuardrail] | None = None,
|
197
|
+
output_guardrails: list[OutputGuardrail] | None = None,
|
198
|
+
max_turns: int | None = None,
|
178
199
|
) -> SerializableRunResult | RunResult:
|
179
200
|
"""
|
180
201
|
Run an agent with automatic TaskMessage creation.
|
@@ -197,6 +218,10 @@ class OpenAIModule:
|
|
197
218
|
tools: Optional list of tools.
|
198
219
|
output_type: Optional output type.
|
199
220
|
tool_use_behavior: Optional tool use behavior.
|
221
|
+
mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
|
222
|
+
input_guardrails: Optional list of input guardrails to run on initial user input.
|
223
|
+
output_guardrails: Optional list of output guardrails to run on final agent output.
|
224
|
+
max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
|
200
225
|
|
201
226
|
Returns:
|
202
227
|
Union[SerializableRunResult, RunResult]: SerializableRunResult when in Temporal, RunResult otherwise.
|
@@ -217,6 +242,10 @@ class OpenAIModule:
|
|
217
242
|
tools=tools,
|
218
243
|
output_type=output_type,
|
219
244
|
tool_use_behavior=tool_use_behavior,
|
245
|
+
mcp_timeout_seconds=mcp_timeout_seconds,
|
246
|
+
input_guardrails=input_guardrails,
|
247
|
+
output_guardrails=output_guardrails,
|
248
|
+
max_turns=max_turns,
|
220
249
|
)
|
221
250
|
return await ActivityHelpers.execute_activity(
|
222
251
|
activity_name=OpenAIActivityName.RUN_AGENT_AUTO_SEND,
|
@@ -242,6 +271,10 @@ class OpenAIModule:
|
|
242
271
|
tools=tools,
|
243
272
|
output_type=output_type,
|
244
273
|
tool_use_behavior=tool_use_behavior,
|
274
|
+
mcp_timeout_seconds=mcp_timeout_seconds,
|
275
|
+
input_guardrails=input_guardrails,
|
276
|
+
output_guardrails=output_guardrails,
|
277
|
+
max_turns=max_turns,
|
245
278
|
)
|
246
279
|
|
247
280
|
async def run_agent_streamed(
|
@@ -263,6 +296,10 @@ class OpenAIModule:
|
|
263
296
|
| StopAtTools
|
264
297
|
| ToolsToFinalOutputFunction
|
265
298
|
) = "run_llm_again",
|
299
|
+
mcp_timeout_seconds: int | None = None,
|
300
|
+
input_guardrails: list[InputGuardrail] | None = None,
|
301
|
+
output_guardrails: list[OutputGuardrail] | None = None,
|
302
|
+
max_turns: int | None = None,
|
266
303
|
) -> RunResultStreaming:
|
267
304
|
"""
|
268
305
|
Run an agent with streaming enabled but no TaskMessage creation.
|
@@ -289,6 +326,10 @@ class OpenAIModule:
|
|
289
326
|
tools: Optional list of tools.
|
290
327
|
output_type: Optional output type.
|
291
328
|
tool_use_behavior: Optional tool use behavior.
|
329
|
+
mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
|
330
|
+
input_guardrails: Optional list of input guardrails to run on initial user input.
|
331
|
+
output_guardrails: Optional list of output guardrails to run on final agent output.
|
332
|
+
max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
|
292
333
|
|
293
334
|
Returns:
|
294
335
|
RunResultStreaming: The result of the agent run with streaming.
|
@@ -318,6 +359,10 @@ class OpenAIModule:
|
|
318
359
|
tools=tools,
|
319
360
|
output_type=output_type,
|
320
361
|
tool_use_behavior=tool_use_behavior,
|
362
|
+
mcp_timeout_seconds=mcp_timeout_seconds,
|
363
|
+
input_guardrails=input_guardrails,
|
364
|
+
output_guardrails=output_guardrails,
|
365
|
+
max_turns=max_turns,
|
321
366
|
)
|
322
367
|
|
323
368
|
async def run_agent_streamed_auto_send(
|
@@ -344,6 +389,9 @@ class OpenAIModule:
|
|
344
389
|
| ToolsToFinalOutputFunction
|
345
390
|
) = "run_llm_again",
|
346
391
|
mcp_timeout_seconds: int | None = None,
|
392
|
+
input_guardrails: list[InputGuardrail] | None = None,
|
393
|
+
output_guardrails: list[OutputGuardrail] | None = None,
|
394
|
+
max_turns: int | None = None,
|
347
395
|
) -> SerializableRunResultStreaming | RunResultStreaming:
|
348
396
|
"""
|
349
397
|
Run an agent with streaming enabled and automatic TaskMessage creation.
|
@@ -364,9 +412,12 @@ class OpenAIModule:
|
|
364
412
|
model: Optional model to use.
|
365
413
|
model_settings: Optional model settings.
|
366
414
|
tools: Optional list of tools.
|
415
|
+
input_guardrails: Optional list of input guardrails to run on initial user input.
|
416
|
+
output_guardrails: Optional list of output guardrails to run on final agent output.
|
367
417
|
output_type: Optional output type.
|
368
418
|
tool_use_behavior: Optional tool use behavior.
|
369
419
|
mcp_timeout_seconds: Optional param to set the timeout threshold for the MCP servers. Defaults to 5 seconds.
|
420
|
+
max_turns: Maximum number of turns the agent can take. Uses Runner's default if None.
|
370
421
|
|
371
422
|
Returns:
|
372
423
|
Union[SerializableRunResultStreaming, RunResultStreaming]: SerializableRunResultStreaming when in Temporal, RunResultStreaming otherwise.
|
@@ -388,6 +439,9 @@ class OpenAIModule:
|
|
388
439
|
output_type=output_type,
|
389
440
|
tool_use_behavior=tool_use_behavior,
|
390
441
|
mcp_timeout_seconds=mcp_timeout_seconds,
|
442
|
+
input_guardrails=input_guardrails,
|
443
|
+
output_guardrails=output_guardrails,
|
444
|
+
max_turns=max_turns
|
391
445
|
)
|
392
446
|
return await ActivityHelpers.execute_activity(
|
393
447
|
activity_name=OpenAIActivityName.RUN_AGENT_STREAMED_AUTO_SEND,
|
@@ -414,4 +468,7 @@ class OpenAIModule:
|
|
414
468
|
output_type=output_type,
|
415
469
|
tool_use_behavior=tool_use_behavior,
|
416
470
|
mcp_timeout_seconds=mcp_timeout_seconds,
|
471
|
+
input_guardrails=input_guardrails,
|
472
|
+
output_guardrails=output_guardrails,
|
473
|
+
max_turns=max_turns,
|
417
474
|
)
|
@@ -229,9 +229,12 @@ def merge_deployment_configs(
|
|
229
229
|
all_env_vars[EnvVarKeys.AUTH_PRINCIPAL_B64.value] = encoded_principal
|
230
230
|
else:
|
231
231
|
raise DeploymentError(f"Auth principal unable to be encoded for agent_env_config: {agent_env_config}")
|
232
|
-
|
232
|
+
|
233
|
+
logger.info(f"Defined agent helm overrides: {agent_env_config.helm_overrides}")
|
234
|
+
logger.info(f"Before-merge helm values: {helm_values}")
|
233
235
|
if agent_env_config.helm_overrides:
|
234
236
|
_deep_merge(helm_values, agent_env_config.helm_overrides)
|
237
|
+
logger.info(f"After-merge helm values: {helm_values}")
|
235
238
|
|
236
239
|
# Set final environment variables
|
237
240
|
# Environment variable precedence: manifest -> environments.yaml -> secrets (highest)
|
@@ -262,14 +262,30 @@ class MyWorkflow(BaseWorkflow):
|
|
262
262
|
```
|
263
263
|
|
264
264
|
### Custom Activities
|
265
|
-
Add custom activities for external operations:
|
265
|
+
Add custom activities for external operations. **Important**: Always specify appropriate timeouts (recommended: 10 minutes):
|
266
266
|
|
267
267
|
```python
|
268
268
|
# In project/activities.py
|
269
|
-
|
269
|
+
from datetime import timedelta
|
270
|
+
from temporalio import activity
|
271
|
+
from temporalio.common import RetryPolicy
|
272
|
+
|
273
|
+
@activity.defn(name="call_external_api")
|
270
274
|
async def call_external_api(data):
|
271
275
|
# HTTP requests, database operations, etc.
|
272
276
|
pass
|
277
|
+
|
278
|
+
# In your workflow, call it with a timeout:
|
279
|
+
result = await workflow.execute_activity(
|
280
|
+
"call_external_api",
|
281
|
+
data,
|
282
|
+
start_to_close_timeout=timedelta(minutes=10), # Recommended: 10 minute timeout
|
283
|
+
heartbeat_timeout=timedelta(minutes=1), # Optional: heartbeat monitoring
|
284
|
+
retry_policy=RetryPolicy(maximum_attempts=3) # Optional: retry policy
|
285
|
+
)
|
286
|
+
|
287
|
+
# Don't forget to register your custom activities in run_worker.py:
|
288
|
+
# all_activities = get_all_activities() + [your_custom_activity_function]
|
273
289
|
```
|
274
290
|
|
275
291
|
### Integration with External Services
|
@@ -0,0 +1,77 @@
|
|
1
|
+
"""
|
2
|
+
Custom Temporal Activities Template
|
3
|
+
====================================
|
4
|
+
This file is for defining custom Temporal activities that can be executed
|
5
|
+
by your workflow. Activities are used for:
|
6
|
+
- External API calls
|
7
|
+
- Database operations
|
8
|
+
- File I/O operations
|
9
|
+
- Heavy computations
|
10
|
+
- Any non-deterministic operations
|
11
|
+
|
12
|
+
IMPORTANT: All activities should have appropriate timeouts!
|
13
|
+
Default recommendation: start_to_close_timeout=timedelta(minutes=10)
|
14
|
+
"""
|
15
|
+
|
16
|
+
from datetime import timedelta
|
17
|
+
from typing import Any, Dict
|
18
|
+
|
19
|
+
from pydantic import BaseModel
|
20
|
+
from temporalio import activity
|
21
|
+
from temporalio.common import RetryPolicy
|
22
|
+
|
23
|
+
from agentex.lib.utils.logging import make_logger
|
24
|
+
|
25
|
+
logger = make_logger(__name__)
|
26
|
+
|
27
|
+
|
28
|
+
# Example activity parameter models
|
29
|
+
class ExampleActivityParams(BaseModel):
|
30
|
+
"""Parameters for the example activity"""
|
31
|
+
data: Dict[str, Any]
|
32
|
+
task_id: str
|
33
|
+
|
34
|
+
|
35
|
+
# Example custom activity
|
36
|
+
@activity.defn(name="example_custom_activity")
|
37
|
+
async def example_custom_activity(params: ExampleActivityParams) -> Dict[str, Any]:
|
38
|
+
"""
|
39
|
+
Example custom activity that demonstrates best practices.
|
40
|
+
|
41
|
+
When calling this activity from your workflow, use:
|
42
|
+
```python
|
43
|
+
result = await workflow.execute_activity(
|
44
|
+
"example_custom_activity",
|
45
|
+
ExampleActivityParams(data={"key": "value"}, task_id=task_id),
|
46
|
+
start_to_close_timeout=timedelta(minutes=10), # Recommended: 10 minute timeout
|
47
|
+
heartbeat_timeout=timedelta(minutes=1), # Optional: heartbeat every minute
|
48
|
+
retry_policy=RetryPolicy(maximum_attempts=3) # Optional: retry up to 3 times
|
49
|
+
)
|
50
|
+
```
|
51
|
+
"""
|
52
|
+
logger.info(f"Processing activity for task {params.task_id} with data: {params.data}")
|
53
|
+
|
54
|
+
# Your activity logic here
|
55
|
+
# This could be:
|
56
|
+
# - API calls
|
57
|
+
# - Database operations
|
58
|
+
# - File processing
|
59
|
+
# - ML model inference
|
60
|
+
# - etc.
|
61
|
+
|
62
|
+
result = {
|
63
|
+
"status": "success",
|
64
|
+
"processed_data": params.data,
|
65
|
+
"task_id": params.task_id
|
66
|
+
}
|
67
|
+
|
68
|
+
return result
|
69
|
+
|
70
|
+
|
71
|
+
# Add more custom activities below as needed
|
72
|
+
# Remember to:
|
73
|
+
# 1. Use appropriate timeouts (default: 10 minutes)
|
74
|
+
# 2. Define clear parameter models with Pydantic
|
75
|
+
# 3. Handle errors appropriately
|
76
|
+
# 4. Use logging for debugging
|
77
|
+
# 5. Keep activities focused on a single responsibility
|
@@ -22,13 +22,15 @@ async def main():
|
|
22
22
|
if task_queue_name is None:
|
23
23
|
raise ValueError("WORKFLOW_TASK_QUEUE is not set")
|
24
24
|
|
25
|
+
all_activities = get_all_activities() + [] # add your own activities here
|
26
|
+
|
25
27
|
# Create a worker with automatic tracing
|
26
28
|
worker = AgentexWorker(
|
27
29
|
task_queue=task_queue_name,
|
28
30
|
)
|
29
31
|
|
30
32
|
await worker.run(
|
31
|
-
activities=
|
33
|
+
activities=all_activities,
|
32
34
|
workflow={{ workflow_class }},
|
33
35
|
)
|
34
36
|
|
@@ -180,9 +180,19 @@ class ACPService:
|
|
180
180
|
self,
|
181
181
|
task_id: str | None = None,
|
182
182
|
task_name: str | None = None,
|
183
|
+
agent_id: str | None = None,
|
184
|
+
agent_name: str | None = None,
|
183
185
|
trace_id: str | None = None,
|
184
186
|
parent_span_id: str | None = None,
|
185
|
-
) -> Task:
|
187
|
+
) -> Task:
|
188
|
+
# Require agent identification
|
189
|
+
if not agent_name and not agent_id:
|
190
|
+
raise ValueError("Either agent_name or agent_id must be provided to identify the agent that owns the task")
|
191
|
+
|
192
|
+
# Require task identification
|
193
|
+
if not task_name and not task_id:
|
194
|
+
raise ValueError("Either task_name or task_id must be provided to identify the task to cancel")
|
195
|
+
|
186
196
|
trace = self._tracer.trace(trace_id=trace_id)
|
187
197
|
async with trace.span(
|
188
198
|
parent_id=parent_span_id,
|
@@ -190,27 +200,32 @@ class ACPService:
|
|
190
200
|
input={
|
191
201
|
"task_id": task_id,
|
192
202
|
"task_name": task_name,
|
203
|
+
"agent_id": agent_id,
|
204
|
+
"agent_name": agent_name,
|
193
205
|
},
|
194
206
|
) as span:
|
195
207
|
heartbeat_if_in_workflow("task cancel")
|
208
|
+
|
209
|
+
# Build params for the agent (task identification)
|
210
|
+
params = {}
|
211
|
+
if task_id:
|
212
|
+
params["task_id"] = task_id
|
196
213
|
if task_name:
|
214
|
+
params["task_name"] = task_name
|
215
|
+
|
216
|
+
# Send cancel request to the correct agent
|
217
|
+
if agent_name:
|
197
218
|
json_rpc_response = await self._agentex_client.agents.rpc_by_name(
|
198
|
-
agent_name=
|
219
|
+
agent_name=agent_name,
|
199
220
|
method="task/cancel",
|
200
|
-
params=
|
201
|
-
"task_name": task_name,
|
202
|
-
},
|
221
|
+
params=params,
|
203
222
|
)
|
204
|
-
|
223
|
+
else: # agent_id is provided (validated above)
|
205
224
|
json_rpc_response = await self._agentex_client.agents.rpc(
|
206
|
-
agent_id=
|
225
|
+
agent_id=agent_id,
|
207
226
|
method="task/cancel",
|
208
|
-
params=
|
209
|
-
"task_id": task_id,
|
210
|
-
},
|
227
|
+
params=params,
|
211
228
|
)
|
212
|
-
else:
|
213
|
-
raise ValueError("Either task_name or task_id must be provided")
|
214
229
|
|
215
230
|
task_entry = Task.model_validate(json_rpc_response.result)
|
216
231
|
if span:
|