legionio 1.4.91 → 1.4.93

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.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/CHANGELOG.md +21 -0
  4. data/completions/_legion +123 -0
  5. data/completions/legion.bash +28 -1
  6. data/lib/legion/cli/dataset_command.rb +148 -0
  7. data/lib/legion/cli/lex/templates/data_pipeline/actors/ingest.rb.erb +24 -0
  8. data/lib/legion/cli/lex/templates/data_pipeline/runners/transform.rb.erb +56 -0
  9. data/lib/legion/cli/lex/templates/data_pipeline/spec/actors/ingest_spec.rb.erb +19 -0
  10. data/lib/legion/cli/lex/templates/data_pipeline/spec/runners/transform_spec.rb.erb +41 -0
  11. data/lib/legion/cli/lex/templates/data_pipeline/transport/exchanges/%name%.rb.erb +16 -0
  12. data/lib/legion/cli/lex/templates/data_pipeline/transport/messages/%name%_output.rb.erb +27 -0
  13. data/lib/legion/cli/lex/templates/data_pipeline/transport/queues/ingest.rb.erb +17 -0
  14. data/lib/legion/cli/lex/templates/llm_agent/helpers/client.rb.erb +43 -0
  15. data/lib/legion/cli/lex/templates/llm_agent/prompts/default.yml.erb +14 -0
  16. data/lib/legion/cli/lex/templates/llm_agent/runners/%name%.rb.erb +37 -0
  17. data/lib/legion/cli/lex/templates/llm_agent/spec/runners/%name%_spec.rb.erb +40 -0
  18. data/lib/legion/cli/lex/templates/service_integration/helpers/auth.rb.erb +37 -0
  19. data/lib/legion/cli/lex/templates/service_integration/helpers/client.rb.erb +53 -0
  20. data/lib/legion/cli/lex/templates/service_integration/runners/%name%.rb.erb +61 -0
  21. data/lib/legion/cli/lex/templates/service_integration/spec/helpers/client_spec.rb.erb +46 -0
  22. data/lib/legion/cli/lex/templates/service_integration/spec/runners/%name%_spec.rb.erb +52 -0
  23. data/lib/legion/cli/lex_command.rb +55 -9
  24. data/lib/legion/cli/lex_templates.rb +74 -2
  25. data/lib/legion/cli/prompt_command.rb +197 -0
  26. data/lib/legion/cli.rb +8 -0
  27. data/lib/legion/version.rb +1 -1
  28. metadata +19 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e39fb298ff77ee509f8a7948c14455a48b4764503688ead869e48ea4108e72a0
4
- data.tar.gz: 0266b0b3d36ecf72e7b2be8d2f0f42ae9737fd0a37add807b32a608dd7fcf5c0
3
+ metadata.gz: 318839858d16ecbaaf6c7db4a0d9562e1641ca24d174fda914b9d359f3f91e69
4
+ data.tar.gz: 91e1415070e2f547bc7c71646e79312f048f8973b2cb004045a8d546a0000387
5
5
  SHA512:
6
- metadata.gz: 5edbd698137c046896280b44cd8af35580808068a24ee48a95dd495e48f57df5eb0976865e6ca8f60938bf96e0d3a969a0085f6ba18a93b8045c34f41958f119
7
- data.tar.gz: 3048fe946eaade9f1e3ecb611d4eced9e78bb5e80126d09e73a55b5e7aa017df594e75f2e17fc3b88af87d9e1d960ee5c988dc0e7022d4260a2fa60ba3645b06
6
+ metadata.gz: e5cd6e2ff71cc11db2a1e8928aae0c53104f931c18829bf470658bbcac3e2f90a18aeb7c564dcc7eb3f113fa9c65b54bb165a0f322fc3b1021a0ebf98148bf66
7
+ data.tar.gz: ff3c9e796fef50d7ca2e7b1970c4516a2d839031f8bee6036fc43b758f0035d85b5bc80fd631c5e55d8a83ce28fe5a7ccfa55fa69b67237b868331d1606d3d34
data/.rubocop.yml CHANGED
@@ -41,6 +41,7 @@ Metrics/BlockLength:
41
41
  - 'lib/legion/api/auth_human.rb'
