ad-agent_architecture 0.0.11 → 0.0.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e62425f0ffeff140581be7741ce8b429fb9437a196294e071d017e343894562
4
- data.tar.gz: b7da8713c09d8179f840a676787aac6608143fde09a112fbe683c9205e9a37d2
3
+ metadata.gz: c41297848057dc821b24cf29aa498f6cf409a03ca73d742d00cc44dc82a77a23
4
+ data.tar.gz: 187f2ba5232c9f28e11812a1458e3ee24aea85019465c31ee851f1ec3ca06489
5
5
  SHA512:
6
- metadata.gz: 9e11fffa978aa478142fe2a540c08c2917aa9a169ab0c19f35a19ea449fb7d9a6b06ddbce72a58b489cd50a367cd2e2dd571622f818a14ad63813a770c931626
7
- data.tar.gz: 733bf3314271fa108de844ffc4fb2189bb7d77742bbfae31e2316b2883995eb2bc21d2888b49da7954db0865bf9cb9c226edce7cc4bde490f4fda535f206f222
6
+ metadata.gz: 17fb7a95b7fc0d0259be2574be526d638de70cd5c25ffefcd22318c8a01b296537a544e249445594e4a32a65676b1d58eb8cb367f46a613084961671698ca994
7
+ data.tar.gz: f930948d7983e1dbced9d35379b3fff71e8145966bad9c63829494bb9f8850d3c4514de345536f852f4f622f11bd64e074b0dce2aa1b2b2b56c3a0e28085a2a2
data/.rubocop.yml CHANGED
@@ -90,12 +90,14 @@ Lint/AmbiguousBlockAssociation:
90
90
 
91
91
  Style/AccessorGrouping:
92
92
  Enabled: false
93
+
93
94
  Metrics/AbcSize:
94
95
  Exclude:
95
96
  - "lib/ad/agent_architecture/**/*"
96
- Metrics/NoExpectationExample:
97
+
98
+ Metrics/PerceivedComplexity:
97
99
  Exclude:
98
- - "spec/ad/agent_architecture/dsl/*"
100
+ - "lib/ad/agent_architecture/dsl/actions/save_database.rb"
99
101
 
100
102
  Layout/SpaceBeforeComma:
101
103
  Enabled: false
@@ -112,18 +114,21 @@ RSpec/SpecFilePathSuffix:
112
114
  Enabled: true
113
115
 
114
116
  RSpec/NamedSubject:
115
- Exclude:
116
- - "**/spec/**/*"
117
+ Enabled: false
117
118
 
118
119
  RSpec/ExampleLength:
119
- Exclude:
120
- - "**/spec/**/*"
120
+ Enabled: false
121
121
 
122
122
  RSpec/MultipleExpectations:
123
123
  Max: 3
124
124
  Exclude:
125
125
  - "spec/ad/agent_architecture/dsl/actions/save_database_spec.rb"
126
126
 
127
+ RSpec/MultipleMemoizedHelpers:
128
+ Enabled: false
129
+
127
130
  Metrics/CyclomaticComplexity:
128
131
  Exclude:
129
132
  - "lib/ad/agent_architecture/dsl/actions/save_database.rb"
