lex-llm-ledger 0.1.9 → 0.1.11

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65d2298886cd461ff9a4a5cfb89ee0b9d16f8cb3af3e5f38a8a6a8910094a14f
4
- data.tar.gz: 74e394c3972fbc45153e898b8ead76c24cf185dcb677f48b032150894b0b4fe2
3
+ metadata.gz: 95df124106575b40d9e7bd4534782f90433c3f8bb12cd3473ca61b359f9f22ad
4
+ data.tar.gz: f8ac1dbc0227c6c1b231406db8e11270ea846bb8a25c1f9a2c70c05dc51631ce
5
5
  SHA512:
6
- metadata.gz: 481103355373158a86cbc6b7b98ee59c6f78bdbee0ca9c1bb67a990675c7389d743fc2ce9b596965688825e1259a8257e06d5b47c2b0edf2623dafd063197c50
7
- data.tar.gz: 5e94a8155fe3c6ce2a15bf3ac3a1e8c235d48d9d9d198e6fef07e89f43b4e10342387ab153d977efcc88743f87566f92a932555b29c5c790059b099baaa06141
6
+ metadata.gz: e6af8de42045313fee0582ee1ee58dbdcdc0d9e9cfa85d5af94c9ca87ae6358a2a5e019aa035e588cf9d8ea1c883b9635ffb09c55bb9bcf90414eda7a7d60de2
7
+ data.tar.gz: 13bcf5ba5f6e0d64254e04e523758fd85f5f050a316b497013769eefde91877d995b8361f18bde8270f8f4b04b037cc6f988455bbcc33148f89c564a7bded585
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.11] - 2026-04-28
4
+
5
+ ### Fixed
6
+ - Replace the retired `legion-llm` runtime dependency with `lex-llm`
7
+ - Define ledger-owned passive `llm.metering` and `llm.audit` exchange classes for transport bindings
8
+
9
+ ## [0.1.10] - 2026-04-27
10
+
11
+ ### Fixed
12
+ - Decode encrypted prompt and tool audit subscription payloads with either string or symbol `iv` headers before dispatch
13
+ - Preserve cleartext prompt and tool audit subscription payload handling when audit encryption is disabled
14
+ - Preserve AMQP headers and properties when prompt and tool subscription actors call their ledger runners
15
+
3
16
  ## [0.1.9] - 2026-04-09
4
17
 
5
18
  ### Fixed
data/Gemfile CHANGED
@@ -1,6 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  source 'https://rubygems.org'
4
+
5
+ group :test do
6
+ llm_base_path = ENV.fetch('LEX_LLM_PATH', File.expand_path('../lex-llm', __dir__))
7
+ gem 'lex-llm', path: llm_base_path if File.directory?(llm_base_path)
8
+ end
9
+
4
10
  gemspec
5
11
 
6
12
  gem 'rubocop-legion', '~> 0.1', require: false
@@ -30,10 +30,10 @@ Gem::Specification.new do |spec|
30
30
 
31
31
  spec.add_dependency 'legion-data', '>= 1.6'
32
32
  spec.add_dependency 'legion-json', '>= 1.2'
33
- spec.add_dependency 'legion-llm', '>= 0.6'
34
33
  spec.add_dependency 'legion-logging', '>= 1.3'
35
34
  spec.add_dependency 'legion-settings', '>= 1.3'
36
35
  spec.add_dependency 'legion-transport', '>= 1.4'
36
+ spec.add_dependency 'lex-llm', '>= 0.1.5'
37
37
 
38
38
  spec.add_development_dependency 'rspec'
39
39
  spec.add_development_dependency 'rubocop'
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/actors/subscription'
4
+ require_relative '../helpers/subscription_message'
4
5
 
5
6
  module Legion
6
7
  module Extensions
@@ -17,6 +18,10 @@ module Legion
17
18
  def use_runner?
18
19
  false
19
20
  end
21
+
22
+ def process_message(message, metadata, delivery_info)
23
+ Helpers::SubscriptionMessage.decode_payload(message, metadata, delivery_info)
24
+ end
20
25
  end
21
26
  end
22
27
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/actors/subscription'
4
+ require_relative '../helpers/subscription_message'
4
5
 
5
6
  module Legion
6
7
  module Extensions
@@ -17,6 +18,10 @@ module Legion
17
18
  def use_runner?