42
42
  - 'lib/legion/cli/auth_command.rb'
43
43
  - 'lib/legion/cli/detect_command.rb'
44
+ - 'lib/legion/cli/prompt_command.rb'
44
45
  - 'lib/legion/api/acp.rb'
45
46
 
46
47
  Metrics/AbcSize:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Legion Changelog
2
2
 
3
+ ## [1.4.93] - 2026-03-20
4
+
5
+ ### Added
6
+ - `legion prompt` CLI subcommand for versioned LLM prompt template management (list, show, create, tag, diff)
7
+ - `legion dataset` CLI subcommand for versioned dataset management (list, show, import, export)
8
+ - Both commands wrap `lex-prompt` and `lex-dataset` extension clients via `begin/rescue LoadError` guards
9
+ - Both commands guard with `Connection.ensure_data` and follow existing `with_*_client` pattern
10
+ - Tab completion entries for `prompt` and `dataset` in `completions/legion.bash` and `completions/_legion`
11
+
12
+ ## [1.4.92] - 2026-03-20
13
+
14
+ ### Added
15
+ - `--template` option on `legion lex create` to scaffold pattern-specific extensions: `llm-agent`, `service-integration`, `data-pipeline` (default: `basic`)
16
+ - `--list-templates` option on `legion lex create` to display available templates with descriptions
17
+ - `LexTemplates::TemplateOverlay` class renders ERB template files into the target extension directory
18
+ - ERB scaffold templates under `lib/legion/cli/lex/templates/`: `llm_agent/`, `service_integration/`, `data_pipeline/`
19
+ - `llm-agent` template: LLM runner with `Legion::LLM.chat` and structured output, helpers/client.rb with model/temperature kwargs, default prompt YAML, spec with LLM mock
20
+ - `service-integration` template: CRUD runners (list/get/create/update/delete), Faraday HTTP client helper with api_key/bearer/basic auth, auth helper, specs with WebMock stubs
21
+ - `data-pipeline` template: transform runner with validate/process/publish pattern, subscription ingest actor, transport exchange/queue/message scaffolds, runner and actor specs
22
+ - Template registry extended with `data-pipeline`, `template_dir` class method, new `llm-agent`/`service-integration` entries with `template_dir` keys
23
+
3
24
  ## [1.4.91] - 2026-03-20
4
25
 
5
26
  ### Fixed
data/completions/_legion CHANGED
@@ -45,6 +45,8 @@ _legion() {
45
45
  commit) _legion_commit ;;
46
46
  pr) _legion_pr ;;
47
47
  review) _legion_review ;;
48
+ prompt) _legion_prompt ;;
49
+ dataset) _legion_dataset ;;
48
50
  esac
49
51
  ;;
50
52
  esac
@@ -62,6 +64,8 @@ _legion_commands() {
62
64
  'init:Interactive project setup wizard'
63
65
  'tty:Launch interactive TTY shell'
64
66
  'ask:Quick AI prompt (shortcut for chat prompt)'
67
+ 'prompt:Manage versioned LLM prompt templates'
68
+ 'dataset:Manage versioned datasets'
65
69
  'version:Show version information'
66
70
  'help:Show help'
67
71
  )
@@ -837,4 +841,123 @@ _legion_check() {
837
841
  '--no-color[Disable color output]'
838
842
  }
839
843
 
