phronomy 0.13.0 → 0.15.0

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +155 -0
  3. data/README.md +266 -38
  4. data/benchmark/bench_agent_invoke.rb +2 -3
  5. data/docs/decisions/004-invoke-timeout-is-not-cancellation.md +14 -67
  6. data/docs/decisions/011-delegate-transport-policy-to-adapters.md +82 -0
  7. data/docs/mcp-client.md +75 -0
  8. data/examples/workflows/agent_event_mapping.rb +104 -0
  9. data/examples/workflows/generic_task_event_mapping.rb +58 -0
  10. data/gemfiles/mcp_1_0.gemfile +9 -0
  11. data/lib/phronomy/agent/agent_invocation.rb +385 -0
  12. data/lib/phronomy/agent/agent_invocation_registry.rb +75 -0
  13. data/lib/phronomy/agent/agent_invocation_session_builder.rb +448 -0
  14. data/lib/phronomy/agent/approval_evaluation_request.rb +102 -0
  15. data/lib/phronomy/agent/async_event_api.rb +471 -0
  16. data/lib/phronomy/agent/base.rb +509 -420
  17. data/lib/phronomy/agent/context/capability/base.rb +57 -119
  18. data/lib/phronomy/agent/llm_operation_result.rb +23 -0
  19. data/lib/phronomy/agent/phase_machine_builder.rb +75 -136
  20. data/lib/phronomy/agent/tool_approval_request.rb +121 -0
  21. data/lib/phronomy/agent/tool_call_intercepted.rb +11 -15
  22. data/lib/phronomy/agent/tool_executor.rb +47 -69
  23. data/lib/phronomy/agent/tool_invocation.rb +634 -0
  24. data/lib/phronomy/agent/tool_invocation_session_builder.rb +378 -0
  25. data/lib/phronomy/agent.rb +21 -9
  26. data/lib/phronomy/configuration.rb +58 -53
  27. data/lib/phronomy/diagnostics.rb +1 -1
  28. data/lib/phronomy/engine/concurrency/blocking_adapter_pool.rb +230 -118
  29. data/lib/phronomy/engine/concurrency/cancellation_token.rb +5 -1
  30. data/lib/phronomy/engine/concurrency/pool_registry.rb +8 -3
  31. data/lib/phronomy/engine/event_loop.rb +507 -303
  32. data/lib/phronomy/engine/fsm_session.rb +181 -140
  33. data/lib/phronomy/engine/runtime/deterministic_scheduler.rb +1 -1
  34. data/lib/phronomy/engine/runtime/shutdown_result.rb +62 -0
  35. data/lib/phronomy/engine/runtime/task_registry.rb +62 -15
  36. data/lib/phronomy/engine/runtime.rb +247 -57
  37. data/lib/phronomy/engine/task.rb +5 -10
  38. data/lib/phronomy/event.rb +8 -8
  39. data/lib/phronomy/generator_verifier.rb +253 -142
  40. data/lib/phronomy/invalid_async_entry_action_error.rb +9 -0
  41. data/lib/phronomy/invalid_async_transition_action_error.rb +11 -0
  42. data/lib/phronomy/invalid_async_workflow_action_error.rb +9 -0
  43. data/lib/phronomy/invocation_context.rb +5 -19
  44. data/lib/phronomy/llm_adapter/base.rb +25 -34
  45. data/lib/phronomy/metrics.rb +6 -3
  46. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +54 -89
  47. data/lib/phronomy/stream_callback_error.rb +35 -0
  48. data/lib/phronomy/testing/scheduler_helpers.rb +12 -3
  49. data/lib/phronomy/tools/mcp.rb +410 -81
  50. data/lib/phronomy/version.rb +1 -1
  51. data/lib/phronomy/workflow/phase_machine_builder.rb +129 -182
  52. data/lib/phronomy/workflow.rb +122 -261
  53. data/lib/phronomy/workflow_context.rb +55 -104
  54. data/lib/phronomy/workflow_runner.rb +239 -291
  55. data/lib/phronomy.rb +30 -23
  56. data/scripts/check_readme_runnable.rb +4 -1
  57. metadata +63 -11
  58. data/lib/phronomy/agent/concerns/retryable.rb +0 -103
  59. data/lib/phronomy/agent/context/capability/scope_policy.rb +0 -54
  60. data/lib/phronomy/agent/invocation_context.rb +0 -171
  61. data/lib/phronomy/agent/invocation_session.rb +0 -346
  62. data/lib/phronomy/agent/suspended_session_registry.rb +0 -54
  63. data/lib/phronomy/engine/concurrency/concurrency_gate.rb +0 -157
  64. data/lib/phronomy/engine/concurrency/gate_registry.rb +0 -51
