agentex-sdk 0.2.4__py3-none-any.whl → 0.2.6__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 +4 -1
- agentex/_files.py +4 -4
- agentex/_version.py +1 -1
- agentex/lib/adk/_modules/acp.py +2 -2
- agentex/lib/adk/_modules/agent_task_tracker.py +2 -2
- agentex/lib/adk/_modules/agents.py +2 -2
- agentex/lib/adk/_modules/events.py +2 -2
- agentex/lib/adk/_modules/messages.py +2 -2
- agentex/lib/adk/_modules/state.py +2 -2
- agentex/lib/adk/_modules/streaming.py +2 -2
- agentex/lib/adk/_modules/tasks.py +2 -2
- agentex/lib/adk/_modules/tracing.py +2 -2
- agentex/lib/adk/providers/_modules/litellm.py +2 -1
- agentex/lib/adk/providers/_modules/openai.py +2 -1
- agentex/lib/adk/providers/_modules/sgp.py +2 -1
- agentex/lib/adk/utils/_modules/client.py +20 -35
- agentex/lib/adk/utils/_modules/templating.py +2 -1
- agentex/lib/cli/commands/agents.py +3 -3
- agentex/lib/cli/handlers/agent_handlers.py +1 -1
- agentex/lib/cli/handlers/cleanup_handlers.py +9 -15
- agentex/lib/cli/handlers/deploy_handlers.py +29 -5
- agentex/lib/cli/handlers/run_handlers.py +19 -93
- agentex/lib/cli/templates/sync/project/acp.py.j2 +15 -64
- agentex/lib/cli/utils/path_utils.py +143 -0
- agentex/lib/core/temporal/activities/__init__.py +2 -1
- agentex/lib/core/tracing/processors/agentex_tracing_processor.py +2 -1
- agentex/lib/environment_variables.py +5 -1
- agentex/lib/sdk/fastacp/base/base_acp_server.py +5 -4
- agentex/lib/sdk/fastacp/impl/agentic_base_acp.py +3 -1
- agentex/lib/types/converters.py +60 -0
- agentex/resources/agents.py +9 -8
- agentex/resources/messages/messages.py +4 -0
- agentex/resources/tasks.py +63 -14
- agentex/types/__init__.py +2 -2
- agentex/types/message_list_params.py +1 -0
- agentex/types/shared/__init__.py +3 -0
- agentex/types/shared/delete_response.py +11 -0
- agentex/types/task_list_params.py +14 -0
- {agentex_sdk-0.2.4.dist-info → agentex_sdk-0.2.6.dist-info}/METADATA +2 -2
- {agentex_sdk-0.2.4.dist-info → agentex_sdk-0.2.6.dist-info}/RECORD +43 -40
- agentex/types/task_delete_by_name_response.py +0 -8
- agentex/types/task_delete_response.py +0 -8
- {agentex_sdk-0.2.4.dist-info → agentex_sdk-0.2.6.dist-info}/WHEEL +0 -0
- {agentex_sdk-0.2.4.dist-info → agentex_sdk-0.2.6.dist-info}/entry_points.txt +0 -0
- {agentex_sdk-0.2.4.dist-info → agentex_sdk-0.2.6.dist-info}/licenses/LICENSE +0 -0
@@ -11,6 +11,11 @@ from agentex.lib.cli.handlers.cleanup_handlers import (
|
|
11
11
|
cleanup_agent_workflows,
|
12
12
|
should_cleanup_on_restart
|
13
13
|
)
|
14
|
+
from agentex.lib.cli.utils.path_utils import (
|
15
|
+
get_file_paths,
|
16
|
+
calculate_uvicorn_target_for_local,
|
17
|
+
)
|
18
|
+
|
14
19
|
from agentex.lib.environment_variables import EnvVarKeys
|
15
20
|
from agentex.lib.sdk.config.agent_manifest import AgentManifest
|
16
21
|
from agentex.lib.utils.logging import make_logger
|
@@ -104,7 +109,10 @@ async def start_temporal_worker_with_reload(
|
|
104
109
|
# PRE-RESTART CLEANUP - NEW!
|
105
110
|
if current_process is not None:
|
106
111
|
# Extract agent name from worker path for cleanup
|
107
|
-
|
112
|
+
|
113
|
+
agent_name = env.get("AGENT_NAME")
|
114
|
+
if agent_name is None:
|
115
|
+
agent_name = worker_path.parent.parent.name
|
108
116
|
|
109
117
|
# Perform cleanup if configured
|
110
118
|
if should_cleanup_on_restart():
|
@@ -180,15 +188,17 @@ async def start_temporal_worker_with_reload(
|
|
180
188
|
|
181
189
|
|
182
190
|
async def start_acp_server(
|
183
|
-
acp_path: Path, port: int, env: dict[str, str]
|
191
|
+
acp_path: Path, port: int, env: dict[str, str], manifest_dir: Path
|
184
192
|
) -> asyncio.subprocess.Process:
|
185
193
|
"""Start the ACP server process"""
|
186
|
-
# Use
|
194
|
+
# Use file path relative to manifest directory if possible
|
195
|
+
uvicorn_target = calculate_uvicorn_target_for_local(acp_path, manifest_dir)
|
196
|
+
|
187
197
|
cmd = [
|
188
198
|
sys.executable,
|
189
199
|
"-m",
|
190
200
|
"uvicorn",
|
191
|
-
f"{
|
201
|
+
f"{uvicorn_target}:acp",
|
192
202
|
"--reload",
|
193
203
|
"--reload-dir",
|
194
204
|
str(acp_path.parent), # Watch the project directory specifically
|
@@ -201,7 +211,7 @@ async def start_acp_server(
|
|
201
211
|
console.print(f"[blue]Starting ACP server from {acp_path} on port {port}...[/blue]")
|
202
212
|
return await asyncio.create_subprocess_exec(
|
203
213
|
*cmd,
|
204
|
-
cwd=
|
214
|
+
cwd=manifest_dir, # Always use manifest directory as CWD for consistency
|
205
215
|
env=env,
|
206
216
|
stdout=asyncio.subprocess.PIPE,
|
207
217
|
stderr=asyncio.subprocess.STDOUT,
|
@@ -218,7 +228,7 @@ async def start_temporal_worker(
|
|
218
228
|
|
219
229
|
return await asyncio.create_subprocess_exec(
|
220
230
|
*cmd,
|
221
|
-
cwd=worker_path.parent,
|
231
|
+
cwd=worker_path.parent, # Use worker directory as CWD for imports to work
|
222
232
|
env=env,
|
223
233
|
stdout=asyncio.subprocess.PIPE,
|
224
234
|
stderr=asyncio.subprocess.STDOUT,
|
@@ -280,8 +290,9 @@ async def run_agent(manifest_path: str):
|
|
280
290
|
)
|
281
291
|
|
282
292
|
# Start ACP server
|
293
|
+
manifest_dir = Path(manifest_path).parent
|
283
294
|
acp_process = await start_acp_server(
|
284
|
-
file_paths["acp"], manifest.local_development.agent.port, agent_env
|
295
|
+
file_paths["acp"], manifest.local_development.agent.port, agent_env, manifest_dir
|
285
296
|
)
|
286
297
|
process_manager.add_process(acp_process)
|
287
298
|
|
@@ -291,7 +302,7 @@ async def run_agent(manifest_path: str):
|
|
291
302
|
tasks = [acp_output_task]
|
292
303
|
|
293
304
|
# Start temporal worker if needed
|
294
|
-
if is_temporal_agent(manifest):
|
305
|
+
if is_temporal_agent(manifest) and file_paths["worker"]:
|
295
306
|
worker_task = await start_temporal_worker_with_reload(file_paths["worker"], agent_env, process_manager)
|
296
307
|
tasks.append(worker_task)
|
297
308
|
|
@@ -323,92 +334,7 @@ async def run_agent(manifest_path: str):
|
|
323
334
|
await process_manager.cleanup_processes()
|
324
335
|
|
325
336
|
|
326
|
-
def resolve_and_validate_path(base_path: Path, configured_path: str, file_type: str) -> Path:
|
327
|
-
"""Resolve and validate a configured path"""
|
328
|
-
path_obj = Path(configured_path)
|
329
|
-
|
330
|
-
if path_obj.is_absolute():
|
331
|
-
# Absolute path - use as-is
|
332
|
-
resolved_path = path_obj
|
333
|
-
else:
|
334
|
-
# Relative path - resolve relative to manifest directory
|
335
|
-
resolved_path = (base_path / configured_path).resolve()
|
336
|
-
|
337
|
-
# Validate the file exists
|
338
|
-
if not resolved_path.exists():
|
339
|
-
raise RunError(
|
340
|
-
f"{file_type} file not found: {resolved_path}\n"
|
341
|
-
f" Configured path: {configured_path}\n"
|
342
|
-
f" Resolved from manifest: {base_path}"
|
343
|
-
)
|
344
|
-
|
345
|
-
# Validate it's actually a file
|
346
|
-
if not resolved_path.is_file():
|
347
|
-
raise RunError(f"{file_type} path is not a file: {resolved_path}")
|
348
|
-
|
349
|
-
return resolved_path
|
350
|
-
|
351
|
-
|
352
|
-
def validate_path_security(resolved_path: Path, manifest_dir: Path) -> None:
|
353
|
-
"""Basic security validation for resolved paths"""
|
354
|
-
try:
|
355
|
-
# Ensure the resolved path is accessible
|
356
|
-
resolved_path.resolve()
|
357
|
-
|
358
|
-
# Optional: Add warnings for paths that go too far up
|
359
|
-
try:
|
360
|
-
# Check if path goes more than 3 levels up from manifest
|
361
|
-
relative_to_manifest = resolved_path.relative_to(manifest_dir.parent.parent.parent)
|
362
|
-
if str(relative_to_manifest).startswith(".."):
|
363
|
-
logger.warning(
|
364
|
-
f"Path goes significantly outside project structure: {resolved_path}"
|
365
|
-
)
|
366
|
-
except ValueError:
|
367
|
-
# Path is outside the tree - that's okay, just log it
|
368
|
-
logger.info(f"Using path outside manifest directory tree: {resolved_path}")
|
369
|
-
|
370
|
-
except Exception as e:
|
371
|
-
raise RunError(f"Path resolution failed: {resolved_path} - {str(e)}") from e
|
372
|
-
|
373
337
|
|
374
|
-
def get_file_paths(manifest: AgentManifest, manifest_path: str) -> dict[str, Path]:
|
375
|
-
"""Get resolved file paths from manifest configuration"""
|
376
|
-
manifest_dir = Path(manifest_path).parent.resolve()
|
377
|
-
|
378
|
-
# Use configured paths or fall back to defaults for backward compatibility
|
379
|
-
if manifest.local_development and manifest.local_development.paths:
|
380
|
-
paths_config = manifest.local_development.paths
|
381
|
-
|
382
|
-
# Resolve ACP path
|
383
|
-
acp_path = resolve_and_validate_path(manifest_dir, paths_config.acp, "ACP server")
|
384
|
-
validate_path_security(acp_path, manifest_dir)
|
385
|
-
|
386
|
-
# Resolve worker path if specified
|
387
|
-
worker_path = None
|
388
|
-
if paths_config.worker:
|
389
|
-
worker_path = resolve_and_validate_path(
|
390
|
-
manifest_dir, paths_config.worker, "Temporal worker"
|
391
|
-
)
|
392
|
-
validate_path_security(worker_path, manifest_dir)
|
393
|
-
else:
|
394
|
-
# Backward compatibility: use old hardcoded structure
|
395
|
-
project_dir = manifest_dir / "project"
|
396
|
-
acp_path = project_dir / "acp.py"
|
397
|
-
worker_path = project_dir / "run_worker.py" if is_temporal_agent(manifest) else None
|
398
|
-
|
399
|
-
# Validate backward compatibility paths
|
400
|
-
if not acp_path.exists():
|
401
|
-
raise RunError(f"ACP file not found: {acp_path}")
|
402
|
-
|
403
|
-
if worker_path and not worker_path.exists():
|
404
|
-
raise RunError(f"Worker file not found: {worker_path}")
|
405
|
-
|
406
|
-
return {
|
407
|
-
"acp": acp_path,
|
408
|
-
"worker": worker_path,
|
409
|
-
"acp_dir": acp_path.parent,
|
410
|
-
"worker_dir": worker_path.parent if worker_path else None,
|
411
|
-
}
|
412
338
|
|
413
339
|
|
414
340
|
def create_agent_environment(manifest: AgentManifest) -> dict[str, str]:
|
@@ -1,75 +1,26 @@
|
|
1
|
-
import
|
2
|
-
from agentex.lib import adk
|
1
|
+
from typing import AsyncGenerator, Union
|
3
2
|
from agentex.lib.sdk.fastacp.fastacp import FastACP
|
4
|
-
from agentex.lib.types.
|
5
|
-
from agentex.lib.types.acp import CancelTaskParams, CreateTaskParams, SendEventParams
|
3
|
+
from agentex.lib.types.acp import SendMessageParams
|
6
4
|
|
5
|
+
from agentex.lib.types.task_message_updates import TaskMessageUpdate
|
6
|
+
from agentex.types.task_message_content import TaskMessageContent
|
7
7
|
from agentex.types.text_content import TextContent
|
8
8
|
from agentex.lib.utils.logging import make_logger
|
9
9
|
|
10
10
|
logger = make_logger(__name__)
|
11
11
|
|
12
12
|
|
13
|
-
# Create an ACP server
|
14
|
-
# This sets up the core server that will handle task creation, events, and cancellation
|
13
|
+
# Create an ACP server
|
15
14
|
acp = FastACP.create(
|
16
|
-
acp_type="
|
17
|
-
config=AgenticACPConfig(
|
18
|
-
type="base",
|
19
|
-
),
|
15
|
+
acp_type="sync",
|
20
16
|
)
|
21
17
|
|
22
|
-
@acp.
|
23
|
-
async def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# Acknowledge that the task has been created.
|
32
|
-
await adk.messages.create(
|
33
|
-
task_id=params.task.id,
|
34
|
-
content=TextContent(
|
35
|
-
author="agent",
|
36
|
-
content=f"Hello! I've received your task. Normally you can do some state initialization here, or just pass and do nothing until you get your first event. For now I'm just acknowledging that I've received a task with the following params:\n\n{json.dumps(params.params, indent=2)}.\n\nYou should only see this message once, when the task is created. All subsequent events will be handled by the `on_task_event_send` handler.",
|
37
|
-
),
|
38
|
-
)
|
39
|
-
|
40
|
-
@acp.on_task_event_send
|
41
|
-
async def handle_event_send(params: SendEventParams):
|
42
|
-
# This handler is called whenever a new event (like a message) is sent to the task
|
43
|
-
|
44
|
-
#########################################################
|
45
|
-
# 2. (👋) Echo back the client's message to show it in the UI.
|
46
|
-
#########################################################
|
47
|
-
|
48
|
-
# This is not done by default so the agent developer has full control over what is shown to the user.
|
49
|
-
if params.event.content:
|
50
|
-
await adk.messages.create(task_id=params.task.id, content=params.event.content)
|
51
|
-
|
52
|
-
#########################################################
|
53
|
-
# 3. (👋) Send a simple response message.
|
54
|
-
#########################################################
|
55
|
-
|
56
|
-
# In future tutorials, this is where we'll add more sophisticated response logic.
|
57
|
-
await adk.messages.create(
|
58
|
-
task_id=params.task.id,
|
59
|
-
content=TextContent(
|
60
|
-
author="agent",
|
61
|
-
content=f"Hello! I've received your message. I can't respond right now, but in future tutorials we'll see how you can get me to intelligently respond to your message.",
|
62
|
-
),
|
63
|
-
)
|
64
|
-
|
65
|
-
@acp.on_task_cancel
|
66
|
-
async def handle_task_cancel(params: CancelTaskParams):
|
67
|
-
# This handler is called when a task is cancelled.
|
68
|
-
# It's useful for cleaning up any resources or state associated with the task.
|
69
|
-
|
70
|
-
#########################################################
|
71
|
-
# 4. (👋) Do task cleanup here.
|
72
|
-
#########################################################
|
73
|
-
|
74
|
-
# This is mostly for durable workflows that are cancellable like Temporal, but we will leave it here for demonstration purposes.
|
75
|
-
logger.info(f"Hello! I've received task cancel for task {params.task.id}: {params.task}. This isn't necessary for this example, but it's good to know that it's available.")
|
18
|
+
@acp.on_message_send
|
19
|
+
async def handle_message_send(
|
20
|
+
params: SendMessageParams
|
21
|
+
) -> TaskMessageContent | list[TaskMessageContent] | AsyncGenerator[TaskMessageUpdate, None]:
|
22
|
+
"""Default message handler with streaming support"""
|
23
|
+
return TextContent(
|
24
|
+
author="agent",
|
25
|
+
content=f"Hello! I've received your message. Here's a generic response, but in future tutorials we'll see how you can get me to intelligently respond to your message. This is what I heard you say: {params.content.content}",
|
26
|
+
)
|
@@ -0,0 +1,143 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from typing import Dict
|
3
|
+
|
4
|
+
from agentex.lib.sdk.config.agent_manifest import AgentManifest
|
5
|
+
from agentex.lib.utils.logging import make_logger
|
6
|
+
|
7
|
+
logger = make_logger(__name__)
|
8
|
+
|
9
|
+
|
10
|
+
class PathResolutionError(Exception):
|
11
|
+
"""An error occurred during path resolution"""
|
12
|
+
|
13
|
+
|
14
|
+
def resolve_and_validate_path(base_path: Path, configured_path: str, file_type: str) -> Path:
|
15
|
+
"""Resolve and validate a configured path"""
|
16
|
+
path_obj = Path(configured_path)
|
17
|
+
|
18
|
+
if path_obj.is_absolute():
|
19
|
+
# Absolute path - resolve to canonical form
|
20
|
+
resolved_path = path_obj.resolve()
|
21
|
+
else:
|
22
|
+
# Relative path - resolve relative to manifest directory
|
23
|
+
resolved_path = (base_path / configured_path).resolve()
|
24
|
+
|
25
|
+
# Validate the file exists
|
26
|
+
if not resolved_path.exists():
|
27
|
+
raise PathResolutionError(
|
28
|
+
f"{file_type} file not found: {resolved_path}\n"
|
29
|
+
f" Configured path: {configured_path}\n"
|
30
|
+
f" Resolved from manifest: {base_path}"
|
31
|
+
)
|
32
|
+
|
33
|
+
# Validate it's actually a file
|
34
|
+
if not resolved_path.is_file():
|
35
|
+
raise PathResolutionError(f"{file_type} path is not a file: {resolved_path}")
|
36
|
+
|
37
|
+
return resolved_path
|
38
|
+
|
39
|
+
|
40
|
+
def validate_path_security(resolved_path: Path, manifest_dir: Path) -> None:
|
41
|
+
"""Basic security validation for resolved paths"""
|
42
|
+
try:
|
43
|
+
# Ensure the resolved path is accessible
|
44
|
+
resolved_path.resolve()
|
45
|
+
|
46
|
+
# Optional: Add warnings for paths that go too far up
|
47
|
+
try:
|
48
|
+
# Check if path goes more than 3 levels up from manifest
|
49
|
+
relative_to_manifest = resolved_path.relative_to(manifest_dir.parent.parent.parent)
|
50
|
+
if str(relative_to_manifest).startswith(".."):
|
51
|
+
logger.warning(
|
52
|
+
f"Path goes significantly outside project structure: {resolved_path}"
|
53
|
+
)
|
54
|
+
except ValueError:
|
55
|
+
# Path is outside the tree - that's okay, just log it
|
56
|
+
logger.info(f"Using path outside manifest directory tree: {resolved_path}")
|
57
|
+
|
58
|
+
except Exception as e:
|
59
|
+
raise PathResolutionError(f"Path resolution failed: {resolved_path} - {str(e)}") from e
|
60
|
+
|
61
|
+
|
62
|
+
def get_file_paths(manifest: AgentManifest, manifest_path: str) -> Dict[str, Path | None]:
|
63
|
+
"""Get resolved file paths from manifest configuration"""
|
64
|
+
manifest_dir = Path(manifest_path).parent.resolve()
|
65
|
+
|
66
|
+
# Use configured paths or fall back to defaults for backward compatibility
|
67
|
+
if manifest.local_development and manifest.local_development.paths:
|
68
|
+
paths_config = manifest.local_development.paths
|
69
|
+
|
70
|
+
# Resolve ACP path
|
71
|
+
acp_path = resolve_and_validate_path(manifest_dir, paths_config.acp, "ACP server")
|
72
|
+
validate_path_security(acp_path, manifest_dir)
|
73
|
+
|
74
|
+
# Resolve worker path if specified
|
75
|
+
worker_path = None
|
76
|
+
if paths_config.worker:
|
77
|
+
worker_path = resolve_and_validate_path(
|
78
|
+
manifest_dir, paths_config.worker, "Temporal worker"
|
79
|
+
)
|
80
|
+
validate_path_security(worker_path, manifest_dir)
|
81
|
+
else:
|
82
|
+
# Backward compatibility: use old hardcoded structure
|
83
|
+
project_dir = manifest_dir / "project"
|
84
|
+
acp_path = (project_dir / "acp.py").resolve()
|
85
|
+
worker_path = (project_dir / "run_worker.py").resolve() if manifest.agent.is_temporal_agent() else None
|
86
|
+
|
87
|
+
# Validate backward compatibility paths
|
88
|
+
if not acp_path.exists():
|
89
|
+
raise PathResolutionError(f"ACP file not found: {acp_path}")
|
90
|
+
|
91
|
+
if worker_path and not worker_path.exists():
|
92
|
+
raise PathResolutionError(f"Worker file not found: {worker_path}")
|
93
|
+
|
94
|
+
return {
|
95
|
+
"acp": acp_path,
|
96
|
+
"worker": worker_path,
|
97
|
+
"acp_dir": acp_path.parent,
|
98
|
+
"worker_dir": worker_path.parent if worker_path else None,
|
99
|
+
}
|
100
|
+
|
101
|
+
|
102
|
+
def calculate_uvicorn_target_for_local(acp_path: Path, manifest_dir: Path) -> str:
|
103
|
+
"""Calculate the uvicorn target path for local development"""
|
104
|
+
# Ensure both paths are resolved to canonical form for accurate comparison
|
105
|
+
acp_resolved = acp_path.resolve()
|
106
|
+
manifest_resolved = manifest_dir.resolve()
|
107
|
+
|
108
|
+
try:
|
109
|
+
# Try to use path relative to manifest directory
|
110
|
+
acp_relative = acp_resolved.relative_to(manifest_resolved)
|
111
|
+
# Convert to module notation: project/acp.py -> project.acp
|
112
|
+
module_path = str(acp_relative.with_suffix('')) # Remove .py extension
|
113
|
+
module_path = module_path.replace('/', '.') # Convert slashes to dots
|
114
|
+
module_path = module_path.replace('\\', '.') # Handle Windows paths
|
115
|
+
return module_path
|
116
|
+
except ValueError:
|
117
|
+
# Path cannot be made relative - use absolute file path
|
118
|
+
logger.warning(f"ACP file {acp_resolved} cannot be made relative to manifest directory {manifest_resolved}, using absolute file path")
|
119
|
+
return str(acp_resolved)
|
120
|
+
|
121
|
+
|
122
|
+
def calculate_docker_acp_module(manifest: AgentManifest, manifest_path: str) -> str:
|
123
|
+
"""Calculate the Python module path for the ACP file in the Docker container
|
124
|
+
|
125
|
+
This should return the same module notation as local development for consistency.
|
126
|
+
"""
|
127
|
+
# Use the same logic as local development
|
128
|
+
manifest_dir = Path(manifest_path).parent
|
129
|
+
|
130
|
+
# Get the configured ACP path (could be relative or absolute)
|
131
|
+
if manifest.local_development and manifest.local_development.paths:
|
132
|
+
acp_config_path = manifest.local_development.paths.acp
|
133
|
+
else:
|
134
|
+
acp_config_path = "project/acp.py" # Default
|
135
|
+
|
136
|
+
# Resolve to actual file path
|
137
|
+
acp_path = resolve_and_validate_path(manifest_dir, acp_config_path, "ACP")
|
138
|
+
|
139
|
+
# Use the same module calculation as local development
|
140
|
+
return calculate_uvicorn_target_for_local(acp_path, manifest_dir)
|
141
|
+
|
142
|
+
|
143
|
+
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from agentex.lib.adk.utils._modules.client import create_async_agentex_client
|
1
2
|
from scale_gp import SGPClient, SGPClientError
|
2
3
|
|
3
4
|
from agentex import AsyncAgentex
|
@@ -58,7 +59,7 @@ def get_all_activities(sgp_client=None):
|
|
58
59
|
|
59
60
|
llm_gateway = LiteLLMGateway()
|
60
61
|
stream_repository = RedisStreamRepository()
|
61
|
-
agentex_client =
|
62
|
+
agentex_client = create_async_agentex_client()
|
62
63
|
tracer = AsyncTracer(agentex_client)
|
63
64
|
|
64
65
|
# Services
|
@@ -1,6 +1,7 @@
|
|
1
1
|
from typing import Any, Dict, override
|
2
2
|
|
3
3
|
from agentex import Agentex, AsyncAgentex
|
4
|
+
from agentex.lib.adk.utils._modules.client import create_async_agentex_client
|
4
5
|
from agentex.lib.core.tracing.processors.tracing_processor_interface import (
|
5
6
|
AsyncTracingProcessor,
|
6
7
|
SyncTracingProcessor,
|
@@ -65,7 +66,7 @@ class AgentexSyncTracingProcessor(SyncTracingProcessor):
|
|
65
66
|
|
66
67
|
class AgentexAsyncTracingProcessor(AsyncTracingProcessor):
|
67
68
|
def __init__(self, config: AgentexTracingProcessorConfig):
|
68
|
-
self.client =
|
69
|
+
self.client = create_async_agentex_client()
|
69
70
|
|
70
71
|
@override
|
71
72
|
async def on_span_start(self, span: Span) -> None:
|
@@ -4,12 +4,15 @@ import os
|
|
4
4
|
from enum import Enum
|
5
5
|
from pathlib import Path
|
6
6
|
|
7
|
+
from agentex.lib.utils.logging import make_logger
|
7
8
|
from dotenv import load_dotenv
|
8
9
|
|
9
10
|
from agentex.lib.utils.model_utils import BaseModel
|
10
11
|
|
11
12
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
12
13
|
|
14
|
+
logger = make_logger(__name__)
|
15
|
+
|
13
16
|
|
14
17
|
class EnvVarKeys(str, Enum):
|
15
18
|
ENVIRONMENT = "ENVIRONMENT"
|
@@ -37,7 +40,7 @@ class Environment(str, Enum):
|
|
37
40
|
PROD = "production"
|
38
41
|
|
39
42
|
|
40
|
-
refreshed_environment_variables = None
|
43
|
+
refreshed_environment_variables: "EnvironmentVariables" | None = None
|
41
44
|
|
42
45
|
|
43
46
|
class EnvironmentVariables(BaseModel):
|
@@ -64,6 +67,7 @@ class EnvironmentVariables(BaseModel):
|
|
64
67
|
if refreshed_environment_variables is not None:
|
65
68
|
return refreshed_environment_variables
|
66
69
|
|
70
|
+
logger.info("Refreshing environment variables")
|
67
71
|
if os.environ.get(EnvVarKeys.ENVIRONMENT) == Environment.DEV:
|
68
72
|
# Load global .env file first
|
69
73
|
global_env_path = PROJECT_ROOT / ".env"
|
@@ -9,7 +9,7 @@ from typing import Any
|
|
9
9
|
|
10
10
|
import httpx
|
11
11
|
import uvicorn
|
12
|
-
from agentex.lib.adk.utils._modules.client import
|
12
|
+
from agentex.lib.adk.utils._modules.client import create_async_agentex_client
|
13
13
|
from fastapi import FastAPI, Request
|
14
14
|
from fastapi.responses import StreamingResponse
|
15
15
|
from pydantic import TypeAdapter, ValidationError
|
@@ -397,9 +397,10 @@ class BaseACPServer(FastAPI):
|
|
397
397
|
|
398
398
|
os.environ["AGENT_ID"] = agent_id
|
399
399
|
os.environ["AGENT_NAME"] = agent_name
|
400
|
-
|
401
|
-
|
402
|
-
|
400
|
+
env_vars.AGENT_ID = agent_id
|
401
|
+
env_vars.AGENT_NAME = agent_name
|
402
|
+
global refreshed_environment_variables
|
403
|
+
refreshed_environment_variables = env_vars
|
403
404
|
logger.info(
|
404
405
|
f"Successfully registered agent '{env_vars.AGENT_NAME}' with Agentex server with acp_url: {full_acp_url}. Registration data: {registration_data}"
|
405
406
|
)
|
@@ -1,4 +1,6 @@
|
|
1
1
|
from typing import Any
|
2
|
+
|
3
|
+
from agentex.lib.adk.utils._modules.client import create_async_agentex_client
|
2
4
|
from typing_extensions import override
|
3
5
|
from agentex import AsyncAgentex
|
4
6
|
from agentex.lib.sdk.fastacp.base.base_acp_server import BaseACPServer
|
@@ -24,7 +26,7 @@ class AgenticBaseACP(BaseACPServer):
|
|
24
26
|
def __init__(self):
|
25
27
|
super().__init__()
|
26
28
|
self._setup_handlers()
|
27
|
-
self._agentex_client =
|
29
|
+
self._agentex_client = create_async_agentex_client()
|
28
30
|
|
29
31
|
@classmethod
|
30
32
|
@override
|
@@ -0,0 +1,60 @@
|
|
1
|
+
from agentex.types.task_message import TaskMessage
|
2
|
+
from agentex.types.text_content import TextContent
|
3
|
+
from agentex.types.tool_request_content import ToolRequestContent
|
4
|
+
from agentex.types.tool_response_content import ToolResponseContent
|
5
|
+
import json
|
6
|
+
from agents import TResponseInputItem
|
7
|
+
|
8
|
+
|
9
|
+
def convert_task_messages_to_oai_agents_inputs(
|
10
|
+
task_messages: list[TaskMessage],
|
11
|
+
) -> list[TResponseInputItem]:
|
12
|
+
"""
|
13
|
+
Convert a list of TaskMessages to a list of OpenAI Agents SDK inputs (TResponseInputItem).
|
14
|
+
|
15
|
+
Args:
|
16
|
+
task_messages: The list of TaskMessages to convert.
|
17
|
+
|
18
|
+
Returns:
|
19
|
+
A list of OpenAI Agents SDK inputs (TResponseInputItem).
|
20
|
+
"""
|
21
|
+
converted_messages = []
|
22
|
+
for task_message in task_messages:
|
23
|
+
task_message_content = task_message.content
|
24
|
+
if isinstance(task_message_content, TextContent):
|
25
|
+
converted_messages.append(
|
26
|
+
{
|
27
|
+
"role": (
|
28
|
+
"user" if task_message_content.author == "user" else "assistant"
|
29
|
+
),
|
30
|
+
"content": task_message_content.content,
|
31
|
+
}
|
32
|
+
)
|
33
|
+
elif isinstance(task_message_content, ToolRequestContent):
|
34
|
+
converted_messages.append(
|
35
|
+
{
|
36
|
+
"type": "function_call",
|
37
|
+
"call_id": task_message_content.tool_call_id,
|
38
|
+
"name": task_message_content.name,
|
39
|
+
"arguments": json.dumps(task_message_content.arguments),
|
40
|
+
}
|
41
|
+
)
|
42
|
+
elif isinstance(task_message_content, ToolResponseContent):
|
43
|
+
content_str = (
|
44
|
+
task_message_content.content
|
45
|
+
if isinstance(task_message_content.content, str)
|
46
|
+
else json.dumps(task_message_content.content)
|
47
|
+
)
|
48
|
+
converted_messages.append(
|
49
|
+
{
|
50
|
+
"type": "function_call_output",
|
51
|
+
"call_id": task_message_content.tool_call_id,
|
52
|
+
"output": content_str,
|
53
|
+
}
|
54
|
+
)
|
55
|
+
else:
|
56
|
+
raise ValueError(
|
57
|
+
f"Unsupported content type for converting TaskMessage to OpenAI Agents SDK input: {type(task_message.content)}"
|
58
|
+
)
|
59
|
+
|
60
|
+
return converted_messages
|
agentex/resources/agents.py
CHANGED
@@ -23,6 +23,7 @@ from ..types.agent import Agent
|
|
23
23
|
from .._base_client import make_request_options
|
24
24
|
from ..types.agent_rpc_response import AgentRpcResponse, CancelTaskResponse, CreateTaskResponse, SendEventResponse, SendMessageResponse, SendMessageStreamResponse
|
25
25
|
from ..types.agent_list_response import AgentListResponse
|
26
|
+
from ..types.shared.delete_response import DeleteResponse
|
26
27
|
|
27
28
|
__all__ = ["AgentsResource", "AsyncAgentsResource"]
|
28
29
|
|
@@ -127,7 +128,7 @@ class AgentsResource(SyncAPIResource):
|
|
127
128
|
extra_query: Query | None = None,
|
128
129
|
extra_body: Body | None = None,
|
129
130
|
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
130
|
-
) ->
|
131
|
+
) -> DeleteResponse:
|
131
132
|
"""
|
132
133
|
Delete an agent by its unique ID.
|
133
134
|
|
@@ -147,7 +148,7 @@ class AgentsResource(SyncAPIResource):
|
|
147
148
|
options=make_request_options(
|
148
149
|
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
149
150
|
),
|
150
|
-
cast_to=
|
151
|
+
cast_to=DeleteResponse,
|
151
152
|
)
|
152
153
|
|
153
154
|
def delete_by_name(
|
@@ -160,7 +161,7 @@ class AgentsResource(SyncAPIResource):
|
|
160
161
|
extra_query: Query | None = None,
|
161
162
|
extra_body: Body | None = None,
|
162
163
|
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
163
|
-
) ->
|
164
|
+
) -> DeleteResponse:
|
164
165
|
"""
|
165
166
|
Delete an agent by its unique name.
|
166
167
|
|
@@ -180,7 +181,7 @@ class AgentsResource(SyncAPIResource):
|
|
180
181
|
options=make_request_options(
|
181
182
|
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
182
183
|
),
|
183
|
-
cast_to=
|
184
|
+
cast_to=DeleteResponse,
|
184
185
|
)
|
185
186
|
|
186
187
|
def retrieve_by_name(
|
@@ -667,7 +668,7 @@ class AsyncAgentsResource(AsyncAPIResource):
|
|
667
668
|
extra_query: Query | None = None,
|
668
669
|
extra_body: Body | None = None,
|
669
670
|
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
670
|
-
) ->
|
671
|
+
) -> DeleteResponse:
|
671
672
|
"""
|
672
673
|
Delete an agent by its unique ID.
|
673
674
|
|
@@ -687,7 +688,7 @@ class AsyncAgentsResource(AsyncAPIResource):
|
|
687
688
|
options=make_request_options(
|
688
689
|
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
689
690
|
),
|
690
|
-
cast_to=
|
691
|
+
cast_to=DeleteResponse,
|
691
692
|
)
|
692
693
|
|
693
694
|
async def delete_by_name(
|
@@ -700,7 +701,7 @@ class AsyncAgentsResource(AsyncAPIResource):
|
|
700
701
|
extra_query: Query | None = None,
|
701
702
|
extra_body: Body | None = None,
|
702
703
|
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
703
|
-
) ->
|
704
|
+
) -> DeleteResponse:
|
704
705
|
"""
|
705
706
|
Delete an agent by its unique name.
|
706
707
|
|
@@ -720,7 +721,7 @@ class AsyncAgentsResource(AsyncAPIResource):
|
|
720
721
|
options=make_request_options(
|
721
722
|
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
|
722
723
|
),
|
723
|
-
cast_to=
|
724
|
+
cast_to=DeleteResponse,
|
724
725
|
)
|
725
726
|
|
726
727
|
async def retrieve_by_name(
|
@@ -192,6 +192,8 @@ class MessagesResource(SyncAPIResource):
|
|
192
192
|
List Messages
|
193
193
|
|
194
194
|
Args:
|
195
|
+
task_id: The task ID
|
196
|
+
|
195
197
|
extra_headers: Send extra headers
|
196
198
|
|
197
199
|
extra_query: Add additional query parameters to the request
|
@@ -377,6 +379,8 @@ class AsyncMessagesResource(AsyncAPIResource):
|
|
377
379
|
List Messages
|
378
380
|
|
379
381
|
Args:
|
382
|
+
task_id: The task ID
|
383
|
+
|
380
384
|
extra_headers: Send extra headers
|
381
385
|
|
382
386
|
extra_query: Add additional query parameters to the request
|