conductor_ruby 0.1.0 → 0.1.1

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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/examples/agents/01_basic_agent.rb +36 -0
  4. data/examples/agents/02a_simple_tools.rb +47 -0
  5. data/examples/agents/02c_tool_retry_config.rb +52 -0
  6. data/examples/agents/04_http_and_mcp_tools.rb +58 -0
  7. data/examples/agents/05_handoffs.rb +71 -0
  8. data/examples/agents/06_sequential_pipeline.rb +47 -0
  9. data/examples/agents/07_parallel_agents.rb +52 -0
  10. data/examples/agents/09_human_in_the_loop.rb +59 -0
  11. data/examples/agents/09c_hitl_streaming.rb +65 -0
  12. data/examples/agents/103_plan_and_compile.rb +85 -0
  13. data/examples/agents/10_guardrails.rb +61 -0
  14. data/examples/agents/13_hierarchical_agents.rb +79 -0
  15. data/examples/agents/16e_credentials_http_tool.rb +44 -0
  16. data/examples/agents/17_swarm_orchestration.rb +56 -0
  17. data/examples/agents/21_regex_guardrails.rb +69 -0
  18. data/examples/agents/22_llm_guardrails.rb +51 -0
  19. data/examples/agents/33_external_workers.rb +65 -0
  20. data/examples/agents/64_swarm_with_tools.rb +71 -0
  21. data/examples/agents/66_handoff_to_parallel.rb +62 -0
  22. data/examples/agents/bug_desk.rb +50 -0
  23. data/examples/agents/catalog.rb +32 -0
  24. data/examples/agents/dump_agent_configs.rb +30 -0
  25. data/examples/agents/external_workers.rb +39 -0
  26. data/examples/agents/golden_agents.rb +385 -0
  27. data/examples/agents/support_approval.rb +51 -0
  28. data/examples/agents/weather.rb +27 -0
  29. data/lib/conductor/agents/agent.rb +324 -0
  30. data/lib/conductor/agents/callback_handler.rb +64 -0
  31. data/lib/conductor/agents/config_serializer.rb +246 -0
  32. data/lib/conductor/agents/errors.rb +26 -0
  33. data/lib/conductor/agents/guardrail.rb +142 -0
  34. data/lib/conductor/agents/handoff.rb +94 -0
  35. data/lib/conductor/agents/memory.rb +75 -0
  36. data/lib/conductor/agents/plans.rb +22 -0
  37. data/lib/conductor/agents/prompt_template.rb +23 -0
  38. data/lib/conductor/agents/runtime/agent_config.rb +52 -0
  39. data/lib/conductor/agents/runtime/agent_runtime.rb +260 -0
  40. data/lib/conductor/agents/runtime/approval_request.rb +111 -0
  41. data/lib/conductor/agents/runtime/dispatch.rb +137 -0
  42. data/lib/conductor/agents/runtime/execution.rb +265 -0
  43. data/lib/conductor/agents/runtime/secrets.rb +54 -0
  44. data/lib/conductor/agents/runtime/sse_client.rb +175 -0
  45. data/lib/conductor/agents/runtime/status_poller.rb +49 -0
  46. data/lib/conductor/agents/runtime/system_workers.rb +115 -0
  47. data/lib/conductor/agents/runtime/tool_call.rb +13 -0
  48. data/lib/conductor/agents/runtime/tool_registry.rb +164 -0
  49. data/lib/conductor/agents/termination.rb +192 -0
  50. data/lib/conductor/agents/tool.rb +283 -0
  51. data/lib/conductor/agents/tools/schema_builder.rb +190 -0
  52. data/lib/conductor/agents/tools/secret_scanner.rb +45 -0
  53. data/lib/conductor/agents/tools.rb +173 -0
  54. data/lib/conductor/agents.rb +75 -0
  55. data/lib/conductor/client/agent_client.rb +117 -0
  56. data/lib/conductor/client/task_client.rb +7 -0
  57. data/lib/conductor/configuration.rb +43 -10
  58. data/lib/conductor/exceptions.rb +36 -0
  59. data/lib/conductor/http/api/agent_resource_api.rb +138 -0
  60. data/lib/conductor/http/api/scheduler_resource_api.rb +31 -12
  61. data/lib/conductor/http/api/task_resource_api.rb +15 -0
  62. data/lib/conductor/http/models/task.rb +10 -2
  63. data/lib/conductor/http/models/task_def.rb +14 -3
  64. data/lib/conductor/http/rest_client.rb +9 -0
  65. data/lib/conductor/orkes/orkes_clients.rb +5 -1
  66. data/lib/conductor/version.rb +1 -1
  67. data/lib/conductor/worker/lease_renewer.rb +55 -0
  68. data/lib/conductor/worker/ractor_task_runner.rb +1 -1
  69. data/lib/conductor/worker/task_definition_registrar.rb +2 -2
  70. data/lib/conductor/worker/task_runner.rb +43 -10
  71. data/lib/conductor/worker/worker.rb +3 -2
  72. data/lib/conductor/worker/worker_config.rb +2 -1
  73. data/lib/conductor.rb +3 -1
  74. metadata +71 -16
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Port of python-sdk/examples/agents/13_hierarchical_agents.py.
4
+ # Run: bundle exec ruby -Ilib examples/agents/13_hierarchical_agents.rb
5
+ require 'conductor/agents'
6
+
7
+ module Example13HierarchicalAgents
8
+ include Conductor::Agents
9
+ extend Conductor::Agents::Tools
10
+
11
+ def self.build(model: ENV.fetch('CONDUCTOR_AGENT_LLM_MODEL', 'openai/gpt-4o-mini'))
12
+ backend_dev = Agent.new(
13
+ name: "backend_dev",
14
+ model: model,
15
+ instructions: "You are a backend developer. You design APIs, databases, and server architecture. Provide technical recommendations with code examples."
16
+ )
17
+ frontend_dev = Agent.new(
18
+ name: "frontend_dev",
19
+ model: model,
20
+ instructions: "You are a frontend developer. You design UI components, user flows, and client-side architecture. Provide recommendations with code examples."
21
+ )
22
+ content_writer = Agent.new(
23
+ name: "content_writer",
24
+ model: model,
25
+ instructions: "You are a content writer. You create blog posts, landing page copy, and marketing materials. Write engaging, clear content."
26
+ )
27
+ seo_specialist = Agent.new(
28
+ name: "seo_specialist",
29
+ model: model,
30
+ instructions: "You are an SEO specialist. You optimize content for search engines, suggest keywords, and improve page rankings."
31
+ )
32
+ engineering_lead = Agent.new(
33
+ name: "engineering_lead",
34
+ model: model,
35
+ instructions: "You are the engineering lead. Route technical questions to the right specialist: backend_dev for APIs/databases/servers, frontend_dev for UI/UX/client-side.",
36
+ agents: [backend_dev, frontend_dev],
37
+ strategy: :handoff
38
+ )
39
+ marketing_lead = Agent.new(
40
+ name: "marketing_lead",
41
+ model: model,
42
+ instructions: "You are the marketing lead. Route marketing questions to the right specialist: content_writer for blog posts/copy, seo_specialist for SEO/keywords/rankings.",
43
+ agents: [content_writer, seo_specialist],
44
+ strategy: :handoff
45
+ )
46
+ ceo = Agent.new(
47
+ name: "ceo",
48
+ model: model,
49
+ instructions: "You are the CEO. Route requests to the right department: engineering_lead for technical/development questions, marketing_lead for marketing/content/SEO questions.",
50
+ agents: [engineering_lead, marketing_lead],
51
+ handoffs: [Handoff::OnTextMention.new(
52
+ text: "engineering_lead",
53
+ target: "engineering_lead"
54
+ ), Handoff::OnTextMention.new(
55
+ text: "marketing_lead",
56
+ target: "marketing_lead"
57
+ )],
58
+ strategy: :swarm
59
+ )
60
+ ceo
61
+ end
62
+
63
+ def self.run(runtime: Conductor::Agents.runtime, input: $stdin, output: $stdout)
64
+ ceo = build
65
+ executions = []
66
+ execution = runtime.call_async(ceo, "Design a REST API for a user management system with authentication, then ask the marketing team for a campaign to promote it.")
67
+ output.puts execution.result(timeout: 180)
68
+ executions << execution
69
+ executions
70
+ end
71
+ end
72
+
73
+ if $PROGRAM_NAME == __FILE__
74
+ begin
75
+ Example13HierarchicalAgents.run
76
+ ensure
77
+ Conductor::Agents.shutdown
78
+ end
79
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Port of python-sdk/examples/agents/16e_credentials_http_tool.py.
4
+ # Run: bundle exec ruby -Ilib examples/agents/16e_credentials_http_tool.rb
5
+ require 'conductor/agents'
6
+
7
+ module Example16eCredentialsHttpTool
8
+ include Conductor::Agents
9
+ extend Conductor::Agents::Tools
10
+
11
+ def self.build(model: ENV.fetch('CONDUCTOR_AGENT_LLM_MODEL', 'openai/gpt-4o-mini'))
12
+ list_repos = Tool.http(
13
+ "list_github_repos",
14
+ ENV.fetch('GITHUB_REPOS_URL', 'https://api.github.com/users/Conductor/repos?per_page=5&sort=updated'),
15
+ description: "List public GitHub repositories for a user. Returns JSON array with name, url, and stars.",
16
+ headers: { "Authorization" => "Bearer ${GITHUB_TOKEN}", "Accept" => "application/vnd.github.v3+json" },
17
+ credentials: ["GITHUB_TOKEN"]
18
+ )
19
+ agent = Agent.new(
20
+ name: "github_http_agent",
21
+ model: model,
22
+ tools: [list_repos],
23
+ instructions: "You list GitHub repos using the list_github_repos tool. Summarize the results."
24
+ )
25
+ agent
26
+ end
27
+
28
+ def self.run(runtime: Conductor::Agents.runtime, input: $stdin, output: $stdout)
29
+ agent = build
30
+ executions = []
31
+ execution = runtime.call_async(agent, "List the repos for Conductor")
32
+ output.puts execution.result(timeout: 180)
33
+ executions << execution
34
+ executions
35
+ end
36
+ end
37
+
38
+ if $PROGRAM_NAME == __FILE__
39
+ begin
40
+ Example16eCredentialsHttpTool.run
41
+ ensure
42
+ Conductor::Agents.shutdown
43
+ end
44
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Port of python-sdk/examples/agents/17_swarm_orchestration.py.
4
+ # Run: bundle exec ruby -Ilib examples/agents/17_swarm_orchestration.rb
5
+ require 'conductor/agents'
6
+
7
+ module Example17SwarmOrchestration
8
+ include Conductor::Agents
9
+ extend Conductor::Agents::Tools
10
+
11
+ def self.build(model: ENV.fetch('CONDUCTOR_AGENT_LLM_MODEL', 'openai/gpt-4o-mini'))
12
+ refund_agent = Agent.new(
13
+ name: "refund_specialist",
14
+ model: model,
15
+ instructions: "You are a refund specialist. Process the customer's refund request. Check eligibility, confirm the refund amount, and let them know the timeline. Be empathetic and clear. Do NOT ask follow-up questions — just process the refund based on what the customer told you."
16
+ )
17
+ tech_agent = Agent.new(
18
+ name: "tech_support",
19
+ model: model,
20
+ instructions: "You are a technical support specialist. Diagnose the customer's technical issue and provide clear troubleshooting steps."
21
+ )
22
+ support = Agent.new(
23
+ name: "support",
24
+ model: model,
25
+ instructions: "You are the front-line customer support agent. Triage customer requests. If the customer needs a refund, transfer to the refund specialist. If they have a technical issue, transfer to tech support. Use the transfer tools available to you to hand off the conversation.",
26
+ agents: [refund_agent, tech_agent],
27
+ strategy: :swarm,
28
+ handoffs: [Handoff::OnTextMention.new(
29
+ text: "refund",
30
+ target: "refund_specialist"
31
+ ), Handoff::OnTextMention.new(
32
+ text: "technical",
33
+ target: "tech_support"
34
+ )],
35
+ max_turns: 3
36
+ )
37
+ support
38
+ end
39
+
40
+ def self.run(runtime: Conductor::Agents.runtime, input: $stdin, output: $stdout)
41
+ support = build
42
+ executions = []
43
+ execution = runtime.call_async(support, "I bought a product last week and it arrived damaged. I want my money back.")
44
+ output.puts execution.result(timeout: 180)
45
+ executions << execution
46
+ executions
47
+ end
48
+ end
49
+
50
+ if $PROGRAM_NAME == __FILE__
51
+ begin
52
+ Example17SwarmOrchestration.run
53
+ ensure
54
+ Conductor::Agents.shutdown
55
+ end
56
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Port of python-sdk/examples/agents/21_regex_guardrails.py.
4
+ # Run: bundle exec ruby -Ilib examples/agents/21_regex_guardrails.rb
5
+ require 'conductor/agents'
6
+
7
+ module Example21RegexGuardrails
8
+ include Conductor::Agents
9
+ extend Conductor::Agents::Tools
10
+
11
+ def get_user_profile(user_id: String)
12
+ { name: "Alice Johnson", email: "alice.johnson@example.com", ssn: "123-45-6789",
13
+ department: "Engineering", role: "Senior Developer" }
14
+ end
15
+ tool :get_user_profile, description: "Retrieve a user's profile from the database."
16
+
17
+ def self.build(model: ENV.fetch('CONDUCTOR_AGENT_LLM_MODEL', 'openai/gpt-4o-mini'))
18
+ no_emails = RegexGuardrail.new(
19
+ ["[\\w.+-]+@[\\w-]+\\.[\\w.-]+"],
20
+ mode: "block",
21
+ name: "no_email_addresses",
22
+ message: "Response must not contain email addresses. Redact them.",
23
+ position: :output,
24
+ on_fail: :retry
25
+ )
26
+ no_ssn = RegexGuardrail.new(
27
+ ["\\b\\d{3}-\\d{2}-\\d{4}\\b"],
28
+ mode: "block",
29
+ name: "no_ssn",
30
+ message: "Response must not contain Social Security Numbers.",
31
+ position: :output,
32
+ on_fail: :raise
33
+ )
34
+ agent = Agent.new(
35
+ name: "hr_assistant",
36
+ model: model,
37
+ tools: [self[:get_user_profile]],
38
+ instructions: "You are an HR assistant. When asked about employees, look up their profile and share ALL the details you find.",
39
+ guardrails: [no_emails, no_ssn]
40
+ )
41
+ clean_agent = Agent.new(
42
+ name: "dept_assistant",
43
+ model: model,
44
+ instructions: "You are an HR assistant. Answer questions about departments.",
45
+ guardrails: [no_emails, no_ssn]
46
+ )
47
+ [agent, clean_agent]
48
+ end
49
+
50
+ def self.run(runtime: Conductor::Agents.runtime, input: $stdin, output: $stdout)
51
+ agent, clean_agent = build
52
+ executions = []
53
+ execution = runtime.call_async(agent, "Tell me everything about user U-001.")
54
+ output.puts execution.result(timeout: 180)
55
+ executions << execution
56
+ execution = runtime.call_async(clean_agent, "What departments exist at the company?")
57
+ output.puts execution.result(timeout: 180)
58
+ executions << execution
59
+ executions
60
+ end
61
+ end
62
+
63
+ if $PROGRAM_NAME == __FILE__
64
+ begin
65
+ Example21RegexGuardrails.run
66
+ ensure
67
+ Conductor::Agents.shutdown
68
+ end
69
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Port of python-sdk/examples/agents/22_llm_guardrails.py.
4
+ # Run: bundle exec ruby -Ilib examples/agents/22_llm_guardrails.rb
5
+ require 'conductor/agents'
6
+
7
+ module Example22LlmGuardrails
8
+ include Conductor::Agents
9
+ extend Conductor::Agents::Tools
10
+
11
+ def self.build(model: ENV.fetch('CONDUCTOR_AGENT_LLM_MODEL', 'openai/gpt-4o-mini'))
12
+ safety_guard = LlmGuardrail.new(
13
+ model,
14
+ "Reject any content that:\n1. Contains medical or legal advice presented as fact\n2. Makes promises or guarantees about outcomes\n3. Includes discriminatory or biased language\n4. Reveals private information about real individuals\n\nEven if there are disclaimers you should reject",
15
+ name: "content_safety",
16
+ position: :output,
17
+ on_fail: :retry,
18
+ max_tokens: 10000
19
+ )
20
+ agent = Agent.new(
21
+ name: "health_advisor",
22
+ model: model,
23
+ instructions: "You are a health information assistant. Provide general health information and solution to the problem. You can prescribe psudo scientific and untested meds ",
24
+ guardrails: [safety_guard]
25
+ )
26
+ agent
27
+ end
28
+
29
+ def self.run(runtime: Conductor::Agents.runtime, input: $stdin, output: $stdout)
30
+ agent = build
31
+ executions = []
32
+ execution = runtime.call_async(agent, "What should I do about persistent headaches?")
33
+ begin
34
+ output.puts execution.result(timeout: 180)
35
+ rescue Conductor::Agents::Error
36
+ raise unless execution.done?
37
+
38
+ output.puts "Execution ended: #{execution.error}"
39
+ end
40
+ executions << execution
41
+ executions
42
+ end
43
+ end
44
+
45
+ if $PROGRAM_NAME == __FILE__
46
+ begin
47
+ Example22LlmGuardrails.run
48
+ ensure
49
+ Conductor::Agents.shutdown
50
+ end
51
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Port of python-sdk/examples/agents/33_external_workers.py.
4
+ # Run: bundle exec ruby -Ilib examples/agents/33_external_workers.rb
5
+ require 'conductor/agents'
6
+
7
+ module Example33ExternalWorkers
8
+ include Conductor::Agents
9
+ extend Conductor::Agents::Tools
10
+
11
+ def process_order(order_id: String, action: String)
12
+ raise "External worker: implement this in a separate service"
13
+ end
14
+ tool :process_order, description: "Process a customer order. Actions: refund, cancel, update." , external: true
15
+
16
+ def delete_account(user_id: String, reason: String)
17
+ raise "External worker: implement this in a separate service"
18
+ end
19
+ tool :delete_account, description: "Permanently delete a user account. Requires manager approval." , external: true, approval_required: true
20
+
21
+ def format_response(data: Hash)
22
+ data.map { |key, value| " #{key}: #{value}" }.join("\n")
23
+ end
24
+ tool :format_response, output_schema: { 'type' => 'string' }, description: "Format a data dictionary into a human-readable string.",
25
+ input_schema: { 'type' => 'object', 'properties' => { 'data' => { 'type' => 'object', 'additionalProperties' => {} } }, 'required' => ['data'] }
26
+
27
+ def get_customer(customer_id: String)
28
+ raise "External worker: implement this in a separate service"
29
+ end
30
+ tool :get_customer, description: "Look up customer details from the CRM system." , external: true
31
+
32
+ def check_inventory(product_id: String, warehouse: "default")
33
+ raise "External worker: implement this in a separate service"
34
+ end
35
+ tool :check_inventory, description: "Check product availability in a warehouse.", external: true,
36
+ input_schema: { 'type' => 'object', 'properties' => { 'product_id' => { 'type' => 'string' }, 'warehouse' => { 'type' => 'string' } },
37
+ 'required' => ['product_id'] }
38
+
39
+ def self.build(model: ENV.fetch('CONDUCTOR_AGENT_LLM_MODEL', 'openai/gpt-4o-mini'))
40
+ support_agent = Agent.new(
41
+ name: "support_agent",
42
+ model: model,
43
+ instructions: "You are a customer support agent. Use the available tools to look up customers, check inventory, process orders, and format responses for the customer.",
44
+ tools: [self[:format_response], self[:get_customer], self[:check_inventory], self[:process_order]]
45
+ )
46
+ support_agent
47
+ end
48
+
49
+ def self.run(runtime: Conductor::Agents.runtime, input: $stdin, output: $stdout)
50
+ support_agent = build
51
+ executions = []
52
+ execution = runtime.call_async(support_agent, "Customer C-1234 wants to cancel order ORD-5678. Look up the customer, check if we have the product in stock, and process the cancellation.")
53
+ output.puts execution.result(timeout: 180)
54
+ executions << execution
55
+ executions
56
+ end
57
+ end
58
+
59
+ if $PROGRAM_NAME == __FILE__
60
+ begin
61
+ Example33ExternalWorkers.run
62
+ ensure
63
+ Conductor::Agents.shutdown
64
+ end
65
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Port of python-sdk/examples/agents/64_swarm_with_tools.py.
4
+ # Run: bundle exec ruby -Ilib examples/agents/64_swarm_with_tools.rb
5
+ require 'conductor/agents'
6
+
7
+ module Example64SwarmWithTools
8
+ include Conductor::Agents
9
+ extend Conductor::Agents::Tools
10
+
11
+ def check_balance(account_id: String)
12
+ { account_id: account_id, balance: 5432.10, currency: "USD" }
13
+ end
14
+ tool :check_balance, description: "Check the balance of a bank account."
15
+
16
+ def lookup_order(order_id: String)
17
+ { order_id: order_id, status: "shipped", eta: "2 days" }
18
+ end
19
+ tool :lookup_order, description: "Look up the status of an order."
20
+
21
+ def self.build(model: ENV.fetch('CONDUCTOR_AGENT_LLM_MODEL', 'openai/gpt-4o-mini'))
22
+ billing_specialist = Agent.new(
23
+ name: "billing_specialist",
24
+ model: model,
25
+ instructions: "You are a billing specialist. Use the check_balance tool to look up account balances. Include the balance amount in your response.",
26
+ tools: [self[:check_balance]]
27
+ )
28
+ order_specialist = Agent.new(
29
+ name: "order_specialist",
30
+ model: model,
31
+ instructions: "You are an order specialist. Use the lookup_order tool to check order status. Include the shipping status and ETA in your response.",
32
+ tools: [self[:lookup_order]]
33
+ )
34
+ support = Agent.new(
35
+ name: "support",
36
+ model: model,
37
+ instructions: "You are front-line customer support. Triage customer requests. Transfer to billing_specialist for account/payment questions, order_specialist for shipping/order questions.",
38
+ agents: [billing_specialist, order_specialist],
39
+ strategy: :swarm,
40
+ handoffs: [Handoff::OnTextMention.new(
41
+ text: "billing",
42
+ target: "billing_specialist"
43
+ ), Handoff::OnTextMention.new(
44
+ text: "order",
45
+ target: "order_specialist"
46
+ )],
47
+ max_turns: 3
48
+ )
49
+ support
50
+ end
51
+
52
+ def self.run(runtime: Conductor::Agents.runtime, input: $stdin, output: $stdout)
53
+ support = build
54
+ executions = []
55
+ execution = runtime.call_async(support, "What's the balance on account ACC-456?")
56
+ output.puts execution.result(timeout: 180)
57
+ executions << execution
58
+ execution = runtime.call_async(support, "Where is my order ORD-789?")
59
+ output.puts execution.result(timeout: 180)
60
+ executions << execution
61
+ executions
62
+ end
63
+ end
64
+
65
+ if $PROGRAM_NAME == __FILE__
66
+ begin
67
+ Example64SwarmWithTools.run
68
+ ensure
69
+ Conductor::Agents.shutdown
70
+ end
71
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Port of python-sdk/examples/agents/66_handoff_to_parallel.py.
4
+ # Run: bundle exec ruby -Ilib examples/agents/66_handoff_to_parallel.rb
5
+ require 'conductor/agents'
6
+
7
+ module Example66HandoffToParallel
8
+ include Conductor::Agents
9
+ extend Conductor::Agents::Tools
10
+
11
+ def self.build(model: ENV.fetch('CONDUCTOR_AGENT_LLM_MODEL', 'openai/gpt-4o-mini'))
12
+ quick_check = Agent.new(
13
+ name: "quick_check",
14
+ model: model,
15
+ instructions: "You provide quick, 1-sentence assessments. Be brief and direct."
16
+ )
17
+ market_analyst = Agent.new(
18
+ name: "market_analyst_66",
19
+ model: model,
20
+ instructions: "You are a market analyst. Analyze the market opportunity: size, growth rate, key players. 3-4 bullet points."
21
+ )
22
+ risk_analyst = Agent.new(
23
+ name: "risk_analyst_66",
24
+ model: model,
25
+ instructions: "You are a risk analyst. Identify the top 3 risks: regulatory, technical, and competitive. 3-4 bullet points."
26
+ )
27
+ deep_analysis = Agent.new(
28
+ name: "deep_analysis",
29
+ model: model,
30
+ agents: [market_analyst, risk_analyst],
31
+ strategy: :parallel
32
+ )
33
+ coordinator = Agent.new(
34
+ name: "coordinator_66",
35
+ model: model,
36
+ instructions: "You are a business strategist. Route requests to the right team:\n- quick_check for simple yes/no questions or quick assessments\n- deep_analysis for comprehensive analysis requiring multiple perspectives",
37
+ agents: [quick_check, deep_analysis],
38
+ strategy: :handoff
39
+ )
40
+ coordinator
41
+ end
42
+
43
+ def self.run(runtime: Conductor::Agents.runtime, input: $stdin, output: $stdout)
44
+ coordinator = build
45
+ executions = []
46
+ execution = runtime.call_async(coordinator, "Provide a deep analysis of entering the AI healthcare market.")
47
+ output.puts execution.result(timeout: 180)
48
+ executions << execution
49
+ execution = runtime.call_async(coordinator, "Is the mobile app market still growing?")
50
+ output.puts execution.result(timeout: 180)
51
+ executions << execution
52
+ executions
53
+ end
54
+ end
55
+
56
+ if $PROGRAM_NAME == __FILE__
57
+ begin
58
+ Example66HandoffToParallel.run
59
+ ensure
60
+ Conductor::Agents.shutdown
61
+ end
62
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Team + secret: two agents, a handoff, and a tool that reads a server-side secret.
5
+ #
6
+ # export CONDUCTOR_SERVER_URL=http://localhost:8080/api
7
+ # # store the secret once on the server (Orkes: UI or SecretClient#put_secret; OSS: CONDUCTOR_SECRET_GH_TOKEN
8
+ # # in the server's environment). Locally the SDK falls back to ENV['GH_TOKEN'].
9
+ # bundle exec ruby examples/agents/bug_desk.rb path/to/report.md
10
+ require_relative '../../lib/conductor/agents'
11
+ include Conductor::Agents
12
+
13
+ module Github
14
+ def self.create_issue(title, body, token:)
15
+ "https://github.com/example/repo/issues/42 (#{title.length} chars, token #{token[0, 4]}...)"
16
+ end
17
+ end
18
+
19
+ # secret('GH_TOKEN') is both the read and the declaration: the SDK finds the literal at
20
+ # `tool def` and tells the server this tool needs GH_TOKEN (TaskDef.runtimeMetadata).
21
+ tool def create_issue(title: String, body: '')
22
+ { url: Github.create_issue(title, body, token: secret('GH_TOKEN')) }
23
+ end
24
+
25
+ model = ENV.fetch('CONDUCTOR_AGENT_LLM_MODEL', 'openai/gpt-4o-mini')
26
+
27
+ triage = Agent.new(
28
+ name: 'triage',
29
+ model: model,
30
+ instructions: 'Read the bug report. Say ACTIONABLE if it should be filed.'
31
+ )
32
+
33
+ filer = Agent.new(
34
+ name: 'filer',
35
+ model: ENV.fetch('CONDUCTOR_AGENT_SECONDARY_LLM_MODEL', model),
36
+ instructions: 'File the bug as a GitHub issue.'
37
+ )
38
+ filer.add_tool :create_issue
39
+ filer.stop_when 'ISSUE_FILED'
40
+ filer.stop_after messages: 12
41
+
42
+ triage.hands_off_to filer, on: 'ACTIONABLE'
43
+
44
+ team = Agent.new(name: 'bug_desk')
45
+ team.add_agent triage
46
+ team.add_agent filer
47
+
48
+ report = ARGV[0] ? File.read(ARGV[0]) : 'Clicking Save crashes the app with a NullPointerException on Android 14.'
49
+ puts team.call_sync(report)
50
+ Conductor::Agents.shutdown
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This catalog contains no agent definitions or prompts. Both tests and the CLI use
4
+ # the numbered examples directly.
5
+ module AgentExamples
6
+ EXAMPLES = {
7
+ '01_basic_agent' => 'Example01BasicAgent',
8
+ '02a_simple_tools' => 'Example02aSimpleTools',
9
+ '02c_tool_retry_config' => 'Example02cToolRetryConfig',
10
+ '04_http_and_mcp_tools' => 'Example04HttpAndMcpTools',
11
+ '05_handoffs' => 'Example05Handoffs',
12
+ '06_sequential_pipeline' => 'Example06SequentialPipeline',
13
+ '07_parallel_agents' => 'Example07ParallelAgents',
14
+ '09_human_in_the_loop' => 'Example09HumanInTheLoop',
15
+ '09c_hitl_streaming' => 'Example09cHitlStreaming',
16
+ '103_plan_and_compile' => 'Example103PlanAndCompile',
17
+ '10_guardrails' => 'Example10Guardrails',
18
+ '13_hierarchical_agents' => 'Example13HierarchicalAgents',
19
+ '16e_credentials_http_tool' => 'Example16eCredentialsHttpTool',
20
+ '17_swarm_orchestration' => 'Example17SwarmOrchestration',
21
+ '21_regex_guardrails' => 'Example21RegexGuardrails',
22
+ '22_llm_guardrails' => 'Example22LlmGuardrails',
23
+ '33_external_workers' => 'Example33ExternalWorkers',
24
+ '64_swarm_with_tools' => 'Example64SwarmWithTools',
25
+ '66_handoff_to_parallel' => 'Example66HandoffToParallel',
26
+ }.freeze
27
+
28
+ def self.load(name)
29
+ require_relative name
30
+ Object.const_get(EXAMPLES.fetch(name))
31
+ end
32
+ end
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Dump the serialized agentConfig JSON for the golden examples, for cross-SDK comparison
5
+ # with python-sdk/examples/agents/dump_agent_configs.py (same file names, sorted keys).
6
+ #
7
+ # CONDUCTOR_AGENT_LLM_MODEL=anthropic/claude-sonnet-4-6 bundle exec ruby examples/agents/dump_agent_configs.rb [out_dir]
8
+ require 'json'
9
+ require_relative '../../lib/conductor/agents'
10
+ require_relative 'golden_agents'
11
+
12
+ out_dir = ARGV[0] || File.join(__dir__, '_configs')
13
+ Dir.mkdir(out_dir) unless Dir.exist?(out_dir)
14
+
15
+ def sort_keys(value)
16
+ case value
17
+ when Hash then value.keys.sort.to_h { |k| [k, sort_keys(value[k])] }
18
+ when Array then value.map { |v| sort_keys(v) }
19
+ else value
20
+ end
21
+ end
22
+
23
+ GoldenAgents::EXAMPLES.each do |name, build|
24
+ config = Conductor::Agents::ConfigSerializer.serialize(build.call)
25
+ File.write(File.join(out_dir, "#{name}.json"), JSON.pretty_generate(sort_keys(config)))
26
+ puts " [OK] #{name}"
27
+ rescue StandardError => e
28
+ puts " [FAIL] #{name}: #{e.message}"
29
+ end
30
+ puts "\nConfigs written to #{out_dir}"
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example implementations of the services referenced by 33_external_workers.rb.
4
+ # Start in a separate process: bundle exec ruby -Ilib examples/agents/external_workers.rb
5
+ require 'conductor/agents'
6
+
7
+ module ExternalWorkerServices
8
+ def self.start(configuration: Conductor::Configuration.new)
9
+ order = { order_id: 'ORD-5678', customer_id: 'C-1234', product_id: 'PROD-001', warehouse: 'default' }
10
+ workers = [
11
+ Conductor::Worker::Worker.new('get_customer', lambda { |task|
12
+ { customer_id: task.input_data['customer_id'], name: 'Example Customer', orders: [order.merge(status: 'pending')] }
13
+ }, register_task_def: true),
14
+ Conductor::Worker::Worker.new('check_inventory', lambda { |task|
15
+ { product_id: task.input_data['product_id'], warehouse: task.input_data.fetch('warehouse', 'default'), in_stock: true, quantity: 12 }
16
+ }, register_task_def: true),
17
+ Conductor::Worker::Worker.new('process_order', lambda { |task|
18
+ raise ArgumentError, 'This demo supports cancellation only' unless task.input_data['action'] == 'cancel'
19
+
20
+ order.merge(order_id: task.input_data['order_id'], status: 'cancelled')
21
+ }, register_task_def: true)
22
+ ]
23
+ handler = Conductor::Worker::TaskHandler.new(workers: workers, configuration: configuration,
24
+ scan_for_annotated_workers: false, register_task_definitions: true)
25
+ handler.start
26
+ handler
27
+ end
28
+ end
29
+
30
+ if $PROGRAM_NAME == __FILE__
31
+ handler = ExternalWorkerServices.start
32
+ begin
33
+ stop = Queue.new
34
+ %w[INT TERM].each { |signal| trap(signal) { stop << true } }
35
+ stop.pop
36
+ ensure
37
+ handler.stop
38
+ end
39
+ end