standard_procedure_operations 0.5.2 → 0.6.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 +112 -356
- data/app/jobs/operations/agent/find_timeouts_job.rb +5 -0
- data/app/jobs/operations/agent/runner_job.rb +5 -0
- data/app/jobs/operations/agent/timeout_job.rb +5 -0
- data/app/jobs/operations/agent/wake_agents_job.rb +5 -0
- data/app/models/concerns/operations/participant.rb +1 -1
- data/app/models/operations/agent/interaction_handler.rb +30 -0
- data/app/models/operations/agent/plan.rb +38 -0
- data/app/models/operations/agent/runner.rb +37 -0
- data/app/models/operations/{task/state_management → agent}/wait_handler.rb +4 -2
- data/app/models/operations/agent.rb +31 -0
- data/app/models/operations/task/data_carrier.rb +0 -2
- data/app/models/operations/task/exports.rb +4 -4
- data/app/models/operations/task/{state_management → plan}/action_handler.rb +3 -1
- data/app/models/operations/task/{state_management → plan}/decision_handler.rb +3 -1
- data/app/models/operations/task/{state_management/completion_handler.rb → plan/result_handler.rb} +3 -1
- data/app/models/operations/task/{state_management.rb → plan.rb} +2 -4
- data/app/models/operations/task/testing.rb +2 -1
- data/app/models/operations/task.rb +46 -30
- data/app/models/operations/task_participant.rb +2 -0
- data/db/migrate/{20250403075414_add_becomes_zombie_at_field.rb → 20250404085321_add_becomes_zombie_at_field.operations.rb} +1 -0
- data/db/migrate/20250407143513_agent_fields.rb +9 -0
- data/db/migrate/20250408124423_add_task_participant_indexes.rb +5 -0
- data/lib/operations/exporters/svg.rb +399 -0
- data/lib/operations/has_data_attributes.rb +50 -0
- data/lib/operations/invalid_state.rb +2 -0
- data/lib/operations/version.rb +1 -1
- data/lib/operations.rb +3 -2
- data/lib/tasks/operations_tasks.rake +3 -3
- metadata +21 -12
- data/app/jobs/operations/task_runner_job.rb +0 -11
- data/app/models/operations/task/background.rb +0 -39
- data/lib/operations/cannot_wait_in_foreground.rb +0 -2
- data/lib/operations/exporters/graphviz.rb +0 -164
@@ -1,164 +0,0 @@
|
|
1
|
-
require "graphviz"
|
2
|
-
|
3
|
-
module Operations
|
4
|
-
module Exporters
|
5
|
-
class Graphviz
|
6
|
-
attr_reader :task_class
|
7
|
-
|
8
|
-
def self.export(task_class)
|
9
|
-
new(task_class).to_dot
|
10
|
-
end
|
11
|
-
|
12
|
-
def initialize(task_class)
|
13
|
-
@task_class = task_class
|
14
|
-
end
|
15
|
-
|
16
|
-
# Generate a DOT representation of the task flow
|
17
|
-
def to_dot
|
18
|
-
graph.output(dot: String)
|
19
|
-
end
|
20
|
-
|
21
|
-
# Generate and save the graph to a file
|
22
|
-
# Supported formats: dot, png, svg, pdf, etc.
|
23
|
-
def save(filename, format: :png)
|
24
|
-
graph.output(format => filename)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Generate GraphViz object representing the task flow
|
28
|
-
def graph
|
29
|
-
@graph ||= build_graph
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def build_graph
|
35
|
-
task_hash = task_class.to_h
|
36
|
-
g = GraphViz.new(:G, type: :digraph, rankdir: "LR")
|
37
|
-
|
38
|
-
# Set up node styles
|
39
|
-
g.node[:shape] = "box"
|
40
|
-
g.node[:style] = "rounded"
|
41
|
-
g.node[:fontname] = "Arial"
|
42
|
-
g.node[:fontsize] = "12"
|
43
|
-
g.edge[:fontname] = "Arial"
|
44
|
-
g.edge[:fontsize] = "10"
|
45
|
-
|
46
|
-
# Create nodes for each state
|
47
|
-
nodes = {}
|
48
|
-
task_hash[:states].each do |state_name, state_info|
|
49
|
-
node_style = node_style_for(state_info[:type])
|
50
|
-
node_label = create_node_label(state_name, state_info)
|
51
|
-
nodes[state_name] = g.add_nodes(state_name.to_s, label: node_label, **node_style)
|
52
|
-
end
|
53
|
-
|
54
|
-
# Add edges for transitions
|
55
|
-
task_hash[:states].each do |state_name, state_info|
|
56
|
-
case state_info[:type]
|
57
|
-
when :decision
|
58
|
-
add_decision_edges(g, nodes, state_name, state_info[:transitions])
|
59
|
-
when :action
|
60
|
-
if state_info[:next_state]
|
61
|
-
g.add_edges(nodes[state_name], nodes[state_info[:next_state]])
|
62
|
-
end
|
63
|
-
when :wait
|
64
|
-
add_wait_edges(g, nodes, state_name, state_info[:transitions])
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
# Mark initial state
|
69
|
-
if nodes[task_hash[:initial_state]]
|
70
|
-
initial_node = g.add_nodes("START", shape: "circle", style: "filled", fillcolor: "#59a14f", fontcolor: "white")
|
71
|
-
g.add_edges(initial_node, nodes[task_hash[:initial_state]])
|
72
|
-
end
|
73
|
-
|
74
|
-
g
|
75
|
-
end
|
76
|
-
|
77
|
-
def node_style_for(type)
|
78
|
-
case type
|
79
|
-
when :decision
|
80
|
-
{shape: "diamond", style: "filled", fillcolor: "#4e79a7", fontcolor: "white"}
|
81
|
-
when :action
|
82
|
-
{shape: "box", style: "filled", fillcolor: "#f28e2b", fontcolor: "white"}
|
83
|
-
when :wait
|
84
|
-
{shape: "box", style: "filled,dashed", fillcolor: "#76b7b2", fontcolor: "white"}
|
85
|
-
when :result
|
86
|
-
{shape: "box", style: "filled", fillcolor: "#59a14f", fontcolor: "white"}
|
87
|
-
else
|
88
|
-
{shape: "box"}
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def create_node_label(state_name, state_info)
|
93
|
-
label = state_name.to_s
|
94
|
-
|
95
|
-
# Add inputs if present
|
96
|
-
if state_info[:inputs].present?
|
97
|
-
inputs_list = state_info[:inputs].map(&:to_s).join(", ")
|
98
|
-
label += "\nInputs: #{inputs_list}"
|
99
|
-
end
|
100
|
-
|
101
|
-
# Add optional inputs if present
|
102
|
-
if state_info[:optional_inputs].present?
|
103
|
-
optional_list = state_info[:optional_inputs].map(&:to_s).join(", ")
|
104
|
-
label += "\nOptional: #{optional_list}"
|
105
|
-
end
|
106
|
-
|
107
|
-
label
|
108
|
-
end
|
109
|
-
|
110
|
-
def add_decision_edges(graph, nodes, state_name, transitions)
|
111
|
-
# Get the handler for this state to access condition labels
|
112
|
-
task_state = task_class.respond_to?(:states) ? task_class.states[state_name.to_sym] : nil
|
113
|
-
handler = task_state[:handler] if task_state
|
114
|
-
|
115
|
-
transitions.each_with_index do |(condition, target), index|
|
116
|
-
# Get custom label if available
|
117
|
-
label = (handler&.respond_to?(:condition_labels) && handler.condition_labels[index]) ? handler.condition_labels[index] : target.to_s
|
118
|
-
|
119
|
-
if (target.is_a?(Symbol) || target.is_a?(String)) && nodes[target.to_sym]
|
120
|
-
graph.add_edges(nodes[state_name], nodes[target.to_sym], label: label)
|
121
|
-
elsif target.respond_to?(:call)
|
122
|
-
# Create a special node to represent the custom action
|
123
|
-
block_node_name = "#{state_name}_#{condition}_block"
|
124
|
-
block_node = graph.add_nodes(block_node_name,
|
125
|
-
label: "#{condition} Custom Action",
|
126
|
-
shape: "note",
|
127
|
-
style: "filled",
|
128
|
-
fillcolor: "#bab0ab",
|
129
|
-
fontcolor: "black")
|
130
|
-
|
131
|
-
graph.add_edges(nodes[state_name], block_node,
|
132
|
-
label: label,
|
133
|
-
style: "dashed")
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
def add_wait_edges(graph, nodes, state_name, transitions)
|
139
|
-
# Get the handler for this state to access condition labels
|
140
|
-
task_state = task_class.respond_to?(:states) ? task_class.states[state_name.to_sym] : nil
|
141
|
-
handler = task_state[:handler] if task_state
|
142
|
-
|
143
|
-
# Add a self-loop for wait condition
|
144
|
-
graph.add_edges(nodes[state_name], nodes[state_name],
|
145
|
-
label: "waiting",
|
146
|
-
style: "dashed",
|
147
|
-
constraint: "false",
|
148
|
-
dir: "back")
|
149
|
-
|
150
|
-
# Add edges for each transition
|
151
|
-
transitions.each_with_index do |(condition, target), index|
|
152
|
-
# Get custom label if available
|
153
|
-
label = (handler&.respond_to?(:condition_labels) && handler.condition_labels[index]) ? handler.condition_labels[index] : target.to_s
|
154
|
-
|
155
|
-
if (target.is_a?(Symbol) || target.is_a?(String)) && nodes[target.to_sym]
|
156
|
-
graph.add_edges(nodes[state_name], nodes[target.to_sym],
|
157
|
-
label: label,
|
158
|
-
style: "solid")
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|