lex-llm-ledger 0.1.9 → 0.1.10

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: a595a76ee91def4ec66dc3ec9b1daa0c40f872704a73659981b8eecebf5bd48e
4
+ data.tar.gz: 2f1c5ff38e83093df3c88e39a9a6c376f13f9af97fa000126a2d24c51324e3f3
5
5
  SHA512:
6
- metadata.gz: 481103355373158a86cbc6b7b98ee59c6f78bdbee0ca9c1bb67a990675c7389d743fc2ce9b596965688825e1259a8257e06d5b47c2b0edf2623dafd063197c50
7
- data.tar.gz: 5e94a8155fe3c6ce2a15bf3ac3a1e8c235d48d9d9d198e6fef07e89f43b4e10342387ab153d977efcc88743f87566f92a932555b29c5c790059b099baaa06141
6
+ metadata.gz: dd1e3ae7439152285ee97d681d72bf98131504ab81e0107bd1a4c3f254c6dc2b4738a8562725bfc8e16b2a3c5d2fe44eb94e8df418b422f9fb48d1f746a8d026
7
+ data.tar.gz: aebc36c406ece5b8c4c51e315eccd771dcca1d70589e776c2a64d5e24d7dd4c59ada66139760b5d8874fa6cbbb6ace87cb846cc8a22092daee77afab0272d6d9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.10] - 2026-04-27
4
+
5
+ ### Fixed
6
+ - Decode encrypted prompt and tool audit subscription payloads with either string or symbol `iv` headers before dispatch
7
+ - Preserve cleartext prompt and tool audit subscription payload handling when audit encryption is disabled
8
+ - Preserve AMQP headers and properties when prompt and tool subscription actors call their ledger runners
9
+
3
10
  ## [0.1.9] - 2026-04-09
4
11
 
5
12
  ### Fixed
@@ -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] || {}
@@ -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.10'
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'
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.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -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