18
19
  false
19
20
  end
21
+
22
+ def process_message(message, metadata, delivery_info)
23
+ Helpers::SubscriptionMessage.decode_payload(message, metadata, delivery_info)
24
+ end
20
25
  end
21
26
  end
22
27
  end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'decryption'
4
+
5
+ module Legion
6
+ module Extensions
7
+ module Llm
8
+ module Ledger
9
+ module Helpers
10
+ module SubscriptionMessage
11
+ module_function
12
+
13
+ def decode_payload(message, metadata, delivery_info)
14
+ headers = metadata_headers(metadata)
15
+ properties = metadata_properties(metadata)
16
+ payload = decrypt_payload(message, headers, properties)
17
+ body = parse_payload(payload, properties)
18
+
19
+ {
20
+ payload: body,
21
+ metadata: {
22
+ headers: headers,
23
+ properties: properties.merge(routing_key: routing_key(delivery_info))
24
+ }
25
+ }
26
+ end
27
+
28
+ def metadata_headers(metadata)
29
+ headers = metadata.respond_to?(:headers) ? metadata.headers : nil
30
+ headers ||= {}
31
+ headers.each_with_object({}) do |(key, value), normalized|
32
+ normalized[key] = value
33
+ normalized[key.to_s] = value unless key.is_a?(String)
34
+ end
35
+ end
36
+
37
+ def metadata_properties(metadata)
38
+ {
39
+ content_encoding: metadata_value(metadata, :content_encoding),
40
+ content_type: metadata_value(metadata, :content_type),
41
+ message_id: metadata_value(metadata, :message_id),
42
+ correlation_id: metadata_value(metadata, :correlation_id),
43
+ app_id: metadata_value(metadata, :app_id),
44
+ timestamp: metadata_value(metadata, :timestamp)
45
+ }.compact
46
+ end
47
+
48
+ def decrypt_payload(message, headers, properties)
49
+ return message unless properties[:content_encoding] == 'encrypted/cs'
50
+
51
+ iv = headers['iv'] || headers[:iv]
52
+ raise DecryptionFailed, 'Encrypted audit record is missing iv header' if iv.nil?
53
+
54
+ Legion::Crypt.decrypt(message, iv)
55
+ end
56
+
57
+ def parse_payload(payload, properties)
58
+ return payload unless properties[:content_type] == 'application/json'
59
+
60
+ if json_load_keyword?(:symbolize_keys)
61
+ Legion::JSON.load(payload, symbolize_keys: true) # rubocop:disable Legion/HelperMigration/DirectJson
62
+ else
63
+ Legion::JSON.load(payload, symbolize_names: true) # rubocop:disable Legion/HelperMigration/DirectJson
64
+ end
65
+ end
66
+
67
+ def runner_args(payload, metadata, message)
68
+ message.key?(:payload) ? [message[:payload], message[:metadata] || {}] : [payload, metadata]
69
+ end
70
+
71
+ def routing_key(delivery_info)
72
+ return delivery_info[:routing_key] if delivery_info.respond_to?(:[])
73
+ return delivery_info.routing_key if delivery_info.respond_to?(:routing_key)
74
+
75
+ nil
76
+ end
77
+
78
+ def metadata_value(metadata, key)
79
+ return metadata.public_send(key) if metadata.respond_to?(key)
80
+ return metadata[key] if metadata.respond_to?(:[]) && metadata_key?(metadata, key)
81
+
82
+ nil
83
+ end
84
+
85
+ def metadata_key?(metadata, key)
86
+ return metadata.key?(key) if metadata.respond_to?(:key?)
87
+ return metadata.members.include?(key) if metadata.respond_to?(:members)
88
+
89
+ true
90
+ end
91
+
92
+ def symbolize(value)
93
+ case value
94
+ when Hash
95
+ value.to_h { |key, nested| [key.to_sym, symbolize(nested)] }
96
+ when Array
97
+ value.map { |nested| symbolize(nested) }
98
+ else
99
+ value
100
+ end
101
+ end
102
+
103
+ def json_load_keyword?(keyword)
104
+ Legion::JSON.method(:load).parameters.any? { |type, name| type == :key && name == keyword }
105
+ end
106
+
107
+ private_class_method :metadata_headers, :metadata_properties, :decrypt_payload, :parse_payload,
108
+ :routing_key, :metadata_value, :metadata_key?, :symbolize, :json_load_keyword?
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -8,7 +8,8 @@ module Legion
8
8
  module Prompts
