phronomy 0.28.0 → 0.29.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/README.md +20 -5
- data/docs/application-recipes.md +229 -0
- data/docs/architecture/dx05-agent-scope.md +119 -0
- data/docs/architecture/dx05-workflow-task-state.md +134 -0
- data/docs/architecture/error-api-review.md +245 -0
- data/docs/failure-reason-codes.md +45 -0
- data/docs/features.md +4 -1
- data/docs/getting-started.md +44 -43
- data/docs/ordered-event-delivery.md +54 -0
- data/docs/workflow-completion.md +92 -0
- data/gemfiles/http_minimum.gemfile +5 -0
- data/lib/phronomy/agent/agent_not_found_error.rb +16 -0
- data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
- data/lib/phronomy/agent/api/agent.rb +13 -0
- data/lib/phronomy/agent/async_event_api.rb +111 -87
- data/lib/phronomy/agent/base.rb +74 -43
- data/lib/phronomy/agent/before_llm_input.rb +7 -1
- data/lib/phronomy/agent/execution/exact_execution.rb +11 -9
- data/lib/phronomy/agent/execution/execution_coordinator.rb +16 -13
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +1 -1
- data/lib/phronomy/agent/execution/initial_preparation.rb +1 -4
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +1 -4
- data/lib/phronomy/agent/filterable.rb +21 -15
- data/lib/phronomy/agent/lifecycle/lifetime_api.rb +76 -0
- data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
- data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +99 -8
- data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
- data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
- data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +3 -3
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +7 -7
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +14 -18
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +5 -9
- data/lib/phronomy/agent/recovery/recovery_support.rb +2 -8
- data/lib/phronomy/agent/stream_callback_error.rb +2 -2
- data/lib/phronomy/common/error.rb +65 -1
- data/lib/phronomy/content_store/base.rb +7 -2
- data/lib/phronomy/context/assembly.rb +11 -18
- data/lib/phronomy/embeddings/authentication_error.rb +11 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +5 -1
- data/lib/phronomy/embeddings/rate_limit_error.rb +11 -0
- data/lib/phronomy/execution/execution_cancellation_error.rb +2 -2
- data/lib/phronomy/execution/execution_timeout_error.rb +2 -2
- data/lib/phronomy/execution/task_result.rb +7 -2
- data/lib/phronomy/filter/filter_block_error.rb +2 -2
- data/lib/phronomy/generation/low_confidence_error.rb +2 -2
- data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +5 -5
- data/lib/phronomy/multi_agent/team_busy_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +20 -7
- data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
- data/lib/phronomy/storage/condition_failed_error.rb +2 -2
- data/lib/phronomy/storage/unique_constraint_error.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +1 -1
- data/lib/phronomy/tool/base.rb +25 -23
- data/lib/phronomy/tracing/base.rb +35 -6
- data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/execution/workflow.rb +30 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +1 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +74 -3
- data/lib/phronomy/workflow/execution/workflow_runner.rb +48 -14
- data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
- data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
- data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +26 -3
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +3 -1
- data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
- data/lib/phronomy.rb +6 -0
- data/scripts/api_snapshot.rb +12 -0
- data/scripts/check_readme_ruby.rb +24 -29
- data/scripts/check_readme_runnable.rb +92 -81
- data/sig/phronomy/agent/agent_not_found_error.rbs +6 -0
- data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -0
- data/sig/phronomy/agent/stream_callback_error.rbs +8 -0
- data/sig/phronomy/agent.rbs +18 -0
- data/sig/phronomy/embeddings/base.rbs +6 -0
- data/sig/phronomy/execution/execution.rbs +2 -2
- data/sig/phronomy/extensions.rbs +1 -0
- data/sig/phronomy/filter/filter_block_error.rbs +6 -0
- data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
- data/sig/phronomy/integrations.rbs +14 -0
- data/sig/phronomy/multi_agent/team_busy_error.rbs +8 -0
- data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -0
- data/sig/phronomy/storage/contracts.rbs +2 -2
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +1 -0
- data/sig/phronomy/workflow.rbs +4 -0
- data/sig/phronomy.rbs +4 -0
- metadata +49 -2
|
@@ -9,7 +9,13 @@ module Phronomy
|
|
|
9
9
|
@runtime = runtime
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
def registry
|
|
12
|
+
def registry
|
|
13
|
+
receiver = WorkflowExecutionRegistry.for(@runtime.event_loop)
|
|
14
|
+
@runtime.__shutdown_participant(key: WorkflowExecutionRegistry) ||
|
|
15
|
+
@runtime.__register_shutdown_participant(key: WorkflowExecutionRegistry, participant: receiver)
|
|
16
|
+
receiver
|
|
17
|
+
end
|
|
18
|
+
|
|
13
19
|
def existing_registry = WorkflowExecutionRegistry.existing_for(@runtime)
|
|
14
20
|
def executing? = @runtime.event_loop.current?
|
|
15
21
|
|
|
@@ -21,18 +27,35 @@ module Phronomy
|
|
|
21
27
|
WorkflowPhaseMachineBuilder.new(**definition).build
|
|
22
28
|
end
|
|
23
29
|
|
|
24
|
-
def build_session(entry_actions:, persist: nil, **options)
|
|
30
|
+
def build_session(entry_actions:, persist: nil, task_execution: nil, **options)
|
|
25
31
|
# Preserve nil/scalar results: initial entry must not re-adopt metadata
|
|
26
32
|
# unless the application actually returns a context.
|
|
27
33
|
initial_actions = entry_actions.to_h do |state_name, callbacks|
|
|
28
34
|
[state_name, callbacks.map { |callback| ->(context) { WorkflowActionRules.entry(callback, context, state_name) } }]
|
|
29
35
|
end
|
|
30
|
-
Phronomy::
|
|
36
|
+
klass = task_execution ? Phronomy::WorkflowTaskSession : Phronomy::FSMSession
|
|
37
|
+
options = options.merge(task_execution: task_execution) if task_execution
|
|
38
|
+
klass.new(**options, entry_actions: initial_actions, event_loop: @runtime.event_loop,
|
|
31
39
|
terminal_policy: persist && WorkflowTerminalPolicy.new(persist: persist))
|
|
32
40
|
end
|
|
33
41
|
|
|
34
42
|
def register_session(session, completion:)
|
|
35
43
|
@runtime.event_loop.register(session, completion: completion, receiver: registry)
|
|
36
44
|
end
|
|
45
|
+
|
|
46
|
+
def subscribe_cancellation(token, &callback)
|
|
47
|
+
subscriptions = Phronomy::Concurrency::ResultSubscriptions.new
|
|
48
|
+
subscriptions.cancellation(token, &callback)
|
|
49
|
+
remaining = token.remaining_monotonic_seconds
|
|
50
|
+
if remaining && !token.cancelled?
|
|
51
|
+
timer = @runtime.timer_queue
|
|
52
|
+
timer.schedule(seconds: remaining, &callback)
|
|
53
|
+
subscriptions.add { timer.cancel(callback) }
|
|
54
|
+
end
|
|
55
|
+
subscriptions
|
|
56
|
+
rescue
|
|
57
|
+
subscriptions&.close
|
|
58
|
+
raise
|
|
59
|
+
end
|
|
37
60
|
end
|
|
38
61
|
end
|
|
@@ -47,7 +47,8 @@ module Phronomy
|
|
|
47
47
|
:context,
|
|
48
48
|
:current_event,
|
|
49
49
|
:selected_transition_action,
|
|
50
|
-
:selected_transition_metadata
|
|
50
|
+
:selected_transition_metadata,
|
|
51
|
+
:workflow_task_execution
|
|
51
52
|
)
|
|
52
53
|
|
|
53
54
|
state_machine :phase, initial: entry do
|
|
@@ -85,6 +86,7 @@ module Phronomy
|
|
|
85
86
|
# Both source exit callbacks and the transition action are
|
|
86
87
|
# before_transition callbacks. Register exits first to preserve the
|
|
87
88
|
# required exit -> transition action -> entry ordering.
|
|
89
|
+
before_transition { |machine| machine.workflow_task_execution&.leave }
|
|
88
90
|
exit_actions.each do |state_name, callables|
|
|
89
91
|
callables.each do |callable|
|
|
90
92
|
before_transition(
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# I07 translates C02 task-state decisions to generic FSM progression. Engine
|
|
5
|
+
# does not learn TaskResult ownership, cancellation or stale-result policy.
|
|
6
|
+
# @api private
|
|
7
|
+
class WorkflowTaskSession < FSMSession
|
|
8
|
+
def initialize(task_execution:, **options)
|
|
9
|
+
super(**options)
|
|
10
|
+
@task_execution = task_execution
|
|
11
|
+
@task_execution.bind(self)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def start
|
|
15
|
+
@task_execution.activate
|
|
16
|
+
super unless @done
|
|
17
|
+
rescue => error
|
|
18
|
+
finish_with_error(error)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def apply_task_context(context)
|
|
22
|
+
return unless @terminal_lifecycle_state == :running
|
|
23
|
+
|
|
24
|
+
@ctx = @context = context
|
|
25
|
+
apply_context_metadata!
|
|
26
|
+
@tracker.context = context
|
|
27
|
+
advance_or_halt
|
|
28
|
+
rescue => error
|
|
29
|
+
finish_with_error(error)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def fail_task_execution(error)
|
|
33
|
+
finish_with_error(error) if @terminal_lifecycle_state == :running
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def handle(event)
|
|
37
|
+
return if event.type == :state_completed && !@task_execution.permits_completion?(event.payload)
|
|
38
|
+
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def post_session_event(type, payload = nil)
|
|
45
|
+
payload = @task_execution.completion_key if type == :state_completed
|
|
46
|
+
super
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def advance_or_halt
|
|
50
|
+
if @task_execution.enter(@current_state, @ctx)
|
|
51
|
+
notify_stable_state!
|
|
52
|
+
else
|
|
53
|
+
super
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def build_tracker(from_state)
|
|
58
|
+
super.tap { |tracker| tracker.workflow_task_execution = @task_execution }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def request_terminal!(...)
|
|
62
|
+
@task_execution.close
|
|
63
|
+
super
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def finish_with_error(error)
|
|
67
|
+
@task_execution.close
|
|
68
|
+
super
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def retire_without_result!(error)
|
|
72
|
+
@task_execution.retire(error)
|
|
73
|
+
super
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lib/phronomy.rb
CHANGED
|
@@ -68,6 +68,8 @@ loader.ignore(
|
|
|
68
68
|
"#{__dir__}/phronomy/runtime_composition/global_runtime.rb",
|
|
69
69
|
"#{__dir__}/phronomy/runtime_composition/execution_defaults.rb"
|
|
70
70
|
)
|
|
71
|
+
# Optional application integrations are loaded explicitly by their consumers.
|
|
72
|
+
loader.ignore("#{__dir__}/phronomy/integrations")
|
|
71
73
|
# Persistence conformance support must never make production loading require RSpec.
|
|
72
74
|
loader.ignore(
|
|
73
75
|
"#{__dir__}/phronomy/testing/persistence_contract.rb",
|
|
@@ -85,6 +87,8 @@ loader.ignore("#{__dir__}/phronomy/agent/agent_purged_error.rb")
|
|
|
85
87
|
loader.ignore("#{__dir__}/phronomy/agent/stream_callback_error.rb")
|
|
86
88
|
loader.ignore("#{__dir__}/phronomy/agent/handoff_error.rb")
|
|
87
89
|
loader.ignore("#{__dir__}/phronomy/agent/agent_already_exists_error.rb")
|
|
90
|
+
loader.ignore("#{__dir__}/phronomy/agent/agent_not_found_error.rb")
|
|
91
|
+
loader.ignore("#{__dir__}/phronomy/agent/agent_unloaded_error.rb")
|
|
88
92
|
loader.setup
|
|
89
93
|
require_relative "phronomy/persistence/transaction"
|
|
90
94
|
require_relative "phronomy/persistence_composition/stores"
|
|
@@ -96,6 +100,8 @@ require_relative "phronomy/agent/agent_purged_error"
|
|
|
96
100
|
require_relative "phronomy/agent/stream_callback_error"
|
|
97
101
|
require_relative "phronomy/agent/handoff_error"
|
|
98
102
|
require_relative "phronomy/agent/agent_already_exists_error"
|
|
103
|
+
require_relative "phronomy/agent/agent_not_found_error"
|
|
104
|
+
require_relative "phronomy/agent/agent_unloaded_error"
|
|
99
105
|
|
|
100
106
|
require_relative "phronomy/runtime_composition/configuration_defaults"
|
|
101
107
|
require_relative "phronomy/llm_adapter/token_usage"
|
data/scripts/api_snapshot.rb
CHANGED
|
@@ -18,6 +18,16 @@ require_relative "../lib/phronomy"
|
|
|
18
18
|
|
|
19
19
|
PUBLIC_API_ENTRIES = [
|
|
20
20
|
# Stable
|
|
21
|
+
Phronomy::Error,
|
|
22
|
+
Phronomy::AgentNotFoundError,
|
|
23
|
+
Phronomy::AgentUnloadedError,
|
|
24
|
+
Phronomy::Agent,
|
|
25
|
+
Phronomy::Agent::Scope,
|
|
26
|
+
Phronomy::Agent::ScopeCleanupError,
|
|
27
|
+
Phronomy::MultiAgent::TeamNotFoundError,
|
|
28
|
+
Phronomy::MultiAgent::TeamBusyError,
|
|
29
|
+
Phronomy::Embeddings::AuthenticationError,
|
|
30
|
+
Phronomy::Embeddings::RateLimitError,
|
|
21
31
|
Phronomy::Agent::Base,
|
|
22
32
|
Phronomy::Agent::ReservedExecution,
|
|
23
33
|
Phronomy::Agent::ExecutionObservation,
|
|
@@ -36,6 +46,8 @@ PUBLIC_API_ENTRIES = [
|
|
|
36
46
|
Phronomy::InvocationContext,
|
|
37
47
|
Phronomy::Tool::Base,
|
|
38
48
|
Phronomy::Workflow,
|
|
49
|
+
Phronomy::Workflow::Builder,
|
|
50
|
+
Phronomy::WorkflowTaskResumeError,
|
|
39
51
|
Phronomy::WorkflowContext,
|
|
40
52
|
Phronomy::Runnable,
|
|
41
53
|
Phronomy::Context::PromptTemplate,
|
|
@@ -1,38 +1,33 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
# Exits non-zero if any block has a syntax error.
|
|
5
|
-
|
|
3
|
+
# Check all Ruby fences, including executable examples, in the entry documents.
|
|
6
4
|
require "tempfile"
|
|
7
5
|
require "open3"
|
|
8
|
-
|
|
9
|
-
readme_path = File.expand_path("../README.md", __dir__)
|
|
10
|
-
readme = File.read(readme_path)
|
|
11
|
-
blocks = readme.scan(/^```ruby\n(.*?)^```/m).map.with_index(1) { |(code), i| [i, code] }
|
|
12
|
-
|
|
13
|
-
puts "Checking #{blocks.size} Ruby code blocks in README.md..."
|
|
6
|
+
require_relative "check_readme_runnable"
|
|
14
7
|
|
|
15
8
|
failures = []
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
9
|
+
count = 0
|
|
10
|
+
paths = ARGV.empty? ? RunnableDocumentation::PATHS : ARGV
|
|
11
|
+
paths.each do |relative|
|
|
12
|
+
text = File.read(File.expand_path(relative, RunnableDocumentation::ROOT))
|
|
13
|
+
text.to_enum(:scan, /^```ruby(?: runnable)?\n(.*?)^```/m).each do
|
|
14
|
+
match = Regexp.last_match
|
|
15
|
+
line = text[0...match.begin(1)].count("\n") + 1
|
|
16
|
+
label = "#{relative}:#{line}"
|
|
17
|
+
count += 1
|
|
18
|
+
Tempfile.create(["documentation_block", ".rb"]) do |file|
|
|
19
|
+
file.write(match[1])
|
|
20
|
+
file.flush
|
|
21
|
+
out, status = Open3.capture2e(RbConfig.ruby, "-c", file.path)
|
|
22
|
+
if status.success?
|
|
23
|
+
puts "OK #{label}"
|
|
24
|
+
else
|
|
25
|
+
failures << label
|
|
26
|
+
puts "FAIL #{label}"
|
|
27
|
+
puts out.gsub(file.path, label)
|
|
28
|
+
end
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
31
|
end
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
puts "All #{blocks.size} Ruby code blocks passed syntax check."
|
|
34
|
-
exit 0
|
|
35
|
-
else
|
|
36
|
-
puts "\n#{failures.size} block(s) failed syntax check: #{failures.join(", ")}"
|
|
37
|
-
exit 1
|
|
38
|
-
end
|
|
32
|
+
puts "#{count} Ruby blocks; #{failures.size} syntax failures"
|
|
33
|
+
exit(failures.empty? ? 0 : 1)
|
|
@@ -1,101 +1,112 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
# Any block that raises NoMethodError / ArgumentError / NameError causes a
|
|
9
|
-
# non-zero exit, failing the CI step.
|
|
10
|
-
#
|
|
11
|
-
# Usage (from the phronomy/ root):
|
|
12
|
-
# bundle exec ruby scripts/check_readme_runnable.rb
|
|
13
|
-
|
|
3
|
+
# Runs annotated, self-contained examples through the real public API.
|
|
4
|
+
# Only the external LLM adapter is replaced. Each block has its own process,
|
|
5
|
+
# configuration and Runtime; an accidental network request fails immediately.
|
|
6
|
+
# Usage: bundle exec ruby scripts/check_readme_runnable.rb [markdown paths...]
|
|
14
7
|
require "tempfile"
|
|
15
8
|
require "open3"
|
|
16
9
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
10
|
+
module RunnableDocumentation
|
|
11
|
+
ROOT = File.expand_path("..", __dir__)
|
|
12
|
+
PATHS = %w[README.md docs/getting-started.md docs/application-recipes.md docs/workflow-completion.md].freeze
|
|
13
|
+
|
|
14
|
+
PREAMBLE = <<~RUBY
|
|
15
|
+
require "bundler/setup"
|
|
16
|
+
require "phronomy"
|
|
17
|
+
require "webmock"
|
|
18
|
+
require "timeout"
|
|
19
|
+
WebMock.enable!
|
|
20
|
+
WebMock.disable_net_connect!
|
|
21
|
+
|
|
22
|
+
class DocumentationLLM < Phronomy::LLMAdapter::Base
|
|
23
|
+
protected
|
|
24
|
+
|
|
25
|
+
def perform_complete(request, cancellation_token:)
|
|
26
|
+
previous = request.messages.last
|
|
27
|
+
if previous&.role == :tool
|
|
28
|
+
return Phronomy::LLMAdapter::Response.new(content: previous.content)
|
|
29
|
+
end
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
module Agent
|
|
32
|
-
# Prepend overrides AsyncEventApi (which is also prepended) so invoke
|
|
33
|
-
# returns a canned response without triggering the EventLoop.
|
|
34
|
-
module CiInvokeStub
|
|
35
|
-
def invoke(input = nil, **)
|
|
36
|
-
{output: "ci-stub-output", messages: []}
|
|
31
|
+
search = request.tools.find do |tool|
|
|
32
|
+
tool.fetch("parameters_schema").fetch("properties", {}).key?("query")
|
|
37
33
|
end
|
|
34
|
+
if search
|
|
35
|
+
return Phronomy::LLMAdapter::Response.new(tool_calls: [
|
|
36
|
+
Phronomy::Tool::CallRequest.new(
|
|
37
|
+
id: SecureRandom.uuid, name: search.fetch("name"),
|
|
38
|
+
arguments: {"query" => "Ruby AI frameworks"}
|
|
39
|
+
)
|
|
40
|
+
])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
Phronomy::LLMAdapter::Response.new(content: "ci-stub-output")
|
|
38
44
|
end
|
|
39
|
-
Base.prepend(CiInvokeStub)
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
def perform_stream(request, cancellation_token:)
|
|
47
|
+
response = perform_complete(request, cancellation_token: cancellation_token)
|
|
48
|
+
yield Phronomy::LLMAdapter::StreamChunk.new(content: response.content) if response.content
|
|
49
|
+
response
|
|
45
50
|
end
|
|
46
51
|
end
|
|
52
|
+
RUBY
|
|
47
53
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
end
|
|
54
|
+
def self.blocks(path)
|
|
55
|
+
text = File.read(path)
|
|
56
|
+
text.to_enum(:scan, /^```ruby runnable\n(.*?)^```/m).map do
|
|
57
|
+
match = Regexp.last_match
|
|
58
|
+
[text[0...match.begin(1)].count("\n") + 1, match[1]]
|
|
54
59
|
end
|
|
55
60
|
end
|
|
56
|
-
# --- end CI preamble ---
|
|
57
|
-
|
|
58
|
-
RUBY
|
|
59
|
-
|
|
60
|
-
readme = File.read(README_PATH)
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
puts "
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
62
|
+
def self.check(paths = PATHS, output: $stdout)
|
|
63
|
+
failures = []
|
|
64
|
+
count = 0
|
|
65
|
+
paths.each do |relative|
|
|
66
|
+
path = File.expand_path(relative, ROOT)
|
|
67
|
+
examples = blocks(path)
|
|
68
|
+
if examples.empty?
|
|
69
|
+
failures << relative
|
|
70
|
+
output.puts "FAIL #{relative}: no ruby runnable blocks"
|
|
71
|
+
end
|
|
72
|
+
examples.each do |line, code|
|
|
73
|
+
count += 1
|
|
74
|
+
label = "#{relative}:#{line}"
|
|
75
|
+
Tempfile.create(["phronomy_documentation", ".rb"]) do |file|
|
|
76
|
+
file.write(PREAMBLE)
|
|
77
|
+
file.write(<<~RUBY)
|
|
78
|
+
Phronomy.with_configuration do |configuration|
|
|
79
|
+
configuration.llm_adapter = DocumentationLLM.new
|
|
80
|
+
begin
|
|
81
|
+
Timeout.timeout(20) do
|
|
82
|
+
eval(#{code.dump}, TOPLEVEL_BINDING, #{path.dump}, #{line})
|
|
83
|
+
end
|
|
84
|
+
ensure
|
|
85
|
+
Phronomy::Runtime.reset_default!(timeout: 5)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
RUBY
|
|
89
|
+
file.flush
|
|
90
|
+
out, err, status = Open3.capture3(
|
|
91
|
+
{"BUNDLE_GEMFILE" => File.join(ROOT, "Gemfile")},
|
|
92
|
+
RbConfig.ruby, file.path, chdir: ROOT
|
|
93
|
+
)
|
|
94
|
+
if status.success?
|
|
95
|
+
output.puts "OK #{label}"
|
|
96
|
+
else
|
|
97
|
+
failures << label
|
|
98
|
+
output.puts "FAIL #{label}"
|
|
99
|
+
output.puts (out + err).gsub(file.path, label).lines.first(15).join
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
90
103
|
end
|
|
104
|
+
output.puts "#{count} runnable examples; #{failures.size} failures"
|
|
105
|
+
failures.empty?
|
|
91
106
|
end
|
|
92
107
|
end
|
|
93
108
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
exit 0
|
|
98
|
-
else
|
|
99
|
-
puts "#{failures.size} block(s) failed: #{failures.join(", ")}"
|
|
100
|
-
exit 1
|
|
109
|
+
if $PROGRAM_NAME == __FILE__
|
|
110
|
+
paths = ARGV.empty? ? RunnableDocumentation::PATHS : ARGV
|
|
111
|
+
exit(RunnableDocumentation.check(paths) ? 0 : 1)
|
|
101
112
|
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module Phronomy
|
|
2
|
+
class StreamCallbackError < Error
|
|
3
|
+
attr_reader event_type: Symbol
|
|
4
|
+
attr_reader original_error: Exception
|
|
5
|
+
attr_reader result: untyped
|
|
6
|
+
def initialize: (event_type: Symbol, original_error: Exception, ?result: untyped, ?code: String?) -> void
|
|
7
|
+
end
|
|
8
|
+
end
|
data/sig/phronomy/agent.rbs
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
module Phronomy
|
|
2
2
|
module Agent
|
|
3
|
+
# The block's TaskResult is awaited and unwrapped; Scope itself is process-local.
|
|
4
|
+
def self.open_scope: [R] () { (Scope) -> (R | Phronomy::TaskResult[R]) } -> R
|
|
5
|
+
|
|
6
|
+
class Scope
|
|
7
|
+
def create: (singleton(Base) agent_class, **untyped options) ?{ (untyped event) -> void } -> Base
|
|
8
|
+
def create_async: (singleton(Base) agent_class, **untyped options) ?{ (untyped event) -> void } -> Phronomy::TaskResult[Base]
|
|
9
|
+
def load: (singleton(Base) agent_class, String agent_id, persistence: Store, **untyped options) ?{ (untyped event) -> void } -> Base
|
|
10
|
+
def load_async: (singleton(Base) agent_class, String agent_id, persistence: Store, **untyped options) ?{ (untyped event) -> void } -> Phronomy::TaskResult[Base]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ScopeCleanupError < Phronomy::Error
|
|
14
|
+
attr_reader failures: Array[{ agent_id: String, error: Exception }]
|
|
15
|
+
attr_reader result: untyped
|
|
16
|
+
def initialize: (failures: Array[{ agent_id: String, error: Exception }], result: untyped) -> void
|
|
17
|
+
end
|
|
18
|
+
|
|
3
19
|
interface _BeforeLLMInputHook
|
|
4
20
|
def call: (Phronomy::Context::LLMInputBuildContext context) -> Phronomy::Context::LLMInputPatch?
|
|
5
21
|
end
|
|
@@ -108,6 +124,8 @@ module Phronomy
|
|
|
108
124
|
def reset_context!: () -> untyped
|
|
109
125
|
def close!: () -> untyped
|
|
110
126
|
def purge!: () -> bool
|
|
127
|
+
def unload_async: () -> Phronomy::TaskResult[bool]
|
|
128
|
+
def unload!: (?timeout: Numeric?) -> bool
|
|
111
129
|
end
|
|
112
130
|
end
|
|
113
131
|
end
|
|
@@ -6,6 +6,12 @@ module Phronomy
|
|
|
6
6
|
class TransportError < Phronomy::Error
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
class AuthenticationError < TransportError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class RateLimitError < TransportError
|
|
13
|
+
end
|
|
14
|
+
|
|
9
15
|
class Base
|
|
10
16
|
def embed: (String text, ?_CancellationSignal? cancellation_token) -> Array[Float]
|
|
11
17
|
|
|
@@ -30,12 +30,12 @@ module Phronomy
|
|
|
30
30
|
|
|
31
31
|
class ExecutionTimeoutError < TimeoutError
|
|
32
32
|
attr_reader outcomes: Array[TaskResult::Outcome[untyped]]
|
|
33
|
-
def initialize: (?String message, outcomes: Array[TaskResult::Outcome[untyped]]) -> void
|
|
33
|
+
def initialize: (?String message, outcomes: Array[TaskResult::Outcome[untyped]], ?code: String?) -> void
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
class ExecutionCancellationError < CancellationError
|
|
37
37
|
attr_reader outcomes: Array[TaskResult::Outcome[untyped]]
|
|
38
|
-
def initialize: (?String message, outcomes: Array[TaskResult::Outcome[untyped]]) -> void
|
|
38
|
+
def initialize: (?String message, outcomes: Array[TaskResult::Outcome[untyped]], ?code: String?) -> void
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
end
|
data/sig/phronomy/extensions.rbs
CHANGED
|
@@ -14,6 +14,7 @@ module Phronomy
|
|
|
14
14
|
|
|
15
15
|
module Tracing
|
|
16
16
|
class Base
|
|
17
|
+
def self.trace_with: [A] (untyped tracer, String name, ?record_output: bool, ?attributes: Hash[Symbol, untyped]) { (untyped span) -> [A, untyped] } -> A
|
|
17
18
|
def start_span: (String name, **untyped attributes) -> untyped
|
|
18
19
|
def finish_span: (untyped span, ?output: untyped, ?usage: untyped, ?error: Exception?) -> untyped
|
|
19
20
|
def trace: [A] (String name, ?input: untyped, **untyped meta) { (untyped span) -> [A, untyped] } -> A
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Phronomy
|
|
2
|
+
module Integrations
|
|
3
|
+
class OrderedEventDelivery
|
|
4
|
+
class ClosedError < Phronomy::Error
|
|
5
|
+
end
|
|
6
|
+
class OverflowError < Phronomy::Error
|
|
7
|
+
end
|
|
8
|
+
def self.open: [T] (?capacity: Integer, ?batch_size: Integer, ?flush_timeout: Numeric?, deliver: ^(untyped) -> untyped, ?prepare_batch: (^(Array[untyped]) -> Array[untyped])?) { (OrderedEventDelivery) -> T } -> T
|
|
9
|
+
def initialize: (?capacity: Integer, ?batch_size: Integer, ?prepare_batch: (^(Array[untyped]) -> Array[untyped])?) { (untyped) -> untyped } -> void
|
|
10
|
+
def publish: (untyped) -> self
|
|
11
|
+
def close_and_wait: (?timeout: Numeric?) -> self
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -16,11 +16,11 @@ module Phronomy
|
|
|
16
16
|
class UniqueConstraintError < ConflictError
|
|
17
17
|
attr_reader resource: String
|
|
18
18
|
attr_reader constraint: Symbol
|
|
19
|
-
def initialize: (resource: Resource, constraint: Symbol) -> void
|
|
19
|
+
def initialize: (resource: Resource, constraint: Symbol, ?code: String?) -> void
|
|
20
20
|
end
|
|
21
21
|
class ConditionFailedError < ConflictError
|
|
22
22
|
attr_reader condition: condition
|
|
23
|
-
def initialize: (condition) -> void
|
|
23
|
+
def initialize: (condition, ?code: String?) -> void
|
|
24
24
|
end
|
|
25
25
|
class BlobConflictError < ConflictError
|
|
26
26
|
end
|
|
@@ -10,5 +10,6 @@ module Phronomy
|
|
|
10
10
|
def compile_transitions: (**untyped definition) -> untyped
|
|
11
11
|
def build_session: (**untyped options) -> untyped
|
|
12
12
|
def register_session: (untyped session, completion: TaskResult[untyped]) -> untyped
|
|
13
|
+
def subscribe_cancellation: (Concurrency::CancellationToken token) { () -> void } -> untyped
|
|
13
14
|
end
|
|
14
15
|
end
|
data/sig/phronomy/workflow.rbs
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
module Phronomy
|
|
2
|
+
class WorkflowTaskResumeError < Error
|
|
3
|
+
def initialize: (?String message, ?code: String?) -> void
|
|
4
|
+
end
|
|
2
5
|
module WorkflowContext
|
|
3
6
|
def phase: () -> Symbol
|
|
4
7
|
def halted?: () -> bool
|
|
@@ -20,6 +23,7 @@ module Phronomy
|
|
|
20
23
|
class Builder
|
|
21
24
|
def initial: (Symbol state_name) -> untyped
|
|
22
25
|
def state: (Symbol name, ?action: untyped) -> untyped
|
|
26
|
+
def task_state: (Symbol name, start: ^(untyped) -> TaskResult[untyped], apply: ^(untyped, untyped) -> untyped) -> void
|
|
23
27
|
def entry: (Symbol name, untyped callable) -> untyped
|
|
24
28
|
def exit: (Symbol name, untyped callable) -> untyped
|
|
25
29
|
def wait_state: (Symbol name) -> untyped
|
data/sig/phronomy.rbs
CHANGED
|
@@ -2,6 +2,10 @@ module Phronomy
|
|
|
2
2
|
VERSION: String
|
|
3
3
|
|
|
4
4
|
class Error < StandardError
|
|
5
|
+
attr_reader code: String?
|
|
6
|
+
def initialize: (?untyped message, ?code: String?) -> void
|
|
7
|
+
def self.diagnostic: (Exception) -> Hash[String, untyped]
|
|
8
|
+
def self.from_diagnostic: (Hash[untyped, untyped], ?remaining: Integer) -> Error
|
|
5
9
|
end
|
|
6
10
|
|
|
7
11
|
class ParseError < Error
|