844
+ _legion_prompt() {
845
+ local -a subcmds
846
+ subcmds=(
847
+ 'list:List all prompts'
848
+ 'show:Show a prompt template and parameters'
849
+ 'create:Create a new prompt'
850
+ 'tag:Tag a prompt version'
851
+ 'diff:Show text diff between two versions of a prompt'
852
+ )
853
+
854
+ _arguments -C \
855
+ '--json[Output as JSON]' \
856
+ '--no-color[Disable color output]' \
857
+ '--help[Show help]' \
858
+ '1: :->cmd' \
859
+ '*:: :->args'
860
+
861
+ case $state in
862
+ cmd) _describe 'prompt command' subcmds ;;
863
+ args)
864
+ case $words[1] in
865
+ list)
866
+ _arguments \
867
+ '--json[Output as JSON]' \
868
+ '--no-color[Disable color output]'
869
+ ;;
870
+ show)
871
+ _arguments \
872
+ ':prompt name:' \
873
+ '--version[Specific version number]:version:' \
874
+ '--tag[Tag name to resolve]:tag:' \
875
+ '--json[Output as JSON]' \
876
+ '--no-color[Disable color output]'
877
+ ;;
878
+ create)
879
+ _arguments \
880
+ ':prompt name:' \
881
+ '--template[Prompt template text]:template:' \
882
+ '--description[Short description]:desc:' \
883
+ '--model-params[Model parameters as JSON]:json:' \
884
+ '--json[Output as JSON]' \
885
+ '--no-color[Disable color output]'
886
+ ;;
887
+ tag)
888
+ _arguments \
889
+ ':prompt name:' \
890
+ ':tag name:' \
891
+ '--version[Version to tag]:version:' \
892
+ '--json[Output as JSON]' \
893
+ '--no-color[Disable color output]'
894
+ ;;
895
+ diff)
896
+ _arguments \
897
+ ':prompt name:' \
898
+ ':version 1:' \
899
+ ':version 2:' \
900
+ '--json[Output as JSON]' \
901
+ '--no-color[Disable color output]'
902
+ ;;
903
+ esac
904
+ ;;
905
+ esac
906
+ }
907
+
908
+ _legion_dataset() {
909
+ local -a subcmds
910
+ subcmds=(
911
+ 'list:List all datasets'
912
+ 'show:Show dataset info and first 10 rows'
913
+ 'import:Import a dataset from a file'
914
+ 'export:Export a dataset to a file'
915
+ )
916
+
917
+ _arguments -C \
918
+ '--json[Output as JSON]' \
919
+ '--no-color[Disable color output]' \
920
+ '--help[Show help]' \
921
+ '1: :->cmd' \
922
+ '*:: :->args'
923
+
924
+ case $state in
925
+ cmd) _describe 'dataset command' subcmds ;;
926
+ args)
927
+ case $words[1] in
928
+ list)
929
+ _arguments \
930
+ '--json[Output as JSON]' \
931
+ '--no-color[Disable color output]'
932
+ ;;
933
+ show)
934
+ _arguments \
935
+ ':dataset name:' \
936
+ '--version[Specific version number]:version:' \
937
+ '--json[Output as JSON]' \
938
+ '--no-color[Disable color output]'
939
+ ;;
940
+ import)
941
+ _arguments \
942
+ ':dataset name:' \
943
+ ':file path:_files' \
944
+ '--format[File format]:format:(json csv jsonl)' \
945
+ '--description[Dataset description]:desc:' \
946
+ '--json[Output as JSON]' \
947
+ '--no-color[Disable color output]'
948
+ ;;
949
+ export)
950
+ _arguments \
951
+ ':dataset name:' \
952
+ ':output path:_files' \
953
+ '--format[File format]:format:(json csv jsonl)' \
954
+ '--version[Version to export]:version:' \
955
+ '--json[Output as JSON]' \
956
+ '--no-color[Disable color output]'
957
+ ;;
958
+ esac
959
+ ;;
960
+ esac
961
+ }
962
+
840
963
  _legion "$@"
@@ -26,7 +26,7 @@ _legion_complete() {
26
26
  cword=$COMP_CWORD
27
27
  }
28
28
 