133
+
134
+
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.0.12](https://github.com/appydave/ad-agent_architecture/compare/v0.0.11...v0.0.12) (2024-07-02)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * refactor DSL and Child DSL components, add support for prompt loading ([f260260](https://github.com/appydave/ad-agent_architecture/commit/f260260ea416d0c9d3a39dc1dd7fd0b66d145a80))
7
+
8
+ ## [0.0.11](https://github.com/appydave/ad-agent_architecture/compare/v0.0.10...v0.0.11) (2024-06-29)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * improve reports ([62aac63](https://github.com/appydave/ad-agent_architecture/commit/62aac63af8b48b30514f21b940005e899538307f))
14
+
1
15
  ## [0.0.10](https://github.com/appydave/ad-agent_architecture/compare/v0.0.9...v0.0.10) (2024-06-29)
2
16
 
3
17
 
data/Gemfile CHANGED
@@ -10,6 +10,7 @@ group :development, :test do
10
10
  gem 'guard-rspec'
11
11
  gem 'guard-rubocop'
12
12
  gem 'guard-shell'
13
+ gem 'listen'
13
14
  gem 'rake'
14
15
  gem 'rake-compiler', require: false
15
16
  gem 'rspec', '~> 3.0'
data/Guardfile CHANGED
@@ -27,10 +27,4 @@ group :green_pass_then_cop, halt_on_fail: true do
27
27
  watch(/{.+\.rb$/)
28
28
  watch(%r{(?:.+/)?\.rubocop(?:_todo)?\.yml$}) { |m| File.dirname(m[0]) }
29
29
  end
30
-
31
- guard :shell do
32
- watch(%r{^spec/usecases/.+\.rbx$}) do |m|
33
- `ruby -r ./spec/usecases/dsl_initialize.rb #{m[0]}`
34
- end
35
- end
36
30
  end
data/bin/watch.rb ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ puts 'Watch Agent Workflow DSLs'
5
+
6
+ require 'pry'
7
+ require 'listen'
8
+
9
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
10
+
11
+ require 'ad/agent_architecture'
12
+
13
+ directory_to_watch = File.expand_path('../spec/usecases', __dir__)
14
+
15
+ callback = proc do |modified, added, _removed|
16
+ (modified + added).each do |file|
17
+ if file.match(%r{spec/usecases/.+\.rbx$})
18
+ # puts "Detected change in #{file}, running command..."
19
+ load file
20
+ end
21
+ end
22
+ end
23
+
24
+ listener = Listen.to(directory_to_watch, &callback)
25
+ listener.start
26
+
27
+ puts "Listening for changes in #{directory_to_watch}..."
28
+
29
+ sleep
@@ -11,12 +11,15 @@ module Ad
11
11
  end
12
12
 
13
13
  def save
14
+ # puts JSON.pretty_generate(@workflow_hash)
14
15
  DB.transaction do
16
+ # puts "Saving Workflow: #{@workflow_hash[:name]}"
15
17
  # Save workflow
16
18
  workflow_record = Ad::AgentArchitecture::Database::Workflow.create(name: @workflow_hash[:name])
17
19
 
18
- # Save attributes
20
+ # Saving attributes
19
21
  attribute_records = @workflow_hash[:attributes].map do |_name, attr|
22
+ # puts "Saving Attribute: #{attr}"
20
23
  Ad::AgentArchitecture::Database::Attribute.create(
21
24
  name: attr[:name], type: attr[:type], is_array: attr[:is_array], workflow: workflow_record
22
25
  )
@@ -26,6 +29,7 @@ module Ad
26
29
 
27
30
  # Save prompts
28
31
  @workflow_hash[:prompts].each_value do |prompt|
32
+ # puts "Saving Prompt: #{prompt}"
29
33
  Ad::AgentArchitecture::Database::Prompt.create(
30
34
  name: prompt[:name], path: prompt[:path], content: prompt[:content], workflow: workflow_record
31
35
  )
@@ -33,22 +37,26 @@ module Ad
33
37
 
34
38
  # Save sections and steps
35
39
  @workflow_hash[:sections].each do |section|
40
+ # puts "Saving Section: #{section}"
36
41
  section_record = Ad::AgentArchitecture::Database::Section.create(
37
42
  name: section[:name], order: section[:order], workflow: workflow_record
38
43
  )
39
44
 
40
45
  section[:steps].each do |step|
46
+ # puts "Saving Step: #{step}"
41
47
  step_record = Ad::AgentArchitecture::Database::Step.create(
42
48
  name: step[:name], order: step[:order], prompt: step[:prompt], section: section_record
43
49
  )
44
50
 
45
51
  step[:input_attributes].each do |attr_name|
52
+ # puts "Saving Input Attribute for Step: #{attr_name}"
46
53
  Ad::AgentArchitecture::Database::InputAttribute.create(
47
54
  step: step_record, attribute: attribute_map[attr_name.to_sym]
48
55
  )
49
56
  end
50
57
 
51
58
  step[:output_attributes].each do |attr_name|
59
+ # puts "Saving Output Attribute for Step: #{attr_name}"
52
60
  Ad::AgentArchitecture::Database::OutputAttribute.create(
53
61
  step: step_record, attribute: attribute_map[attr_name.to_sym]
54
62
  )
@@ -56,6 +64,9 @@ module Ad
56
64
  end
57
65
  end
58
66
  end
67
+ rescue StandardError => e
68
+ puts "An error occurred: #{e.message}"
69
+ raise
59
70
  end
60
71
  end
61
72
  end
@@ -7,16 +7,22 @@ module Ad
7
7
  class AgentDsl
8
8
  attr_reader :workflow
9
9
 
10
- def self.create(name:, &block)
10
+ def self.create(name, &block)
11
11
  new(name).tap do |dsl|
12
12
  dsl.instance_eval(&block) if block_given?
13
13
  end
14
14
  end
15
15
 
16
16
  def initialize(name)
17
+ raise ArgumentError, 'Agent name must be a string or symbol' unless name.is_a?(String) || name.is_a?(Symbol)
18
+
17
19
  @workflow = WorkflowDsl.new(name)
18
20
  end
19
21
 
22
+ def settings(&block)
23
+ @workflow.settings(&block)
24
+ end
25
+
20
26
  def attributes(&block)
21
27
  @workflow.attributes(&block)
22
28
  end
@@ -25,8 +31,10 @@ module Ad
25
31
  @workflow.prompts(&block)
26
32
  end
27
33
 
28
- def section(name:, &block)
29
- @workflow.section(name: name, &block)
34
+ def section(name, &block)
35
+ raise ArgumentError, 'Section name must be a string or symbol' unless name.is_a?(String) || name.is_a?(Symbol)
36
+
37
+ @workflow.section(name, &block)
30
38
  end
31
39
 
32
40
  def save
@@ -4,13 +4,11 @@ module Ad
4
4
  module AgentArchitecture
5
5
  module Dsl
6
6
  # This class is responsible for defining the attributes of a workflow
7
- class AttributeDsl
8
- def initialize(attributes)
9
- @attributes = attributes
10
- end
11
-
7
+ class AttributeDsl < ChildDsl
12
8
  def attribute(name, type:, is_array: false)
13
- @attributes[name] = { name: name, type: type, is_array: is_array }
9
+ raise ArgumentError, 'Attribute name must be a string or symbol' unless name.is_a?(String) || name.is_a?(Symbol)
10
+
11
+ workflow[:attributes][name] = { name: name, type: type, is_array: is_array }
14
12
  end
15
13
  end
16
14
  end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ad
4
+ module AgentArchitecture
5
+ module Dsl
6
+ # This is the base class for all child DSLs
7
+ class ChildDsl
8
+ attr_reader :workflow
9
+
10
+ def initialize(workflow)
11
+ @workflow = workflow
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -4,13 +4,19 @@ module Ad
4
4
  module AgentArchitecture
5
5
  module Dsl
6
6
  # This class is responsible for defining the prompts of a workflow
7
- class PromptDsl
8
- def initialize(prompts)
9
- @prompts = prompts
7
+ class PromptDsl < ChildDsl
8
+ def prompt(name, path: nil, content: nil)
9
+ raise ArgumentError, 'Prompt name must be a string or symbol' unless name.is_a?(String) || name.is_a?(Symbol)
10
+
11
+ workflow[:prompts][name] = { name: name, content: content }
10
12
  end
11
13
 
12
- def prompt(name, path: nil, content: nil)
13
- @prompts[name] = { name: name, path: path, content: content }
14
+ def prompt_file(file)
15
+ prompt_path = workflow[:settings][:prompt_path]
16
+ raise 'Prompt path not defined in settings. Set "prompt_path" to the path where you are keping prompts' unless prompt_path
17
+
18
+ full_path = File.join(prompt_path, file)
19
+ File.read(full_path)
14
20
  end
15
21
  end
16
22
  end
@@ -4,16 +4,19 @@ module Ad
4
4
  module AgentArchitecture
5
5
  module Dsl
6
6
  # This class is responsible for creating a section in the workflow
7
- class SectionDsl
8
- def initialize(name, order, sections)
7
+ class SectionDsl < ChildDsl
8
+ def initialize(workflow, name, order)
9
+ super(workflow)
10
+
9
11
  @section = { name: name, order: order, steps: [] }
10
- @sections = sections
12
+ @workflow[:sections] << @section
11
13
  @current_step_order = 1
12
- @sections << @section
13
14
  end
14
15
 
15
- def step(name:, &block)
16
- StepDsl.new(name, @current_step_order, @section[:steps]).instance_eval(&block)
16
+ def step(name, &block)
17
+ raise ArgumentError, 'Step name must be a string or symbol' unless name.is_a?(String) || name.is_a?(Symbol)
18
+
19
+ StepDsl.new(workflow, @section, name, @current_step_order).instance_eval(&block)
17
20
  @current_step_order += 1
18
21
  end
19
22
  end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ad
4
+ module AgentArchitecture
5
+ module Dsl
6
+ # This class is responsible for defining the settings of a workflow
7
+ class SettingsDsl < ChildDsl
8
+ def method_missing(name, *args, &block)
9
+ if args.length == 1 && block.nil?
10
+ workflow[:settings][name] = args.first
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def respond_to_missing?(_name, _include_private = false)
17
+ true
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -4,11 +4,22 @@ module Ad
4
4
  module AgentArchitecture
5
5
  module Dsl
6
6
  # This class is responsible for defining the steps of a section
7
- class StepDsl
8
- def initialize(name, order, steps)
9
- @step = { name: name, order: order, input_attributes: [], output_attributes: [], prompt: '' }
10
- @steps = steps
11
- @steps << @step
7
+ class StepDsl < ChildDsl
8
+ attr_reader :section
9
+ attr_reader :step
10
+
11
+ def initialize(workflow, section, name, order)
12
+ super(workflow)
13
+ @step = {
14
+ name: name,
15
+ order: order,
16
+ prompt: '',
17
+ input_attributes: [],
18
+ output_attributes: []
19
+ }
20
+
21
+ @section = section
22
+ @section[:steps] << @step
12
23
  end
13
24
 
14
25
  def input(attr_name, **_opts)
@@ -8,24 +8,33 @@ module Ad
8
8
  attr_reader :workflow
9
9
 
10
10
  def initialize(name)
11
- @workflow = { name: name, sections: [], attributes: {}, prompts: {} }
11
+ @workflow = { name: name, sections: [], attributes: {}, prompts: {}, settings: {} }
12
12
  @current_section_order = 1
13
13
  end
14
14
 
15
+ def settings(&block)
16
+ dsl = SettingsDsl.new(@workflow)
17
+ dsl.instance_eval(&block) if block_given?
18
+ dsl
19
+ end
20
+
15
21
  def attributes(&block)
16
- dsl = AttributeDsl.new(@workflow[:attributes])
22
+ dsl = AttributeDsl.new(@workflow)
17
23
  dsl.instance_eval(&block) if block_given?
24
+ dsl
18
25
  end
19
26
 
20
27
  def prompts(&block)
21
- dsl = PromptDsl.new(@workflow[:prompts])
28
+ dsl = PromptDsl.new(@workflow)
22
29
  dsl.instance_eval(&block) if block_given?
30
+ dsl
23
31
  end
24
32
 
25
- def section(name:, &block)
26
- dsl = SectionDsl.new(name, @current_section_order, @workflow[:sections])
27
- dsl.instance_eval(&block) if block_given?
33
+ def section(name, &block)
34
+ dsl = SectionDsl.new(@workflow, name, @current_section_order)
28
35
  @current_section_order += 1
36
+ dsl.instance_eval(&block) if block_given?
37
+ dsl
29
38
  end
30
39
  end
31
40
  end
@@ -22,7 +22,7 @@ module Ad
22
22
  # puts step.input_attributes.first
23
23
  # An ERROR here means you have not configured an attribute name
24
24
  log.kv 'Input Attributes', step.input_attributes.map { |ia| ia.attribute.name }.join(', ')
25
- log.kv 'Output Attributes', step.output_attributes.map { |oa| oa.attribute.name }.join(', ')
25
+ log.kv 'Output Attributes', step.output_attributes.map { |oa| oa.attribute&.name }.join(', ')
26
26
  end
27
27
  end
28
28
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ad
4
4
  module AgentArchitecture
5
- VERSION = '0.0.11'
5
+ VERSION = '0.0.13'
6
6
  end
7
7
  end
@@ -13,10 +13,12 @@ Ad::AgentArchitecture::Database::CreateSchema.new(DB).execute
13
13
 
14
14
  require 'ad/agent_architecture/database/models'
15
15
  require 'ad/agent_architecture/database/sql_query'
16
+ require 'ad/agent_architecture/dsl/child_dsl'
16
17
  require 'ad/agent_architecture/dsl/attribute_dsl'
17
18
  require 'ad/agent_architecture/dsl/prompt_dsl'
18
19
  require 'ad/agent_architecture/dsl/section_dsl'
19
20
  require 'ad/agent_architecture/dsl/step_dsl'
21
+ require 'ad/agent_architecture/dsl/settings_dsl'
20
22
  require 'ad/agent_architecture/dsl/workflow_dsl'
21
23
  require 'ad/agent_architecture/dsl/agent_dsl'
22
24
  require 'ad/agent_architecture/dsl/actions/save_database'
@@ -25,15 +27,25 @@ require 'ad/agent_architecture/dsl/actions/save_yaml'
25
27
  require 'ad/agent_architecture/report/workflow_detail_report'
26
28
  require 'ad/agent_architecture/report/workflow_list_report'
27
29
 
28
- # Alias'
30
+ # AgentDsl Alias
29
31
  Agent = Ad::AgentArchitecture::Dsl::AgentDsl
30
32
 
31
33
  module Ad
34
+ # Module for the Agent Architecture gem
32
35
  module AgentArchitecture
33
36
  # raise Ad::AgentArchitecture::Error, 'Sample message'
34
37
  Error = Class.new(StandardError)
35
38
 
36
- # Your code goes here...
39
+ # Define the root path of the gem
40
+ ROOT_PATH = Pathname.new(File.expand_path('../..', __dir__))
41
+
42
+ def self.gem_root
43
+ ROOT_PATH
44
+ end
45
+
46
+ def self.gem_relative_file(*args)
47
+ File.join(ROOT_PATH, *args)
48
+ end
37
49
  end
38
50
  end
39
51
 
data/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "ad-agent_architecture",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "ad-agent_architecture",
9
- "version": "0.0.11",
9
+ "version": "0.0.13",
10
10
  "devDependencies": {
11
11
  "@klueless-js/semantic-release-rubygem": "github:klueless-js/semantic-release-rubygem",
12
12
  "@semantic-release/changelog": "^6.0.3",
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ad-agent_architecture",
3
- "version": "0.0.11",
3
+ "version": "0.0.13",
4
4
  "description": "Architecture/Schema for AI Agents",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ad-agent_architecture
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-06-29 00:00:00.000000000 Z
11
+ date: 2024-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: k_log
@@ -75,6 +75,7 @@ files:
75
75
  - Rakefile
76
76
  - bin/console
77
77
  - bin/setup
78
+ - bin/watch.rb
78
79
  - docs/erd.svg
79
80
  - docs/images/sample-workflow.png
80
81
  - docs/requirements.md
@@ -87,10 +88,11 @@ files:
87
88
  - lib/ad/agent_architecture/dsl/actions/save_json.rb
88
89
  - lib/ad/agent_architecture/dsl/actions/save_yaml.rb
89
90
  - lib/ad/agent_architecture/dsl/agent_dsl.rb
90
- - lib/ad/agent_architecture/dsl/agent_workflow_dsl.rb.old
91
91
  - lib/ad/agent_architecture/dsl/attribute_dsl.rb
92
+ - lib/ad/agent_architecture/dsl/child_dsl.rb
92
93
  - lib/ad/agent_architecture/dsl/prompt_dsl.rb
93
94
  - lib/ad/agent_architecture/dsl/section_dsl.rb
95
+ - lib/ad/agent_architecture/dsl/settings_dsl.rb
94
96
  - lib/ad/agent_architecture/dsl/step_dsl.rb
95
97
  - lib/ad/agent_architecture/dsl/workflow_dsl.rb
96
98
  - lib/ad/agent_architecture/report/workflow_detail_report.rb
@@ -1,84 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Ad
4
- module AgentArchitecture
5
- module Dsl
6
- # DSL for defining agent workflows
7
- class AgentWorkflowDsl
8
- def self.create(name:, &block)
9
- new(name).tap do |dsl|
10
- dsl.instance_eval(&block) if block_given?
11
- end
12
- end
13
-
14
- def initialize(name)
15
- @workflow = { name: name, sections: [], attributes: {}, prompts: [] }
16
- @current_section_order = 1
17
- end
18
-
19
- def attributes(&block)
20
- instance_eval(&block) if block_given?
21
- end
22
-
23
- def attribute(name, type:, is_array: false)
24
- @workflow[:attributes][name] = { name: name, type: type, is_array: is_array }
25
- end
26
-
27
- def prompts(&block)
28
- instance_eval(&block) if block_given?
29
- end
30
-
31
- def define_prompt(name, path: nil, content: nil)
32
- @workflow[:prompts] << { name: name, path: path, content: content }
33
- end
34
-
35
- def section(name:, &block)
36
- @current_step_order = 1
37
- @current_section = { name: name, order: @current_section_order, steps: [] }
38
- @current_section_order += 1
39
- instance_eval(&block) if block_given?
40
- @workflow[:sections] << @current_section
41
- end
42
-
43
- def step(name:, &block)
44
- @current_step = { name: name, order: @current_step_order, input_attributes: [], output_attributes: [] }
45
- @current_step_order += 1
46
- instance_eval(&block) if block_given?
47
- @current_section[:steps] << @current_step
48
- end
49
-
50
- def input(attr_name, **_opts)
51
- @current_step[:input_attributes] << attr_name
52
- end
53
-
54
- def output(attr_name, **_opts)
55
- @current_step[:output_attributes] << attr_name
56
- end
57
-
58
- def prompt(prompt_text, **_opts)
59
- @current_step[:prompt] = prompt_text
60
- end
61
-
62
- def save
63
- Ad::AgentArchitecture::Dsl::Actions::SaveDatabase.new(@workflow).save
64
-
65
- self
66
- end
67
-
68
- def save_json(file_name: nil)
69
- full_file_name = file_name || 'workflow.json'
70
- Ad::AgentArchitecture::Dsl::Actions::SaveJson.new(@workflow).save(full_file_name)
71
-
72
- self
73
- end
74
-
75
- def save_yaml(file_name: nil)
76
- full_file_name = file_name || 'workflow.yaml'
77
- Ad::AgentArchitecture::Dsl::Actions::SaveYaml.new(@workflow).save(full_file_name)
78
-
79
- self
80
- end
81
- end
82
- end
83
- end
84
- end