klue-langcraft 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +18 -3
- data/CHANGELOG.md +26 -0
- data/bin/langcraft.rb +177 -0
- data/lib/klue/langcraft/dsl/interpreter.rb +18 -18
- data/lib/klue/langcraft/dsl/klue_runner.rb +68 -0
- data/lib/klue/langcraft/dsl/watcher.rb +88 -0
- data/lib/klue/langcraft/dsl/webhook.rb +57 -0
- data/lib/klue/langcraft/version.rb +1 -1
- data/lib/klue/langcraft.rb +3 -6
- data/package-lock.json +2 -2
- data/package.json +1 -1
- metadata +6 -8
- data/bin/dsl_watcher.rb +0 -10
- data/lib/base_process.rb +0 -41
- data/lib/dsl_folder_watcher.rb +0 -50
- data/lib/dsl_interpreter.rb +0 -112
- data/lib/dsl_process_data.rb +0 -31
- data/lib/process_file_collector.rb +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 991b394224dd15a2eccfe3ce1db7b6bca00304d8c6cf75ac8763a34beb6b9d06
|
4
|
+
data.tar.gz: 9a814b4cb28a1eefe7763c7705f652f754c567404748e326c60049e7d9f7f7c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e34c11144fefacb28af86e336e48f5bc905fb2a4db2ea75079689fcd21a1ecbc2f5ea8a6ffac8fbc16713a32323007279c3b208655c3864d29861848d84b38d2
|
7
|
+
data.tar.gz: 990a9b599923fc5a7117f0fafe0a20057f63798ebbef06a02de8b9debc3b900b6bc1797a9fcd472301b74d10944077077e25aa333fd001a81b88619fdecd668e
|
data/.rubocop.yml
CHANGED
@@ -40,8 +40,23 @@ Metrics/BlockLength:
|
|
40
40
|
- shared_examples_for
|
41
41
|
- transaction
|
42
42
|
|
43
|
+
Metrics/AbcSize:
|
44
|
+
Exclude:
|
45
|
+
- bin/*
|
46
|
+
|
47
|
+
|
43
48
|
Metrics/MethodLength:
|
44
49
|
Max: 25
|
50
|
+
Exclude:
|
51
|
+
- bin/*
|
52
|
+
|
53
|
+
Metrics/PerceivedComplexity:
|
54
|
+
Exclude:
|
55
|
+
- bin/*
|
56
|
+
|
57
|
+
Metrics/CyclomaticComplexity:
|
58
|
+
Exclude:
|
59
|
+
- bin/*
|
45
60
|
|
46
61
|
Layout/LineLength:
|
47
62
|
Max: 200
|
@@ -55,7 +70,7 @@ Lint/UnusedMethodArgument:
|
|
55
70
|
Style/BlockComments:
|
56
71
|
Enabled: false
|
57
72
|
Include:
|
58
|
-
- "**/spec
|
73
|
+
- "**/spec/**/*"
|
59
74
|
|
60
75
|
# My Preferences - Start
|
61
76
|
Metrics/ClassLength:
|
@@ -75,8 +90,8 @@ Style/EmptyMethod:
|
|
75
90
|
Exclude:
|
76
91
|
- "**/spec/**/*"
|
77
92
|
Metrics/ParameterLists:
|
78
|
-
|
79
|
-
|
93
|
+
Enabled: false
|
94
|
+
|
80
95
|
Layout/EmptyLineBetweenDefs:
|
81
96
|
Exclude:
|
82
97
|
- "**/spec/**/*"
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
# [0.3.0](https://github.com/appydave/klue-langcraft/compare/v0.2.0...v0.3.0) (2024-10-26)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* fix cop ([d77187a](https://github.com/appydave/klue-langcraft/commit/d77187ac59088b7ce6ca23630a06fbb007f05fb4))
|
7
|
+
|
8
|
+
|
9
|
+
### Features
|
10
|
+
|
11
|
+
* update cli for watch and process ([9f0eeb1](https://github.com/appydave/klue-langcraft/commit/9f0eeb1a9409f6734d4ab930ca5c3164b71446eb))
|
12
|
+
|
13
|
+
# [0.2.0](https://github.com/appydave/klue-langcraft/compare/v0.1.1...v0.2.0) (2024-10-25)
|
14
|
+
|
15
|
+
|
16
|
+
### Bug Fixes
|
17
|
+
|
18
|
+
* fix ci issue ([a3bfd35](https://github.com/appydave/klue-langcraft/commit/a3bfd35371b7c0ca70b0b57d70d4541c7a4ec54b))
|
19
|
+
* fix cop ([123070b](https://github.com/appydave/klue-langcraft/commit/123070bd1f636a81d01da469c2e5072c57384bae))
|
20
|
+
* update dsl_interpreter ([2507045](https://github.com/appydave/klue-langcraft/commit/2507045c3f7f205603a3f899785e9c5445c4c0bb))
|
21
|
+
|
22
|
+
|
23
|
+
### Features
|
24
|
+
|
25
|
+
* refactor dsl processor ([5875dc9](https://github.com/appydave/klue-langcraft/commit/5875dc957789f113da762a6dcb55cda66bfb8a3d))
|
26
|
+
|
1
27
|
## [0.1.1](https://github.com/appydave/klue-langcraft/compare/v0.1.0...v0.1.1) (2024-09-22)
|
2
28
|
|
3
29
|
|
data/bin/langcraft.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __dir__)
|
5
|
+
|
6
|
+
require 'klue/langcraft'
|
7
|
+
require 'optparse'
|
8
|
+
|
9
|
+
# CLI class for the Klue Langcraft application.
|
10
|
+
# This class handles command-line interactions, parsing options,
|
11
|
+
# and executing the appropriate commands for processing files
|
12
|
+
# or watching directories.
|
13
|
+
class CLI
|
14
|
+
def initialize
|
15
|
+
@commands = {
|
16
|
+
'process' => method(:process_files),
|
17
|
+
'watch' => method(:watch_files)
|
18
|
+
}
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
if ARGV.empty?
|
23
|
+
print_help
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
command, *args = ARGV
|
28
|
+
if @commands.key?(command)
|
29
|
+
@commands[command].call(args)
|
30
|
+
else
|
31
|
+
puts "Unknown command: #{command}"
|
32
|
+
print_help
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def process_files(args)
|
39
|
+
options = {}
|
40
|
+
parser = OptionParser.new do |opts|
|
41
|
+
opts.banner = 'Usage: langcraft process [options]'
|
42
|
+
|
43
|
+
opts.on('-i', '--input FILE', 'Input file (required)') do |file|
|
44
|
+
options[:input] = file
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on('-b', '--basic-output FILE', 'Output file') do |file|
|
48
|
+
options[:basic_output] = file
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on('-e', '--enhanced-output FILE', 'Output file') do |file|
|
52
|
+
options[:enhanced_output] = file
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on('-u', '--webhook-url URL', 'Webhook URL') do |url|
|
56
|
+
options[:webhook_url] = url
|
57
|
+
end
|
58
|
+
|
59
|
+
opts.on('-d', '--debug LEVEL', %i[none info detailed], 'Debug level (none, info, detailed)') do |level|
|
60
|
+
options[:debug] = level
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on('-h', '--help', 'Show this message') do
|
64
|
+
puts opts
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
parser.parse!(args)
|
70
|
+
|
71
|
+
if validate_options(options)
|
72
|
+
run_klue_runner(options)
|
73
|
+
else
|
74
|
+
print_usage(parser)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def validate_options(options)
|
79
|
+
if options[:input].nil?
|
80
|
+
puts 'Error: Input file is required.'
|
81
|
+
false
|
82
|
+
else
|
83
|
+
true
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def run_klue_runner(options)
|
88
|
+
klue_runner = Klue::Langcraft::DSL::KlueRunner.new
|
89
|
+
klue_runner.run(
|
90
|
+
input_file: options[:input],
|
91
|
+
basic_output_file: options[:basic_output],
|
92
|
+
enhanced_output_file: options[:enhanced_output],
|
93
|
+
webhook_url: options[:webhook_url],
|
94
|
+
debug: options[:debug] || :none
|
95
|
+
)
|
96
|
+
rescue StandardError => e
|
97
|
+
puts "Error: #{e.message}"
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
|
101
|
+
def print_usage(parser)
|
102
|
+
puts 'Error: Invalid or missing options.'
|
103
|
+
puts
|
104
|
+
puts parser
|
105
|
+
exit 1
|
106
|
+
end
|
107
|
+
|
108
|
+
def watch_files(args)
|
109
|
+
options = {
|
110
|
+
directories: [],
|
111
|
+
create_basic_json: false,
|
112
|
+
create_enhanced_json: false,
|
113
|
+
log_level: :info,
|
114
|
+
webhook_url: nil,
|
115
|
+
extensions: ['.klue'] # Default extension
|
116
|
+
}
|
117
|
+
OptionParser.new do |opts|
|
118
|
+
opts.banner = 'Usage: server watch [options]'
|
119
|
+
|
120
|
+
opts.on('-w', '--watch-directory DIRECTORY', 'Directory to watch (can be specified multiple times)') do |dir|
|
121
|
+
options[:directories] << File.expand_path(dir) # Expand the path here
|
122
|
+
end
|
123
|
+
|
124
|
+
opts.on('-f', '--flags TYPE', 'Set processing flags (none, basic, enhanced, all)') do |type|
|
125
|
+
types = type.split(',').map(&:strip).map(&:downcase)
|
126
|
+
options[:create_basic_json] = types.include?('basic') || types.include?('all')
|
127
|
+
options[:create_enhanced_json] = types.include?('enhanced') || types.include?('all')
|
128
|
+
end
|
129
|
+
|
130
|
+
opts.on('-l', '--log-level LEVEL', %i[none info detailed], 'Log level (none, info, detailed)') do |level|
|
131
|
+
options[:log_level] = level
|
132
|
+
end
|
133
|
+
|
134
|
+
opts.on('-u', '--webhook-url URL', 'Webhook URL') do |url|
|
135
|
+
options[:webhook_url] = url
|
136
|
+
end
|
137
|
+
|
138
|
+
opts.on('-e', '--extensions EXT1,EXT2,...', Array, 'File extensions to watch (default: .klue)') do |exts|
|
139
|
+
options[:extensions] = exts.map { |ext| ext.start_with?('.') ? ext : ".#{ext}" }
|
140
|
+
end
|
141
|
+
|
142
|
+
opts.on('-h', '--help', 'Show this message') do
|
143
|
+
puts opts
|
144
|
+
exit
|
145
|
+
end
|
146
|
+
end.parse!(args)
|
147
|
+
|
148
|
+
# If no directories were specified, use the current directory
|
149
|
+
options[:directories] = [Dir.pwd] if options[:directories].empty?
|
150
|
+
|
151
|
+
# Ensure all directories are expanded
|
152
|
+
options[:directories].map! { |dir| File.expand_path(dir) }
|
153
|
+
|
154
|
+
Klue::Langcraft::DSL::Watcher.new(
|
155
|
+
options[:directories],
|
156
|
+
create_basic_json: options[:create_basic_json],
|
157
|
+
create_enhanced_json: options[:create_enhanced_json],
|
158
|
+
log_level: options[:log_level],
|
159
|
+
webhook_url: options[:webhook_url],
|
160
|
+
extensions: options[:extensions]
|
161
|
+
).start
|
162
|
+
end
|
163
|
+
|
164
|
+
def print_help
|
165
|
+
puts 'Klue Langcraft Server'
|
166
|
+
puts 'Usage: server [command] [options]'
|
167
|
+
puts ''
|
168
|
+
puts 'Commands:'
|
169
|
+
puts ' process Process a single file'
|
170
|
+
puts ' watch Watch directories for changes'
|
171
|
+
puts ''
|
172
|
+
puts "Run 'server [command] --help' for more information on a command."
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Run the CLI
|
177
|
+
CLI.new.run
|
@@ -17,18 +17,18 @@ module Klue
|
|
17
17
|
attr_accessor :processed
|
18
18
|
|
19
19
|
def initialize
|
20
|
-
|
21
|
-
@processed = false
|
20
|
+
klue_reset
|
22
21
|
end
|
23
22
|
|
24
23
|
def process(input: nil, input_file: nil, output_file: nil)
|
25
|
-
|
26
|
-
|
24
|
+
klue_reset
|
25
|
+
klue_validate_input_arguments(input, input_file)
|
26
|
+
klue_input_content = klue_input_content(input, input_file)
|
27
27
|
|
28
28
|
@processed = true
|
29
|
-
instance_eval(
|
29
|
+
instance_eval(klue_input_content)
|
30
30
|
|
31
|
-
|
31
|
+
klue_write_output(output_file) if output_file
|
32
32
|
data
|
33
33
|
end
|
34
34
|
|
@@ -36,7 +36,7 @@ module Klue
|
|
36
36
|
raise "You must call 'process' before using other methods" unless @processed
|
37
37
|
|
38
38
|
key = method_name
|
39
|
-
value =
|
39
|
+
value = klue_process_args(args, block)
|
40
40
|
|
41
41
|
if @data[key]
|
42
42
|
@data[key] = [@data[key]] unless @data[key].is_a?(Array)
|
@@ -50,7 +50,7 @@ module Klue
|
|
50
50
|
@processed || super
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
53
|
+
def klue_process_args(args, block)
|
54
54
|
positional_args = []
|
55
55
|
named_args = {}
|
56
56
|
|
@@ -82,32 +82,32 @@ module Klue
|
|
82
82
|
|
83
83
|
private
|
84
84
|
|
85
|
-
def
|
85
|
+
def klue_reset
|
86
|
+
@data = {}
|
87
|
+
@processed = false
|
88
|
+
end
|
89
|
+
|
90
|
+
def klue_validate_input_arguments(input, input_file)
|
86
91
|
raise ArgumentError, 'Either input or input_file must be provided' unless input || input_file
|
87
92
|
raise ArgumentError, 'Both input and input_file cannot be provided' if input && input_file
|
88
93
|
end
|
89
94
|
|
90
|
-
def
|
95
|
+
def klue_input_content(input, input_file)
|
91
96
|
input_file ? File.read(input_file) : input
|
92
97
|
end
|
93
98
|
|
94
|
-
def
|
95
|
-
output_path =
|
99
|
+
def klue_write_output(output_file)
|
100
|
+
output_path = klue_output_path(output_file)
|
96
101
|
File.write(output_path, JSON.pretty_generate(data))
|
97
102
|
end
|
98
103
|
|
99
|
-
def
|
104
|
+
def klue_output_path(output_file)
|
100
105
|
if Pathname.new(output_file).absolute?
|
101
106
|
output_file
|
102
107
|
else
|
103
108
|
File.join(File.dirname(output_file), File.basename(output_file))
|
104
109
|
end
|
105
110
|
end
|
106
|
-
|
107
|
-
# Convert to JSON
|
108
|
-
def to_json(*_args)
|
109
|
-
@data.to_json
|
110
|
-
end
|
111
111
|
end
|
112
112
|
end
|
113
113
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Klue
|
4
|
+
module Langcraft
|
5
|
+
module DSL
|
6
|
+
# KlueRunner handles the processing of DSL input data
|
7
|
+
# It manages the execution of various processors and the output of processed data
|
8
|
+
class KlueRunner
|
9
|
+
attr_reader :interpreter, :pipeline, :webhook
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@interpreter = Klue::Langcraft::DSL::Interpreter.new
|
13
|
+
@pipeline = Klue::Langcraft::DSL::ProcessDataPipeline.new(Klue::Langcraft::DSL::ProcessMatcher.new)
|
14
|
+
@webhook = Klue::Langcraft::DSL::Webhook.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Run the KlueRunner with the given input data
|
18
|
+
# @param input [String] The input data to process
|
19
|
+
# @param input_file [String] The input file to process (input data and file are mutually exclusive)
|
20
|
+
# @param basic_output_file [String] The output file to write the processed data, this file is before any processing
|
21
|
+
# @param enhanced_output_file [String] The output file to write the processed data, this file is after processing
|
22
|
+
def run(
|
23
|
+
input: nil,
|
24
|
+
input_file: nil,
|
25
|
+
basic_output_file: nil,
|
26
|
+
enhanced_output_file: nil,
|
27
|
+
webhook_url: nil,
|
28
|
+
log_level: :none
|
29
|
+
)
|
30
|
+
@log_level = log_level
|
31
|
+
|
32
|
+
log_info('Processing input')
|
33
|
+
data = interpreter.process(input: input, input_file: input_file, output_file: basic_output_file)
|
34
|
+
log_detailed('Interpreter output:', data)
|
35
|
+
|
36
|
+
log_info('Executing pipeline - enhance')
|
37
|
+
enhanced_data = pipeline.execute(data)
|
38
|
+
log_detailed('Enhanced output:', enhanced_data)
|
39
|
+
|
40
|
+
if enhanced_output_file
|
41
|
+
log_info("Writing enhanced output to file: #{enhanced_output_file}")
|
42
|
+
@pipeline.write_output(enhanced_data, enhanced_output_file)
|
43
|
+
end
|
44
|
+
|
45
|
+
if webhook_url
|
46
|
+
log_info("Delivering data to webhook: #{webhook_url}")
|
47
|
+
@webhook.deliver(webhook_url, enhanced_data)
|
48
|
+
end
|
49
|
+
|
50
|
+
log_info('Processing complete')
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def log_info(message)
|
56
|
+
puts message if %i[info detailed].include?(@log_level)
|
57
|
+
end
|
58
|
+
|
59
|
+
def log_detailed(message, data)
|
60
|
+
return unless @log_level == :detailed
|
61
|
+
|
62
|
+
puts message
|
63
|
+
pp data
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'listen'
|
4
|
+
|
5
|
+
module Klue
|
6
|
+
module Langcraft
|
7
|
+
module DSL
|
8
|
+
# Watcher class for monitoring file changes in specified directories.
|
9
|
+
# This class sets up a file system listener to watch for changes to files
|
10
|
+
# with specified extensions, and processes those files using a KlueRunner.
|
11
|
+
class Watcher
|
12
|
+
def initialize(directories, **options)
|
13
|
+
@directories = directories.map { |dir| File.expand_path(dir) }
|
14
|
+
@options = options
|
15
|
+
@klue_runner = Klue::Langcraft::DSL::KlueRunner.new
|
16
|
+
@extensions = options[:extensions] || ['.klue']
|
17
|
+
end
|
18
|
+
|
19
|
+
def start
|
20
|
+
listener = create_listener
|
21
|
+
log_watcher_info
|
22
|
+
listener.start
|
23
|
+
sleep
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def create_listener
|
29
|
+
extension_regex = create_extension_regex
|
30
|
+
Listen.to(*@directories, only: extension_regex) do |modified, added, _removed|
|
31
|
+
process_changed_files(modified + added)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_extension_regex
|
36
|
+
Regexp.union(@extensions.map { |ext| /#{Regexp.escape(ext)}$/ })
|
37
|
+
end
|
38
|
+
|
39
|
+
def process_changed_files(files)
|
40
|
+
files.each { |file| process_file(file) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def process_file(file)
|
44
|
+
log_info('Processing file', file)
|
45
|
+
options = create_file_options(file)
|
46
|
+
run_klue_runner(options)
|
47
|
+
rescue StandardError => e
|
48
|
+
log_error('Error processing file', "#{file}: #{e.message}")
|
49
|
+
end
|
50
|
+
|
51
|
+
def create_file_options(file)
|
52
|
+
options = @options.dup
|
53
|
+
options[:input_file] = file
|
54
|
+
options[:basic_output_file] = file.sub(/#{File.extname(file)}$/, '.json') if @options[:create_basic_json]
|
55
|
+
options[:enhanced_output_file] = file.sub(/#{File.extname(file)}$/, '.enhanced.json') if @options[:create_enhanced_json]
|
56
|
+
options
|
57
|
+
end
|
58
|
+
|
59
|
+
def run_klue_runner(options)
|
60
|
+
@klue_runner.run(
|
61
|
+
input_file: options[:input_file],
|
62
|
+
basic_output_file: options[:basic_output_file],
|
63
|
+
enhanced_output_file: options[:enhanced_output_file],
|
64
|
+
webhook_url: options[:webhook_url],
|
65
|
+
log_level: options[:log_level]
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def log_watcher_info
|
70
|
+
log_info('Watching directories', @directories.join(', '))
|
71
|
+
log_info('Watching file extensions', @extensions.join(', '))
|
72
|
+
log_info('Create basic JSON', @options[:create_basic_json])
|
73
|
+
log_info('Create enhanced JSON', @options[:create_enhanced_json])
|
74
|
+
log_info('Webhook URL', @options[:webhook_url])
|
75
|
+
log_info('Log level', @options[:log_level])
|
76
|
+
end
|
77
|
+
|
78
|
+
def log_info(label, value)
|
79
|
+
puts "#{label.ljust(30)}: #{value}" if %i[info detailed].include?(@options[:log_level])
|
80
|
+
end
|
81
|
+
|
82
|
+
def log_error(label, value)
|
83
|
+
puts "#{label.ljust(30)}: #{value}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
module Klue
|
8
|
+
module Langcraft
|
9
|
+
module DSL
|
10
|
+
# Webhook class for handling HTTP POST requests to specified URLs.
|
11
|
+
# This class is responsible for sending processed data to external services
|
12
|
+
# via webhooks, handling the delivery process, and logging the results.
|
13
|
+
class Webhook
|
14
|
+
def deliver(webhook_url, data)
|
15
|
+
klue_type = extract_klue_type(data)
|
16
|
+
uri = build_uri(webhook_url, klue_type)
|
17
|
+
response = send_request(uri, build_payload(klue_type, data))
|
18
|
+
log_response(response, uri)
|
19
|
+
response
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def extract_klue_type(data)
|
25
|
+
data.keys.first.to_s
|
26
|
+
end
|
27
|
+
|
28
|
+
def build_uri(webhook_url, klue_type)
|
29
|
+
URI.parse("#{webhook_url}?klue-type=#{klue_type}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def build_payload(klue_type, data)
|
33
|
+
{ klue_type: klue_type, data: data }
|
34
|
+
end
|
35
|
+
|
36
|
+
def send_request(uri, payload)
|
37
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
38
|
+
request = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/json' })
|
39
|
+
request.body = payload.to_json
|
40
|
+
http.request(request)
|
41
|
+
end
|
42
|
+
|
43
|
+
def log_response(response, uri)
|
44
|
+
puts "Response: #{response.code} - #{response.message}"
|
45
|
+
puts "Endpoint: #{uri}"
|
46
|
+
log_response_body(response)
|
47
|
+
end
|
48
|
+
|
49
|
+
def log_response_body(response)
|
50
|
+
body = JSON.parse(response.body)
|
51
|
+
puts "DSL Type: #{body['type']}"
|
52
|
+
# puts JSON.pretty_generate(body['data'])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/klue/langcraft.rb
CHANGED
@@ -19,12 +19,9 @@ require 'klue/langcraft/dsl/processors/full_name_processor'
|
|
19
19
|
require 'klue/langcraft/dsl/interpreter'
|
20
20
|
require 'klue/langcraft/dsl/process_matcher'
|
21
21
|
require 'klue/langcraft/dsl/process_data_pipeline'
|
22
|
-
|
23
|
-
require '
|
24
|
-
require '
|
25
|
-
require 'dsl_interpreter'
|
26
|
-
require 'dsl_folder_watcher'
|
27
|
-
require 'dsl_process_data'
|
22
|
+
require 'klue/langcraft/dsl/webhook'
|
23
|
+
require 'klue/langcraft/dsl/watcher'
|
24
|
+
require 'klue/langcraft/dsl/klue_runner'
|
28
25
|
|
29
26
|
module Klue
|
30
27
|
module Langcraft
|
data/package-lock.json
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "klue-langcraft",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.4.0",
|
4
4
|
"lockfileVersion": 3,
|
5
5
|
"requires": true,
|
6
6
|
"packages": {
|
7
7
|
"": {
|
8
8
|
"name": "klue-langcraft",
|
9
|
-
"version": "0.
|
9
|
+
"version": "0.4.0",
|
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
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: klue-langcraft
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
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-10-
|
11
|
+
date: 2024-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: appydave-tools
|
@@ -61,7 +61,7 @@ files:
|
|
61
61
|
- README.md
|
62
62
|
- Rakefile
|
63
63
|
- bin/console
|
64
|
-
- bin/
|
64
|
+
- bin/langcraft.rb
|
65
65
|
- bin/setup
|
66
66
|
- docs/dsl-class-diagram.md
|
67
67
|
- docs/dsl-examples.md
|
@@ -78,24 +78,22 @@ files:
|
|
78
78
|
- docs/project-plan/project_done.svg
|
79
79
|
- docs/project-plan/project_in_progress.svg
|
80
80
|
- docs/project-plan/project_todo.svg
|
81
|
-
- lib/base_process.rb
|
82
|
-
- lib/dsl_folder_watcher.rb
|
83
|
-
- lib/dsl_interpreter.rb
|
84
|
-
- lib/dsl_process_data.rb
|
85
81
|
- lib/klue/langcraft.rb
|
86
82
|
- lib/klue/langcraft/dsl/interpreter.rb
|
83
|
+
- lib/klue/langcraft/dsl/klue_runner.rb
|
87
84
|
- lib/klue/langcraft/dsl/process_data_pipeline.rb
|
88
85
|
- lib/klue/langcraft/dsl/process_matcher.rb
|
89
86
|
- lib/klue/langcraft/dsl/processor_config.rb
|
90
87
|
- lib/klue/langcraft/dsl/processors/file_collector_processor.rb
|
91
88
|
- lib/klue/langcraft/dsl/processors/full_name_processor.rb
|
92
89
|
- lib/klue/langcraft/dsl/processors/processor.rb
|
90
|
+
- lib/klue/langcraft/dsl/watcher.rb
|
91
|
+
- lib/klue/langcraft/dsl/webhook.rb
|
93
92
|
- lib/klue/langcraft/tokenizer-old-needs-revisit/-brief.md
|
94
93
|
- lib/klue/langcraft/tokenizer-old-needs-revisit/parser.rb
|
95
94
|
- lib/klue/langcraft/tokenizer-old-needs-revisit/sample_usage.rb
|
96
95
|
- lib/klue/langcraft/tokenizer-old-needs-revisit/tokenizer.rb
|
97
96
|
- lib/klue/langcraft/version.rb
|
98
|
-
- lib/process_file_collector.rb
|
99
97
|
- package-lock.json
|
100
98
|
- package.json
|
101
99
|
- sig/klue/langcraft.rbs
|
data/bin/dsl_watcher.rb
DELETED
data/lib/base_process.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# file: lib/base_process.rb
|
4
|
-
|
5
|
-
class BaseProcess
|
6
|
-
attr_reader :key
|
7
|
-
|
8
|
-
def initialize(key)
|
9
|
-
@key = key
|
10
|
-
end
|
11
|
-
|
12
|
-
def deep_match(input, predicate)
|
13
|
-
matches = []
|
14
|
-
|
15
|
-
# If the current input is a Hash, iterate over each key-value pair
|
16
|
-
if input.is_a?(Hash)
|
17
|
-
|
18
|
-
input.each do |key, value|
|
19
|
-
|
20
|
-
# If the value matches the predicate, add it to matches
|
21
|
-
if predicate.call(key, value)
|
22
|
-
matches << value
|
23
|
-
end
|
24
|
-
|
25
|
-
# Continue searching deeper within the value
|
26
|
-
matches.concat(deep_match(value, predicate))
|
27
|
-
end
|
28
|
-
|
29
|
-
# If the input is an Array, iterate over each item
|
30
|
-
elsif input.is_a?(Array)
|
31
|
-
|
32
|
-
input.each do |item|
|
33
|
-
|
34
|
-
# Continue searching within each item of the array
|
35
|
-
matches.concat(deep_match(item, predicate))
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
matches
|
40
|
-
end
|
41
|
-
end
|
data/lib/dsl_folder_watcher.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class DSLFolderWatcher
|
4
|
-
def self.watch(folder_path)
|
5
|
-
puts "Watching: #{folder_path}"
|
6
|
-
listener = Listen.to(folder_path) do |modified, added, _removed|
|
7
|
-
changes = (modified + added).uniq
|
8
|
-
|
9
|
-
# DEBOUNCE CURRENTLY NOT WORKING
|
10
|
-
# debounce_map = {}
|
11
|
-
# debounce_interval = 1 # seconds
|
12
|
-
|
13
|
-
changes.each do |file_path|
|
14
|
-
next unless File.extname(file_path) == '.klue'
|
15
|
-
|
16
|
-
puts file_path
|
17
|
-
|
18
|
-
# debounce_map[file_path] ||= Time.now
|
19
|
-
# next unless Time.now - debounce_map[file_path] >= debounce_interval
|
20
|
-
|
21
|
-
# debounce_map[file_path] = Time.now
|
22
|
-
|
23
|
-
base_name = file_path.gsub(/\.klue$/, '')
|
24
|
-
input_file = "#{base_name}.klue"
|
25
|
-
output_file = "#{base_name}.json"
|
26
|
-
|
27
|
-
interpreter = DSLInterpreter.new
|
28
|
-
if interpreter.process('', input_file, output_file)
|
29
|
-
# Process the JSON data to add 'process-data' details
|
30
|
-
dsl_processor = DSLProcessData.new
|
31
|
-
dsl_processor.process('', output_file, output_file)
|
32
|
-
# SKIP EXTEND FILE FOR NOW AND REWRITE THE OUTPUTFILE
|
33
|
-
# dsl_processor.process('', output_file, extended_output_file)
|
34
|
-
|
35
|
-
# interpreter.send_to_endpoint
|
36
|
-
else
|
37
|
-
puts 'Skipping further processing due to errors in DSL interpretation.'
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# Remove old entries from debounce_map to prevent memory bloat
|
42
|
-
# debounce_map.each_key do |key|
|
43
|
-
# debounce_map.delete(key) if Time.now - debounce_map[key] > debounce_interval * 2
|
44
|
-
# end
|
45
|
-
end
|
46
|
-
listener.start
|
47
|
-
puts "Wait for changes: #{folder_path}"
|
48
|
-
sleep
|
49
|
-
end
|
50
|
-
end
|
data/lib/dsl_interpreter.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# ChatReferences:
|
4
|
-
# - https://chatgpt.com/c/67064770-d524-8002-8344-3091e895d150
|
5
|
-
# - https://chatgpt.com/c/6706289c-9b9c-8002-86e3-f9198c1c608a
|
6
|
-
# - https://chatgpt.com/c/670dcd34-5dbc-8002-ad7a-d4df54a6a2e0
|
7
|
-
#
|
8
|
-
class DSLInterpreter
|
9
|
-
def initialize
|
10
|
-
@data = {}
|
11
|
-
end
|
12
|
-
|
13
|
-
# Capturing top-level DSL methods
|
14
|
-
def method_missing(method_name, *args, &block)
|
15
|
-
key = method_name
|
16
|
-
value = process_args(args, block)
|
17
|
-
|
18
|
-
# Append key-value to the current context of @data
|
19
|
-
if @data[key]
|
20
|
-
@data[key] = [@data[key]] unless @data[key].is_a?(Array)
|
21
|
-
@data[key] << value
|
22
|
-
else
|
23
|
-
@data[key] = value
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# A method to handle parameters and nested blocks
|
28
|
-
def process_args(args, block)
|
29
|
-
data = {}
|
30
|
-
|
31
|
-
# Handling positional and named parameters separately
|
32
|
-
positional_args = []
|
33
|
-
named_args = {}
|
34
|
-
|
35
|
-
args.each do |arg|
|
36
|
-
if arg.is_a?(Hash)
|
37
|
-
named_args.merge!(arg)
|
38
|
-
else
|
39
|
-
positional_args << arg
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# Assign positional parameters generically
|
44
|
-
positional_args.each_with_index do |arg, index|
|
45
|
-
data[:"param#{index + 1}"] = arg
|
46
|
-
end
|
47
|
-
|
48
|
-
# Merge named parameters directly
|
49
|
-
data.merge!(named_args)
|
50
|
-
|
51
|
-
# Handling a nested block
|
52
|
-
if block
|
53
|
-
interpreter = DSLInterpreter.new
|
54
|
-
interpreter.instance_eval(&block)
|
55
|
-
data.merge!(interpreter.data)
|
56
|
-
end
|
57
|
-
|
58
|
-
data.empty? ? nil : data
|
59
|
-
end
|
60
|
-
|
61
|
-
# To access data after interpreting
|
62
|
-
attr_reader :data
|
63
|
-
|
64
|
-
# Reading file and evaluating as Ruby
|
65
|
-
def process(base_path, input_file, output_file)
|
66
|
-
file_path = File.join(base_path, input_file)
|
67
|
-
content = File.read(file_path)
|
68
|
-
|
69
|
-
# begin
|
70
|
-
instance_eval(content)
|
71
|
-
# rescue SyntaxError => e
|
72
|
-
# puts "Syntax error in DSL file: #{input_file}"
|
73
|
-
# puts "Error message: #{e.message}"
|
74
|
-
# puts "Error occurred at line: #{e.backtrace.first}"
|
75
|
-
# return false # Indicate that processing failed
|
76
|
-
# rescue StandardError => e
|
77
|
-
# puts "Error processing DSL file: #{input_file}"
|
78
|
-
# puts "Error message: #{e.message}"
|
79
|
-
# puts "Error occurred at: #{e.backtrace.first}"
|
80
|
-
# return false # Indicate that processing failed
|
81
|
-
# end
|
82
|
-
|
83
|
-
output_path = File.join(base_path, output_file)
|
84
|
-
File.write(output_path, JSON.pretty_generate(to_hash))
|
85
|
-
true # Indicate that processing succeeded
|
86
|
-
end
|
87
|
-
|
88
|
-
# Convert to hash or JSON as required
|
89
|
-
def to_hash
|
90
|
-
@data
|
91
|
-
end
|
92
|
-
|
93
|
-
def to_json(*_args)
|
94
|
-
@data.to_json
|
95
|
-
end
|
96
|
-
|
97
|
-
# Method to send data to an endpoint
|
98
|
-
def send_to_endpoint
|
99
|
-
root_key = @data.keys.first
|
100
|
-
action_type = root_key.to_s
|
101
|
-
|
102
|
-
uri = URI.parse("http://localhost:4567/dsl/#{action_type}")
|
103
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
104
|
-
request = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/json' })
|
105
|
-
payload = { action_type: action_type, data: @data }
|
106
|
-
request.body = payload.to_json
|
107
|
-
|
108
|
-
response = http.request(request)
|
109
|
-
puts "Response: #{response.code} - #{response.message}"
|
110
|
-
puts "Endpoint: #{uri}"
|
111
|
-
end
|
112
|
-
end
|
data/lib/dsl_process_data.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
class DSLProcessData
|
4
|
-
PROCESSORS = [{ file_collector: ProcessFileCollector }].freeze
|
5
|
-
|
6
|
-
# Method to process the JSON file after initial evaluation
|
7
|
-
def process(base_path, input_file, output_file)
|
8
|
-
json_file_path = File.join(base_path, input_file)
|
9
|
-
data = JSON.parse(File.read(json_file_path))
|
10
|
-
|
11
|
-
# Loop through the processors and execute matching ones
|
12
|
-
PROCESSORS.each do |processor_entry|
|
13
|
-
key, processor_class = processor_entry.first
|
14
|
-
processor = processor_class.new(key)
|
15
|
-
|
16
|
-
next unless processor.match?(data)
|
17
|
-
|
18
|
-
result = processor.execute(data)
|
19
|
-
|
20
|
-
data['process-data'] ||= {}
|
21
|
-
|
22
|
-
result.each do |key, result|
|
23
|
-
data['process-data'][key.to_s] = result unless result.empty?
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Write the updated JSON data to an extended file
|
28
|
-
extended_output_file = File.join(base_path, output_file)
|
29
|
-
File.write(extended_output_file, JSON.pretty_generate(data))
|
30
|
-
end
|
31
|
-
end
|
@@ -1,92 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# file: lib/process_file_collector.rb
|
4
|
-
|
5
|
-
class ProcessFileCollector < BaseProcess
|
6
|
-
def initialize(key)
|
7
|
-
super
|
8
|
-
@found = nil
|
9
|
-
# @matcher = ->(key, value) { key.to_s == 'file_collector' && value.is_a?(Hash) }
|
10
|
-
@matcher = lambda do |key, value|
|
11
|
-
if key.to_s == 'file_collector'
|
12
|
-
if value.is_a?(Array)
|
13
|
-
value.any? { |v| v.is_a?(Hash) }
|
14
|
-
else
|
15
|
-
value.is_a?(Hash)
|
16
|
-
end
|
17
|
-
else
|
18
|
-
false
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def match?(input)
|
24
|
-
@found = deep_match(input, @matcher)
|
25
|
-
!@found.empty?
|
26
|
-
end
|
27
|
-
|
28
|
-
def execute(_input)
|
29
|
-
# Iterate over each `file_collector` found and process individually
|
30
|
-
results = {}
|
31
|
-
|
32
|
-
@found.each do |data|
|
33
|
-
next unless data.is_a?(Hash)
|
34
|
-
|
35
|
-
# Extract the `as` key if present
|
36
|
-
as_key = data['as']
|
37
|
-
|
38
|
-
working_directory = File.expand_path(data['root'])
|
39
|
-
|
40
|
-
options = Appydave::Tools::GptContext::Options.new(
|
41
|
-
working_directory: working_directory,
|
42
|
-
include_patterns: extract_patterns(data.dig('files', 'include')),
|
43
|
-
exclude_patterns: extract_patterns(data.dig('files', 'exclude')),
|
44
|
-
format: 'json',
|
45
|
-
line_limit: data['line_length']
|
46
|
-
)
|
47
|
-
|
48
|
-
collector = Appydave::Tools::GptContext::FileCollector.new(options)
|
49
|
-
json = collector.build
|
50
|
-
|
51
|
-
# Structuring the result under `process-data` with `as` as key
|
52
|
-
result_data = {
|
53
|
-
type: 'file_collector',
|
54
|
-
data: {
|
55
|
-
working_directory: working_directory,
|
56
|
-
files: JSON.parse(json)
|
57
|
-
}
|
58
|
-
}
|
59
|
-
|
60
|
-
# If `as` key exists, use it to store under process-data with that identifier
|
61
|
-
if as_key
|
62
|
-
results[as_key] = result_data
|
63
|
-
else
|
64
|
-
# Generate a unique key if no `as` key is defined
|
65
|
-
unique_key = "file_collector_#{results.size + 1}"
|
66
|
-
results[unique_key] = result_data
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
results
|
71
|
-
rescue SyntaxError, NameError, NoMethodError => e
|
72
|
-
puts "Ruby evaluation error in ProcessFileCollector: #{e.message}"
|
73
|
-
puts "Error occurred at: #{e.backtrace.first}"
|
74
|
-
{}
|
75
|
-
rescue StandardError => e
|
76
|
-
puts "Unexpected error in ProcessFileCollector: #{e.message}"
|
77
|
-
puts e.backtrace.join("\n")
|
78
|
-
{}
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def extract_patterns(files_data)
|
84
|
-
if files_data.is_a?(Hash)
|
85
|
-
[files_data['param1']]
|
86
|
-
elsif files_data.is_a?(Array)
|
87
|
-
files_data.map { |entry| entry['param1'] }
|
88
|
-
else
|
89
|
-
[]
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|