@@ -0,0 +1,378 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "state_machines"
4
+
5
+ module Phronomy
6
+ module Agent
7
+ # Builds an FSMSession for one ToolInvocation.
8
+ #
9
+ # Authorization and execution Tasks are observed by ToolInvocation-specific
10
+ # callbacks that post explicit FSM events. The generated phase machine does
11
+ # not await Tasks.
12
+ #
13
+ # @api private
14
+ class ToolInvocationSessionBuilder
15
+ AUTO_STATE_SET = {
16
+ idle: true,
17
+ validating: true,
18
+ queued: true
19
+ }.freeze
20
+
21
+ DECLARED_STATES = %i[
22
+ idle validating authorizing awaiting_approval authorized queued running
23
+ completed failed rejected cancelled
24
+ ].freeze
25
+
26
+ WAIT_STATES = %i[awaiting_approval authorized].freeze
27
+
28
+ EXTERNAL_EVENTS = {
29
+ authorization_completed: [
30
+ {from: :authorizing, to: :cancelled, guard: ->(ctx) {
31
+ ctx.cancelled?
32
+ }},
33
+ {from: :authorizing, to: :failed, guard: ->(ctx) {
34
+ ctx.failed?
35
+ }},
36
+ {from: :authorizing, to: :rejected, guard: ->(ctx) {
37
+ ctx.rejected?
38
+ }},
39
+ {from: :authorizing, to: :awaiting_approval, guard: ->(ctx) {
40
+ ctx.awaiting_approval?
41
+ }},
42
+ {from: :authorizing, to: :authorized, guard: ->(ctx) {
43
+ ctx.authorized?
44
+ }}
45
+ ],
46
+ execution_completed: [
47
+ {from: :running, to: :cancelled, guard: ->(ctx) {
48
+ ctx.cancelled?
49
+ }},
50
+ {from: :running, to: :failed, guard: ->(ctx) {
51
+ ctx.failed?
52
+ }},
53
+ {from: :running, to: :completed, guard: ->(ctx) {
54
+ ctx.execution_completed?
55
+ }}
56
+ ],
57
+ approve: [
58
+ {from: :awaiting_approval, to: :authorized, guard: nil}
59
+ ],
60
+ reject: [
61
+ {from: :awaiting_approval, to: :rejected, guard: nil}
62
+ ],
63
+ dispatch: [
64
+ {from: :authorized, to: :queued, guard: nil}
65
+ ],
66
+ cancel: [
67
+ {from: :awaiting_approval, to: :cancelled, guard: nil},
68
+ {from: :authorized, to: :cancelled, guard: nil},
69
+ {from: :queued, to: :cancelled, guard: nil},
70
+ {from: :running, to: :cancelled, guard: nil}
71
+ ]
72
+ }.freeze
73
+
74
+ def self.build(
75
+ tool_invocation:,
76
+ runtime: Phronomy::Runtime.instance
77
+ )
78
+ build_session(
79
+ tool_invocation: tool_invocation,
80
+ runtime: runtime
81
+ )
82
+ end
83
+
84
+ def self.build_for_resume(
85
+ tool_invocation:,
86
+ resume_event:,
87
+ resume_phase:,
88
+ runtime: Phronomy::Runtime.instance
89
+ )
90
+ build_session(
91
+ tool_invocation: tool_invocation,
92
+ runtime: runtime,
93
+ resume_event: resume_event,
94
+ resume_phase: resume_phase
95
+ )
96
+ end
97
+
98
+ def self.build_session(
99
+ tool_invocation:,
100
+ runtime:,
101
+ resume_event: nil,
102
+ resume_phase: nil
103
+ )
104
+ actions = build_entry_actions(runtime)
105
+ phase_machine = build_phase_machine(actions)
106
+
107
+ Phronomy::FSMSession.new(
108
+ id: tool_invocation.id,
109
+ context: tool_invocation,
110
+ entry_point: :idle,
111
+ phase_machine_class: phase_machine,
112
+ entry_actions: {},
113
+ auto_state_set: AUTO_STATE_SET,
114
+ declared_states: DECLARED_STATES,
115
+ wait_state_names: WAIT_STATES,
116
+ external_events: EXTERNAL_EVENTS,
117
+ recursion_limit: 20,
118
+ event_loop: runtime.event_loop,
119
+ resume_event: resume_event,
120
+ resume_phase: resume_phase
121
+ )
122
+ end
123
+ private_class_method :build_session
124
+
125
+ def self.build_entry_actions(runtime)
126
+ {
127
+ validating: [method(:validating_action)],
128
+ authorizing: [
129
+ method(:authorizing_action).curry.call(runtime)
130
+ ],
131
+ awaiting_approval: [
132
+ method(:awaiting_approval_action).curry.call(runtime)
133
+ ],
134
+ authorized: [
135
+ method(:authorized_action).curry.call(runtime)
136
+ ],
137
+ queued: [method(:queued_action)],
138
+ running: [
139
+ method(:running_action).curry.call(runtime)
140
+ ],
141
+ completed: [
142
+ method(:completed_action).curry.call(runtime)
143
+ ],
144
+ failed: [
145
+ method(:failed_action).curry.call(runtime)
146
+ ],
147
+ rejected: [
148
+ method(:rejected_action).curry.call(runtime)
149
+ ],
150
+ cancelled: [
151
+ method(:cancelled_action).curry.call(runtime)
152
+ ]
153
+ }
154
+ end
155
+ private_class_method :build_entry_actions
156
+
157
+ def self.build_phase_machine(entry_actions)
158
+ callbacks = entry_actions
159
+ callback_builder = method(:build_entry_callback)
160
+
161
+ Class.new do
162
+ attr_accessor :context, :current_event
163
+
164
+ state_machine :phase, initial: :idle do
165
+ state :idle
166
+ state :validating
167
+ state :authorizing
168
+ state :awaiting_approval
169
+ state :authorized
170
+ state :queued
171
+ state :running
172
+ state :completed
173
+ state :failed
174
+ state :rejected
175
+ state :cancelled
176
+
177
+ event :state_completed do
178
+ transition idle: :validating
179
+
180
+ transition validating: :failed,
181
+ if: ->(machine) { machine.context&.failed? }
182
+ transition validating: :completed,
183
+ if: ->(machine) {
184
+ machine.context&.validation_completed?
185
+ }
186
+ transition validating: :authorizing,
187
+ if: ->(machine) {
188
+ machine.context&.validation_passed?
189
+ }
190
+
191
+ transition queued: :running
192
+ end
193
+
194
+ event :authorization_completed do
195
+ transition authorizing: :cancelled,
196
+ if: ->(machine) { machine.context&.cancelled? }
197
+ transition authorizing: :failed,
198
+ if: ->(machine) { machine.context&.failed? }
199
+ transition authorizing: :rejected,
200
+ if: ->(machine) { machine.context&.rejected? }
201
+ transition authorizing: :awaiting_approval,
202
+ if: ->(machine) {
203
+ machine.context&.awaiting_approval?
204
+ }
205
+ transition authorizing: :authorized,
206
+ if: ->(machine) { machine.context&.authorized? }
207
+ end
208
+
209
+ event :execution_completed do
210
+ transition running: :cancelled,
211
+ if: ->(machine) { machine.context&.cancelled? }
212
+ transition running: :failed,
213
+ if: ->(machine) { machine.context&.failed? }
214
+ transition running: :completed,
215
+ if: ->(machine) {
216
+ machine.context&.execution_completed?
217
+ }
218
+ end
219
+
220
+ event :approve do
221
+ transition awaiting_approval: :authorized
222
+ end
223
+
224
+ event :reject do
225
+ transition awaiting_approval: :rejected
226
+ end
227
+
228
+ event :dispatch do
229
+ transition authorized: :queued
230
+ end
231
+
232
+ event :cancel do
233
+ transition awaiting_approval: :cancelled
234
+ transition authorized: :cancelled
235
+ transition queued: :cancelled
236
+ transition running: :cancelled
237
+ end
238
+
239
+ callbacks.each do |state_name, callables|
240
+ callables.each do |callable|
241
+ after_transition(
242
+ to: state_name,
243
+ do: callback_builder.call(callable, state_name)
244
+ )
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ private_class_method :build_phase_machine
251
+
252
+ def self.build_entry_callback(callable, state_name)
253
+ ->(machine) {
254
+ result = callable.call(machine.context)
255
+ if result.is_a?(Phronomy::Task)
256
+ raise Phronomy::InvalidAsyncEntryActionError,
257
+ "Tool entry action for #{state_name.inspect} returned Phronomy::Task"
258
+ end
259
+ machine.context = result if result.respond_to?(:set_graph_metadata)
260
+ }
261
+ end
262
+ private_class_method :build_entry_callback
263
+
264
+ def self.validating_action(invocation)
265
+ invocation.validate!
266
+ end
267
+ private_class_method :validating_action
268
+
269
+ def self.authorizing_action(runtime, invocation)
270
+ task = invocation.authorization_task(runtime: runtime)
271
+ observe_task(
272
+ runtime,
273
+ invocation,
274
+ task,
275
+ event_type: :authorization_completed
276
+ )
277
+ invocation
278
+ end
279
+ private_class_method :authorizing_action
280
+
281
+ def self.awaiting_approval_action(runtime, invocation)
282
+ invocation.mark_awaiting_approval!
283
+ notify_parent(runtime, invocation, :tool_approval_required)
284
+ invocation
285
+ end
286
+ private_class_method :awaiting_approval_action
287
+
288
+ def self.authorized_action(runtime, invocation)
289
+ invocation.mark_authorized!
290
+ notify_parent(runtime, invocation, :tool_authorized)
291
+ invocation
292
+ end
293
+ private_class_method :authorized_action
294
+
295
+ def self.queued_action(invocation)
296
+ invocation.mark_queued!
297
+ end
298
+ private_class_method :queued_action
299
+
300
+ def self.running_action(runtime, invocation)
301
+ # execution_task checks dispatchable? which requires :queued status;
302
+ # mark_running! is deferred until after the task is started.
303
+ task = invocation.execution_task(runtime: runtime)
304
+ invocation.mark_running!
305
+ observe_task(
306
+ runtime,
307
+ invocation,
308
+ task,
309
+ event_type: :execution_completed
310
+ )
311
+ invocation
312
+ end
313
+ private_class_method :running_action
314
+
315
+ def self.observe_task(
316
+ runtime,
317
+ invocation,
318
+ task,
319
+ event_type:
320
+ )
321
+ task.on_complete do |outcome, error|
322
+ payload = error || outcome
323
+ accepted = runtime.event_loop.post_to_session(
324
+ Phronomy::Event.new(
325
+ type: event_type,
326
+ target_id: invocation.id,
327
+ payload: payload
328
+ )
329
+ )
330
+ next if accepted
331
+
332
+ Phronomy.configuration.logger&.warn(
333
+ "[Phronomy] Dropped #{event_type.inspect} for " \
334
+ "ToolInvocation #{invocation.id}"
335
+ )
336
+ end
337
+ end
338
+ private_class_method :observe_task
339
+
340
+ def self.completed_action(runtime, invocation)
341
+ notify_parent(runtime, invocation, :tool_completed)
342
+ invocation
343
+ end
344
+ private_class_method :completed_action
345
+
346
+ def self.failed_action(runtime, invocation)
347
+ notify_parent(runtime, invocation, :tool_failed)
348
+ invocation
349
+ end
350
+ private_class_method :failed_action
351
+
352
+ def self.rejected_action(runtime, invocation)
353
+ invocation.mark_rejected!
354
+ notify_parent(runtime, invocation, :tool_rejected)
355
+ invocation
356
+ end
357
+ private_class_method :rejected_action
358
+
359
+ def self.cancelled_action(runtime, invocation)
360
+ invocation.mark_cancelled!
361
+ notify_parent(runtime, invocation, :tool_cancelled)
362
+ invocation
363
+ end
364
+ private_class_method :cancelled_action
365
+
366
+ def self.notify_parent(runtime, invocation, event_type)
367
+ runtime.event_loop.post_to_session(
368
+ Phronomy::Event.new(
369
+ type: event_type,
370
+ target_id: invocation.parent_agent_invocation_id,
371
+ payload: {tool_invocation_id: invocation.id}
372
+ )
373
+ )
374
+ end
375
+ private_class_method :notify_parent
376
+ end
377
+ end
378
+ end
@@ -2,16 +2,28 @@
2
2
 
3
3
  module Phronomy
4
4
  module Agent
5
- # Represents a single event emitted during a streaming agent invocation.
5
+ # Immutable event emitted by Agent async APIs.
6
6
  #
7
- # type values:
8
- # :token — a content delta from the LLM (payload: { content: String })
9
- # :tool_call — the LLM requested a tool call (payload: { tool_call: Object })
10
- # :tool_result — a tool finished executing (payload: { tool_call_id: String, tool_name: String,
11
- # tool_result: Object })
12
- # :done — the agent finished (payload: { output: String, messages: Array,
13
- # usage: TokenUsage })
14
- # :error — an unrecoverable error occurred (payload: { error: Exception })
7
+ # invoke_async and stream_async share lifecycle and Tool events. Streaming
8
+ # additionally emits :token events.
9
+ #
10
+ # Common event types:
11
+ # :tool_call
12
+ # :tool_result
13
+ # :approval_required
14
+ # :done
15
+ # :error
16
+ # :timeout
17
+ # :cancelled
18
+ #
19
+ # Streaming-only event type:
20
+ # :token
15
21
  StreamEvent = Data.define(:type, :payload)
16
22
  end
17
23
  end
24
+
25
+ require_relative "agent/async_event_api"
26
+
27
+ unless Phronomy::Agent::Base < Phronomy::Agent::AsyncEventApi
28
+ Phronomy::Agent::Base.prepend(Phronomy::Agent::AsyncEventApi)
29
+ end
@@ -10,6 +10,9 @@ module Phronomy
10
10
  # config.recursion_limit = 50
