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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/examples/agents/01_basic_agent.rb +36 -0
- data/examples/agents/02a_simple_tools.rb +47 -0
- data/examples/agents/02c_tool_retry_config.rb +52 -0
- data/examples/agents/04_http_and_mcp_tools.rb +58 -0
- data/examples/agents/05_handoffs.rb +71 -0
- data/examples/agents/06_sequential_pipeline.rb +47 -0
- data/examples/agents/07_parallel_agents.rb +52 -0
- data/examples/agents/09_human_in_the_loop.rb +59 -0
- data/examples/agents/09c_hitl_streaming.rb +65 -0
- data/examples/agents/103_plan_and_compile.rb +85 -0
- data/examples/agents/10_guardrails.rb +61 -0
- data/examples/agents/13_hierarchical_agents.rb +79 -0
- data/examples/agents/16e_credentials_http_tool.rb +44 -0
- data/examples/agents/17_swarm_orchestration.rb +56 -0
- data/examples/agents/21_regex_guardrails.rb +69 -0
- data/examples/agents/22_llm_guardrails.rb +51 -0
- data/examples/agents/33_external_workers.rb +65 -0
- data/examples/agents/64_swarm_with_tools.rb +71 -0
- data/examples/agents/66_handoff_to_parallel.rb +62 -0
- data/examples/agents/bug_desk.rb +50 -0
- data/examples/agents/catalog.rb +32 -0
- data/examples/agents/dump_agent_configs.rb +30 -0
- data/examples/agents/external_workers.rb +39 -0
- data/examples/agents/golden_agents.rb +385 -0
- data/examples/agents/support_approval.rb +51 -0
- data/examples/agents/weather.rb +27 -0
- data/lib/conductor/agents/agent.rb +324 -0
- data/lib/conductor/agents/callback_handler.rb +64 -0
- data/lib/conductor/agents/config_serializer.rb +246 -0
- data/lib/conductor/agents/errors.rb +26 -0
- data/lib/conductor/agents/guardrail.rb +142 -0
- data/lib/conductor/agents/handoff.rb +94 -0
- data/lib/conductor/agents/memory.rb +75 -0
- data/lib/conductor/agents/plans.rb +22 -0
- data/lib/conductor/agents/prompt_template.rb +23 -0
- data/lib/conductor/agents/runtime/agent_config.rb +52 -0
- data/lib/conductor/agents/runtime/agent_runtime.rb +260 -0
- data/lib/conductor/agents/runtime/approval_request.rb +111 -0
- data/lib/conductor/agents/runtime/dispatch.rb +137 -0
- data/lib/conductor/agents/runtime/execution.rb +265 -0
- data/lib/conductor/agents/runtime/secrets.rb +54 -0
- data/lib/conductor/agents/runtime/sse_client.rb +175 -0
- data/lib/conductor/agents/runtime/status_poller.rb +49 -0
- data/lib/conductor/agents/runtime/system_workers.rb +115 -0
- data/lib/conductor/agents/runtime/tool_call.rb +13 -0
- data/lib/conductor/agents/runtime/tool_registry.rb +164 -0
- data/lib/conductor/agents/termination.rb +192 -0
- data/lib/conductor/agents/tool.rb +283 -0
- data/lib/conductor/agents/tools/schema_builder.rb +190 -0
- data/lib/conductor/agents/tools/secret_scanner.rb +45 -0
- data/lib/conductor/agents/tools.rb +173 -0
- data/lib/conductor/agents.rb +75 -0
- data/lib/conductor/client/agent_client.rb +117 -0
- data/lib/conductor/client/task_client.rb +7 -0
- data/lib/conductor/configuration.rb +43 -10
- data/lib/conductor/exceptions.rb +36 -0
- data/lib/conductor/http/api/agent_resource_api.rb +138 -0
- data/lib/conductor/http/api/scheduler_resource_api.rb +31 -12
- data/lib/conductor/http/api/task_resource_api.rb +15 -0
- data/lib/conductor/http/models/task.rb +10 -2
- data/lib/conductor/http/models/task_def.rb +14 -3
- data/lib/conductor/http/rest_client.rb +9 -0
- data/lib/conductor/orkes/orkes_clients.rb +5 -1
- data/lib/conductor/version.rb +1 -1
- data/lib/conductor/worker/lease_renewer.rb +55 -0
- data/lib/conductor/worker/ractor_task_runner.rb +1 -1
- data/lib/conductor/worker/task_definition_registrar.rb +2 -2
- data/lib/conductor/worker/task_runner.rb +43 -10
- data/lib/conductor/worker/worker.rb +3 -2
- data/lib/conductor/worker/worker_config.rb +2 -1
- data/lib/conductor.rb +3 -1
- metadata +71 -16
|
@@ -67,27 +67,24 @@ module Conductor
|
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
# Pause a schedule
|
|
70
|
+
#
|
|
71
|
+
# Per-schedule pause/resume is PUT-mapped on OSS Conductor but GET-only
|
|
72
|
+
# on some Orkes Conductor deployments. PUT is tried first and a 405
|
|
73
|
+
# response falls back to GET, mirroring the python-sdk/csharp-sdk/rust-sdk
|
|
74
|
+
# clients.
|
|
70
75
|
# @param [String] name Schedule name
|
|
71
76
|
# @return [void]
|
|
72
77
|
def pause_schedule(name)
|
|
73
|
-
|
|
74
|
-
'/scheduler/schedules/{name}/pause',
|
|
75
|
-
'GET',
|
|
76
|
-
path_params: { name: name },
|
|
77
|
-
return_http_data_only: true
|
|
78
|
-
)
|
|
78
|
+
call_with_verb_fallback('/scheduler/schedules/{name}/pause', name)
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
# Resume a schedule
|
|
82
|
+
#
|
|
83
|
+
# See {#pause_schedule} for the PUT-with-GET-fallback rationale.
|
|
82
84
|
# @param [String] name Schedule name
|
|
83
85
|
# @return [void]
|
|
84
86
|
def resume_schedule(name)
|
|
85
|
-
|
|
86
|
-
'/scheduler/schedules/{name}/resume',
|
|
87
|
-
'GET',
|
|
88
|
-
path_params: { name: name },
|
|
89
|
-
return_http_data_only: true
|
|
90
|
-
)
|
|
87
|
+
call_with_verb_fallback('/scheduler/schedules/{name}/resume', name)
|
|
91
88
|
end
|
|
92
89
|
|
|
93
90
|
# Pause all schedules
|
|
@@ -205,6 +202,28 @@ module Conductor
|
|
|
205
202
|
return_http_data_only: true
|
|
206
203
|
)
|
|
207
204
|
end
|
|
205
|
+
|
|
206
|
+
private
|
|
207
|
+
|
|
208
|
+
# Try PUT first (OSS dialect); fall back to GET on 405 (some Orkes
|
|
209
|
+
# deployments only accept GET for these two routes).
|
|
210
|
+
def call_with_verb_fallback(templated_path, name)
|
|
211
|
+
@api_client.call_api(
|
|
212
|
+
templated_path,
|
|
213
|
+
'PUT',
|
|
214
|
+
path_params: { name: name },
|
|
215
|
+
return_http_data_only: true
|
|
216
|
+
)
|
|
217
|
+
rescue Conductor::ApiError => e
|
|
218
|
+
raise unless e.status == 405
|
|
219
|
+
|
|
220
|
+
@api_client.call_api(
|
|
221
|
+
templated_path,
|
|
222
|
+
'GET',
|
|
223
|
+
path_params: { name: name },
|
|
224
|
+
return_http_data_only: true
|
|
225
|
+
)
|
|
226
|
+
end
|
|
208
227
|
end
|
|
209
228
|
end
|
|
210
229
|
end
|
|
@@ -73,6 +73,21 @@ module Conductor
|
|
|
73
73
|
)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
# Update task status using the v2 endpoint (POST /tasks/update-v2)
|
|
77
|
+
# Supports lease extension via TaskResult#extend_lease and returns the next
|
|
78
|
+
# task for the same worker when the server has one queued.
|
|
79
|
+
# @param [TaskResult] body Task result
|
|
80
|
+
# @return [Task, nil] Next task if the server returned one, nil on 204
|
|
81
|
+
def update_task_v2(body)
|
|
82
|
+
@api_client.call_api(
|
|
83
|
+
'/tasks/update-v2',
|
|
84
|
+
'POST',
|
|
85
|
+
body: body,
|
|
86
|
+
return_type: 'Task',
|
|
87
|
+
return_http_data_only: true
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
76
91
|
# Get task details
|
|
77
92
|
# @param [String] task_id Task ID
|
|
78
93
|
# @return [Task] Task object
|
|
@@ -50,7 +50,8 @@ module Conductor
|
|
|
50
50
|
first_start_time: 'Integer',
|
|
51
51
|
loop_over_task: 'Boolean',
|
|
52
52
|
task_definition: 'TaskDef',
|
|
53
|
-
queue_wait_time: 'Integer'
|
|
53
|
+
queue_wait_time: 'Integer',
|
|
54
|
+
runtime_metadata: 'Hash<String, String>'
|
|
54
55
|
}.freeze
|
|
55
56
|
|
|
56
57
|
ATTRIBUTE_MAP = {
|
|
@@ -96,7 +97,8 @@ module Conductor
|
|
|
96
97
|
first_start_time: :firstStartTime,
|
|
97
98
|
loop_over_task: :loopOverTask,
|
|
98
99
|
task_definition: :taskDefinition,
|
|
99
|
-
queue_wait_time: :queueWaitTime
|
|
100
|
+
queue_wait_time: :queueWaitTime,
|
|
101
|
+
runtime_metadata: :runtimeMetadata
|
|
100
102
|
}.freeze
|
|
101
103
|
|
|
102
104
|
attr_accessor :task_type, :status, :input_data, :reference_task_name,
|
|
@@ -112,6 +114,11 @@ module Conductor
|
|
|
112
114
|
:iteration, :sub_workflow_id, :subworkflow_changed, :parent_task_id,
|
|
113
115
|
:first_start_time, :loop_over_task, :task_definition, :queue_wait_time
|
|
114
116
|
|
|
117
|
+
# Wire-only map of secret name => resolved value. The server fills it at poll
|
|
118
|
+
# time from TaskDef#runtime_metadata (names) and never persists it.
|
|
119
|
+
# @return [Hash<String, String>]
|
|
120
|
+
attr_accessor :runtime_metadata
|
|
121
|
+
|
|
115
122
|
# Initialize a new Task
|
|
116
123
|
# @param [Hash] attributes Model attributes in the form of hash
|
|
117
124
|
def initialize(attributes = {})
|
|
@@ -125,6 +132,7 @@ module Conductor
|
|
|
125
132
|
# Set default values for collections
|
|
126
133
|
@input_data ||= {}
|
|
127
134
|
@output_data ||= {}
|
|
135
|
+
@runtime_metadata ||= {}
|
|
128
136
|
end
|
|
129
137
|
|
|
130
138
|
# Check if task is in terminal state
|
|
@@ -37,7 +37,9 @@ module Conductor
|
|
|
37
37
|
execution_name_space: 'String',
|
|
38
38
|
owner_email: 'String',
|
|
39
39
|
poll_timeout_seconds: 'Integer',
|
|
40
|
-
backoff_scale_factor: 'Integer'
|
|
40
|
+
backoff_scale_factor: 'Integer',
|
|
41
|
+
enforce_schema: 'Boolean',
|
|
42
|
+
runtime_metadata: 'Array<String>'
|
|
41
43
|
}.freeze
|
|
42
44
|
|
|
43
45
|
ATTRIBUTE_MAP = {
|
|
@@ -58,7 +60,9 @@ module Conductor
|
|
|
58
60
|
execution_name_space: :executionNameSpace,
|
|
59
61
|
owner_email: :ownerEmail,
|
|
60
62
|
poll_timeout_seconds: :pollTimeoutSeconds,
|
|
61
|
-
backoff_scale_factor: :backoffScaleFactor
|
|
63
|
+
backoff_scale_factor: :backoffScaleFactor,
|
|
64
|
+
enforce_schema: :enforceSchema,
|
|
65
|
+
runtime_metadata: :runtimeMetadata
|
|
62
66
|
}.freeze
|
|
63
67
|
|
|
64
68
|
attr_accessor :name, :description, :retry_count, :timeout_seconds,
|
|
@@ -67,7 +71,12 @@ module Conductor
|
|
|
67
71
|
:concurrent_exec_limit, :rate_limit_per_frequency,
|
|
68
72
|
:rate_limit_frequency_in_seconds, :isolation_group_id,
|
|
69
73
|
:execution_name_space, :owner_email, :poll_timeout_seconds,
|
|
70
|
-
:backoff_scale_factor
|
|
74
|
+
:backoff_scale_factor, :enforce_schema
|
|
75
|
+
|
|
76
|
+
# Names of secrets the server must resolve and attach to every task of this
|
|
77
|
+
# type (delivered as Task#runtime_metadata). Requires conductor-oss >= 3.32.0-rc.8.
|
|
78
|
+
# @return [Array<String>]
|
|
79
|
+
attr_accessor :runtime_metadata
|
|
71
80
|
|
|
72
81
|
def initialize(params = {})
|
|
73
82
|
@name = params[:name]
|
|
@@ -88,6 +97,8 @@ module Conductor
|
|
|
88
97
|
@owner_email = params[:owner_email]
|
|
89
98
|
@poll_timeout_seconds = params[:poll_timeout_seconds]
|
|
90
99
|
@backoff_scale_factor = params[:backoff_scale_factor] || 1
|
|
100
|
+
@enforce_schema = params.fetch(:enforce_schema, false)
|
|
101
|
+
@runtime_metadata = params[:runtime_metadata] || []
|
|
91
102
|
end
|
|
92
103
|
end
|
|
93
104
|
end
|
|
@@ -112,11 +112,20 @@ module Conductor
|
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
# Retry middleware (3 retries with exponential backoff)
|
|
115
|
+
#
|
|
116
|
+
# `exceptions` has to be spelled out: faraday-retry's
|
|
117
|
+
# DEFAULT_EXCEPTIONS covers timeouts but not Faraday::ConnectionFailed,
|
|
118
|
+
# which is what the net_http_persistent adapter raises for
|
|
119
|
+
# Errno::ECONNRESET/EPIPE -- a write to a pooled socket the peer closed
|
|
120
|
+
# first. net-http-persistent retries stale sockets itself, but only for
|
|
121
|
+
# idempotent requests, so POSTs surfaced these as hard ApiErrors while
|
|
122
|
+
# every GET was silently protected.
|
|
115
123
|
conn.request :retry,
|
|
116
124
|
max: 3,
|
|
117
125
|
interval: 0.5,
|
|
118
126
|
backoff_factor: 2,
|
|
119
127
|
retry_statuses: [408, 429, 500, 502, 503, 504],
|
|
128
|
+
exceptions: Faraday::Retry::Middleware::DEFAULT_EXCEPTIONS + [Faraday::ConnectionFailed],
|
|
120
129
|
methods: %i[get post put patch delete]
|
|
121
130
|
|
|
122
131
|
# Connection settings
|
|
@@ -8,7 +8,7 @@ module Conductor
|
|
|
8
8
|
# Usage:
|
|
9
9
|
# config = Conductor::Configuration.new
|
|
10
10
|
# config.server_url = 'https://developer.orkescloud.com/api'
|
|
11
|
-
# config.authentication_settings = Conductor::
|
|
11
|
+
# config.authentication_settings = Conductor::AuthenticationSettings.new(
|
|
12
12
|
# key_id: 'your_key', key_secret: 'your_secret'
|
|
13
13
|
# )
|
|
14
14
|
# clients = Conductor::Orkes::OrkesClients.new(config)
|
|
@@ -61,6 +61,10 @@ module Conductor
|
|
|
61
61
|
Client::SchemaClient.new(@api_client)
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
+
def get_agent_client
|
|
65
|
+
Client::AgentClient.new(@api_client)
|
|
66
|
+
end
|
|
67
|
+
|
|
64
68
|
def get_workflow_executor
|
|
65
69
|
Workflow::WorkflowExecutor.new(@configuration)
|
|
66
70
|
end
|
data/lib/conductor/version.rb
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'concurrent'
|
|
4
|
+
require_relative '../http/models/task_result'
|
|
5
|
+
|
|
6
|
+
module Conductor
|
|
7
|
+
module Worker
|
|
8
|
+
# Renews a task's lease while its worker body runs. Each active task has one
|
|
9
|
+
# interruptible heartbeat thread, independent of the polling/execution pool.
|
|
10
|
+
class LeaseRenewer
|
|
11
|
+
INTERVAL_FACTOR = 0.8
|
|
12
|
+
|
|
13
|
+
def initialize(task_client:, logger:)
|
|
14
|
+
@task_client = task_client
|
|
15
|
+
@logger = logger
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def during(task, worker_id:)
|
|
19
|
+
interval = task.response_timeout_seconds.to_f * INTERVAL_FACTOR
|
|
20
|
+
return yield unless interval.positive?
|
|
21
|
+
|
|
22
|
+
stopped = Concurrent::Event.new
|
|
23
|
+
heartbeat = Thread.new do
|
|
24
|
+
Thread.current.name = "conductor-lease-#{task.task_id}"
|
|
25
|
+
delay = interval
|
|
26
|
+
# Retry transient failures within the remaining lease window.
|
|
27
|
+
delay = renew(task, worker_id) ? interval : [interval / 4, 1.0].min until stopped.wait(delay)
|
|
28
|
+
end
|
|
29
|
+
yield
|
|
30
|
+
ensure
|
|
31
|
+
stopped&.set
|
|
32
|
+
# Drain an in-flight heartbeat before the caller can submit a final result.
|
|
33
|
+
heartbeat&.join
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def renew(task, worker_id)
|
|
39
|
+
result = Http::Models::TaskResult.new(
|
|
40
|
+
task_id: task.task_id,
|
|
41
|
+
workflow_instance_id: task.workflow_instance_id,
|
|
42
|
+
worker_id: worker_id,
|
|
43
|
+
status: Http::Models::TaskResultStatus::IN_PROGRESS,
|
|
44
|
+
extend_lease: true
|
|
45
|
+
)
|
|
46
|
+
# The original endpoint handles lease updates without claiming more work.
|
|
47
|
+
@task_client.update_task(result)
|
|
48
|
+
true
|
|
49
|
+
rescue StandardError => e
|
|
50
|
+
@logger.warn("Lease renewal failed for task #{task.task_id}: #{e.class}: #{e.message}")
|
|
51
|
+
false
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -143,7 +143,7 @@ module Conductor
|
|
|
143
143
|
server_api_url: @configuration_hash[:server_api_url]
|
|
144
144
|
)
|
|
145
145
|
if @configuration_hash[:authentication_settings]
|
|
146
|
-
config.authentication_settings =
|
|
146
|
+
config.authentication_settings = AuthenticationSettings.new(
|
|
147
147
|
key_id: @configuration_hash[:authentication_settings][:key_id],
|
|
148
148
|
key_secret: @configuration_hash[:authentication_settings][:key_secret]
|
|
149
149
|
)
|
|
@@ -210,7 +210,7 @@ module Conductor
|
|
|
210
210
|
raise unless e.status == 404
|
|
211
211
|
|
|
212
212
|
# Task def doesn't exist, create it
|
|
213
|
-
@metadata_client.register_task_def(
|
|
213
|
+
@metadata_client.register_task_def(task_def)
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
# Register task def only if it doesn't exist
|
|
@@ -222,7 +222,7 @@ module Conductor
|
|
|
222
222
|
raise unless e.status == 404
|
|
223
223
|
|
|
224
224
|
# Task def doesn't exist, create it
|
|
225
|
-
@metadata_client.register_task_def(
|
|
225
|
+
@metadata_client.register_task_def(task_def)
|
|
226
226
|
end
|
|
227
227
|
end
|
|
228
228
|
|
|
@@ -10,6 +10,7 @@ require_relative '../exceptions'
|
|
|
10
10
|
require_relative 'task_context'
|
|
11
11
|
require_relative 'task_in_progress'
|
|
12
12
|
require_relative 'worker_config'
|
|
13
|
+
require_relative 'lease_renewer'
|
|
13
14
|
require_relative 'events/task_runner_events'
|
|
14
15
|
require_relative 'events/sync_event_dispatcher'
|
|
15
16
|
require_relative 'events/listener_registry'
|
|
@@ -43,6 +44,7 @@ module Conductor
|
|
|
43
44
|
|
|
44
45
|
# Create task client for API communication
|
|
45
46
|
@task_client = Client::TaskClient.new(@configuration)
|
|
47
|
+
@lease_renewer = LeaseRenewer.new(task_client: @task_client, logger: @logger)
|
|
46
48
|
|
|
47
49
|
# Resolve worker configuration
|
|
48
50
|
resolved_config = WorkerConfig.resolve(
|
|
@@ -68,6 +70,9 @@ module Conductor
|
|
|
68
70
|
@poll_count = Concurrent::AtomicFixnum.new(0)
|
|
69
71
|
@shutdown = Concurrent::AtomicBoolean.new(false)
|
|
70
72
|
@mutex = Mutex.new
|
|
73
|
+
# Prefer POST /tasks/update-v2 (lease extension, next-task return); fall back
|
|
74
|
+
# to POST /tasks once if the server does not serve it (404/405).
|
|
75
|
+
@use_update_v2 = Concurrent::AtomicBoolean.new(true)
|
|
71
76
|
end
|
|
72
77
|
|
|
73
78
|
# Main polling loop (runs until shutdown)
|
|
@@ -176,6 +181,7 @@ module Conductor
|
|
|
176
181
|
@worker_id = config[:worker_id]
|
|
177
182
|
@domain = config[:domain]
|
|
178
183
|
@poll_timeout = config[:poll_timeout]
|
|
184
|
+
@lease_extend_enabled = config[:lease_extend_enabled]
|
|
179
185
|
end
|
|
180
186
|
|
|
181
187
|
# Cleanup completed task futures
|
|
@@ -305,16 +311,21 @@ module Conductor
|
|
|
305
311
|
# Execute a task and update the result
|
|
306
312
|
# @param task [Hash] Task data from API
|
|
307
313
|
def execute_and_update(task)
|
|
308
|
-
|
|
314
|
+
while task
|
|
315
|
+
task_result = execute_task(task)
|
|
309
316
|
|
|
310
|
-
|
|
311
|
-
|
|
317
|
+
# Skip update for TaskInProgress (task stays in IN_PROGRESS state)
|
|
318
|
+
return if task_result.nil?
|
|
312
319
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
320
|
+
# Don't update if result is IN_PROGRESS (will be polled again)
|
|
321
|
+
return if task_result.status == Http::Models::TaskResultStatus::IN_PROGRESS &&
|
|
322
|
+
task_result.callback_after_seconds&.positive?
|
|
316
323
|
|
|
317
|
-
|
|
324
|
+
# update-v2 has already claimed the returned task. Reuse this executor slot
|
|
325
|
+
# rather than dropping it or exceeding the worker's concurrency limit.
|
|
326
|
+
response = update_task_with_retry(task_result)
|
|
327
|
+
task = response.is_a?(Http::Models::Task) ? response : nil
|
|
328
|
+
end
|
|
318
329
|
end
|
|
319
330
|
|
|
320
331
|
# Execute a task
|
|
@@ -345,7 +356,11 @@ module Conductor
|
|
|
345
356
|
|
|
346
357
|
begin
|
|
347
358
|
# Execute worker
|
|
348
|
-
task_result = @
|
|
359
|
+
task_result = if @lease_extend_enabled
|
|
360
|
+
@lease_renewer.during(task_obj, worker_id: @worker_id) { @worker.execute(task_obj) }
|
|
361
|
+
else
|
|
362
|
+
@worker.execute(task_obj)
|
|
363
|
+
end
|
|
349
364
|
|
|
350
365
|
duration_ms = (Time.now - start_time) * 1000
|
|
351
366
|
|
|
@@ -458,11 +473,11 @@ module Conductor
|
|
|
458
473
|
|
|
459
474
|
start_time = Time.now
|
|
460
475
|
begin
|
|
461
|
-
|
|
476
|
+
next_task = send_task_update(task_result)
|
|
462
477
|
duration_ms = (Time.now - start_time) * 1000
|
|
463
478
|
|
|
464
479
|
publish_task_update_completed(task_result, duration_ms)
|
|
465
|
-
return
|
|
480
|
+
return next_task
|
|
466
481
|
rescue StandardError => e
|
|
467
482
|
duration_ms = (Time.now - start_time) * 1000
|
|
468
483
|
@logger.error("Task update failed (attempt #{attempt + 1}/#{RETRY_BACKOFFS.size}): #{e.message}")
|
|
@@ -474,6 +489,24 @@ module Conductor
|
|
|
474
489
|
end
|
|
475
490
|
end
|
|
476
491
|
end
|
|
492
|
+
nil
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# Send the task result to the server, preferring the v2 endpoint
|
|
496
|
+
# @param task_result [TaskResult]
|
|
497
|
+
def send_task_update(task_result)
|
|
498
|
+
return @task_client.update_task(task_result) unless @use_update_v2.true? && running?
|
|
499
|
+
|
|
500
|
+
task_result.extend_lease = false if task_result.extend_lease.nil?
|
|
501
|
+
begin
|
|
502
|
+
@task_client.update_task_v2(task_result)
|
|
503
|
+
rescue ApiError => e
|
|
504
|
+
raise unless [404, 405].include?(e.status)
|
|
505
|
+
|
|
506
|
+
@logger.info('Server does not support /tasks/update-v2, falling back to /tasks')
|
|
507
|
+
@use_update_v2.make_false
|
|
508
|
+
@task_client.update_task(task_result)
|
|
509
|
+
end
|
|
477
510
|
end
|
|
478
511
|
|
|
479
512
|
def publish_task_update_completed(task_result, duration_ms)
|
|
@@ -22,7 +22,7 @@ module Conductor
|
|
|
22
22
|
attr_accessor :poll_interval, :thread_count, :domain, :worker_id,
|
|
23
23
|
:poll_timeout, :register_task_def, :overwrite_task_def,
|
|
24
24
|
:strict_schema, :paused, :isolation, :executor,
|
|
25
|
-
:task_def_template
|
|
25
|
+
:task_def_template, :lease_extend_enabled
|
|
26
26
|
|
|
27
27
|
# Default configuration values
|
|
28
28
|
DEFAULTS = {
|
|
@@ -36,7 +36,8 @@ module Conductor
|
|
|
36
36
|
strict_schema: false,
|
|
37
37
|
paused: false,
|
|
38
38
|
isolation: :thread,
|
|
39
|
-
executor: :thread_pool
|
|
39
|
+
executor: :thread_pool,
|
|
40
|
+
lease_extend_enabled: false
|
|
40
41
|
}.freeze
|
|
41
42
|
|
|
42
43
|
# Initialize a worker
|
|
@@ -22,7 +22,8 @@ module Conductor
|
|
|
22
22
|
strict_schema: { type: :boolean, default: false },
|
|
23
23
|
paused: { type: :boolean, default: false },
|
|
24
24
|
isolation: { type: :symbol, default: :thread }, # :thread or :ractor
|
|
25
|
-
executor: { type: :symbol, default: :thread_pool }
|
|
25
|
+
executor: { type: :symbol, default: :thread_pool }, # :thread_pool or :fiber
|
|
26
|
+
lease_extend_enabled: { type: :boolean, default: false }
|
|
26
27
|
}.freeze
|
|
27
28
|
|
|
28
29
|
class << self
|
data/lib/conductor.rb
CHANGED
|
@@ -69,6 +69,7 @@ require_relative 'conductor/http/api/gateway_auth_resource_api'
|
|
|
69
69
|
require_relative 'conductor/http/api/schema_resource_api'
|
|
70
70
|
require_relative 'conductor/http/api/integration_resource_api'
|
|
71
71
|
require_relative 'conductor/http/api/prompt_resource_api'
|
|
72
|
+
require_relative 'conductor/http/api/agent_resource_api'
|
|
72
73
|
# OSS Clients
|
|
73
74
|
require_relative 'conductor/client/workflow_client'
|
|
74
75
|
require_relative 'conductor/client/task_client'
|
|
@@ -80,6 +81,7 @@ require_relative 'conductor/client/authorization_client'
|
|
|
80
81
|
require_relative 'conductor/client/schema_client'
|
|
81
82
|
require_relative 'conductor/client/integration_client'
|
|
82
83
|
require_relative 'conductor/client/prompt_client'
|
|
84
|
+
require_relative 'conductor/client/agent_client'
|
|
83
85
|
# Orkes-specific models
|
|
84
86
|
require_relative 'conductor/orkes/models/metadata_tag'
|
|
85
87
|
require_relative 'conductor/orkes/models/rate_limit_tag'
|
|
@@ -147,7 +149,7 @@ module Conductor
|
|
|
147
149
|
# @example
|
|
148
150
|
# Conductor.configure do |config|
|
|
149
151
|
# config.server_url = 'http://localhost:7001/api'
|
|
150
|
-
# config.authentication_settings = Conductor::
|
|
152
|
+
# config.authentication_settings = Conductor::AuthenticationSettings.new(
|
|
151
153
|
# key_id: 'my_key',
|
|
152
154
|
# key_secret: 'my_secret'
|
|
153
155
|
# )
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: conductor_ruby
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Conductor OSS
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-24 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: faraday
|
|
@@ -80,6 +80,20 @@ dependencies:
|
|
|
80
80
|
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: '2.0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: json_schemer
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '2.0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '2.0'
|
|
83
97
|
- !ruby/object:Gem::Dependency
|
|
84
98
|
name: pry
|
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -150,20 +164,6 @@ dependencies:
|
|
|
150
164
|
- - "~>"
|
|
151
165
|
- !ruby/object:Gem::Version
|
|
152
166
|
version: '2.0'
|
|
153
|
-
- !ruby/object:Gem::Dependency
|
|
154
|
-
name: vcr
|
|
155
|
-
requirement: !ruby/object:Gem::Requirement
|
|
156
|
-
requirements:
|
|
157
|
-
- - "~>"
|
|
158
|
-
- !ruby/object:Gem::Version
|
|
159
|
-
version: '6.0'
|
|
160
|
-
type: :development
|
|
161
|
-
prerelease: false
|
|
162
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
-
requirements:
|
|
164
|
-
- - "~>"
|
|
165
|
-
- !ruby/object:Gem::Version
|
|
166
|
-
version: '6.0'
|
|
167
167
|
- !ruby/object:Gem::Dependency
|
|
168
168
|
name: webmock
|
|
169
169
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -190,6 +190,32 @@ files:
|
|
|
190
190
|
- LICENSE
|
|
191
191
|
- README.md
|
|
192
192
|
- examples/agentic_workflows/llm_chat.rb
|
|
193
|
+
- examples/agents/01_basic_agent.rb
|
|
194
|
+
- examples/agents/02a_simple_tools.rb
|
|
195
|
+
- examples/agents/02c_tool_retry_config.rb
|
|
196
|
+
- examples/agents/04_http_and_mcp_tools.rb
|
|
197
|
+
- examples/agents/05_handoffs.rb
|
|
198
|
+
- examples/agents/06_sequential_pipeline.rb
|
|
199
|
+
- examples/agents/07_parallel_agents.rb
|
|
200
|
+
- examples/agents/09_human_in_the_loop.rb
|
|
201
|
+
- examples/agents/09c_hitl_streaming.rb
|
|
202
|
+
- examples/agents/103_plan_and_compile.rb
|
|
203
|
+
- examples/agents/10_guardrails.rb
|
|
204
|
+
- examples/agents/13_hierarchical_agents.rb
|
|
205
|
+
- examples/agents/16e_credentials_http_tool.rb
|
|
206
|
+
- examples/agents/17_swarm_orchestration.rb
|
|
207
|
+
- examples/agents/21_regex_guardrails.rb
|
|
208
|
+
- examples/agents/22_llm_guardrails.rb
|
|
209
|
+
- examples/agents/33_external_workers.rb
|
|
210
|
+
- examples/agents/64_swarm_with_tools.rb
|
|
211
|
+
- examples/agents/66_handoff_to_parallel.rb
|
|
212
|
+
- examples/agents/bug_desk.rb
|
|
213
|
+
- examples/agents/catalog.rb
|
|
214
|
+
- examples/agents/dump_agent_configs.rb
|
|
215
|
+
- examples/agents/external_workers.rb
|
|
216
|
+
- examples/agents/golden_agents.rb
|
|
217
|
+
- examples/agents/support_approval.rb
|
|
218
|
+
- examples/agents/weather.rb
|
|
193
219
|
- examples/dynamic_workflow.rb
|
|
194
220
|
- examples/event_handler.rb
|
|
195
221
|
- examples/event_listener_examples.rb
|
|
@@ -213,6 +239,33 @@ files:
|
|
|
213
239
|
- examples/workflow_dsl.rb
|
|
214
240
|
- examples/workflow_ops.rb
|
|
215
241
|
- lib/conductor.rb
|
|
242
|
+
- lib/conductor/agents.rb
|
|
243
|
+
- lib/conductor/agents/agent.rb
|
|
244
|
+
- lib/conductor/agents/callback_handler.rb
|
|
245
|
+
- lib/conductor/agents/config_serializer.rb
|
|
246
|
+
- lib/conductor/agents/errors.rb
|
|
247
|
+
- lib/conductor/agents/guardrail.rb
|
|
248
|
+
- lib/conductor/agents/handoff.rb
|
|
249
|
+
- lib/conductor/agents/memory.rb
|
|
250
|
+
- lib/conductor/agents/plans.rb
|
|
251
|
+
- lib/conductor/agents/prompt_template.rb
|
|
252
|
+
- lib/conductor/agents/runtime/agent_config.rb
|
|
253
|
+
- lib/conductor/agents/runtime/agent_runtime.rb
|
|
254
|
+
- lib/conductor/agents/runtime/approval_request.rb
|
|
255
|
+
- lib/conductor/agents/runtime/dispatch.rb
|
|
256
|
+
- lib/conductor/agents/runtime/execution.rb
|
|
257
|
+
- lib/conductor/agents/runtime/secrets.rb
|
|
258
|
+
- lib/conductor/agents/runtime/sse_client.rb
|
|
259
|
+
- lib/conductor/agents/runtime/status_poller.rb
|
|
260
|
+
- lib/conductor/agents/runtime/system_workers.rb
|
|
261
|
+
- lib/conductor/agents/runtime/tool_call.rb
|
|
262
|
+
- lib/conductor/agents/runtime/tool_registry.rb
|
|
263
|
+
- lib/conductor/agents/termination.rb
|
|
264
|
+
- lib/conductor/agents/tool.rb
|
|
265
|
+
- lib/conductor/agents/tools.rb
|
|
266
|
+
- lib/conductor/agents/tools/schema_builder.rb
|
|
267
|
+
- lib/conductor/agents/tools/secret_scanner.rb
|
|
268
|
+
- lib/conductor/client/agent_client.rb
|
|
216
269
|
- lib/conductor/client/authorization_client.rb
|
|
217
270
|
- lib/conductor/client/integration_client.rb
|
|
218
271
|
- lib/conductor/client/metadata_client.rb
|
|
@@ -225,6 +278,7 @@ files:
|
|
|
225
278
|
- lib/conductor/configuration.rb
|
|
226
279
|
- lib/conductor/configuration/authentication_settings.rb
|
|
227
280
|
- lib/conductor/exceptions.rb
|
|
281
|
+
- lib/conductor/http/api/agent_resource_api.rb
|
|
228
282
|
- lib/conductor/http/api/application_resource_api.rb
|
|
229
283
|
- lib/conductor/http/api/authorization_resource_api.rb
|
|
230
284
|
- lib/conductor/http/api/event_resource_api.rb
|
|
@@ -302,6 +356,7 @@ files:
|
|
|
302
356
|
- lib/conductor/worker/events/task_runner_events.rb
|
|
303
357
|
- lib/conductor/worker/events/workflow_events.rb
|
|
304
358
|
- lib/conductor/worker/fiber_executor.rb
|
|
359
|
+
- lib/conductor/worker/lease_renewer.rb
|
|
305
360
|
- lib/conductor/worker/ractor_task_runner.rb
|
|
306
361
|
- lib/conductor/worker/task_context.rb
|
|
307
362
|
- lib/conductor/worker/task_definition_registrar.rb
|