diff --git a/examples/agentic_workflows/mcp_weather_agent.py b/examples/agentic_workflows/mcp_weather_agent.py index 8c0b21c7..d89586ae 100644 --- a/examples/agentic_workflows/mcp_weather_agent.py +++ b/examples/agentic_workflows/mcp_weather_agent.py @@ -90,7 +90,7 @@ def create_mcp_agent_workflow(executor: WorkflowExecutor, mcp_server: str) -> Co plan_task = LlmChatComplete( task_ref_name="plan_action", llm_provider="anthropic", - model="claude-sonnet-4-20250514", + model="claude-sonnet-5", messages=[ ChatMessage( role="system", diff --git a/examples/agents/56_rag_agent.py b/examples/agents/56_rag_agent.py index abccf4c9..3ae8af33 100644 --- a/examples/agents/56_rag_agent.py +++ b/examples/agents/56_rag_agent.py @@ -65,7 +65,7 @@ "text": ( "Agent Configuration. Agents are defined with a name, model, instructions, " "and tools. The model field uses the format 'provider/model_name', e.g. " - "'openai/gpt-4o' or 'anthropic/claude-sonnet-4-20250514'. Instructions can be " + "'openai/gpt-4o' or 'anthropic/claude-sonnet-5'. Instructions can be " "a string or a PromptTemplate referencing a stored prompt. Tools can be " "@tool-decorated Python functions, http_tool for REST APIs, mcp_tool for " "MCP servers, or agent_tool to wrap another agent as a callable tool. " diff --git a/examples/agents/59_coding_agent.py b/examples/agents/59_coding_agent.py index cc531e5f..02d1cb24 100644 --- a/examples/agents/59_coding_agent.py +++ b/examples/agents/59_coding_agent.py @@ -26,7 +26,7 @@ qa_tester = Agent( name="qa_tester", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are a meticulous QA engineer. Review the code written by the " "coder for correctness, edge cases, and bugs. Write and execute test " @@ -45,7 +45,7 @@ coder = Agent( name="coder", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are an expert Python developer. Write clean, well-structured " "Python code to solve the given problem. Always execute your code to " diff --git a/examples/agents/60_github_coding_agent.py b/examples/agents/60_github_coding_agent.py index 0ff32d2a..6d2f1305 100644 --- a/examples/agents/60_github_coding_agent.py +++ b/examples/agents/60_github_coding_agent.py @@ -249,7 +249,7 @@ def create_pull_request(title: str, body: str, issue_number: int = 0) -> str: github_agent = Agent( name="github_agent", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are a GitHub operations specialist. You handle all git and " "GitHub CLI interactions.\n\n" @@ -282,7 +282,7 @@ def create_pull_request(title: str, body: str, issue_number: int = 0) -> str: coder = Agent( name="coder", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are an expert developer. Write clean, well-structured code.\n\n" "WHEN YOU RECEIVE A TASK:\n" @@ -311,7 +311,7 @@ def create_pull_request(title: str, body: str, issue_number: int = 0) -> str: qa_tester = Agent( name="qa_tester", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are a meticulous QA engineer. Review the code written by the " "coder for correctness, edge cases, and bugs.\n\n" @@ -340,7 +340,7 @@ def create_pull_request(title: str, body: str, issue_number: int = 0) -> str: coding_team = Agent( name="coding_team", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are a coding team coordinator. Delegate the incoming request " "to github_agent to get started — it will pick an issue and set " diff --git a/examples/agents/60a_github_coding_agent_simple.py b/examples/agents/60a_github_coding_agent_simple.py index 7454e012..4da16585 100644 --- a/examples/agents/60a_github_coding_agent_simple.py +++ b/examples/agents/60a_github_coding_agent_simple.py @@ -41,7 +41,7 @@ github_agent = Agent( name="github_agent", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are a GitHub operations specialist. You handle all git and " "GitHub CLI interactions.\n\n" @@ -77,7 +77,7 @@ coder = Agent( name="coder", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are an expert developer. You write clean, well-structured code.\n\n" f"The repo is cloned at {WORK_DIR}.\n\n" @@ -109,7 +109,7 @@ qa_tester = Agent( name="qa_tester", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are a meticulous QA engineer. Review the code written by the " "coder for correctness, edge cases, and bugs.\n\n" @@ -134,7 +134,7 @@ coding_team = Agent( name="coding_team", - model="anthropic/claude-sonnet-4-20250514", + model="anthropic/claude-sonnet-4-6", instructions=( "You are a coding team coordinator. Delegate the incoming request " "to github_agent to get started — it will pick an issue and set " diff --git a/examples/agents/74_cli_error_output.py b/examples/agents/74_cli_error_output.py index 4f0a586d..87b82f50 100644 --- a/examples/agents/74_cli_error_output.py +++ b/examples/agents/74_cli_error_output.py @@ -36,7 +36,7 @@ with AgentRuntime() as rt: result = rt.run(agent, prompt) result.print_result() - output = result.output or "" + output = str(result.output) # Verify the agent saw the error output assert "No such file or directory" in output or "nonexistent" in output, ( diff --git a/examples/agents/adk/35_rag_agent.py b/examples/agents/adk/35_rag_agent.py index 250224bf..c9060a8f 100644 --- a/examples/agents/adk/35_rag_agent.py +++ b/examples/agents/adk/35_rag_agent.py @@ -76,7 +76,7 @@ "text": ( "Agent Configuration. Agents are defined with a name, model, instructions, " "and tools. The model field uses the format 'provider/model_name', e.g. " - "'openai/gpt-4o' or 'anthropic/claude-sonnet-4-20250514'. Instructions can be " + "'openai/gpt-4o' or 'anthropic/claude-sonnet-5'. Instructions can be " "a string or a PromptTemplate referencing a stored prompt. Tools can be " "@tool-decorated Python functions, http_tool for REST APIs, mcp_tool for " "MCP servers, or agent_tool to wrap another agent as a callable tool. " diff --git a/examples/agents/langgraph/46_crash_and_resume.py b/examples/agents/langgraph/46_crash_and_resume.py index adc112e5..df27a41c 100644 --- a/examples/agents/langgraph/46_crash_and_resume.py +++ b/examples/agents/langgraph/46_crash_and_resume.py @@ -104,80 +104,80 @@ def generate_report(analysis: str) -> str: ) -# -- Phase 1: Deploy, start, serve briefly, then crash -------------------- - -print("=" * 60) -print("Phase 1: Deploy + start, then simulate crash") -print("=" * 60) - -with AgentRuntime() as runtime: - # Deploy the workflow definition (in production, do this once in CI/CD) - runtime.deploy(graph) - print("Agent deployed to server.") - - # Start an execution by name — the agent is already deployed on the server, - # so we only need to send the name and prompt (not the full graph object). - handle = runtime.start( - "sales_analyst", - "Fetch the Q4 2025 sales data, run a full trend analysis on it, " - "then generate an executive summary report. " - "Call each tool in sequence — do not skip any step.", - ) - print(f"Execution started: {handle.execution_id}") - - # Save execution_id so we can check status later - with open(SESSION_FILE, "w") as f: - f.write(handle.execution_id) - - # Serve workers just long enough for the first tool to start - print("\nServing workers briefly...") - runtime.serve(graph, blocking=False) - time.sleep(8) - -print("\nRuntime closed — workers are dead, workflow persists on server.") -print() - -with open(SESSION_FILE) as f: - saved_execution_id = f.read().strip() - -# -- Pause: let the user see the stalled execution in the UI -------------- - -ui_link = f"{UI_BASE}/execution/{saved_execution_id}" -print("-" * 60) -print("Open the Conductor UI to see the execution in RUNNING state:") -print(f" {ui_link}") -print() -print("The workflow is alive on the server but stalled — no workers are") -print("polling to pick up the next task. The completed steps are") -print("preserved; only the remaining steps need to run.") -print("-" * 60) -input("\nPress Enter to resume (restart workers)...") -print() - - -# -- Phase 2: Restart serve — workers pick up stalled tasks ---------------- - -print("=" * 60) -print("Phase 2: Restart serve() — workers reconnect automatically") -print("=" * 60) - -with AgentRuntime() as runtime: - # serve() re-registers the same workers. The server dispatches - # stalled tasks to them — no resume() call needed. - print("\nServing workers (non-blocking for demo)...") - runtime.serve(graph, blocking=False) - - # Poll until the execution completes - print(f"Polling execution: {saved_execution_id}") - status = runtime.get_status(saved_execution_id) - while not status.is_complete: - time.sleep(2) +if __name__ == "__main__": + # -- Phase 1: Deploy, start, serve briefly, then crash -------------------- + + print("=" * 60) + print("Phase 1: Deploy + start, then simulate crash") + print("=" * 60) + + with AgentRuntime() as runtime: + # Deploy the workflow definition (in production, do this once in CI/CD) + runtime.deploy(graph) + print("Agent deployed to server.") + + # Start an execution by name — the agent is already deployed on the server, + # so we only need to send the name and prompt (not the full graph object). + handle = runtime.start( + "sales_analyst", + "Fetch the Q4 2025 sales data, run a full trend analysis on it, " + "then generate an executive summary report. " + "Call each tool in sequence — do not skip any step.", + ) + print(f"Execution started: {handle.execution_id}") + + # Save execution_id so we can check status later + with open(SESSION_FILE, "w") as f: + f.write(handle.execution_id) + + # Serve workers just long enough for the first tool to start + print("\nServing workers briefly...") + runtime.serve(graph, blocking=False) + time.sleep(8) + + print("\nRuntime closed — workers are dead, workflow persists on server.") + print() + + with open(SESSION_FILE) as f: + saved_execution_id = f.read().strip() + + # -- Pause: let the user see the stalled execution in the UI -------------- + + ui_link = f"{UI_BASE}/execution/{saved_execution_id}" + print("-" * 60) + print("Open the Conductor UI to see the execution in RUNNING state:") + print(f" {ui_link}") + print() + print("The workflow is alive on the server but stalled — no workers are") + print("polling to pick up the next task. The completed steps are") + print("preserved; only the remaining steps need to run.") + print("-" * 60) + input("\nPress Enter to resume (restart workers)...") + print() + + # -- Phase 2: Restart serve — workers pick up stalled tasks ---------------- + + print("=" * 60) + print("Phase 2: Restart serve() — workers reconnect automatically") + print("=" * 60) + + with AgentRuntime() as runtime: + # serve() re-registers the same workers. The server dispatches + # stalled tasks to them — no resume() call needed. + print("\nServing workers (non-blocking for demo)...") + runtime.serve(graph, blocking=False) + + # Poll until the execution completes + print(f"Polling execution: {saved_execution_id}") status = runtime.get_status(saved_execution_id) - print(f" status: {status.status}") + while not status.is_complete: + time.sleep(2) + status = runtime.get_status(saved_execution_id) + print(f" status: {status.status}") - print(f"\nStatus: {status.status}") - print(f"Output: {status.output}") - print("\nCheck the completed execution in the UI:") - print(f" {ui_link}") + print(f"\nStatus: {status.status}") + print(f"Output: {status.output}") + print("\nCheck the completed execution in the UI:") + print(f" {ui_link}") -print("\nDone — same workflow, seamless resume after simulated crash.") + print("\nDone — same workflow, seamless resume after simulated crash.") diff --git a/examples/agents/settings.py b/examples/agents/settings.py index 0037ca42..9503fd54 100644 --- a/examples/agents/settings.py +++ b/examples/agents/settings.py @@ -3,10 +3,10 @@ Set ``CONDUCTOR_AGENT_LLM_MODEL`` as an environment variable to override the default model used by all examples:: - export CONDUCTOR_AGENT_LLM_MODEL=anthropic/claude-sonnet-4-20250514 + export CONDUCTOR_AGENT_LLM_MODEL=anthropic/claude-sonnet-4-6 export CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash -If unset, defaults to ``anthropic/claude-sonnet-4-6``. +If unset, defaults to ``openai/gpt-4o``. ``CONDUCTOR_AGENT_SECONDARY_LLM_MODEL`` provides a second model for multi-model examples (e.g., cheap triage vs capable specialist). Defaults to ``openai/gpt-4o``. diff --git a/scripts/run_examples.sh b/scripts/run_examples.sh index 3a50b6d4..121e9ebc 100755 --- a/scripts/run_examples.sh +++ b/scripts/run_examples.sh @@ -15,17 +15,27 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -EXAMPLES_DIR="$(cd "$SCRIPT_DIR/../examples" && pwd)" +EXAMPLES_DIR="$(cd "$SCRIPT_DIR/../examples/agents" && pwd)" TIMEOUT="${EXAMPLE_TIMEOUT:-300}" # Cross-platform python: honour PYTHON env var, then try python3, then python. PYTHON="${PYTHON:-$(command -v python3 2>/dev/null || command -v python 2>/dev/null || echo python3)}" +# Cross-platform timeout: GNU coreutils provides `timeout`; macOS ships it as +# `gtimeout`, and only when coreutils is installed. Without either, run the +# examples unbounded rather than failing every one of them. +TIMEOUT_BIN="$(command -v timeout 2>/dev/null || command -v gtimeout 2>/dev/null || true)" +if [[ -z "$TIMEOUT_BIN" ]]; then + echo "Warning: neither 'timeout' nor 'gtimeout' found; examples will run" >&2 + echo " without a time limit. On macOS: brew install coreutils" >&2 +fi + # Cross-platform temp dir: honour TMPDIR (set on macOS/Linux), fall back to /tmp. TMP_BASE="${TMPDIR:-${TEMP:-/tmp}}" # Examples that require external services not typically available in a -# standard test environment. +# standard test environment. Run them with --all once the services and +# credentials they need are in place. SKIP_BY_DEFAULT=( "04_http_and_mcp_tools" # needs MCP server running "04_mcp_weather" # needs MCP server running @@ -33,6 +43,27 @@ SKIP_BY_DEFAULT=( "25_semantic_memory" # needs vector store / extra deps "26_opentelemetry_tracing" # needs OTel collector "28_gpt_assistant_agent" # needs OpenAI Assistants API key + + # Act on real third-party accounts: these clone, branch, push, open pull + # requests or post messages, so a default run must not reach them. + "16c_credentials_cli_tools" # needs AWS credentials; can open PRs + "16d_credentials_gh_cli" # needs an authenticated gh CLI + "60_github_coding_agent" # pushes branches, opens PRs + "60a_github_coding_agent_simple" # clones and pushes to a real repo + "61_github_coding_agent_chained" # needs GITHUB_TOKEN; pushes branches + "61a_github_coding_agent_claude_code" # needs GITHUB_TOKEN; pushes branches + "91_slack_autofix_agent" # pushes, opens PRs, posts to Slack + + # Block on stdin with no canned response in HITL_STDIN below, so they would + # sit until the per-example timeout. + "09d_human_tool" + "18_manual_selection" + "32_human_guardrail" + "62_coding_agent_openai" + "78_approval_workflow" + "81_chat_repl" + "82_coding_agent" + "82b_coding_agent_tui" ) # HITL examples that call input() — we pipe automated responses via stdin. @@ -117,7 +148,11 @@ echo " Running ${#EXAMPLES[@]} examples" if [[ ${#SKIPPED[@]} -gt 0 ]]; then echo " Skipping ${#SKIPPED[@]}: ${SKIPPED[*]}" fi -echo " Timeout: ${TIMEOUT}s per example" +if [[ -n "$TIMEOUT_BIN" ]]; then + echo " Timeout: ${TIMEOUT}s per example" +else + echo " Timeout: none (no timeout binary found)" +fi echo "==========================================" echo "" @@ -141,9 +176,9 @@ for example in "${EXAMPLES[@]}"; do if [[ -n "$STDIN_RESPONSE" ]]; then # Use `yes` to provide unlimited identical responses — handles # cases where the LLM calls an approval tool multiple times. - RUN_CMD="yes '$STDIN_RESPONSE' | timeout $TIMEOUT $PYTHON $example" + RUN_CMD="yes '$STDIN_RESPONSE' | ${TIMEOUT_BIN:+$TIMEOUT_BIN $TIMEOUT }$PYTHON $example" else - RUN_CMD="timeout $TIMEOUT $PYTHON $example" + RUN_CMD="${TIMEOUT_BIN:+$TIMEOUT_BIN $TIMEOUT }$PYTHON $example" fi if eval "$RUN_CMD" > "$LOG_FILE" 2>&1; then