phronomy 0.17.0 → 0.18.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -1134
- data/README.md +68 -1280
- data/benchmark/bench_regression.rb +25 -5
- data/docs/changelog/0.14-and-earlier.md +1137 -0
- data/docs/decisions/008-orchestrator-uses-os-threads.md +46 -48
- data/docs/decisions/010-cooperative-first-concurrency.md +155 -235
- data/docs/features.md +87 -0
- data/docs/getting-started.md +351 -0
- data/docs/migrations/0.15.md +35 -0
- data/docs/migrations/0.16.md +43 -0
- data/docs/runtime-and-concurrency.md +258 -0
- data/examples/workflows/generic_task_event_mapping.rb +14 -6
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +2 -2
- data/lib/phronomy/agent/async_event_api.rb +3 -3
- data/lib/phronomy/agent/base.rb +35 -19
- data/lib/phronomy/agent/context/capability/base.rb +13 -3
- data/lib/phronomy/agent/execution_coordinator.rb +6 -6
- data/lib/phronomy/agent/shared_state.rb +2 -0
- data/lib/phronomy/agent/tool_executor.rb +29 -71
- data/lib/phronomy/agent/tool_invocation.rb +97 -47
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +55 -161
- data/lib/phronomy/configuration.rb +5 -29
- data/lib/phronomy/diagnostics.rb +12 -41
- data/lib/phronomy/engine/concurrency/async_queue.rb +5 -188
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +6 -7
- data/lib/phronomy/engine/concurrency/cancellation_token.rb +48 -3
- data/lib/phronomy/engine/concurrency/deadline.rb +2 -3
- data/lib/phronomy/engine/concurrency/offload_pool.rb +696 -0
- data/lib/phronomy/engine/concurrency/pool_registry.rb +5 -5
- data/lib/phronomy/engine/event_loop.rb +89 -190
- data/lib/phronomy/engine/runtime/timer_queue.rb +48 -71
- data/lib/phronomy/engine/runtime/timer_service.rb +13 -21
- data/lib/phronomy/engine/runtime.rb +45 -158
- data/lib/phronomy/engine/task.rb +136 -277
- data/lib/phronomy/llm_adapter/base.rb +14 -14
- data/lib/phronomy/llm_adapter/ruby_llm.rb +3 -4
- data/lib/phronomy/llm_adapter.rb +2 -2
- data/lib/phronomy/metrics.rb +15 -30
- data/lib/phronomy/multi_agent/fan_out_invocation.rb +146 -0
- data/lib/phronomy/multi_agent/fan_out_session_builder.rb +125 -0
- data/lib/phronomy/multi_agent/handoff.rb +1 -0
- data/lib/phronomy/multi_agent/orchestrator.rb +147 -99
- data/lib/phronomy/multi_agent/team_coordinator.rb +2 -0
- data/lib/phronomy/testing/eval/comparison.rb +23 -0
- data/lib/phronomy/testing/eval/dataset.rb +27 -0
- data/lib/phronomy/testing/eval/eval_case.rb +13 -0
- data/lib/phronomy/testing/eval/eval_result.rb +16 -0
- data/lib/phronomy/testing/eval/metrics.rb +43 -0
- data/lib/phronomy/testing/eval/runner.rb +52 -0
- data/lib/phronomy/testing/eval/scorer/base.rb +15 -0
- data/lib/phronomy/testing/eval/scorer/exact_match.rb +25 -0
- data/lib/phronomy/testing/eval/scorer/includes_scorer.rb +25 -0
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +46 -0
- data/lib/phronomy/testing/eval/scorer.rb +10 -0
- data/lib/phronomy/testing/eval.rb +9 -0
- data/lib/phronomy/testing/fake_clock.rb +6 -53
- data/lib/phronomy/testing.rb +2 -6
- data/lib/phronomy/tools/agent.rb +141 -6
- data/lib/phronomy/vector_store/async_backend.rb +21 -17
- data/lib/phronomy/vector_store/base.rb +2 -2
- data/lib/phronomy/vector_store/embeddings/base.rb +6 -5
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow_runner.rb +2 -4
- data/lib/phronomy.rb +7 -4
- data/scripts/api_snapshot.rb +4 -4
- metadata +23 -31
- data/lib/phronomy/engine/concurrency/blocking_adapter_pool.rb +0 -561
- data/lib/phronomy/engine/runtime/deterministic_scheduler.rb +0 -439
- data/lib/phronomy/engine/runtime/fake_scheduler.rb +0 -165
- data/lib/phronomy/engine/runtime/runtime_metrics.rb +0 -116
- data/lib/phronomy/engine/runtime/scheduler.rb +0 -98
- data/lib/phronomy/engine/runtime/scheduler_timer_adapter.rb +0 -79
- data/lib/phronomy/engine/runtime/task_registry.rb +0 -95
- data/lib/phronomy/engine/runtime/thread_scheduler.rb +0 -30
- data/lib/phronomy/engine/task/backend.rb +0 -80
- data/lib/phronomy/engine/task/deferred_backend.rb +0 -73
- data/lib/phronomy/engine/task/fiber_backend.rb +0 -157
- data/lib/phronomy/engine/task/immediate_backend.rb +0 -89
- data/lib/phronomy/engine/task/mapped_backend.rb +0 -90
- data/lib/phronomy/engine/task/thread_backend.rb +0 -84
- data/lib/phronomy/engine/task_group.rb +0 -193
- data/lib/phronomy/eval/comparison.rb +0 -47
- data/lib/phronomy/eval/dataset.rb +0 -45
- data/lib/phronomy/eval/eval_case.rb +0 -17
- data/lib/phronomy/eval/eval_result.rb +0 -29
- data/lib/phronomy/eval/metrics.rb +0 -66
- data/lib/phronomy/eval/runner.rb +0 -94
- data/lib/phronomy/eval/scorer/base.rb +0 -22
- data/lib/phronomy/eval/scorer/exact_match.rb +0 -31
- data/lib/phronomy/eval/scorer/includes_scorer.rb +0 -32
- data/lib/phronomy/eval/scorer/llm_judge.rb +0 -72
- data/lib/phronomy/eval/scorer.rb +0 -9
- data/lib/phronomy/eval.rb +0 -7
- data/lib/phronomy/testing/fake_scheduler.rb +0 -104
- data/lib/phronomy/testing/scheduler_helpers.rb +0 -68
|
@@ -6,7 +6,7 @@ module Phronomy
|
|
|
6
6
|
# optionally a {Deadline}.
|
|
7
7
|
#
|
|
8
8
|
# +CancellationScope+ replaces ad-hoc +Timeout.timeout+ calls in agent and
|
|
9
|
-
# tool code.
|
|
9
|
+
# tool code. All work performed within a scope should observe the scope's
|
|
10
10
|
# token; when the scope is cancelled (explicitly or by deadline expiry) the
|
|
11
11
|
# token is cancelled and all child tasks that check it will stop.
|
|
12
12
|
#
|
|
@@ -18,10 +18,9 @@ module Phronomy
|
|
|
18
18
|
#
|
|
19
19
|
# @example Explicit cancellation
|
|
20
20
|
# scope = Phronomy::Concurrency::CancellationScope.new
|
|
21
|
-
# Phronomy::Runtime.instance.
|
|
22
|
-
# scope.token
|
|
23
|
-
#
|
|
24
|
-
# end
|
|
21
|
+
# operation = Phronomy::Runtime.instance.offload.submit(
|
|
22
|
+
# cancellation_token: scope.token
|
|
23
|
+
# ) { synchronous_operation }
|
|
25
24
|
# scope.cancel! if some_condition
|
|
26
25
|
class CancellationScope
|
|
27
26
|
# @return [CancellationToken] the token owned by this scope
|
|
@@ -33,7 +32,7 @@ module Phronomy
|
|
|
33
32
|
# @param parent_token [CancellationToken, nil] when provided, cancellation of
|
|
34
33
|
# the parent token is propagated to this scope's token via a callback
|
|
35
34
|
# (for explicit cancel) and/or the Runtime timer queue (for monotonic
|
|
36
|
-
# deadline expiry).
|
|
35
|
+
# deadline expiry). No polling thread is spawned.
|
|
37
36
|
# @api private
|
|
38
37
|
def initialize(parent_token: nil)
|
|
39
38
|
@token = Phronomy::Concurrency::CancellationToken.new
|
|
@@ -92,7 +91,7 @@ module Phronomy
|
|
|
92
91
|
end
|
|
93
92
|
|
|
94
93
|
# Pops from +queue+ with a timeout derived from the attached deadline (or
|
|
95
|
-
# +fallback_timeout+ seconds when no deadline is set).
|
|
94
|
+
# +fallback_timeout+ seconds when no deadline is set). If the pop times out,
|
|
96
95
|
# the scope is cancelled and the block is called (or a {TimeoutError} raised).
|
|
97
96
|
#
|
|
98
97
|
# @param queue [Phronomy::Concurrency::AsyncQueue] the queue to pop from
|
|
@@ -28,8 +28,20 @@ module Phronomy
|
|
|
28
28
|
[remaining, 0.0].max
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
# Registers a callback for explicit cancellation.
|
|
32
|
+
#
|
|
33
|
+
# Deadline expiry by itself only changes {#cancelled?}. Components that need
|
|
34
|
+
# callback delivery for a monotonic deadline must promote that deadline to
|
|
35
|
+
# +cancel!+ through the Runtime timer queue.
|
|
36
|
+
#
|
|
37
|
+
# Cancellation callbacks are independent notifications. Failure of one
|
|
38
|
+
# callback is logged and does not suppress delivery to other callbacks.
|
|
39
|
+
#
|
|
40
|
+
# @return [self]
|
|
31
41
|
# @api public
|
|
32
42
|
def on_cancel(&block)
|
|
43
|
+
raise ArgumentError, "on_cancel requires a block" unless block
|
|
44
|
+
|
|
33
45
|
already_cancelled = @mutex.synchronize do
|
|
34
46
|
if @cancelled
|
|
35
47
|
true
|
|
@@ -38,19 +50,29 @@ module Phronomy
|
|
|
38
50
|
false
|
|
39
51
|
end
|
|
40
52
|
end
|
|
41
|
-
block
|
|
53
|
+
deliver_cancel_callback(block) if already_cancelled
|
|
42
54
|
self
|
|
43
55
|
end
|
|
44
56
|
|
|
57
|
+
# Explicitly cancels the token and invokes each currently registered callback
|
|
58
|
+
# once. The callback registry is cleared before callbacks run so completed
|
|
59
|
+
# registrations are not retained for the lifetime of a long-lived token.
|
|
60
|
+
#
|
|
61
|
+
# Cancellation callbacks are isolated from one another: a StandardError raised
|
|
62
|
+
# by one callback is logged and the remaining callbacks are still delivered.
|
|
63
|
+
#
|
|
64
|
+
# @return [self]
|
|
45
65
|
# @api public
|
|
46
66
|
def cancel!
|
|
47
67
|
callbacks = @mutex.synchronize do
|
|
48
68
|
return self if @cancelled
|
|
49
69
|
|
|
50
70
|
@cancelled = true
|
|
51
|
-
@cancel_callbacks
|
|
71
|
+
callbacks = @cancel_callbacks
|
|
72
|
+
@cancel_callbacks = []
|
|
73
|
+
callbacks
|
|
52
74
|
end
|
|
53
|
-
callbacks.each(
|
|
75
|
+
callbacks.each { |callback| deliver_cancel_callback(callback) }
|
|
54
76
|
self
|
|
55
77
|
end
|
|
56
78
|
|
|
@@ -66,6 +88,29 @@ module Phronomy
|
|
|
66
88
|
def raise_if_cancelled!(message = "invocation cancelled")
|
|
67
89
|
raise Phronomy::CancellationError, message if cancelled?
|
|
68
90
|
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def deliver_cancel_callback(callback)
|
|
95
|
+
callback.call
|
|
96
|
+
rescue => error
|
|
97
|
+
Phronomy.configuration.logger&.error do
|
|
98
|
+
"[CancellationToken] on_cancel callback raised #{error.class}: #{error.message}"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Removes an explicit-cancellation callback that is no longer needed.
|
|
103
|
+
#
|
|
104
|
+
# This exists so framework operation registrations do not keep their captured
|
|
105
|
+
# state alive when the operation completes before the token is cancelled. It is
|
|
106
|
+
# intentionally not part of the public cancellation API.
|
|
107
|
+
#
|
|
108
|
+
# A concurrent +cancel!+ may already have taken the callback for delivery;
|
|
109
|
+
# callers must therefore make their callback idempotent.
|
|
110
|
+
def unregister_cancel_callback(callback)
|
|
111
|
+
@mutex.synchronize { @cancel_callbacks.delete(callback) }
|
|
112
|
+
self
|
|
113
|
+
end
|
|
69
114
|
end
|
|
70
115
|
end
|
|
71
116
|
end
|
|
@@ -44,9 +44,8 @@ module Phronomy
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Attaches this deadline to a {CancellationToken} by cancelling the token
|
|
47
|
-
# when the deadline expires.
|
|
48
|
-
#
|
|
49
|
-
# per deadline.
|
|
47
|
+
# when the deadline expires. The Runtime timer queue is driven by EventLoop;
|
|
48
|
+
# no timer-specific OS thread is created.
|
|
50
49
|
#
|
|
51
50
|
# @param token [CancellationToken]
|
|
52
51
|
# @param timer_queue [Runtime::TimerQueue, nil] queue to register with;
|