29
- local top_commands="chat commit pr review memory plan init tty ask version help"
29
+ local top_commands="chat commit pr review memory plan init tty ask prompt dataset version help"
30
30
  local global_flags="--json --no-color --verbose --config-dir --help"
31
31
 
32
32
  # Top-level command
@@ -264,6 +264,33 @@ _legion_complete() {
264
264
  COMPREPLY=($(compgen -W "--json --no-color --help" -- "${cur}"))
265
265
  ;;
266
266
 
267
+ prompt)
268
+ if [[ $cword -eq 2 ]]; then
269
+ COMPREPLY=($(compgen -W "list show create tag diff" -- "${cur}"))
270
+ else
271
+ case "${words[2]}" in
272
+ list) COMPREPLY=($(compgen -W "--json --no-color --help" -- "${cur}")) ;;
273
+ show) COMPREPLY=($(compgen -W "--version --tag --json --no-color --help" -- "${cur}")) ;;
274
+ create) COMPREPLY=($(compgen -W "--template --description --model-params --json --no-color --help" -- "${cur}")) ;;
275
+ tag) COMPREPLY=($(compgen -W "--version --json --no-color --help" -- "${cur}")) ;;
276
+ diff) COMPREPLY=($(compgen -W "--json --no-color --help" -- "${cur}")) ;;
277
+ esac
278
+ fi
279
+ ;;
280
+
281
+ dataset)
282
+ if [[ $cword -eq 2 ]]; then
283
+ COMPREPLY=($(compgen -W "list show import export" -- "${cur}"))
284
+ else
285
+ case "${words[2]}" in
286
+ list) COMPREPLY=($(compgen -W "--json --no-color --help" -- "${cur}")) ;;
287
+ show) COMPREPLY=($(compgen -W "--version --json --no-color --help" -- "${cur}")) ;;
288
+ import) COMPREPLY=($(compgen -W "--format --description --json --no-color --help" -- "${cur}")) ;;
289
+ export) COMPREPLY=($(compgen -W "--format --version --json --no-color --help" -- "${cur}")) ;;
290
+ esac
291
+ fi
292
+ ;;
293
+
267
294
  dream)
268
295
  COMPREPLY=($(compgen -W "--wait --json --no-color --help" -- "${cur}"))
269
296
  ;;
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ module Legion
6
+ module CLI
7
+ class Dataset < Thor
8
+ def self.exit_on_failure?
9
+ true
10
+ end
11
+
12
+ class_option :json, type: :boolean, default: false, desc: 'Output as JSON'
13
+ class_option :no_color, type: :boolean, default: false, desc: 'Disable color output'
14
+ class_option :verbose, type: :boolean, default: false, aliases: ['-V'], desc: 'Verbose logging'
15
+ class_option :config_dir, type: :string, desc: 'Config directory path'
16
+
17
+ desc 'list', 'List all datasets'
18
+ def list
19
+ out = formatter
20
+ with_dataset_client do |client|
21
+ datasets = client.list_datasets
22
+ if options[:json]
23
+ out.json(datasets)
24
+ elsif datasets.empty?
25
+ out.warn('No datasets found')
26
+ else
27
+ rows = datasets.map do |d|
28
+ [d[:name].to_s, (d[:description] || '').to_s,
29
+ (d[:latest_version] || '-').to_s, (d[:row_count] || 0).to_s]
30
+ end
31
+ out.table(%w[name description version row_count], rows)
32
+ end
33
+ end
34
+ end
35
+ default_task :list
36
+
37
+ desc 'show NAME', 'Show dataset info and first 10 rows'
38
+ option :version, type: :numeric, desc: 'Specific version number'
39
+ def show(name)
40
+ out = formatter
41
+ with_dataset_client do |client|
42
+ kwargs = { name: name }
43
+ kwargs[:version] = options[:version] if options[:version]
44
+ result = client.get_dataset(**kwargs)
45
+ if result[:error]
46
+ out.error("Dataset '#{name}': #{result[:error]}")
47
+ raise SystemExit, 1
48
+ end
49
+
50
+ if options[:json]
51
+ out.json(result)
52
+ else
53
+ out.header("Dataset: #{result[:name]}")
54
+ out.spacer
55
+ out.detail({ version: result[:version], row_count: result[:row_count] })
56
+ out.spacer
57
+ preview = (result[:rows] || []).first(10)
58
+ if preview.empty?
59
+ out.warn('No rows in this dataset version')
60
+ else
61
+ rows = preview.map do |r|
62
+ [r[:row_index].to_s, r[:input].to_s.slice(0, 60), (r[:expected_output] || '').to_s.slice(0, 60)]
63
+ end
64
+ out.table(%w[index input expected_output], rows)
65
+ remaining = result[:row_count].to_i - preview.size
66
+ out.warn("... #{remaining} more rows not shown") if remaining.positive?
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ desc 'import NAME PATH', 'Import a dataset from a file'
73
+ option :format, type: :string, default: 'json', enum: %w[json csv jsonl], desc: 'File format'
74
+ option :description, type: :string, desc: 'Dataset description'
75
+ def import(name, path)
76
+ out = formatter
77
+ with_dataset_client do |client|
78
+ unless File.exist?(path)
79
+ out.error("File not found: #{path}")
80
+ raise SystemExit, 1
81
+ end
82
+
83
+ result = client.import_dataset(
84
+ name: name,
85
+ path: path,
86
+ format: options[:format],
87
+ description: options[:description]
88
+ )
89
+ if options[:json]
90
+ out.json(result)
91
+ else
92
+ out.success("Imported '#{result[:name]}' v#{result[:version]} (#{result[:row_count]} rows)")
93
+ end
94
+ end
95
+ end
96
+
97
+ desc 'export NAME PATH', 'Export a dataset to a file'
98
+ option :format, type: :string, default: 'json', enum: %w[json csv jsonl], desc: 'File format'
99
+ option :version, type: :numeric, desc: 'Version to export'
100
+ def export(name, path)
101
+ out = formatter
102
+ with_dataset_client do |client|
103
+ kwargs = { name: name, path: path, format: options[:format] }
104
+ kwargs[:version] = options[:version] if options[:version]
105
+ result = client.export_dataset(**kwargs)
106
+ if options[:json]
107
+ out.json(result)
108
+ else
109
+ out.success("Exported #{result[:row_count]} rows to #{result[:path]}")
110
+ end
111
+ end
112
+ end
113
+
114
+ no_commands do
115
+ def formatter
116
+ @formatter ||= Output::Formatter.new(
117
+ json: options[:json],
118
+ color: !options[:no_color]
119
+ )
120
+ end
121
+
122
+ def with_dataset_client
123
+ Connection.config_dir = options[:config_dir] if options[:config_dir]
124
+ Connection.log_level = options[:verbose] ? 'debug' : 'error'
125
+ Connection.ensure_data
126
+
127
+ begin
128
+ require 'legion/extensions/dataset'
129
+ require 'legion/extensions/dataset/runners/dataset'
130
+ require 'legion/extensions/dataset/client'
131
+ rescue LoadError
132
+ formatter.error('lex-dataset gem is not installed (gem install lex-dataset)')
133
+ raise SystemExit, 1
134
+ end
135
+
136
+ db = Legion::Data.db
137
+ client = Legion::Extensions::Dataset::Client.new(db: db)
138
+ yield client
139
+ rescue CLI::Error => e
140
+ formatter.error(e.message)
141
+ raise SystemExit, 1
142
+ ensure
143
+ Connection.shutdown
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module <%= lex_class %>
6
+ module Actors
7
+ class Ingest < Legion::Extensions::Actors::Subscription
8
+ include Legion::Extensions::<%= lex_class %>::Runners::Transform
9
+
10
+ QUEUE = 'legion.<%= lex_name %>.ingest'
11
+ EXCHANGE = 'legion.<%= lex_name %>'
12
+
13
+ def self.queue
14
+ QUEUE
15
+ end
16
+
17
+ def self.exchange
18
+ EXCHANGE
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module <%= lex_class %>
6
+ module Runners
7
+ module Transform
8
+ extend Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?('Helpers::Lex')
9
+
10
+ # Main transform entry point. Receives a payload hash, returns transformed output.
11
+ def transform(payload:, options: {}, **)
12
+ validated = validate_input(payload)
13
+ return validated unless validated[:success]
14
+
15
+ result = process(validated[:data], options)
16
+ publish_output(result) if defined?(Legion::Transport)
17
+
18
+ { success: true, data: result }
19
+ rescue StandardError => e
20
+ handle_error(e, payload)
21
+ end
22
+
23
+ private
24
+
25
+ def validate_input(payload)
26
+ return { success: false, reason: 'payload is required' } if payload.nil?
27
+ return { success: false, reason: 'payload must be a Hash' } unless payload.is_a?(Hash)
28
+
29
+ { success: true, data: payload }
30
+ end
31
+
32
+ def process(data, _options)
33
+ # TODO: implement transformation logic
34
+ data
35
+ end
36
+
37
+ def publish_output(result)
38
+ return unless defined?(Legion::Transport)
39
+
40
+ Legion::Transport::Messages::<%= name_class %>Output.new(data: result).publish
41
+ rescue StandardError
42
+ nil
43
+ end
44
+
45
+ def handle_error(error, payload)
46
+ {
47
+ success: false,
48
+ reason: error.message,
49
+ payload: payload
50
+ }
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::<%= lex_class %>::Actors::Ingest do
4
+ it 'inherits from Subscription actor' do
5
+ expect(described_class.ancestors).to include(Legion::Extensions::Actors::Subscription)
6
+ end
7
+
8
+ it 'includes the Transform runner' do
9
+ expect(described_class.ancestors).to include(Legion::Extensions::<%= lex_class %>::Runners::Transform)
10
+ end
11
+
12
+ it 'defines a queue name' do
13
+ expect(described_class::QUEUE).to include('<%= lex_name %>')
14
+ end
15
+
16
+ it 'defines an exchange name' do
17
+ expect(described_class::EXCHANGE).to include('<%= lex_name %>')
18
+ end
19
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Legion::Extensions::<%= lex_class %>::Runners::Transform do
4
+ subject { described_class }
5
+
6
+ let(:test_class) do
7
+ Class.new do
8
+ extend Legion::Extensions::<%= lex_class %>::Runners::Transform
9
+ end
10
+ end
11
+
12
+ it { should be_a Module }
13
+ it { is_expected.to respond_to(:transform).with_any_keywords }
14
+
15
+ describe '#transform' do
16
+ it 'returns success for a valid payload' do
17
+ result = test_class.transform(payload: { key: 'value' })
18
+ expect(result[:success]).to be true
19
+ expect(result[:data]).to be_a(Hash)
20
+ end
21
+
22
+ it 'returns failure when payload is nil' do
23
+ result = test_class.transform(payload: nil)
24
+ expect(result[:success]).to be false
25
+ expect(result[:reason]).to include('required')
26
+ end
27
+
28
+ it 'returns failure when payload is not a Hash' do
29
+ result = test_class.transform(payload: 'not a hash')
30
+ expect(result[:success]).to be false
31
+ expect(result[:reason]).to include('Hash')
32
+ end
33
+
34
+ it 'handles unexpected errors gracefully' do
35
+ allow(test_class).to receive(:process).and_raise(StandardError, 'unexpected')
36
+ result = test_class.transform(payload: { key: 'value' })
37
+ expect(result[:success]).to be false
38
+ expect(result[:reason]).to eq('unexpected')
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module <%= lex_class %>
6
+ module Transport
7
+ module Exchanges
8
+ class <%= name_class %> < Legion::Transport::Exchange
9
+ EXCHANGE_NAME = 'legion.<%= lex_name %>'
10
+ EXCHANGE_TYPE = :direct
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module <%= lex_class %>
6
+ module Transport
7
+ module Messages
8
+ class <%= name_class %>Output < Legion::Transport::Message
9
+ EXCHANGE_NAME = 'legion.<%= lex_name %>'
10
+ ROUTING_KEY = 'output'
11
+
12
+ attr_accessor :data
13
+
14
+ def initialize(data:)
15
+ @data = data
16
+ super()
17
+ end
18
+
19
+ def to_payload
20
+ { data: @data }
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module <%= lex_class %>
6
+ module Transport
7
+ module Queues
8
+ class Ingest < Legion::Transport::Queue
9
+ QUEUE_NAME = 'legion.<%= lex_name %>.ingest'
10
+ EXCHANGE_NAME = 'legion.<%= lex_name %>'
11
+ ROUTING_KEY = 'ingest'
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module <%= lex_class %>
6
+ module Helpers
7
+ class Client
8
+ attr_reader :model, :temperature, :max_tokens
9
+
10
+ def initialize(model: nil, temperature: 0.7, max_tokens: 1024, **)
11
+ @model = model
12
+ @temperature = temperature
13
+ @max_tokens = max_tokens
14
+ end
15
+
16
+ def chat(prompt:, **override)
17
+ return { success: false, reason: 'legion-llm not available' } unless defined?(Legion::LLM)
18
+
19
+ opts = { prompt: prompt, temperature: @temperature, max_tokens: @max_tokens }
20
+ opts[:model] = @model if @model
21
+ opts.merge!(override)
22
+
23
+ Legion::LLM.chat(**opts)
24
+ rescue StandardError => e
25
+ { success: false, reason: e.message }
26
+ end
27
+
28
+ def structured(prompt:, schema:, **override)
29
+ return { success: false, reason: 'legion-llm not available' } unless defined?(Legion::LLM)
30
+
31
+ opts = { prompt: prompt, schema: schema }
32
+ opts[:model] = @model if @model
33
+ opts.merge!(override)
34
+
35
+ Legion::LLM.structured(**opts)
36
+ rescue StandardError => e
37
+ { success: false, reason: e.message }
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ ---
2
+ # Default prompt template for <%= gem_name %>
3
+ # Customize system and user prompts for your use case.
4
+
5
+ system: |
6
+ You are a helpful assistant for <%= lex_class %>.
7
+ Respond concisely and accurately.
8
+
9
+ user: |
10
+ <%= '{{input}}' %>
11
+
12
+ examples:
13
+ - input: "What can you help me with?"
14
+ expected: "I can help you with <%= lex_class.downcase %>-related tasks."
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module <%= lex_class %>
6
+ module Runners
7
+ module <%= name_class %>
8
+ extend Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined?('Helpers::Lex')
9
+
10
+ def run(prompt:, model: nil, temperature: nil, structured: false, schema: nil, **)
11
+ llm_opts = { prompt: prompt }
12
+ llm_opts[:model] = model if model
13
+ llm_opts[:temperature] = temperature if temperature
14
+
15
+ if structured && defined?(Legion::LLM)
16
+ response = Legion::LLM.structured(prompt: prompt, schema: schema || default_schema)
17
+ elsif defined?(Legion::LLM)
18
+ response = Legion::LLM.chat(**llm_opts)
19
+ else
20
+ return { success: false, reason: 'legion-llm not available' }
21
+ end
22
+
23
+ { success: true, response: response }
24
+ rescue StandardError => e
25
+ { success: false, reason: e.message }
26
+ end
27
+
28
+ private
29
+
30
+ def default_schema
31
+ { type: 'object', properties: { result: { type: 'string' } } }
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end