11
11
  # end
12
12
  class Configuration
13
+ STREAM_CALLBACK_ERROR_POLICIES = %i[report fail_task].freeze
14
+ private_constant :STREAM_CALLBACK_ERROR_POLICIES
15
+
13
16
  # Default LLM model name (nil delegates to RubyLLM default)
14
17
  attr_accessor :default_model
15
18
 
@@ -86,20 +89,6 @@ module Phronomy
86
89
  # Phronomy.configure { |c| c.llm_adapter = MyAsyncLLMAdapter.new }
87
90
  attr_accessor :llm_adapter
88
91
 
89
- # Default backpressure strategy for {BlockingAdapterPool#submit} when the
90
- # queue is full. One of +:wait+ (block until a slot is available),
91
- # +:raise+ (raise {Phronomy::BackpressureError}), or +:timeout+ (raise
92
- # {Phronomy::TimeoutError} after +backpressure_timeout+ seconds).
93
- # @return [:wait, :raise, :timeout]
94
- attr_accessor :backpressure
95
-
96
- # Seconds to wait before raising {Phronomy::TimeoutError} when
97
- # +backpressure+ is +:timeout+.
98
- # @return [Numeric, nil]
99
- attr_accessor :backpressure_timeout
100
-
101
- # Warn when an event spends longer than this many seconds waiting in the
102
- # EventLoop queue before being dispatched (starvation detection).
103
92
  # Set to +nil+ to disable the warning.
104
93
  # @return [Numeric, nil]
105
94
  attr_accessor :event_loop_starvation_threshold_seconds
@@ -120,37 +109,45 @@ module Phronomy
120
109
  # @return [Float, nil]
121
110
  attr_accessor :blocking_detect_threshold_ms
122
111
 
123
- # Maximum number of concurrent agent tasks (invoke_async calls in-flight).
124
- # nil = unlimited (default). When at capacity, behaviour is controlled by
125
- # +backpressure+ (:wait, :raise/:reject, :timeout).
126
- # @return [Integer, nil]
127
- attr_accessor :max_concurrent_agent_tasks
128
-
129
- # Maximum number of concurrent tool tasks (parallel tool calls in-flight).
130
- # nil = unlimited (default).
131
- # @return [Integer, nil]
132
- attr_accessor :max_concurrent_tool_tasks
133
-
134
- # Maximum number of concurrent workflow tasks.
135
- # nil = unlimited (default).
136
- # @return [Integer, nil]
137
- attr_accessor :max_concurrent_workflow_tasks
138
-
139
- # Maximum number of concurrent LLM calls in-flight.
140
- # nil = unlimited (default).
141
- # @return [Integer, nil]
142
- attr_accessor :max_concurrent_llm_calls
143
-
144
- # Upper bound on the number of streaming token chunks that may be buffered
145
- # in the {AsyncQueue} used by {Agent#stream} before the LLM producer is
146
- # throttled. When nil (default), the queue is unbounded.
147
- # @return [Integer, nil]
148
- attr_accessor :stream_queue_max_size
149
-
150
- # Maximum number of concurrent vector-store searches in-flight.
151
- # nil = unlimited (default).
152
- # @return [Integer, nil]
153
- attr_accessor :max_concurrent_vector_searches
112
+ # Determines how an unhandled Application exception from a terminal stream
113
+ # callback affects the Task returned by Agent#stream_async or
114
+ # Agent#approve_async.
115
+ #
116
+ # +:report+ logs the callback failure and preserves the Agent result.
117
+ # +:fail_task+ logs the callback failure and fails the current Task with
118
+ # {Phronomy::StreamCallbackError}. Neither policy terminates EventLoop.
119
+ #
120
+ # Default: +:report+.
121
+ # @return [:report, :fail_task]
122
+ attr_reader :stream_callback_error_policy
123
+
124
+ # Number of OS worker threads in the default {BlockingAdapterPool}.
125
+ # All LLM calls, MCP tool calls, and other blocking I/O share this pool.
126
+ # Increase for higher LLM/tool throughput; decrease to limit
127
+ # concurrency (e.g. to stay within a provider's rate limit).
128
+ # Default: 10.
129
+ # @return [Integer]
130
+ attr_accessor :blocking_io_pool_size
131
+
132
+ # Maximum number of operations that may wait in the {BlockingAdapterPool}
133
+ # queue before {Phronomy::BackpressureError} is raised (on_full: :raise) or
134
+ # the caller blocks (on_full: :wait, the default). Default: 100.
135
+ # @return [Integer]
136
+ attr_accessor :blocking_io_queue_size
137
+
138
+ # Worker count for Tool authorization evaluation. The named pool is owned
139
+ # by Runtime#pool(:authorization) and shares PoolRegistry lifecycle.
140
+ # @return [Integer]
141
+ attr_accessor :authorization_pool_size
142
+
143
+ # Maximum queued Tool authorization evaluations.
144
+ # @return [Integer]
145
+ attr_accessor :authorization_queue_size
146
+
147
+ # Operation-wide deadline for approval_facts, requires_approval callables,
148
+ # and Agent#tool_approval_policy. Timeout fails closed to Human approval.
149
+ # @return [Numeric]
150
+ attr_accessor :authorization_timeout
154
151
 
155
152
  # Scheduler starvation threshold (milliseconds).
156
153
  # When a task waits more than this many milliseconds after calling
@@ -188,6 +185,16 @@ module Phronomy
188
185
  # @return [Boolean]
189
186
  attr_accessor :strict_runtime_guards
190
187
 
188
+ def stream_callback_error_policy=(value)
189
+ unless STREAM_CALLBACK_ERROR_POLICIES.include?(value)
190
+ allowed = STREAM_CALLBACK_ERROR_POLICIES.map(&:inspect).join(", ")
191
+ raise Phronomy::ConfigurationError,
192
+ "stream_callback_error_policy must be one of: #{allowed}"
193
+ end
194
+
195
+ @stream_callback_error_policy = value
196
+ end
197
+
191
198
  def initialize
192
199
  @recursion_limit = 25
193
200
  @tracer = Phronomy::Tracing::NullTracer.new
@@ -195,18 +202,16 @@ module Phronomy
195
202
  @parallel_tool_execution = false
196
203
  @event_loop_stop_grace_seconds = 5
197
204
  @llm_adapter = Phronomy::LLMAdapter::RubyLLM.new
198
- @backpressure = :wait
199
- @backpressure_timeout = nil
200
205
  @event_loop_starvation_threshold_seconds = nil
201
206
  @event_loop_dispatch_threshold_seconds = nil
202
207
  @scheduler_debug = false
203
208
  @blocking_detect_threshold_ms = nil
204
- @max_concurrent_agent_tasks = nil
205
- @max_concurrent_tool_tasks = nil
206
- @max_concurrent_workflow_tasks = nil
207
- @max_concurrent_llm_calls = nil
208
- @stream_queue_max_size = nil
209
- @max_concurrent_vector_searches = nil
209
+ @stream_callback_error_policy = :report
210
+ @blocking_io_pool_size = 10
211
+ @blocking_io_queue_size = 100
212
+ @authorization_pool_size = 4
213
+ @authorization_queue_size = 100
214
+ @authorization_timeout = 5
210
215
  @starvation_threshold_ms = 50
211
216
  @runtime_backend = :thread
212
217
  @strict_runtime_guards = false
@@ -52,7 +52,7 @@ module Phronomy
52
52
  # @return [void]
53
53
  # @api private
54
54
  def self.assert_not_in_event_loop!
55
- return unless Phronomy::EventLoop.current?
55
+ return unless Phronomy::Runtime.in_event_loop_context?
56
56
 
57
57
  raise Phronomy::SchedulerReentrancyError,
58
58
  "Blocking invoke called from inside an EventLoop action. " \