lex-apollo 0.4.21 → 0.4.22

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: 31dc882a8d5fd761bceaba21ff853a3d3dd898df9d4377464b72b110d306af0a
4
- data.tar.gz: 8dcfd2bc7c2557b298f3d503cc94b07e5a78b5a9777789f724dede41b9d0efdb
3
+ metadata.gz: 9a3e0ff5897e31c3f8e91f5b71eb96fd9ee90a15a041a5034ae936e2188806ae
4
+ data.tar.gz: c3aba4e4e156c940298dde9c54da46b77e87ffa05db60b7f4b3877307fa20c1c
5
5
  SHA512:
6
- metadata.gz: 5e066b3c62555b4866bfe99d6bed13a034e2b6b23f613ae7906e8eaebee90ecb2f9dd937a5e928dd8337af8945bfba9a1528deb4d3200eb82b8a92b081b6cf59
7
- data.tar.gz: dd1d625f4efc30df8951ee80db9ee0cfc1c25137bae5d8571dd09265bd5eec79add3482eba3cc88d77f43b052994597577abe634080cd995eb4dc0db7601458e
6
+ metadata.gz: e8d3000ec6c34b23c5f49eea11b2a03d8ef117d395f9134782cf1c2fe91650b569b036571aaafd023d36d3fbd74725d492e44b98ed701348717b3221a7e45986
7
+ data.tar.gz: 811fd837e8370dde5d998daadad819f648da2f4b1aae3258c82573efb773a4901c6aff8b6cbcbe60eb9f964cee9146b81288d923429321cc7e70e046cd14371b
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.22] - 2026-04-28
4
+
5
+ ### Fixed
6
+ - `/api/apollo/stats` now returns the health UI metrics Interlink expects: `recent_24h`, `avg_confidence`, and a synthesized `by_status["active"]` count for non-archived entries. This keeps the Knowledge Health cards populated instead of rendering missing values. Fixes #16.
7
+
3
8
  ## [0.4.21] - 2026-04-27
4
9
 
5
10
  ### Changed
@@ -9,6 +9,37 @@ module Legion
9
9
  class Api < Sinatra::Base
10
10
  set :host_authorization, permitted: :any
11
11
 
12
+ class << self
13
+ def stats_payload(now: Time.now)
14
+ return { error: 'apollo_data_not_available' } unless defined?(Legion::Data::Model::ApolloEntry)
15
+
16
+ entries = Legion::Data::Model::ApolloEntry
17
+ by_status = grouped_counts(entries, :status)
18
+ by_status['active'] = entries.exclude(status: 'archived').count
19
+
20
+ stats = {
21
+ total_entries: entries.count,
22
+ recent_24h: entries.where { created_at >= (now - 86_400) }.count,
23
+ avg_confidence: average_confidence(entries),
24
+ by_status: by_status,
25
+ by_content_type: grouped_counts(entries, :content_type)
26
+ }
27
+ stats[:total_relations] = Legion::Data::Model::ApolloRelation.count if defined?(Legion::Data::Model::ApolloRelation)
28
+ stats
29
+ end
30
+
31
+ private
32
+
33
+ def grouped_counts(entries, column)
34
+ entries.group_and_count(column).all.to_h { |row| [row[column].to_s, row[:count]] }
35
+ end
36
+
37
+ def average_confidence(entries)
38
+ avg = entries.avg(:confidence)
39
+ avg&.to_f&.round(3)
40
+ end
41
+ end
42
+
12
43
  before do
13
44
  content_type :json
14
45
  end
@@ -140,18 +171,7 @@ module Legion
140
171
 
141
172
  # Statistics
142
173
  get '/api/apollo/stats' do
