Skip to content

Commit 7091f45

Browse files
committed
Adding changes
1 parent cc4b5fe commit 7091f45

File tree

9 files changed

+199
-113
lines changed

9 files changed

+199
-113
lines changed

examples/tutorials/10_agentic/00_base/080_batch_events/test_batch_events.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
# Configuration
1212
BASE_URL = "http://localhost:5003"
13-
AGENT_ID = "b4f32d71-ff69-4ac9-84d1-eb2937fea0c7"
13+
# AGENT_ID = "b4f32d71-ff69-4ac9-84d1-eb2937fea0c7"
14+
AGENT_ID = "58e78cd0-c898-4009-b5d9-eada8ebcad83"
1415
RPC_ENDPOINT = f"{BASE_URL}/agents/{AGENT_ID}/rpc"
1516

1617
async def send_rpc_request(method: str, params: dict):
@@ -107,4 +108,4 @@ async def main():
107108
print(f"📋 Task ID: {task_id}")
108109

109110
if __name__ == "__main__":
110-
asyncio.run(main())
111+
asyncio.run(main())

examples/tutorials/10_agentic/10_temporal/010_agent_chat/project/run_worker.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from workflow import At010AgentChatWorkflow
99

1010

11+
1112
environment_variables = EnvironmentVariables.refresh()
1213

1314
logger = make_logger(__name__)

examples/tutorials/10_agentic/10_temporal/020_state_machine/project/run_worker.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import asyncio
2-
import os
32

43
from agentex.lib.core.temporal.activities import get_all_activities
54
from agentex.lib.core.temporal.workers.worker import AgentexWorker

examples/tutorials/10_agentic/10_temporal/020_state_machine/project/workflow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import asyncio
2-
import json
32
from typing import override
43

54
from temporalio import workflow
@@ -26,6 +25,7 @@
2625
if environment_variables.AGENT_NAME is None:
2726
raise ValueError("Environment variable AGENT_NAME is not set")
2827

28+
2929
logger = make_logger(__name__)
3030

3131
@workflow.defn(name=environment_variables.WORKFLOW_NAME)

examples/tutorials/10_agentic/10_temporal/020_state_machine/project/workflows/deep_research/clarify_user_query.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
logger = make_logger(__name__)
1212

13+
1314
FOLLOW_UP_QUESTION_TEMPLATE = """
1415
Given the following research query from the user, ask a follow up question to clarify the research direction.
1516
<query>

src/agentex/lib/cli/handlers/cleanup_handlers.py

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -168,19 +168,13 @@ def cleanup_single_task(client: Agentex, agent_name: str, task_id: str) -> None:
168168
"""
169169
try:
170170
# Use the agent RPC method to cancel the task
171-
try:
172-
client.agents.rpc_by_name(
173-
agent_name=agent_name,
174-
method="task/cancel",
175-
params={"task_id": task_id}
176-
)
177-
logger.debug(f"Successfully cancelled task {task_id} via agent '{agent_name}'")
178-
except Exception as e:
179-
# If RPC cancel fails, try direct task deletion as fallback
180-
logger.warning(f"RPC task/cancel failed for task {task_id}, trying direct deletion: {e}")
181-
client.tasks.delete(task_id=task_id)
182-
logger.debug(f"Successfully deleted task {task_id} directly")
183-
171+
client.agents.rpc_by_name(
172+
agent_name=agent_name,
173+
method="task/cancel",
174+
params={"task_id": task_id}
175+
)
176+
logger.debug(f"Successfully cancelled task {task_id} via agent '{agent_name}'")
177+
184178
except Exception as e:
185-
logger.warning(f"Failed to cleanup task {task_id}: {e}")
186-
raise
179+
logger.warning(f"RPC task/cancel failed for task {task_id}: {e}")
180+
raise

src/agentex/lib/cli/handlers/deploy_handlers.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from agentex.lib.cli.utils.auth_utils import _encode_principal_context
1212
from agentex.lib.cli.utils.exceptions import DeploymentError, HelmError
1313
from agentex.lib.cli.utils.kubectl_utils import check_and_switch_cluster_context
14+
from agentex.lib.cli.utils.path_utils import calculate_docker_acp_module, PathResolutionError
1415
from agentex.lib.environment_variables import EnvVarKeys
1516
from agentex.lib.sdk.config.agent_config import AgentConfig
1617
from agentex.lib.sdk.config.agent_manifest import AgentManifest
@@ -100,10 +101,24 @@ def convert_env_vars_dict_to_list(env_vars: dict[str, str]) -> list[dict[str, st
100101
return [{"name": key, "value": value} for key, value in env_vars.items()]
101102

102103

104+
def add_acp_command_to_helm_values(helm_values: dict[str, Any], manifest: AgentManifest, manifest_path: str) -> None:
105+
"""Add dynamic ACP command to helm values based on manifest configuration"""
106+
try:
107+
docker_acp_module = calculate_docker_acp_module(manifest, manifest_path)
108+
# Create the uvicorn command with the correct module path
109+
helm_values["command"] = ["uvicorn", f"{docker_acp_module}:acp", "--host", "0.0.0.0", "--port", "8000"]
110+
logger.info(f"Using dynamic ACP command: uvicorn {docker_acp_module}:acp")
111+
except (PathResolutionError, Exception) as e:
112+
# Fallback to default command structure
113+
logger.warning(f"Could not calculate dynamic ACP module ({e}), using default: project.acp")
114+
helm_values["command"] = ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"]
115+
116+
103117
def merge_deployment_configs(
104118
manifest: AgentManifest,
105119
cluster_config: ClusterConfig | None,
106120
deploy_overrides: InputDeployOverrides,
121+
manifest_path: str,
107122
) -> dict[str, Any]:
108123
agent_config: AgentConfig = manifest.agent
109124

@@ -231,10 +246,16 @@ def merge_deployment_configs(
231246
# Convert the env vars to a list of dictionaries
232247
if "env" in helm_values:
233248
helm_values["env"] = convert_env_vars_dict_to_list(helm_values["env"])
249+
250+
# Convert the temporal worker env vars to a list of dictionaries
234251
if TEMPORAL_WORKER_KEY in helm_values and "env" in helm_values[TEMPORAL_WORKER_KEY]:
235252
helm_values[TEMPORAL_WORKER_KEY]["env"] = convert_env_vars_dict_to_list(
236253
helm_values[TEMPORAL_WORKER_KEY]["env"]
237254
)
255+
256+
# Add dynamic ACP command based on manifest configuration
257+
add_acp_command_to_helm_values(helm_values, manifest, manifest_path)
258+
238259
print("Deploying with the following helm values: ", helm_values)
239260
return helm_values
240261

@@ -290,7 +311,7 @@ def deploy_agent(
290311
add_helm_repo()
291312

292313
# Merge configurations
293-
helm_values = merge_deployment_configs(manifest, override_config, deploy_overrides)
314+
helm_values = merge_deployment_configs(manifest, override_config, deploy_overrides, manifest_path)
294315

295316
# Create values file
296317
values_file = create_helm_values_file(helm_values)

src/agentex/lib/cli/handlers/run_handlers.py

Lines changed: 19 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@
1111
cleanup_agent_workflows,
1212
should_cleanup_on_restart
1313
)
14+
from agentex.lib.cli.utils.path_utils import (
15+
get_file_paths,
16+
calculate_uvicorn_target_for_local,
17+
)
18+
1419
from agentex.lib.environment_variables import EnvVarKeys
1520
from agentex.lib.sdk.config.agent_manifest import AgentManifest
1621
from agentex.lib.utils.logging import make_logger
@@ -104,7 +109,10 @@ async def start_worker() -> asyncio.subprocess.Process:
104109
# PRE-RESTART CLEANUP - NEW!
105110
if current_process is not None:
106111
# Extract agent name from worker path for cleanup
107-
agent_name = worker_path.parent.parent.name
112+
113+
agent_name = env.get("AGENT_NAME")
114+
if agent_name is None:
115+
agent_name = worker_path.parent.parent.name
108116

109117
# Perform cleanup if configured
110118
if should_cleanup_on_restart():
@@ -180,15 +188,17 @@ async def start_worker() -> asyncio.subprocess.Process:
180188

181189

182190
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
184192
) -> asyncio.subprocess.Process:
185193
"""Start the ACP server process"""
186-
# Use the actual file path instead of module path for better reload detection
194+
# Use file path relative to manifest directory if possible
195+
uvicorn_target = calculate_uvicorn_target_for_local(acp_path, manifest_dir)
196+
187197
cmd = [
188198
sys.executable,
189199
"-m",
190200
"uvicorn",
191-
f"{acp_path.parent.name}.acp:acp",
201+
f"{uvicorn_target}:acp",
192202
"--reload",
193203
"--reload-dir",
194204
str(acp_path.parent), # Watch the project directory specifically
@@ -201,7 +211,7 @@ async def start_acp_server(
201211
console.print(f"[blue]Starting ACP server from {acp_path} on port {port}...[/blue]")
202212
return await asyncio.create_subprocess_exec(
203213
*cmd,
204-
cwd=acp_path.parent.parent,
214+
cwd=manifest_dir, # Always use manifest directory as CWD for consistency
205215
env=env,
206216
stdout=asyncio.subprocess.PIPE,
207217
stderr=asyncio.subprocess.STDOUT,
@@ -218,7 +228,7 @@ async def start_temporal_worker(
218228

219229
return await asyncio.create_subprocess_exec(
220230
*cmd,
221-
cwd=worker_path.parent,
231+
cwd=worker_path.parent, # Use worker directory as CWD for imports to work
222232
env=env,
223233
stdout=asyncio.subprocess.PIPE,
224234
stderr=asyncio.subprocess.STDOUT,
@@ -280,8 +290,9 @@ async def run_agent(manifest_path: str):
280290
)
281291

282292
# Start ACP server
293+
manifest_dir = Path(manifest_path).parent
283294
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
285296
)
286297
process_manager.add_process(acp_process)
287298

@@ -291,7 +302,7 @@ async def run_agent(manifest_path: str):
291302
tasks = [acp_output_task]
292303

293304
# Start temporal worker if needed
294-
if is_temporal_agent(manifest):
305+
if is_temporal_agent(manifest) and file_paths["worker"]:
295306
worker_task = await start_temporal_worker_with_reload(file_paths["worker"], agent_env, process_manager)
296307
tasks.append(worker_task)
297308

@@ -323,92 +334,7 @@ async def run_agent(manifest_path: str):
323334
await process_manager.cleanup_processes()
324335

325336

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-
373337

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-
}
412338

413339

414340
def create_agent_environment(manifest: AgentManifest) -> dict[str, str]:

0 commit comments

Comments
 (0)