9
9
  extend self
10
10
 
11
- def write_prompt_record(payload, metadata = {})
11
+ def write_prompt_record(payload = nil, metadata = {}, **message)
12
+ payload, metadata = normalize_runner_args(payload, metadata, message)
12
13
  headers = metadata[:headers] || {}
13
14
  props = metadata[:properties] || {}
14
15
 
@@ -34,6 +35,10 @@ module Legion
34
35
 
35
36
  private
36
37
 
38
+ def normalize_runner_args(payload, metadata, message)
39
+ Helpers::SubscriptionMessage.runner_args(payload, metadata, message)
40
+ end
41
+
37
42
  def build_prompt_record(body, ctx, props, headers, expires_at)
38
43
  routing = body[:routing] || {}
39
44
  tokens = body[:tokens] || {}
@@ -8,7 +8,8 @@ module Legion
8
8
  module Tools
9
9
  extend self
10
10
 
11
- def write_tool_record(payload, metadata = {})
11
+ def write_tool_record(payload = nil, metadata = {}, **message)
12
+ payload, metadata = normalize_runner_args(payload, metadata, message)
12
13
  headers = metadata[:headers] || {}
13
14
  props = metadata[:properties] || {}
14
15
 
@@ -35,6 +36,10 @@ module Legion
35
36
 
36
37
  private
37
38
 
39
+ def normalize_runner_args(payload, metadata, message)
40
+ Helpers::SubscriptionMessage.runner_args(payload, metadata, message)
41
+ end
42
+
38
43
  def build_tool_record(body, ctx, tool, props, headers, expires_at)
39
44
  src = tool[:source] || {}
40
45
  cls = body[:classification] || {}
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Llm
6
+ module Ledger
7
+ module Transport
8
+ module Exchanges
9
+ class Audit < ::Legion::Transport::Exchange
10
+ def exchange_name
11
+ 'llm.audit'
12
+ end
13
+
14
+ def default_type
15
+ 'topic'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Llm
6
+ module Ledger
7
+ module Transport
8
+ module Exchanges
9
+ class Metering < ::Legion::Transport::Exchange
10
+ def exchange_name
11
+ 'llm.metering'
12
+ end
13
+
14
+ def default_type
15
+ 'topic'
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/transport'
4
- require 'legion/llm/transport/exchanges/metering'
5
- require 'legion/llm/transport/exchanges/audit'
4
+ require_relative 'exchanges/metering'
5
+ require_relative 'exchanges/audit'
6
6
 
7
7
  module Legion
8
8
  module Extensions
@@ -14,17 +14,17 @@ module Legion
14
14
  def self.additional_e_to_q
15
15
  [
16
16
  {
17
- from: Legion::LLM::Transport::Exchanges::Metering,
17
+ from: Legion::Extensions::Llm::Ledger::Transport::Exchanges::Metering,
18
18
  to: Legion::Extensions::Llm::Ledger::Transport::Queues::MeteringWrite,
19
19
  routing_key: 'metering.#'
20
20
  },
21
21
  {
22
- from: Legion::LLM::Transport::Exchanges::Audit,
22
+ from: Legion::Extensions::Llm::Ledger::Transport::Exchanges::Audit,
23
23
  to: Legion::Extensions::Llm::Ledger::Transport::Queues::AuditPrompts,
24
24
  routing_key: 'audit.prompt.#'
25
25
  },
26
26
  {
27
- from: Legion::LLM::Transport::Exchanges::Audit,
27
+ from: Legion::Extensions::Llm::Ledger::Transport::Exchanges::Audit,
28
28
  to: Legion::Extensions::Llm::Ledger::Transport::Queues::AuditTools,
29
29
  routing_key: 'audit.tool.#'
30
30
  }
@@ -4,7 +4,7 @@ module Legion
4
4
  module Extensions
5
5
  module Llm
6
6
  module Ledger
7
- VERSION = '0.1.9'
7
+ VERSION = '0.1.11'
8
8
  end
9
9
  end
10
10
  end
@@ -4,6 +4,7 @@ require_relative 'ledger/version'
4
4
  require_relative 'ledger/helpers/decryption'
5
5
  require_relative 'ledger/helpers/retention'
6
6
  require_relative 'ledger/helpers/queries'
7
+ require_relative 'ledger/helpers/subscription_message'
7
8
  require_relative 'ledger/runners/metering'
8
9
  require_relative 'ledger/runners/prompts'
9
10
  require_relative 'ledger/runners/tools'
@@ -11,12 +12,8 @@ require_relative 'ledger/runners/usage_reporter'
11
12
  require_relative 'ledger/runners/provider_stats'
12
13
 
13
14
  if Legion::Extensions.const_defined?(:Core, false)
14
- begin
15
- require 'legion/llm/transport/exchanges/metering'
16
- require 'legion/llm/transport/exchanges/audit'
17
- rescue LoadError => _e
18
- nil
19
- end
15
+ require_relative 'ledger/transport/exchanges/metering'
16
+ require_relative 'ledger/transport/exchanges/audit'
20
17
  require_relative 'ledger/transport/queues/metering_write'
21
18
  require_relative 'ledger/transport/queues/audit_prompts'
22
19
  require_relative 'ledger/transport/queues/audit_tools'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-llm-ledger
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.9
4
+ version: 0.1.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -38,21 +38,21 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.2'
40
40
  - !ruby/object:Gem::Dependency
41
- name: legion-llm
41
+ name: legion-logging
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - ">="
45
45
  - !ruby/object:Gem::Version
46
- version: '0.6'
46
+ version: '1.3'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - ">="
52
52
  - !ruby/object:Gem::Version
53
- version: '0.6'
53
+ version: '1.3'
54
54
  - !ruby/object:Gem::Dependency
55
- name: legion-logging
55
+ name: legion-settings
56
56
  requirement: !ruby/object:Gem::Requirement
57
57
  requirements:
58
58
  - - ">="
@@ -66,33 +66,33 @@ dependencies:
66
66
  - !ruby/object:Gem::Version
67
67
  version: '1.3'
68
68
  - !ruby/object:Gem::Dependency
69
- name: legion-settings
69
+ name: legion-transport
70
70
  requirement: !ruby/object:Gem::Requirement
71
71
  requirements:
72
72
  - - ">="
73
73
  - !ruby/object:Gem::Version
74
- version: '1.3'
74
+ version: '1.4'
75
75
  type: :runtime
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: '1.3'
81
+ version: '1.4'
82
82
  - !ruby/object:Gem::Dependency
83
- name: legion-transport
83
+ name: lex-llm
84
84
  requirement: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - ">="
87
87
  - !ruby/object:Gem::Version
88
- version: '1.4'
88
+ version: 0.1.5
89
89
  type: :runtime
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - ">="
94
94
  - !ruby/object:Gem::Version
95
- version: '1.4'
95
+ version: 0.1.5
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: rspec
98
98
  requirement: !ruby/object:Gem::Requirement
@@ -198,6 +198,7 @@ files:
198
198
  - lib/legion/extensions/llm/ledger/helpers/decryption.rb
199
199
  - lib/legion/extensions/llm/ledger/helpers/queries.rb
200
200
  - lib/legion/extensions/llm/ledger/helpers/retention.rb
201
+ - lib/legion/extensions/llm/ledger/helpers/subscription_message.rb
201
202
  - lib/legion/extensions/llm/ledger/migrations/001_create_metering_records.rb
202
203
  - lib/legion/extensions/llm/ledger/migrations/002_create_prompt_records.rb
203
204
  - lib/legion/extensions/llm/ledger/migrations/003_create_tool_records.rb
@@ -206,6 +207,8 @@ files:
206
207
  - lib/legion/extensions/llm/ledger/runners/provider_stats.rb
207
208
  - lib/legion/extensions/llm/ledger/runners/tools.rb
208
209
  - lib/legion/extensions/llm/ledger/runners/usage_reporter.rb
210
+ - lib/legion/extensions/llm/ledger/transport/exchanges/audit.rb
211
+ - lib/legion/extensions/llm/ledger/transport/exchanges/metering.rb
209
212
  - lib/legion/extensions/llm/ledger/transport/queues/audit_prompts.rb
210
213
  - lib/legion/extensions/llm/ledger/transport/queues/audit_tools.rb
211
214
  - lib/legion/extensions/llm/ledger/transport/queues/metering_write.rb