phronomy 0.16.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/.mutant.yml +8 -9
- data/CHANGELOG.md +151 -1134
- data/CONTRIBUTING.md +28 -16
- data/README.md +69 -1249
- data/benchmark/baseline.json +2 -3
- data/benchmark/bench_agent_invoke.rb +4 -4
- data/benchmark/bench_context_assembler.rb +134 -34
- data/benchmark/bench_regression.rb +26 -6
- data/benchmark/bench_tool_schema.rb +2 -35
- data/docs/changelog/0.14-and-earlier.md +1137 -0
- data/docs/decisions/005-static-knowledge-class-level-cache.md +12 -1
- data/docs/decisions/008-orchestrator-uses-os-threads.md +46 -48
- data/docs/decisions/010-cooperative-first-concurrency.md +155 -228
- data/docs/decisions/011-build-context-as-single-llm-input-authority.md +2 -2
- data/docs/decisions/013-journal-backed-knowledge-as-context-candidates.md +122 -0
- 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.rb +2 -36
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +157 -94
- data/lib/phronomy/agent/agent_root.rb +1 -2
- data/lib/phronomy/agent/async_event_api.rb +3 -3
- data/lib/phronomy/agent/base.rb +170 -333
- data/lib/phronomy/agent/context/capability/base.rb +177 -298
- data/lib/phronomy/agent/context_assembler.rb +65 -29
- data/lib/phronomy/agent/context_parts/unit_builders/dependency_aware_unit_builder.rb +19 -89
- data/lib/phronomy/agent/context_plan_validator.rb +0 -33
- data/lib/phronomy/agent/execution_coordinator.rb +6 -7
- data/lib/phronomy/agent/journal_projection.rb +28 -2
- data/lib/phronomy/agent/ruby_llm_materializer.rb +2 -111
- data/lib/phronomy/agent/shared_state.rb +48 -138
- data/lib/phronomy/agent/token_budget_resolver.rb +5 -4
- data/lib/phronomy/agent/tool_executor.rb +29 -71
- data/lib/phronomy/agent/tool_invocation.rb +180 -336
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +55 -161
- data/lib/phronomy/agent.rb +6 -10
- data/lib/phronomy/configuration.rb +4 -171
- 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 +51 -79
- 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 +49 -377
- 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/llm_context_window/token_budget.rb +8 -79
- 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 +252 -256
- data/lib/phronomy/multi_agent/team_coordinator.rb +44 -133
- 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/vector_store/in_memory.rb +2 -2
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow_runner.rb +2 -4
- data/lib/phronomy.rb +7 -121
- data/scripts/api_snapshot.rb +4 -15
- metadata +24 -38
- data/lib/phronomy/agent/context/knowledge/base.rb +0 -58
- data/lib/phronomy/agent/context/knowledge/entity_knowledge.rb +0 -102
- data/lib/phronomy/agent/context/knowledge/static_knowledge.rb +0 -58
- data/lib/phronomy/agent/fsm_runtime_adapter.rb +0 -210
- 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 -265
- 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/knowledge_source.rb +0 -12
- data/lib/phronomy/llm_context_window/assembler.rb +0 -191
- data/lib/phronomy/llm_context_window/context_version_cache.rb +0 -52
- data/lib/phronomy/testing/fake_scheduler.rb +0 -104
- data/lib/phronomy/testing/scheduler_helpers.rb +0 -68
data/lib/phronomy.rb
CHANGED
|
@@ -5,36 +5,18 @@ require "ruby_llm"
|
|
|
5
5
|
require_relative "phronomy/ruby_llm_patches"
|
|
6
6
|
|
|
7
7
|
loader = Zeitwerk::Loader.for_gem
|
|
8
|
-
# Teach Zeitwerk that "llm" maps to "LLM" so that file names such as
|
|
9
|
-
# ruby_llm_embeddings.rb resolve to RubyLLMEmbeddings (not RubyLlmEmbeddings).
|
|
10
8
|
loader.inflector.inflect("ruby_llm_embeddings" => "RubyLLMEmbeddings")
|
|
11
|
-
# RAG: Zeitwerk would infer "Rag" — override to "RAG".
|
|
12
9
|
loader.inflector.inflect("rag" => "RAG")
|
|
13
|
-
# FSMSession: Zeitwerk would infer "FsmSession" — override to "FSMSession".
|
|
14
|
-
# Phronomy::FSMSession is the top-level cooperative execution engine shared by
|
|
15
|
-
# WorkflowRunner, AgentInvocationSessionBuilder, and ToolInvocationSessionBuilder.
|
|
16
10
|
loader.inflector.inflect("fsm_session" => "FSMSession")
|
|
17
|
-
# LLMAdapter: Zeitwerk would infer "LlmAdapter" — override to "LLMAdapter".
|
|
18
11
|
loader.inflector.inflect("llm_adapter" => "LLMAdapter")
|
|
19
|
-
# LLMOperationResult: preserve the LLM acronym for the Agent result carrier.
|
|
20
12
|
loader.inflector.inflect("llm_operation_result" => "LLMOperationResult")
|
|
21
|
-
# LLMAdapter::RubyLLM: "ruby_llm" maps to "RubyLLM" (not "RubyLlm").
|
|
22
13
|
loader.inflector.inflect("ruby_llm" => "RubyLLM")
|
|
23
|
-
# CanonicalJSON: preserve uppercase JSON acronym.
|
|
24
14
|
loader.inflector.inflect("canonical_json" => "CanonicalJSON")
|
|
25
|
-
# RubyLLMMaterializer: preserve the double-uppercase LLM acronym.
|
|
26
15
|
loader.inflector.inflect("ruby_llm_materializer" => "RubyLLMMaterializer")
|
|
27
|
-
# LLMCallRecord: preserve uppercase LLM acronym.
|
|
28
16
|
loader.inflector.inflect("llm_call_record" => "LLMCallRecord")
|
|
29
|
-
# LLMInputManifest: preserve uppercase LLM acronym.
|
|
30
17
|
loader.inflector.inflect("llm_input_manifest" => "LLMInputManifest")
|
|
31
|
-
# LLMInputBuildContext / LLMInputPatch: preserve uppercase LLM acronym.
|
|
32
18
|
loader.inflector.inflect("llm_input_build_context" => "LLMInputBuildContext")
|
|
33
19
|
loader.inflector.inflect("llm_input_patch" => "LLMInputPatch")
|
|
34
|
-
# Collapse engine/ so that its contents autoload directly under Phronomy::
|
|
35
|
-
# (no Engine:: prefix). e.g. engine/event_loop.rb => Phronomy::EventLoop.
|
|
36
|
-
# This allows the execution engine to be organised in its own subdirectory
|
|
37
|
-
# without changing any class names or callers.
|
|
38
20
|
loader.collapse("#{__dir__}/phronomy/engine")
|
|
39
21
|
loader.setup
|
|
40
22
|
|
|
@@ -42,70 +24,31 @@ require_relative "phronomy/version"
|
|
|
42
24
|
require_relative "phronomy/token_usage"
|
|
43
25
|
|
|
44
26
|
module Phronomy
|
|
45
|
-
# Exception hierarchy
|
|
46
27
|
class Error < StandardError; end
|
|
47
28
|
class ParseError < Error; end
|
|
48
29
|
class RecursionLimitError < Error; end
|
|
49
30
|
class ToolError < Error; end
|
|
50
|
-
# Base error for Phronomy-owned timed boundaries and generic timeout primitives.
|
|
51
31
|
class TimeoutError < Error; end
|
|
52
|
-
|
|
53
32
|
class ConfigurationError < Error; end
|
|
54
|
-
|
|
55
33
|
class HandoffError < Error; end
|
|
56
34
|
|
|
57
|
-
# Raised when a network or transport layer call fails (e.g. LLM API unreachable,
|
|
58
|
-
# MCP server connection refused). Distinguishable from application-level errors
|
|
59
|
-
# so callers can apply network-specific retry logic.
|
|
60
35
|
class TransportError < Error; end
|
|
61
|
-
|
|
62
|
-
# Raised when the LLM API returns a rate-limit response (HTTP 429 or equivalent).
|
|
63
|
-
# Callers should back off and retry after the indicated delay.
|
|
64
36
|
class RateLimitError < TransportError; end
|
|
65
|
-
|
|
66
|
-
# Raised when the LLM API rejects the request due to an invalid or revoked API key.
|
|
67
|
-
# Callers should not retry without fixing the credentials.
|
|
68
37
|
class AuthenticationError < TransportError; end
|
|
69
|
-
|
|
70
|
-
# Raised when the prompt exceeds the model's context window limit.
|
|
71
38
|
class ContextLengthError < Error; end
|
|
72
|
-
|
|
73
|
-
# Raised when a workflow or agent execution is explicitly cancelled.
|
|
74
|
-
# Separate from TimeoutError (deadline exceeded) — this is an intentional stop.
|
|
75
39
|
class CancellationError < Error; end
|
|
76
40
|
|
|
77
|
-
# Raised when
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
#
|
|
81
|
-
# scheduler
|
|
82
|
-
|
|
83
|
-
# +#await+ inside scheduler tasks instead.
|
|
84
|
-
#
|
|
85
|
-
# This error is only raised when:
|
|
86
|
-
# Phronomy.configure { |c| c.strict_runtime_guards = true }
|
|
87
|
-
#
|
|
88
|
-
# By default a warning is logged and execution continues.
|
|
89
|
-
#
|
|
90
|
-
# @see Phronomy::Runtime.in_scheduler_context?
|
|
91
|
-
class SchedulerReentrancyError < Error; end
|
|
92
|
-
|
|
93
|
-
# Raised when work is submitted to a Runtime whose shutdown has begun, or
|
|
94
|
-
# when a Runtime cannot be reset because owned resources are still alive.
|
|
95
|
-
class RuntimeShutdownError < Error; end
|
|
41
|
+
# Raised when a synchronous API would block the EventLoop control thread.
|
|
42
|
+
class EventLoopReentrancyError < Error; end
|
|
43
|
+
|
|
44
|
+
# Backward-compatible error class name for callers that still rescue the old
|
|
45
|
+
# scheduler-oriented exception. New code should use EventLoopReentrancyError.
|
|
46
|
+
class SchedulerReentrancyError < EventLoopReentrancyError; end
|
|
96
47
|
|
|
97
|
-
|
|
48
|
+
class RuntimeShutdownError < Error; end
|
|
98
49
|
class RuntimeShutdownReentrancyError < RuntimeShutdownError; end
|
|
99
50
|
|
|
100
|
-
# Raised by {Phronomy::GeneratorVerifier#invoke} when +raise_if_untrusted: true+
|
|
101
|
-
# and the pipeline's combined confidence score falls below the configured threshold.
|
|
102
|
-
#
|
|
103
|
-
# @example
|
|
104
|
-
# rescue Phronomy::LowConfidenceError => e
|
|
105
|
-
# puts e.result.confidence # => e.g. 0.45
|
|
106
|
-
# puts e.result.output # best-effort answer despite low confidence
|
|
107
51
|
class LowConfidenceError < Error
|
|
108
|
-
# @return [Phronomy::GeneratorVerifier::Result] the untrusted result
|
|
109
52
|
attr_reader :result
|
|
110
53
|
|
|
111
54
|
def initialize(result)
|
|
@@ -114,9 +57,6 @@ module Phronomy
|
|
|
114
57
|
end
|
|
115
58
|
end
|
|
116
59
|
|
|
117
|
-
# Raised by a {Phronomy::Filter::Base} subclass when the filter rejects a
|
|
118
|
-
# value without transforming it (blocking the pipeline).
|
|
119
|
-
# @api public
|
|
120
60
|
class FilterBlockError < Error
|
|
121
61
|
attr_reader :filter
|
|
122
62
|
|
|
@@ -126,27 +66,9 @@ module Phronomy
|
|
|
126
66
|
end
|
|
127
67
|
end
|
|
128
68
|
|
|
129
|
-
# Raised when an operation is submitted to a {BlockingAdapterPool} that has
|
|
130
|
-
# already been shut down via {BlockingAdapterPool#shutdown}.
|
|
131
69
|
class PoolShutdownError < Error; end
|
|
132
|
-
|
|
133
|
-
# Raised when a concurrency limit is exceeded and the configured backpressure
|
|
134
|
-
# strategy is +:raise+. The caller should back off and retry.
|
|
135
70
|
class BackpressureError < Error; end
|
|
136
71
|
|
|
137
|
-
# Raised by {CancellationScope#pop_queue} when the deadline expires before a
|
|
138
|
-
# result is available. Extends {TimeoutError} for backwards compatibility.
|
|
139
|
-
class ScopeTimeoutError < TimeoutError; end
|
|
140
|
-
|
|
141
|
-
# Deprecated compatibility constant. Workflow entry/exit actions are
|
|
142
|
-
# synchronous and the Workflow DSL no longer accepts +action_timeout:+.
|
|
143
|
-
class ActionTimeoutError < TimeoutError; end
|
|
144
|
-
|
|
145
|
-
# Raised when a {Phronomy::WorkflowContext} field is mutated from a thread
|
|
146
|
-
# that does not own the context (i.e. not the EventLoop dispatch thread).
|
|
147
|
-
# Only raised in EventLoop mode. Use +context.merge(...)+ to produce a new
|
|
148
|
-
# context, or deliver updates as +:action_completed+ event payloads
|
|
149
|
-
# via {Agent::Base#invoke_async} + {Task#map}.
|
|
150
72
|
class WorkflowContextOwnershipError < Error; end
|
|
151
73
|
|
|
152
74
|
class << self
|
|
@@ -158,36 +80,10 @@ module Phronomy
|
|
|
158
80
|
yield configuration
|
|
159
81
|
end
|
|
160
82
|
|
|
161
|
-
# Resets the global Phronomy configuration to defaults.
|
|
162
|
-
#
|
|
163
|
-
# **Intended for test suites only.** Calling this in a production process
|
|
164
|
-
# will drop all runtime configuration (tracer, model, tokenizer, etc.)
|
|
165
|
-
# globally and immediately affect all subsequent agent and workflow calls.
|
|
166
|
-
#
|
|
167
|
-
# **Parallel test suites warning:** When tests run in parallel (e.g.
|
|
168
|
-
# `parallel_tests` or `parallel_rspec`), +reset_configuration!+ in one
|
|
169
|
-
# worker will clear configuration shared with other workers in the same
|
|
170
|
-
# process. Prefer process-isolation strategies (forked workers) over
|
|
171
|
-
# thread-based parallelism when using this method.
|
|
172
|
-
#
|
|
173
|
-
# Typical usage in a sequential test suite:
|
|
174
|
-
# after { Phronomy.reset_configuration! }
|
|
175
83
|
def reset_configuration!
|
|
176
84
|
@configuration = Configuration.new
|
|
177
85
|
end
|
|
178
86
|
|
|
179
|
-
# Yields the current {Configuration} object, then restores the original
|
|
180
|
-
# configuration on exit (even if the block raises).
|
|
181
|
-
#
|
|
182
|
-
# Intended for test helpers that need to temporarily override settings
|
|
183
|
-
# without permanently mutating the global configuration.
|
|
184
|
-
#
|
|
185
|
-
# @yield [config] the current {Configuration} instance (mutable)
|
|
186
|
-
# @example
|
|
187
|
-
# Phronomy.with_configuration do |c|
|
|
188
|
-
# c.logger = Logger.new($stdout)
|
|
189
|
-
# end
|
|
190
|
-
# @api public
|
|
191
87
|
def with_configuration
|
|
192
88
|
original = @configuration&.dup
|
|
193
89
|
yield configuration
|
|
@@ -195,16 +91,6 @@ module Phronomy
|
|
|
195
91
|
@configuration = original
|
|
196
92
|
end
|
|
197
93
|
|
|
198
|
-
# Shuts down and clears the process-wide default Runtime, then resets
|
|
199
|
-
# global configuration. Intended for test suites only.
|
|
200
|
-
#
|
|
201
|
-
# Runtime execution failure and resource cleanup are separate. The
|
|
202
|
-
# singleton is cleared when cleanup completed, even if execution failed.
|
|
203
|
-
#
|
|
204
|
-
# @param timeout [Numeric] maximum graceful wait for Runtime tasks and
|
|
205
|
-
# EventLoop shutdown
|
|
206
|
-
# @return [Phronomy::Runtime::ShutdownResult]
|
|
207
|
-
# @api public
|
|
208
94
|
def reset_runtime!(timeout: configuration.event_loop_stop_grace_seconds)
|
|
209
95
|
previous_grace = @configuration&.event_loop_stop_grace_seconds
|
|
210
96
|
result = Runtime.reset_default!(timeout: timeout)
|
data/scripts/api_snapshot.rb
CHANGED
|
@@ -3,24 +3,19 @@
|
|
|
3
3
|
|
|
4
4
|
# scripts/api_snapshot.rb
|
|
5
5
|
#
|
|
6
|
-
# Dumps the public instance methods of all Stable/Beta
|
|
7
|
-
# JSON.
|
|
8
|
-
#
|
|
6
|
+
# Dumps the public instance methods of all Stable/Beta product API classes to
|
|
7
|
+
# JSON. Testing helpers are intentionally excluded from this compatibility gate.
|
|
8
|
+
# The snapshot is stored in spec/fixtures/api_snapshot.json and is used by
|
|
9
|
+
# spec/phronomy/api_compatibility_spec.rb to detect unintended API removals.
|
|
9
10
|
#
|
|
10
11
|
# Usage:
|
|
11
|
-
# # Regenerate spec/fixtures/api_snapshot.json (run when intentionally adding
|
|
12
|
-
# # or removing public API methods after updating the stability table):
|
|
13
12
|
# ruby scripts/api_snapshot.rb --write
|
|
14
|
-
#
|
|
15
|
-
# # Print snapshot to stdout (useful for manual inspection):
|
|
16
13
|
# ruby scripts/api_snapshot.rb
|
|
17
14
|
|
|
18
15
|
require "json"
|
|
19
16
|
require "fileutils"
|
|
20
17
|
require_relative "../lib/phronomy"
|
|
21
18
|
|
|
22
|
-
# Classes and modules whose public API is tracked.
|
|
23
|
-
# Add an entry whenever a new class/module is promoted to Stable or Beta in README.md.
|
|
24
19
|
PUBLIC_API_ENTRIES = [
|
|
25
20
|
# Stable
|
|
26
21
|
Phronomy::Agent::Base,
|
|
@@ -37,17 +32,13 @@ PUBLIC_API_ENTRIES = [
|
|
|
37
32
|
Phronomy::VectorStore::Base,
|
|
38
33
|
Phronomy::VectorStore::InMemory,
|
|
39
34
|
Phronomy::VectorStore::Embeddings::Base,
|
|
40
|
-
Phronomy::Agent::Context::Knowledge::Base,
|
|
41
|
-
Phronomy::Agent::Context::Knowledge::StaticKnowledge,
|
|
42
35
|
Phronomy::Tracing::Base,
|
|
43
36
|
Phronomy::Tracing::NullTracer,
|
|
44
|
-
Phronomy::Eval::Runner,
|
|
45
37
|
Phronomy::Tools::Mcp,
|
|
46
38
|
Phronomy::Tools::Agent,
|
|
47
39
|
Phronomy::Tools::VectorSearch
|
|
48
40
|
].freeze
|
|
49
41
|
|
|
50
|
-
# Baseline methods common to all Ruby objects — excluded from the snapshot.
|
|
51
42
|
BASELINE_INSTANCE_METHODS = (
|
|
52
43
|
Object.public_instance_methods |
|
|
53
44
|
Kernel.public_instance_methods
|
|
@@ -60,7 +51,6 @@ BASELINE_CLASS_METHODS = (
|
|
|
60
51
|
|
|
61
52
|
def snapshot_entry(klass)
|
|
62
53
|
if klass.instance_of?(Module)
|
|
63
|
-
# Module — capture instance methods defined in this module only
|
|
64
54
|
own_methods = klass.public_instance_methods(false).sort
|
|
65
55
|
{
|
|
66
56
|
"name" => klass.name,
|
|
@@ -68,7 +58,6 @@ def snapshot_entry(klass)
|
|
|
68
58
|
"public_instance_methods" => own_methods
|
|
69
59
|
}
|
|
70
60
|
else
|
|
71
|
-
# Class — capture public instance methods minus universal baseline
|
|
72
61
|
instance_methods = (klass.public_instance_methods - BASELINE_INSTANCE_METHODS).sort
|
|
73
62
|
class_methods = (klass.public_methods(false) - BASELINE_CLASS_METHODS).sort
|
|
74
63
|
{
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: phronomy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.18.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Raizo T.C.S
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-14 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ruby_llm
|
|
@@ -104,6 +104,7 @@ files:
|
|
|
104
104
|
- benchmark/bench_vector_store.rb
|
|
105
105
|
- benchmark/bench_workflow.rb
|
|
106
106
|
- benchmark/run_all.rb
|
|
107
|
+
- docs/changelog/0.14-and-earlier.md
|
|
107
108
|
- docs/decisions/001-rubyllm-as-provider-layer.md
|
|
108
109
|
- docs/decisions/002-workflow-context-immutability.md
|
|
109
110
|
- docs/decisions/003-event-loop-singleton.md
|
|
@@ -117,7 +118,13 @@ files:
|
|
|
117
118
|
- docs/decisions/011-build-context-as-single-llm-input-authority.md
|
|
118
119
|
- docs/decisions/011-delegate-transport-policy-to-adapters.md
|
|
119
120
|
- docs/decisions/012-canonical-execution-log-and-context-policy.md
|
|
121
|
+
- docs/decisions/013-journal-backed-knowledge-as-context-candidates.md
|
|
122
|
+
- docs/features.md
|
|
123
|
+
- docs/getting-started.md
|
|
120
124
|
- docs/mcp-client.md
|
|
125
|
+
- docs/migrations/0.15.md
|
|
126
|
+
- docs/migrations/0.16.md
|
|
127
|
+
- docs/runtime-and-concurrency.md
|
|
121
128
|
- examples/workflows/agent_event_mapping.rb
|
|
122
129
|
- examples/workflows/generic_task_event_mapping.rb
|
|
123
130
|
- gemfiles/mcp_1_0.gemfile
|
|
@@ -137,9 +144,6 @@ files:
|
|
|
137
144
|
- lib/phronomy/agent/concerns/filterable.rb
|
|
138
145
|
- lib/phronomy/agent/context/capability/base.rb
|
|
139
146
|
- lib/phronomy/agent/context/instruction/prompt_template.rb
|
|
140
|
-
- lib/phronomy/agent/context/knowledge/base.rb
|
|
141
|
-
- lib/phronomy/agent/context/knowledge/entity_knowledge.rb
|
|
142
|
-
- lib/phronomy/agent/context/knowledge/static_knowledge.rb
|
|
143
147
|
- lib/phronomy/agent/context_assembler.rb
|
|
144
148
|
- lib/phronomy/agent/context_candidate.rb
|
|
145
149
|
- lib/phronomy/agent/context_candidate_resolver.rb
|
|
@@ -159,7 +163,6 @@ files:
|
|
|
159
163
|
- lib/phronomy/agent/context_selection_unit.rb
|
|
160
164
|
- lib/phronomy/agent/derived_content_spec.rb
|
|
161
165
|
- lib/phronomy/agent/execution_coordinator.rb
|
|
162
|
-
- lib/phronomy/agent/fsm_runtime_adapter.rb
|
|
163
166
|
- lib/phronomy/agent/immutable.rb
|
|
164
167
|
- lib/phronomy/agent/journal_projection.rb
|
|
165
168
|
- lib/phronomy/agent/journal_record.rb
|
|
@@ -187,44 +190,18 @@ files:
|
|
|
187
190
|
- lib/phronomy/context_budget_exceeded_error.rb
|
|
188
191
|
- lib/phronomy/diagnostics.rb
|
|
189
192
|
- lib/phronomy/engine/concurrency/async_queue.rb
|
|
190
|
-
- lib/phronomy/engine/concurrency/blocking_adapter_pool.rb
|
|
191
193
|
- lib/phronomy/engine/concurrency/cancellation_scope.rb
|
|
192
194
|
- lib/phronomy/engine/concurrency/cancellation_token.rb
|
|
193
195
|
- lib/phronomy/engine/concurrency/deadline.rb
|
|
196
|
+
- lib/phronomy/engine/concurrency/offload_pool.rb
|
|
194
197
|
- lib/phronomy/engine/concurrency/pool_registry.rb
|
|
195
198
|
- lib/phronomy/engine/event_loop.rb
|
|
196
199
|
- lib/phronomy/engine/fsm_session.rb
|
|
197
200
|
- lib/phronomy/engine/runtime.rb
|
|
198
|
-
- lib/phronomy/engine/runtime/deterministic_scheduler.rb
|
|
199
|
-
- lib/phronomy/engine/runtime/fake_scheduler.rb
|
|
200
|
-
- lib/phronomy/engine/runtime/runtime_metrics.rb
|
|
201
|
-
- lib/phronomy/engine/runtime/scheduler.rb
|
|
202
|
-
- lib/phronomy/engine/runtime/scheduler_timer_adapter.rb
|
|
203
201
|
- lib/phronomy/engine/runtime/shutdown_result.rb
|
|
204
|
-
- lib/phronomy/engine/runtime/task_registry.rb
|
|
205
|
-
- lib/phronomy/engine/runtime/thread_scheduler.rb
|
|
206
202
|
- lib/phronomy/engine/runtime/timer_queue.rb
|
|
207
203
|
- lib/phronomy/engine/runtime/timer_service.rb
|
|
208
204
|
- lib/phronomy/engine/task.rb
|
|
209
|
-
- lib/phronomy/engine/task/backend.rb
|
|
210
|
-
- lib/phronomy/engine/task/deferred_backend.rb
|
|
211
|
-
- lib/phronomy/engine/task/fiber_backend.rb
|
|
212
|
-
- lib/phronomy/engine/task/immediate_backend.rb
|
|
213
|
-
- lib/phronomy/engine/task/mapped_backend.rb
|
|
214
|
-
- lib/phronomy/engine/task/thread_backend.rb
|
|
215
|
-
- lib/phronomy/engine/task_group.rb
|
|
216
|
-
- lib/phronomy/eval.rb
|
|
217
|
-
- lib/phronomy/eval/comparison.rb
|
|
218
|
-
- lib/phronomy/eval/dataset.rb
|
|
219
|
-
- lib/phronomy/eval/eval_case.rb
|
|
220
|
-
- lib/phronomy/eval/eval_result.rb
|
|
221
|
-
- lib/phronomy/eval/metrics.rb
|
|
222
|
-
- lib/phronomy/eval/runner.rb
|
|
223
|
-
- lib/phronomy/eval/scorer.rb
|
|
224
|
-
- lib/phronomy/eval/scorer/base.rb
|
|
225
|
-
- lib/phronomy/eval/scorer/exact_match.rb
|
|
226
|
-
- lib/phronomy/eval/scorer/includes_scorer.rb
|
|
227
|
-
- lib/phronomy/eval/scorer/llm_judge.rb
|
|
228
205
|
- lib/phronomy/event.rb
|
|
229
206
|
- lib/phronomy/execution_rehydration_required_error.rb
|
|
230
207
|
- lib/phronomy/filter.rb
|
|
@@ -236,15 +213,14 @@ files:
|
|
|
236
213
|
- lib/phronomy/invalid_async_workflow_action_error.rb
|
|
237
214
|
- lib/phronomy/invalid_context_budget_configuration_error.rb
|
|
238
215
|
- lib/phronomy/invocation_context.rb
|
|
239
|
-
- lib/phronomy/knowledge_source.rb
|
|
240
216
|
- lib/phronomy/llm_adapter.rb
|
|
241
217
|
- lib/phronomy/llm_adapter/base.rb
|
|
242
218
|
- lib/phronomy/llm_adapter/ruby_llm.rb
|
|
243
|
-
- lib/phronomy/llm_context_window/assembler.rb
|
|
244
|
-
- lib/phronomy/llm_context_window/context_version_cache.rb
|
|
245
219
|
- lib/phronomy/llm_context_window/token_budget.rb
|
|
246
220
|
- lib/phronomy/llm_context_window/token_estimator.rb
|
|
247
221
|
- lib/phronomy/metrics.rb
|
|
222
|
+
- lib/phronomy/multi_agent/fan_out_invocation.rb
|
|
223
|
+
- lib/phronomy/multi_agent/fan_out_session_builder.rb
|
|
248
224
|
- lib/phronomy/multi_agent/handoff.rb
|
|
249
225
|
- lib/phronomy/multi_agent/orchestrator.rb
|
|
250
226
|
- lib/phronomy/multi_agent/parallel_tool_chat.rb
|
|
@@ -261,9 +237,19 @@ files:
|
|
|
261
237
|
- lib/phronomy/state_store/in_memory.rb
|
|
262
238
|
- lib/phronomy/stream_callback_error.rb
|
|
263
239
|
- lib/phronomy/testing.rb
|
|
240
|
+
- lib/phronomy/testing/eval.rb
|
|
241
|
+
- lib/phronomy/testing/eval/comparison.rb
|
|
242
|
+
- lib/phronomy/testing/eval/dataset.rb
|
|
243
|
+
- lib/phronomy/testing/eval/eval_case.rb
|
|
244
|
+
- lib/phronomy/testing/eval/eval_result.rb
|
|
245
|
+
- lib/phronomy/testing/eval/metrics.rb
|
|
246
|
+
- lib/phronomy/testing/eval/runner.rb
|
|
247
|
+
- lib/phronomy/testing/eval/scorer.rb
|
|
248
|
+
- lib/phronomy/testing/eval/scorer/base.rb
|
|
249
|
+
- lib/phronomy/testing/eval/scorer/exact_match.rb
|
|
250
|
+
- lib/phronomy/testing/eval/scorer/includes_scorer.rb
|
|
251
|
+
- lib/phronomy/testing/eval/scorer/llm_judge.rb
|
|
264
252
|
- lib/phronomy/testing/fake_clock.rb
|
|
265
|
-
- lib/phronomy/testing/fake_scheduler.rb
|
|
266
|
-
- lib/phronomy/testing/scheduler_helpers.rb
|
|
267
253
|
- lib/phronomy/token_usage.rb
|
|
268
254
|
- lib/phronomy/tools/agent.rb
|
|
269
255
|
- lib/phronomy/tools/mcp.rb
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Agent
|
|
5
|
-
module Context
|
|
6
|
-
module Knowledge
|
|
7
|
-
# Abstract base class for all KnowledgeSource implementations.
|
|
8
|
-
#
|
|
9
|
-
# Subclasses must implement #fetch(query:) and return an Array of chunk Hashes.
|
|
10
|
-
# Each chunk Hash must contain:
|
|
11
|
-
# :content [String] the text to inject into the context
|
|
12
|
-
# :type [Symbol] semantic tag (e.g. :static, :rag, :entity)
|
|
13
|
-
class Base
|
|
14
|
-
# Retrieve knowledge chunks relevant to the given query.
|
|
15
|
-
#
|
|
16
|
-
# @param query [String, nil] the current user input used to select relevant chunks
|
|
17
|
-
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil] optional token; raises CancellationError when cancelled
|
|
18
|
-
# @return [Array<Hash>] array of { content: String, type: Symbol }
|
|
19
|
-
# @api public
|
|
20
|
-
def fetch(query: nil, cancellation_token: nil)
|
|
21
|
-
cancellation_token&.raise_if_cancelled!
|
|
22
|
-
raise NotImplementedError, "#{self.class}#fetch is not implemented"
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Submits a {#fetch} call to {BlockingAdapterPool} and returns a
|
|
26
|
-
# {BlockingAdapterPool::PendingOperation}.
|
|
27
|
-
# Callers can fan out multiple fetches in parallel and await them all.
|
|
28
|
-
#
|
|
29
|
-
# @param query [String, nil]
|
|
30
|
-
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
|
|
31
|
-
# @param timeout [Numeric, nil] seconds before the operation is abandoned
|
|
32
|
-
# @return [BlockingAdapterPool::PendingOperation]
|
|
33
|
-
# @api public
|
|
34
|
-
def fetch_async(query: nil, cancellation_token: nil, timeout: nil)
|
|
35
|
-
Phronomy::Runtime.instance.blocking_io.submit(
|
|
36
|
-
timeout: timeout,
|
|
37
|
-
cancellation_token: cancellation_token
|
|
38
|
-
) do
|
|
39
|
-
fetch(query: query, cancellation_token: cancellation_token)
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Returns true when this source's content is considered static (i.e. does
|
|
44
|
-
# not change between agent invocations). Static sources are eligible for
|
|
45
|
-
# fingerprint-based caching in ContextVersionCache.
|
|
46
|
-
#
|
|
47
|
-
# Override in subclasses that return fixed content.
|
|
48
|
-
#
|
|
49
|
-
# @return [Boolean]
|
|
50
|
-
# @api public
|
|
51
|
-
def static?
|
|
52
|
-
false
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Agent
|
|
5
|
-
module Context
|
|
6
|
-
module Knowledge
|
|
7
|
-
# A KnowledgeSource that extracts named-entity facts from conversation history.
|
|
8
|
-
#
|
|
9
|
-
# This is the knowledge-injection counterpart of the old EntityMemory.
|
|
10
|
-
# It scans saved user messages with a regex heuristic (no LLM call) and
|
|
11
|
-
# returns the discovered facts as a single knowledge chunk tagged :entity.
|
|
12
|
-
#
|
|
13
|
-
# EntityKnowledge is stateful: it accumulates extracted facts via #update(messages:)
|
|
14
|
-
# which should be called each time new messages are saved.
|
|
15
|
-
#
|
|
16
|
-
# Supported extraction patterns (case-insensitive):
|
|
17
|
-
# "my name is Alice" → { name: "Alice" }
|
|
18
|
-
# "I am Alice" → { identity: "Alice" }
|
|
19
|
-
# "I'm a software engineer" → { occupation: "software engineer" }
|
|
20
|
-
# "I work at / for Acme" → { workplace: "Acme" }
|
|
21
|
-
# "I live in Tokyo" → { location: "Tokyo" }
|
|
22
|
-
# "I'm from Tokyo" → { location: "Tokyo" }
|
|
23
|
-
# "I like / love Ruby" → { preference: "Ruby" }
|
|
24
|
-
#
|
|
25
|
-
# @example
|
|
26
|
-
# ks = Phronomy::Agent::Context::Knowledge::EntityKnowledge.new
|
|
27
|
-
# ks.update(messages: chat_messages)
|
|
28
|
-
# agent = MyAgent.new
|
|
29
|
-
# agent.add_knowledge_source(ks)
|
|
30
|
-
# agent.invoke("What is my name?")
|
|
31
|
-
class EntityKnowledge < Base
|
|
32
|
-
PATTERNS = [
|
|
33
|
-
[:name, /\bmy name is\s+([A-Za-z][A-Za-z0-9 \-']*)/i],
|
|
34
|
-
[:identity, /\bI\s+am\s+([A-Z][A-Za-z0-9 \-']+)/],
|
|
35
|
-
[:occupation, /\bI(?:'m| am) a(?:n)?\s+([A-Za-z][A-Za-z0-9 \-']*)/i],
|
|
36
|
-
[:workplace, /\bI (?:work|worked) (?:at|for|in)\s+([A-Za-z0-9][A-Za-z0-9 \-'.&,]*)/i],
|
|
37
|
-
[:location, /\bI live in\s+([A-Za-z][A-Za-z0-9 \-']*)/i],
|
|
38
|
-
[:location, /\bI(?:'m| am) from\s+([A-Za-z][A-Za-z0-9 \-']*)/i],
|
|
39
|
-
[:preference, /\bI (?:like|love|enjoy)\s+([A-Za-z][A-Za-z0-9 \-']*)/i]
|
|
40
|
-
].freeze
|
|
41
|
-
|
|
42
|
-
def initialize
|
|
43
|
-
@entities = {}
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Scan messages and accumulate entity facts.
|
|
47
|
-
# Call this after saving a new set of messages (e.g. from a ConversationManager save hook).
|
|
48
|
-
#
|
|
49
|
-
# @param messages [Array] message objects responding to #role and #content
|
|
50
|
-
# @api public
|
|
51
|
-
def update(messages:)
|
|
52
|
-
messages.each do |msg|
|
|
53
|
-
next unless msg.role.to_sym == :user
|
|
54
|
-
|
|
55
|
-
extract(msg.content.to_s).each { |key, value| @entities[key] = value }
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Returns a single chunk containing all known entity facts in XML context format.
|
|
60
|
-
# Returns an empty array when no entities have been discovered.
|
|
61
|
-
#
|
|
62
|
-
# @param query [String, nil] unused — entity knowledge is always fully injected
|
|
63
|
-
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil] optional; raises CancellationError when cancelled
|
|
64
|
-
# @return [Array<Hash>]
|
|
65
|
-
# @api public
|
|
66
|
-
def fetch(query: nil, cancellation_token: nil)
|
|
67
|
-
cancellation_token&.raise_if_cancelled!
|
|
68
|
-
return [] if @entities.empty?
|
|
69
|
-
|
|
70
|
-
lines = @entities.map { |key, value| "- #{key}: #{value}" }.join("\n")
|
|
71
|
-
content = <<~CONTENT.chomp
|
|
72
|
-
Known facts about the user:
|
|
73
|
-
#{lines}
|
|
74
|
-
CONTENT
|
|
75
|
-
[{content: content, type: :entity}]
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Returns the current entity store (primarily for testing).
|
|
79
|
-
#
|
|
80
|
-
# @return [Hash]
|
|
81
|
-
# @api public
|
|
82
|
-
def entities
|
|
83
|
-
@entities.dup
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
private
|
|
87
|
-
|
|
88
|
-
def extract(text)
|
|
89
|
-
found = {}
|
|
90
|
-
PATTERNS.each do |key, pattern|
|
|
91
|
-
if (match = text.match(pattern))
|
|
92
|
-
value = match[1].strip.sub(/[.!?]\s+.*$/, "").gsub(/[.,;!?]+$/, "")
|
|
93
|
-
found[key] = value unless value.empty?
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
found
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Agent
|
|
5
|
-
module Context
|
|
6
|
-
module Knowledge
|
|
7
|
-
# A KnowledgeSource backed by fixed text provided at construction time.
|
|
8
|
-
#
|
|
9
|
-
# Useful for injecting static documents, policy files, or configuration
|
|
10
|
-
# knowledge that does not change per request.
|
|
11
|
-
#
|
|
12
|
-
# @example
|
|
13
|
-
# ks = Phronomy::Agent::Context::Knowledge::StaticKnowledge.new(
|
|
14
|
-
# "Our refund policy: ...",
|
|
15
|
-
# type: :policy
|
|
16
|
-
# )
|
|
17
|
-
# agent = MyAgent.new
|
|
18
|
-
# agent.add_knowledge_source(ks)
|
|
19
|
-
# agent.invoke("What is the refund policy?")
|
|
20
|
-
class StaticKnowledge < Base
|
|
21
|
-
# @param text [String] the static knowledge text to inject
|
|
22
|
-
# @param type [Symbol] semantic tag for the chunk (default :static)
|
|
23
|
-
# @param source [String, nil] label identifying where this knowledge came from
|
|
24
|
-
# (e.g. a filename). Included in the context XML tag and exposed to the LLM
|
|
25
|
-
# so that agents can produce grounded citations.
|
|
26
|
-
# @api public
|
|
27
|
-
def initialize(text, type: :static, source: nil)
|
|
28
|
-
@text = text.to_s
|
|
29
|
-
@type = type
|
|
30
|
-
@source = source
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Returns the fixed text as a single chunk, regardless of query.
|
|
34
|
-
#
|
|
35
|
-
# @param query [String, nil] ignored for static knowledge
|
|
36
|
-
# @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil] optional; raises CancellationError when cancelled
|
|
37
|
-
# @return [Array<Hash>]
|
|
38
|
-
# @api public
|
|
39
|
-
def fetch(query: nil, cancellation_token: nil)
|
|
40
|
-
cancellation_token&.raise_if_cancelled!
|
|
41
|
-
return [] if @text.empty?
|
|
42
|
-
|
|
43
|
-
chunk = {content: @text, type: @type}
|
|
44
|
-
chunk[:source] = @source if @source
|
|
45
|
-
[chunk]
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Static knowledge content never changes between invocations.
|
|
49
|
-
# @return [true]
|
|
50
|
-
# @api public
|
|
51
|
-
def static?
|
|
52
|
-
true
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|