143
- stats = {}
144
- if defined?(Legion::Data::Model::ApolloEntry)
145
- stats[:total_entries] = Legion::Data::Model::ApolloEntry.count
146
- stats[:by_status] = Legion::Data::Model::ApolloEntry.group_and_count(:status).all
147
- .to_h { |r| [r[:status], r[:count]] }
148
- stats[:by_content_type] = Legion::Data::Model::ApolloEntry.group_and_count(:content_type).all
149
- .to_h { |r| [r[:content_type], r[:count]] }
150
- stats[:total_relations] = Legion::Data::Model::ApolloRelation.count if defined?(Legion::Data::Model::ApolloRelation)
151
- else
152
- stats[:error] = 'apollo_data_not_available'
153
- end
154
- stats.to_json
174
+ self.class.stats_payload.to_json
155
175
  end
156
176
  end
157
177
  end
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Apollo
6
- VERSION = '0.4.21'
6
+ VERSION = '0.4.22'
7
7
  end
8
8
  end
9
9
  end
@@ -25,4 +25,68 @@ RSpec.describe Legion::Extensions::Apollo::Api do
25
25
  it 'is defined as a Sinatra app' do
26
26
  expect(described_class.superclass).to eq(Sinatra::Base)
27
27
  end
28
+
29
+ describe '.stats_payload' do
30
+ let(:entry_model) { class_double('Legion::Data::Model::ApolloEntry') }
31
+ let(:relation_model) { class_double('Legion::Data::Model::ApolloRelation', count: 4) }
32
+ let(:status_counts) do
33
+ instance_double(
34
+ 'StatusCounts',
35
+ all: [
36
+ { status: 'candidate', count: 2 },
37
+ { status: 'confirmed', count: 3 },
38
+ { status: 'archived', count: 1 }
39
+ ]
40
+ )
41
+ end
42
+ let(:content_type_counts) do
43
+ instance_double(
44
+ 'ContentTypeCounts',
45
+ all: [
46
+ { content_type: 'document_chunk', count: 5 },
47
+ { content_type: 'observation', count: 1 }
48
+ ]
49
+ )
50
+ end
51
+ let(:active_entries) { instance_double('ActiveEntries', count: 5) }
52
+ let(:recent_entries) { instance_double('RecentEntries', count: 2) }
53
+
54
+ before do
55
+ stub_const('Legion::Data::Model::ApolloEntry', entry_model)
56
+ stub_const('Legion::Data::Model::ApolloRelation', relation_model)
57
+ allow(entry_model).to receive(:count).and_return(6)
58
+ allow(entry_model).to receive(:avg).with(:confidence).and_return(0.81234)
59
+ allow(entry_model).to receive(:exclude).with(status: 'archived').and_return(active_entries)
60
+ allow(entry_model).to receive(:where).and_return(recent_entries)
61
+ allow(entry_model).to receive(:group_and_count).with(:status).and_return(status_counts)
62
+ allow(entry_model).to receive(:group_and_count).with(:content_type).and_return(content_type_counts)
63
+ end
64
+
65
+ it 'returns the health UI metrics expected by Interlink' do
66
+ payload = described_class.stats_payload(now: Time.utc(2026, 4, 28, 12, 0, 0))
67
+
68
+ expect(payload).to include(
69
+ total_entries: 6,
70
+ recent_24h: 2,
71
+ avg_confidence: 0.812,
72
+ total_relations: 4
73
+ )
74
+ expect(payload[:by_status]).to include(
75
+ 'candidate' => 2,
76
+ 'confirmed' => 3,
77
+ 'archived' => 1,
78
+ 'active' => 5
79
+ )
80
+ expect(payload[:by_content_type]).to eq(
81
+ 'document_chunk' => 5,
82
+ 'observation' => 1
83
+ )
84
+ end
85
+
86
+ it 'returns an apollo data error when the entry model is unavailable' do
87
+ hide_const('Legion::Data::Model::ApolloEntry')
88
+
89
+ expect(described_class.stats_payload).to eq(error: 'apollo_data_not_available')
90
+ end
91
+ end
28
92
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lex-apollo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.21
4
+ version: 0.4.22
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity