lex-metering 0.1.2 → 0.1.4

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: 6e70bf639989419388bb625a4f105d7ad6376b534b35f839d8623427bfc3fe43
4
- data.tar.gz: b6c1250c1e19d2d80ee88772416aa4b3f05f4123b774698b6b4ae6ef5b6f02a4
3
+ metadata.gz: db002082af15a2d97671a7b9ed75897f21ec03d16e360962517fc848527c9ec6
4
+ data.tar.gz: cb642644b254256b1fef4a17933f9276a3a972ebee52a37c80e4bf88786e1b57
5
5
  SHA512:
6
- metadata.gz: d65a2b2829f638b80ce726b19c616f81a4dc4c436482bea8bee837d489053070b929f59fe240e09dff4478be52cdce3dee43aa127cc21dea0b49833b920ec246
7
- data.tar.gz: 614785e8c7af6964c0312c5f18ff362c0fd1f7e29656e6db76cd94c2029774108050671363adb8b17012f334ce71e86750f6547eae298490875fde11637c092c
6
+ metadata.gz: 7e107e84a2d199254a12daad43880e9f3b593b3076b0e42964a7fe0ef07410c4a75923761da4128cb0942c4b45db1b791b4923e4e2263484c3b61621d54cf979
7
+ data.tar.gz: 60c7c096ea83b68c01fef84244449764ac3ff357465a9aa2a5e74f71f72b94f838ac2bd8805dd969c9cbbe613bdf3a04145e62a47c8d6923134b96399828a767
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.1.4] - 2026-03-18
4
+
5
+ ### Changed
6
+ - `record` no longer writes directly to database; returns hash only for RabbitMQ publishing
7
+ - `data_required?` changed to `false` — extension loads without legion-data
8
+
9
+ ## [0.1.3] - 2026-03-18
10
+
11
+ ### Fixed
12
+ - `worker_costs` period filtering now uses cross-DB `Sequel.lit('recorded_at >= ?', cutoff)` with Ruby time arithmetic instead of PostgreSQL-only `CURRENT_TIMESTAMP - INTERVAL` syntax
13
+
3
14
  ## [0.1.2] - 2026-03-17
4
15
 
5
16
  ### Changed
data/CLAUDE.md CHANGED
@@ -11,7 +11,7 @@ Captures LLM token usage metrics per task for cost attribution and intelligent r
11
11
  ## Gem Info
12
12
 
13
13
  - **Gem name**: `lex-metering`
14
- - **Version**: `0.1.0`
14
+ - **Version**: `0.1.4`
15
15
  - **Module**: `Legion::Extensions::Metering`
16
16
  - **Ruby**: `>= 3.4`
17
17
  - **License**: MIT
@@ -62,14 +62,14 @@ lib/legion/extensions/metering/
62
62
 
63
63
  ## Integration Points
64
64
 
65
- - **legion-data**: `data_required? true` — will not load if DB unavailable. Accesses `metering_records` as a raw Sequel dataset (no Sequel::Model subclass).
65
+ - **legion-data**: `data_required? false` — loads without DB. `record` returns hash only (for RabbitMQ publishing). Query methods (`worker_costs`, `team_costs`, `routing_stats`, `cleanup_old_records`) still access `metering_records` as a raw Sequel dataset when `Legion::Data` is available.
66
66
  - **LegionIO MCP**: `legion.routing_stats` MCP tool calls `routing_stats` runner
67
67
  - **REST API**: `GET /api/tasks/:id` includes a `:metering` block when lex-metering data exists for the task
68
68
  - **Digital Workers**: `legion worker costs` CLI command delegates to `worker_costs` runner
69
69
 
70
70
  ## Development Notes
71
71
 
72
- - Extension has `data_required? true` (both at module level and instance level) will skip loading if `legion-data` is not connected
72
+ - Extension has `data_required? false` loads without `legion-data`; `record` builds hash only (no DB insert), query methods still require `Legion::Data`
73
73
  - No explicit actors — gets auto-generated subscription actors from the framework
74
74
  - `routing_stats` uses `select_append { avg(latency_ms).as(avg_latency) }` — Sequel virtual row syntax
75
- - Time interval filtering uses `Sequel.lit("CURRENT_TIMESTAMP - INTERVAL '...'")` which is PostgreSQL syntax; SQLite uses different interval syntax (known limitation)
75
+ - Time interval filtering uses `Sequel.lit('recorded_at >= ?', cutoff)` with Ruby `Time` arithmetic for cross-database compatibility (PostgreSQL, SQLite, MySQL)
@@ -5,6 +5,8 @@ module Legion
5
5
  module Metering
6
6
  module Runners
7
7
  module Metering
8
+ PERIOD_DAYS = { 'daily' => 1, 'weekly' => 7, 'monthly' => 30 }.freeze
9
+
8
10
  def record(worker_id: nil, task_id: nil, provider: nil, model_id: nil,
9
11
  input_tokens: 0, output_tokens: 0, thinking_tokens: 0,
10
12
  input_context_bytes: 0, latency_ms: 0, routing_reason: nil,
@@ -27,7 +29,6 @@ module Legion
27
29
  recorded_at: Time.now.utc
28
30
  }
29
31
 
30
- Legion::Data.connection[:metering_records].insert(record) if defined?(Legion::Data) && Legion::Data.connection
31
32
  Legion::Logging.debug "[metering] recorded: provider=#{provider} model=#{model_id} " \
32
33
  "tokens=#{record[:total_tokens]} latency=#{latency_ms}ms wall_clock=#{wall_clock_ms}ms"
33
34
  record
@@ -35,15 +36,7 @@ module Legion
35
36
 
36
37
  def worker_costs(worker_id:, period: 'daily', **)
37
38
  ds = Legion::Data.connection[:metering_records].where(worker_id: worker_id)
38
-
39
- case period
40
- when 'daily'
41
- ds = ds.where { recorded_at >= Sequel.lit("CURRENT_TIMESTAMP - INTERVAL '1 day'") }
42
- when 'weekly'
43
- ds = ds.where { recorded_at >= Sequel.lit("CURRENT_TIMESTAMP - INTERVAL '7 days'") }
44
- when 'monthly'
45
- ds = ds.where { recorded_at >= Sequel.lit("CURRENT_TIMESTAMP - INTERVAL '30 days'") }
46
- end
39
+ ds = apply_period_filter(ds, period)
47
40
 
48
41
  {
49
42
  worker_id: worker_id,
@@ -93,6 +86,16 @@ module Legion
93
86
  Legion::Logging.info "[metering] cleanup: purged=#{count} retention_days=#{retention_days} cutoff=#{cutoff}"
94
87
  { purged: count, retention_days: retention_days, cutoff: cutoff }
95
88
  end
89
+
90
+ private
91
+
92
+ def apply_period_filter(dataset, period)
93
+ days = PERIOD_DAYS[period]
94
+ return dataset unless days
95
+
96
+ cutoff = Time.now.utc - (days * 86_400)
97
+ dataset.where(::Sequel.lit('recorded_at >= ?', cutoff))
98
+ end
96
99
  end
97
100
  end
98
101
  end
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Metering
6
- VERSION = '0.1.2'
6
+ VERSION = '0.1.4'
7
7
  end
8
8
  end
9
9
  end
@@ -8,11 +8,11 @@ module Legion
8
8
  extend Legion::Extensions::Core if Legion::Extensions.const_defined? :Core
9
9
 
10
10
  def self.data_required?
11
- true
11
+ false
12
12
  end
13
13
 
14
14
  def data_required?
15
- true
15
+ false
16
16
  end
17
17
  end
18
18
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-metering
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity