phronomy 0.1.4 → 0.2.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 +56 -0
- data/README.md +49 -38
- data/docs/trustworthy_ai_enhancements.md +4 -4
- data/lib/phronomy/actor.rb +68 -0
- data/lib/phronomy/agent/base.rb +80 -52
- data/lib/phronomy/context/context_version_cache.rb +10 -33
- data/lib/phronomy/memory/conversation_manager.rb +9 -38
- data/lib/phronomy/memory/retrieval/semantic.rb +7 -7
- data/lib/phronomy/memory/storage/active_record.rb +20 -0
- data/lib/phronomy/memory/storage/base.rb +22 -0
- data/lib/phronomy/memory/storage/in_memory.rb +65 -26
- data/lib/phronomy/state_store/active_record.rb +1 -1
- data/lib/phronomy/state_store/base.rb +14 -16
- data/lib/phronomy/state_store/in_memory.rb +23 -10
- data/lib/phronomy/state_store/redis.rb +1 -1
- data/lib/phronomy/thread_actor_registry.rb +52 -0
- data/lib/phronomy/tool/base.rb +1 -1
- data/lib/phronomy/tool/mcp_tool.rb +10 -9
- data/lib/phronomy/tracing/langfuse_tracer.rb +3 -3
- data/lib/phronomy/trust_pipeline.rb +41 -49
- data/lib/phronomy/vector_store/in_memory.rb +5 -7
- data/lib/phronomy/vector_store/redis_search.rb +4 -6
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow.rb +281 -0
- data/lib/phronomy/workflow_context.rb +119 -0
- data/lib/phronomy/workflow_runner.rb +262 -0
- data/lib/phronomy.rb +30 -34
- metadata +25 -10
- data/lib/phronomy/graph/compiled_graph.rb +0 -191
- data/lib/phronomy/graph/parallel_node.rb +0 -193
- data/lib/phronomy/graph/state.rb +0 -105
- data/lib/phronomy/graph/state_graph.rb +0 -149
- data/lib/phronomy/graph.rb +0 -13
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Graph
|
|
5
|
-
# Represents a set of branches that execute concurrently as a single graph node.
|
|
6
|
-
#
|
|
7
|
-
# Each branch is a callable (Proc, lambda, or any object responding to #call)
|
|
8
|
-
# that receives the current state and returns either a Hash of field updates or
|
|
9
|
-
# nil. All branches run in separate threads; their results are merged in
|
|
10
|
-
# registration order using the following policy:
|
|
11
|
-
#
|
|
12
|
-
# :replace fields — last-write-wins (rightmost branch wins)
|
|
13
|
-
# :append fields — all Arrays are concatenated
|
|
14
|
-
# :merge fields — all Hashes are deep-merged (rightmost wins on conflict)
|
|
15
|
-
#
|
|
16
|
-
# @example Basic two-branch node
|
|
17
|
-
# graph.add_parallel_node(:step,
|
|
18
|
-
# ->(state) { { field_a: "from_a" } },
|
|
19
|
-
# ->(state) { { field_b: "from_b" } }
|
|
20
|
-
# )
|
|
21
|
-
#
|
|
22
|
-
# @example With timeout and failure policy
|
|
23
|
-
# graph.add_parallel_node(:step,
|
|
24
|
-
# branch_a, branch_b,
|
|
25
|
-
# timeout: 10,
|
|
26
|
-
# on_error: :best_effort
|
|
27
|
-
# )
|
|
28
|
-
#
|
|
29
|
-
# Timeout support
|
|
30
|
-
# Pass +timeout:+ (seconds, Numeric) to limit how long the node may run.
|
|
31
|
-
# If any thread does not finish within the limit, all threads are killed and
|
|
32
|
-
# +Phronomy::Graph::TimeoutError+ is raised (for +:raise+ policy) or recorded
|
|
33
|
-
# in the result hash under +:parallel_errors+ (for +:best_effort+ policy).
|
|
34
|
-
#
|
|
35
|
-
# Failure policies (+on_error:+)
|
|
36
|
-
# :raise (default)
|
|
37
|
-
# Re-raises the first exception after all threads are joined.
|
|
38
|
-
# Mirrors the original Thread#value semantics.
|
|
39
|
-
# :best_effort
|
|
40
|
-
# Collects successful results and stores any errors in the update Hash
|
|
41
|
-
# under the key +:parallel_errors+ (Array of exception objects).
|
|
42
|
-
# The caller's state class should declare:
|
|
43
|
-
# field :parallel_errors, type: :append, default: -> { [] }
|
|
44
|
-
# Unknown keys are silently ignored by the State#merge machinery.
|
|
45
|
-
class ParallelNode
|
|
46
|
-
# @param branches [Array<#call>] at least one callable branch required
|
|
47
|
-
# @param timeout [Numeric, nil] wall-clock limit in seconds; nil = unlimited
|
|
48
|
-
# @param on_error [Symbol] :raise (default) or :best_effort
|
|
49
|
-
def initialize(branches, timeout: nil, on_error: :raise)
|
|
50
|
-
raise ArgumentError, "branches must be a non-empty Array" if branches.empty?
|
|
51
|
-
unless %i[raise best_effort].include?(on_error)
|
|
52
|
-
raise ArgumentError, "on_error must be :raise or :best_effort, got #{on_error.inspect}"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
@branches = branches
|
|
56
|
-
@timeout = timeout
|
|
57
|
-
@on_error = on_error
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Executes all branches concurrently and merges their results.
|
|
61
|
-
#
|
|
62
|
-
# @param state [Object] state object (includes Phronomy::Graph::State)
|
|
63
|
-
# @return [Hash, nil] merged update hash, or nil when all branches return nil
|
|
64
|
-
def call(state)
|
|
65
|
-
threads = @branches.map { |branch| Thread.new { branch.call(state) } }
|
|
66
|
-
deadline = @timeout ? (Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout) : nil
|
|
67
|
-
state_class = state.class
|
|
68
|
-
|
|
69
|
-
if @on_error == :best_effort
|
|
70
|
-
gather_best_effort(threads, deadline, state_class)
|
|
71
|
-
else
|
|
72
|
-
gather_raise(threads, deadline, state_class)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
private
|
|
77
|
-
|
|
78
|
-
# Joins all threads, enforcing the deadline. Re-raises branch exceptions.
|
|
79
|
-
def gather_raise(threads, deadline, state_class)
|
|
80
|
-
if deadline
|
|
81
|
-
threads.each do |t|
|
|
82
|
-
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
83
|
-
next if t.join([remaining, 0].max)
|
|
84
|
-
|
|
85
|
-
# Thread did not finish within the time limit.
|
|
86
|
-
# Use Thread#raise instead of Thread#kill so that ensure blocks in
|
|
87
|
-
# branches (DB connection return, Mutex release, etc.) are executed.
|
|
88
|
-
timeout_error = Phronomy::Graph::TimeoutError.new(
|
|
89
|
-
"parallel branch timed out after #{@timeout}s"
|
|
90
|
-
)
|
|
91
|
-
threads.each { |thr| thr.raise(timeout_error) unless thr.stop? }
|
|
92
|
-
threads.each do |thr|
|
|
93
|
-
thr.join(0.1)
|
|
94
|
-
rescue
|
|
95
|
-
nil
|
|
96
|
-
end
|
|
97
|
-
raise Phronomy::Graph::TimeoutError,
|
|
98
|
-
"parallel branch timed out after #{@timeout}s"
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# All threads are done. Thread#value re-raises any stored exception.
|
|
103
|
-
merge_results(threads.map(&:value), state_class)
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# Joins all threads, collecting errors instead of re-raising them.
|
|
107
|
-
def gather_best_effort(threads, deadline, state_class)
|
|
108
|
-
errors = []
|
|
109
|
-
results = threads.map do |t|
|
|
110
|
-
if deadline
|
|
111
|
-
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
112
|
-
# Thread#join(limit) re-raises the thread's stored exception in the calling
|
|
113
|
-
# thread when the thread terminated abnormally within the limit.
|
|
114
|
-
# Rescue here so the error is collected rather than propagated.
|
|
115
|
-
begin
|
|
116
|
-
joined = t.join([remaining, 0].max)
|
|
117
|
-
rescue => e
|
|
118
|
-
errors << e
|
|
119
|
-
next nil
|
|
120
|
-
end
|
|
121
|
-
if joined.nil?
|
|
122
|
-
timeout_error = Phronomy::Graph::TimeoutError.new(
|
|
123
|
-
"branch timed out after #{@timeout}s"
|
|
124
|
-
)
|
|
125
|
-
t.raise(timeout_error) unless t.stop?
|
|
126
|
-
begin
|
|
127
|
-
t.join(0.1)
|
|
128
|
-
rescue
|
|
129
|
-
nil
|
|
130
|
-
end
|
|
131
|
-
errors << Phronomy::Graph::TimeoutError.new(
|
|
132
|
-
"branch timed out after #{@timeout}s"
|
|
133
|
-
)
|
|
134
|
-
next nil
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
begin
|
|
139
|
-
t.value
|
|
140
|
-
rescue => e
|
|
141
|
-
errors << e
|
|
142
|
-
nil
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
merged = merge_results(results, state_class) || {}
|
|
147
|
-
merged[:parallel_errors] = errors unless errors.empty?
|
|
148
|
-
merged.empty? ? nil : merged
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
# Merges an Array of per-branch result Hashes (nils are skipped).
|
|
152
|
-
# Field merge policy is determined from the State class field declarations:
|
|
153
|
-
# :replace fields — last-write-wins (rightmost branch wins)
|
|
154
|
-
# :append fields — all Arrays are concatenated
|
|
155
|
-
# :merge fields — all Hashes are deep-merged (rightmost wins on conflict)
|
|
156
|
-
# Unknown / undeclared fields fall back to type-based heuristics.
|
|
157
|
-
def merge_results(results, state_class = nil)
|
|
158
|
-
merged = results.compact.each_with_object({}) do |result, acc|
|
|
159
|
-
next unless result.is_a?(Hash)
|
|
160
|
-
|
|
161
|
-
result.each do |key, val|
|
|
162
|
-
acc[key] = acc.key?(key) ? merge_values(acc[key], val, state_class&.fields&.dig(key, :type)) : val
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
merged.empty? ? nil : merged
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# Merges two values for the same state field key across branches.
|
|
170
|
-
# Uses the declared field policy when available, otherwise falls back to
|
|
171
|
-
# type-based heuristics (Array → concat, Hash → deep-merge, scalar → last-write-wins).
|
|
172
|
-
def merge_values(old_val, new_val, policy = nil)
|
|
173
|
-
case policy
|
|
174
|
-
when :append
|
|
175
|
-
(old_val.is_a?(Array) && new_val.is_a?(Array)) ? old_val + new_val : new_val
|
|
176
|
-
when :merge
|
|
177
|
-
(old_val.is_a?(Hash) && new_val.is_a?(Hash)) ? old_val.merge(new_val) : new_val
|
|
178
|
-
when :replace
|
|
179
|
-
new_val
|
|
180
|
-
else
|
|
181
|
-
# Unknown field or no State class: fall back to type-based heuristic.
|
|
182
|
-
if old_val.is_a?(Array) && new_val.is_a?(Array)
|
|
183
|
-
old_val + new_val
|
|
184
|
-
elsif old_val.is_a?(Hash) && new_val.is_a?(Hash)
|
|
185
|
-
old_val.merge(new_val)
|
|
186
|
-
else
|
|
187
|
-
new_val
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
end
|
|
191
|
-
end
|
|
192
|
-
end
|
|
193
|
-
end
|
data/lib/phronomy/graph/state.rb
DELETED
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Graph
|
|
5
|
-
# Module for defining graph state.
|
|
6
|
-
# Include in a class and use the field DSL to declare state fields.
|
|
7
|
-
#
|
|
8
|
-
# Field update policies:
|
|
9
|
-
# :replace (default) -- overwrites with the new value
|
|
10
|
-
# :append -- appends to an Array
|
|
11
|
-
# :merge -- deep-merges into a Hash
|
|
12
|
-
#
|
|
13
|
-
# @example
|
|
14
|
-
# class MyState
|
|
15
|
-
# include Phronomy::Graph::State
|
|
16
|
-
# field :messages, type: :append, default: -> { [] }
|
|
17
|
-
# field :query, type: :replace
|
|
18
|
-
# field :metadata, type: :merge, default: -> { {} }
|
|
19
|
-
# end
|
|
20
|
-
module State
|
|
21
|
-
def self.included(base)
|
|
22
|
-
base.extend(ClassMethods)
|
|
23
|
-
base.instance_variable_set(:@fields, {})
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
module ClassMethods
|
|
27
|
-
# Defines a state field.
|
|
28
|
-
# @param name [Symbol]
|
|
29
|
-
# @param type [Symbol] :replace / :append / :merge
|
|
30
|
-
# @param default [Object, Proc, nil]
|
|
31
|
-
def field(name, type: :replace, default: nil)
|
|
32
|
-
@fields[name] = {type: type, default: default}
|
|
33
|
-
attr_accessor name
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def fields
|
|
37
|
-
@fields
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Internal graph metadata accessors (not user-defined fields).
|
|
42
|
-
# These are preserved through merge but excluded from to_h.
|
|
43
|
-
attr_reader :thread_id, :current_nodes, :halted_before
|
|
44
|
-
|
|
45
|
-
# Sets internal graph metadata. Returns self.
|
|
46
|
-
# @param thread_id [String, nil]
|
|
47
|
-
# @param current_nodes [Array<Symbol>]
|
|
48
|
-
# @param halted_before [Boolean]
|
|
49
|
-
def set_graph_metadata(thread_id: nil, current_nodes: [], halted_before: false)
|
|
50
|
-
@thread_id = thread_id
|
|
51
|
-
@current_nodes = current_nodes || []
|
|
52
|
-
@halted_before = halted_before
|
|
53
|
-
self
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def initialize(**attrs)
|
|
57
|
-
self.class.fields.each do |name, config|
|
|
58
|
-
default = config[:default].is_a?(Proc) ? config[:default].call : config[:default]
|
|
59
|
-
send(:"#{name}=", attrs.fetch(name, default))
|
|
60
|
-
end
|
|
61
|
-
@thread_id = nil
|
|
62
|
-
@current_nodes = []
|
|
63
|
-
@halted_before = false
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Immutably updates state fields. Returns a new instance with the applied changes.
|
|
67
|
-
# Internal graph metadata (thread_id, current_nodes, halted_before) is preserved.
|
|
68
|
-
# @param updates [Hash] { field_name => new_value }
|
|
69
|
-
# @return [self.class] new state instance
|
|
70
|
-
def merge(updates)
|
|
71
|
-
new_attrs = {}
|
|
72
|
-
self.class.fields.each_key do |name|
|
|
73
|
-
field_config = self.class.fields[name]
|
|
74
|
-
new_attrs[name] = if updates.key?(name)
|
|
75
|
-
case field_config[:type]
|
|
76
|
-
when :append
|
|
77
|
-
Array(send(name)) + Array(updates[name])
|
|
78
|
-
when :merge
|
|
79
|
-
(send(name) || {}).merge(updates[name])
|
|
80
|
-
else
|
|
81
|
-
updates[name]
|
|
82
|
-
end
|
|
83
|
-
else
|
|
84
|
-
send(name)
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
new_state = self.class.new(**new_attrs)
|
|
88
|
-
new_state.set_graph_metadata(
|
|
89
|
-
thread_id: @thread_id,
|
|
90
|
-
current_nodes: @current_nodes,
|
|
91
|
-
halted_before: @halted_before
|
|
92
|
-
)
|
|
93
|
-
new_state
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Converts user-defined fields to a Hash (excludes internal graph metadata).
|
|
97
|
-
# @return [Hash]
|
|
98
|
-
def to_h
|
|
99
|
-
self.class.fields.keys.each_with_object({}) do |name, h|
|
|
100
|
-
h[name] = send(name)
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Graph
|
|
5
|
-
# Declarative agent workflow definition class.
|
|
6
|
-
# Assembles nodes and edges, then returns an executable CompiledGraph via compile.
|
|
7
|
-
class StateGraph
|
|
8
|
-
START = :__start__
|
|
9
|
-
FINISH = :__end__
|
|
10
|
-
|
|
11
|
-
attr_reader :nodes, :edges, :entry_point
|
|
12
|
-
|
|
13
|
-
# @param state_class [Class] class that includes Phronomy::Graph::State
|
|
14
|
-
def initialize(state_class)
|
|
15
|
-
@state_class = state_class
|
|
16
|
-
@nodes = {}
|
|
17
|
-
@edges = {}
|
|
18
|
-
@conditional_edges = {}
|
|
19
|
-
@entry_point = nil
|
|
20
|
-
@before_callbacks = {}
|
|
21
|
-
@after_callbacks = {}
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Adds a node.
|
|
25
|
-
# @param name [Symbol]
|
|
26
|
-
# @param callable [#call, nil] node execution logic (block accepted)
|
|
27
|
-
# @return [self]
|
|
28
|
-
def add_node(name, callable = nil, &block)
|
|
29
|
-
@nodes[name] = callable || block
|
|
30
|
-
self
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Adds a directed edge.
|
|
34
|
-
# @param from [Symbol]
|
|
35
|
-
# @param to [Symbol]
|
|
36
|
-
# @param condition [Proc, nil] guard condition — receives state and returns truthy/falsy.
|
|
37
|
-
# When nil, the edge is unconditional. When multiple edges exist from the same node,
|
|
38
|
-
# they are evaluated in registration order and the first matching edge is taken.
|
|
39
|
-
# @return [self]
|
|
40
|
-
def add_edge(from, to, condition = nil)
|
|
41
|
-
@edges[from] ||= []
|
|
42
|
-
@edges[from] << {to: to, condition: condition}
|
|
43
|
-
self
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Adds a conditional edge.
|
|
47
|
-
# @param from [Symbol]
|
|
48
|
-
# @param condition [Proc] receives state and returns the next node name
|
|
49
|
-
# @param mapping [Hash, nil] maps condition return value to a node name (optional)
|
|
50
|
-
# @return [self]
|
|
51
|
-
def add_conditional_edges(from, condition, mapping = nil)
|
|
52
|
-
@conditional_edges[from] = {condition: condition, mapping: mapping}
|
|
53
|
-
self
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Sets the entry point node.
|
|
57
|
-
# @param node_name [Symbol]
|
|
58
|
-
# @return [self]
|
|
59
|
-
def set_entry_point(node_name)
|
|
60
|
-
@entry_point = node_name
|
|
61
|
-
self
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Registers a callback to run before the given node executes.
|
|
65
|
-
# Return :halt from the block to pause execution; any other value continues.
|
|
66
|
-
# Callbacks registered here become defaults for every CompiledGraph produced by compile.
|
|
67
|
-
# @param node [Symbol]
|
|
68
|
-
# @yield [state] the current state
|
|
69
|
-
# @return [self]
|
|
70
|
-
def interrupt_before(node, &block)
|
|
71
|
-
@before_callbacks[node] = block
|
|
72
|
-
self
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Registers a callback to run after the given node completes.
|
|
76
|
-
# Return :halt from the block to pause execution; any other value continues.
|
|
77
|
-
# Callbacks registered here become defaults for every CompiledGraph produced by compile.
|
|
78
|
-
# @param node [Symbol]
|
|
79
|
-
# @yield [state] the state after the node ran
|
|
80
|
-
# @return [self]
|
|
81
|
-
def interrupt_after(node, &block)
|
|
82
|
-
@after_callbacks[node] = block
|
|
83
|
-
self
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Adds a parallel node that executes multiple branches concurrently.
|
|
87
|
-
# Each branch callable receives the current state and must return a Hash or nil.
|
|
88
|
-
# Results are merged in registration order (see ParallelNode for merge policy).
|
|
89
|
-
#
|
|
90
|
-
# @param name [Symbol]
|
|
91
|
-
# @param branches [Array<#call>] at least one callable required
|
|
92
|
-
# @param timeout [Numeric, nil] wall-clock limit in seconds (nil = unlimited)
|
|
93
|
-
# @param on_error [Symbol] :raise (default) or :best_effort
|
|
94
|
-
# @return [self]
|
|
95
|
-
def add_parallel_node(name, *branches, timeout: nil, on_error: :raise)
|
|
96
|
-
raise ArgumentError, "add_parallel_node requires at least one branch" if branches.empty?
|
|
97
|
-
|
|
98
|
-
@nodes[name] = ParallelNode.new(branches, timeout: timeout, on_error: on_error)
|
|
99
|
-
self
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# Embeds a compiled subgraph as a single node in this graph.
|
|
103
|
-
# The subgraph is invoked with a Hash derived from the parent state;
|
|
104
|
-
# its final state Hash is returned and merged into the parent state.
|
|
105
|
-
#
|
|
106
|
-
# @param name [Symbol]
|
|
107
|
-
# @param subgraph [CompiledGraph] the compiled subgraph to embed
|
|
108
|
-
# @param input_mapper [Proc, nil] maps parent state → input Hash for the subgraph;
|
|
109
|
-
# defaults to state.to_h (passes all parent fields)
|
|
110
|
-
# @param output_mapper [Proc, nil] maps subgraph final state → Hash to merge back;
|
|
111
|
-
# defaults to sub_state.to_h (passes all subgraph fields)
|
|
112
|
-
# @return [self]
|
|
113
|
-
def add_subgraph(name, subgraph, input_mapper: nil, output_mapper: nil)
|
|
114
|
-
add_node(name) do |state|
|
|
115
|
-
input = input_mapper ? input_mapper.call(state) : state.to_h
|
|
116
|
-
sub_thread_id = "#{state.thread_id}/#{name}"
|
|
117
|
-
sub_state = subgraph.invoke(input, config: {thread_id: sub_thread_id})
|
|
118
|
-
output_mapper ? output_mapper.call(sub_state) : sub_state.to_h
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
# Compiles the graph and returns a CompiledGraph.
|
|
123
|
-
# Callbacks registered on StateGraph are inherited; additional callbacks can be
|
|
124
|
-
# registered on the returned CompiledGraph to override or extend them.
|
|
125
|
-
# @param state_store [Phronomy::StateStore::Base, nil] optional state store
|
|
126
|
-
# to use for this compiled graph, overriding the global default.
|
|
127
|
-
# @return [CompiledGraph]
|
|
128
|
-
def compile(state_store: nil)
|
|
129
|
-
if @entry_point.nil? && @nodes.size > 1
|
|
130
|
-
raise ArgumentError,
|
|
131
|
-
"set_entry_point was not called; call set_entry_point(:node_name) " \
|
|
132
|
-
"before compile when the graph has multiple nodes"
|
|
133
|
-
end
|
|
134
|
-
CompiledGraph.new(
|
|
135
|
-
state_class: @state_class,
|
|
136
|
-
nodes: @nodes,
|
|
137
|
-
edges: @edges,
|
|
138
|
-
conditional_edges: @conditional_edges,
|
|
139
|
-
entry_point: @entry_point || @nodes.keys.first,
|
|
140
|
-
before_callbacks: @before_callbacks.dup,
|
|
141
|
-
after_callbacks: @after_callbacks.dup,
|
|
142
|
-
state_store: state_store
|
|
143
|
-
)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
attr_reader :conditional_edges
|
|
147
|
-
end
|
|
148
|
-
end
|
|
149
|
-
end
|
data/lib/phronomy/graph.rb
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "graph/state"
|
|
4
|
-
require_relative "graph/parallel_node"
|
|
5
|
-
require_relative "graph/state_graph"
|
|
6
|
-
require_relative "graph/compiled_graph"
|
|
7
|
-
|
|
8
|
-
module Phronomy
|
|
9
|
-
module Graph
|
|
10
|
-
# Raised when a parallel branch exceeds the wall-clock limit set via +timeout:+.
|
|
11
|
-
class TimeoutError < Phronomy::Error; end
|
|
12
|
-
end
|
|
13
|
-
end
|