legion-apollo 0.3.2 → 0.3.3

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: 1c8e724eefe292faefec05ca4fc589ed935f4734bc5c4726b7c5d70ed3b69dbb
4
- data.tar.gz: 3fc0246858d50305bd2d956e87c9ec844c71a769c4f81955f43d2640159a175c
3
+ metadata.gz: 56d4a2af4184bafd3474940f42a2b4e918490470b8faa3e5334f96bf27baff02
4
+ data.tar.gz: 6b4e4b7fcf9b93331de7e32214bee5415b0d6a47b20d4624894247c8350cc62c
5
5
  SHA512:
6
- metadata.gz: eb2815139791a2ed4abd5f09f6ed2b5cf27b89c399ed00276fc58e8ebfaceede00eb1b0cc02f43371ce5099d12467398e7ce92e58352cc854bb4b5eecc4a2c1c
7
- data.tar.gz: 763ff525cdf09284bc3a7c3337407bfcaa8a750f5dddaeb66e1a1b082742a9e158b813a39a0d606c4c1740ba26137c6b565c6152688bd6f007b601ed98dd6fd1
6
+ metadata.gz: 05a8fc891f57c4e9d71619c993c343753cc7674984847224957aa7f5621faa240b9d9017bdf9bd277a17ed1ef39d626229ac9058e66da55d7a477a47aa8b5c51
7
+ data.tar.gz: d80855fc0f2026c2035fd2b411a2d6daaa3684125455fa46ab161dbae0e9831d00437be0faa171718cbf811d5834a1ddd138e491891c96ad87d428ae4ce2f15d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.3] - 2026-03-28
4
+
5
+ ### Added
6
+ - `Legion::Apollo::Routes` Sinatra extension module (`lib/legion/apollo/routes.rb`): all `/api/apollo/*` route definitions extracted from `LegionIO/lib/legion/api/apollo.rb`. Self-registers with `Legion::API.register_library_routes('apollo', Legion::Apollo::Routes)` during `Legion::Apollo.start`, immediately after `@started` is set (before `Local.start` / `seed_self_knowledge`).
7
+ - `register_routes` private method on `Legion::Apollo` module.
8
+
9
+ ### Changed
10
+ - `Legion::Apollo.start` now calls `register_routes` after setting `@started = true`.
11
+
3
12
  ## [0.3.2] - 2026-03-26
4
13
 
5
14
  ### Added
@@ -0,0 +1,242 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helpers/tag_normalizer'
4
+
5
+ # Self-registering route module for legion-apollo.
6
+ # All routes previously defined in LegionIO/lib/legion/api/apollo.rb now live here
7
+ # and are mounted via Legion::API.register_library_routes when legion-apollo boots.
8
+ #
9
+ # LegionIO/lib/legion/api/apollo.rb is preserved for backward compatibility but guards
10
+ # its registration with defined?(Legion::Apollo::Routes) so double-registration is avoided.
11
+
12
+ module Legion
13
+ module Apollo
14
+ # Sinatra route module for Apollo API endpoints. Self-registers at boot.
15
+ module Routes # rubocop:disable Metrics/ModuleLength
16
+ def self.registered(app)
17
+ app.helpers ApolloHelpers
18
+ register_status_route(app)
19
+ register_stats_route(app)
20
+ register_query_route(app)
21
+ register_ingest_route(app)
22
+ register_related_route(app)
23
+ register_maintenance_route(app)
24
+ register_graph_route(app)
25
+ register_expertise_route(app)
26
+ end
27
+
28
+ def self.register_status_route(app)
29
+ app.get '/api/apollo/status' do
30
+ available = apollo_runner_available?
31
+ data_connected = apollo_data_connected?
32
+ status_code = available && data_connected ? 200 : 503
33
+
34
+ json_response({ available: available, data_connected: data_connected },
35
+ status_code: status_code)
36
+ end
37
+ end
38
+
39
+ def self.register_stats_route(app)
40
+ app.get '/api/apollo/stats' do
41
+ halt 503, json_error('apollo_unavailable', 'apollo is not available', status_code: 503) unless apollo_loaded?
42
+
43
+ stats = apollo_stats
44
+ if stats[:error]
45
+ halt 503, json_error('apollo_stats_unavailable', stats[:error], status_code: 503)
46
+ else
47
+ json_response(stats)
48
+ end
49
+ end
50
+ end
51
+
52
+ def self.register_query_route(app) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
53
+ app.post '/api/apollo/query' do
54
+ halt 503, json_error('apollo_unavailable', 'apollo is not available', status_code: 503) unless apollo_loaded?
55
+
56
+ body = parse_request_body
57
+ default_limit = defined?(Legion::Settings) ? (Legion::Settings[:apollo]&.dig(:default_limit) || 5) : 5
58
+ result = apollo_runner.handle_query(
59
+ query: body[:query],
60
+ limit: body[:limit] || default_limit,
61
+ min_confidence: body[:min_confidence] || 0.3,
62
+ status: body[:status] || [:confirmed],
63
+ tags: body[:tags],
64
+ domain: body[:domain],
65
+ agent_id: body[:agent_id] || 'api'
66
+ )
67
+ json_response(result)
68
+ end
69
+ end
70
+
71
+ def self.register_ingest_route(app) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
72
+ app.post '/api/apollo/ingest' do
73
+ halt 503, json_error('apollo_unavailable', 'apollo is not available', status_code: 503) unless apollo_loaded?
74
+
75
+ body = parse_request_body
76
+ max_tags = defined?(Legion::Settings) ? (Legion::Settings[:apollo]&.dig(:max_tags) || 20) : 20
77
+ # TagNormalizer hard-caps to MAX_TAGS=20 internally; clamp here to make that limit explicit.
78
+ effective_max_tags = [max_tags, Legion::Apollo::Helpers::TagNormalizer::MAX_TAGS].min
79
+ tags = Legion::Apollo::Helpers::TagNormalizer.normalize(Array(body[:tags])).first(effective_max_tags)
80
+ result = apollo_runner.handle_ingest(
81
+ content: body[:content],
82
+ content_type: body[:content_type] || :observation,
83
+ tags: tags,
84
+ source_agent: body[:source_agent] || 'api',
85
+ source_provider: body[:source_provider],
86
+ source_channel: body[:source_channel] || 'rest_api',
87
+ knowledge_domain: body[:knowledge_domain],
88
+ context: body[:context] || {}
89
+ )
90
+ json_response(result, status_code: 201)
91
+ end
92
+ end
93
+
94
+ def self.register_related_route(app)
95
+ app.get '/api/apollo/entries/:id/related' do
96
+ halt 503, json_error('apollo_unavailable', 'apollo is not available', status_code: 503) unless apollo_loaded?
97
+
98
+ result = apollo_runner.related_entries(
99
+ entry_id: params[:id].to_i,
100
+ relation_types: params[:relation_types]&.split(','),
101
+ depth: (params[:depth] || 2).to_i
102
+ )
103
+ json_response(result)
104
+ end
105
+ end
106
+
107
+ def self.register_maintenance_route(app) # rubocop:disable Metrics/MethodLength
108
+ app.post '/api/apollo/maintenance' do
109
+ halt 503, json_error('apollo_unavailable', 'apollo is not available', status_code: 503) unless apollo_loaded?
110
+
111
+ body = parse_request_body
112
+ action_str = body[:action]
113
+ unless %w[
114
+ decay_cycle corroboration
115
+ ].include?(action_str)
116
+ halt 400,
117
+ json_error('invalid_action', 'action must be decay_cycle or corroboration', status_code: 400)
118
+ end
119
+
120
+ action = action_str.to_sym
121
+
122
+ result = run_maintenance(action)
123
+ json_response(result)
124
+ end
125
+ end
126
+
127
+ def self.register_graph_route(app)
128
+ app.get '/api/apollo/graph' do
129
+ halt 503, json_error('apollo_unavailable', 'apollo is not available', status_code: 503) unless apollo_loaded?
130
+
131
+ json_response(apollo_graph_topology)
132
+ end
133
+ end
134
+
135
+ def self.register_expertise_route(app)
136
+ app.get '/api/apollo/expertise' do
137
+ halt 503, json_error('apollo_unavailable', 'apollo is not available', status_code: 503) unless apollo_loaded?
138
+
139
+ json_response(apollo_expertise_map)
140
+ end
141
+ end
142
+
143
+ class << self
144
+ private :register_status_route, :register_stats_route, :register_query_route,
145
+ :register_ingest_route, :register_related_route, :register_maintenance_route,
146
+ :register_graph_route, :register_expertise_route
147
+ end
148
+
149
+ # Helper methods mixed into the Sinatra app context
150
+ module ApolloHelpers
151
+ def apollo_runner_available?
152
+ return false unless defined?(Legion::Extensions::Apollo::Runners::Knowledge)
153
+
154
+ required = %i[handle_query handle_ingest related_entries]
155
+ required.all? { |m| Legion::Extensions::Apollo::Runners::Knowledge.respond_to?(m) }
156
+ rescue StandardError => e
157
+ if defined?(Legion::Logging)
158
+ Legion::Logging.debug("Apollo#apollo_runner_available? check failed: #{e.message}")
159
+ end
160
+ false
161
+ end
162
+
163
+ def apollo_loaded?
164
+ apollo_runner_available? && apollo_data_connected?
165
+ end
166
+
167
+ def apollo_data_connected?
168
+ defined?(Legion::Data) && Legion::Data.respond_to?(:connection) && !Legion::Data.connection.nil?
169
+ rescue StandardError => e
170
+ Legion::Logging.debug("Apollo#apollo_data_connected? check failed: #{e.message}") if defined?(Legion::Logging)
171
+ false
172
+ end
173
+
174
+ def apollo_runner
175
+ Legion::Extensions::Apollo::Runners::Knowledge
176
+ end
177
+
178
+ def apollo_maintenance_runner # rubocop:disable Metrics/MethodLength
179
+ @apollo_maintenance_runner ||= begin
180
+ unless defined?(Legion::Extensions::Apollo::Runners::Maintenance)
181
+ halt 503, json_error('maintenance_unavailable', 'Apollo maintenance runner is not loaded')
182
+ end
183
+
184
+ runner = Object.new.extend(Legion::Extensions::Apollo::Runners::Maintenance)
185
+ required = %i[run_decay_cycle check_corroboration]
186
+ unless required.all? { |m| runner.respond_to?(m) }
187
+ halt 503, json_error('maintenance_unavailable', 'Apollo maintenance runner is missing required actions')
188
+ end
189
+
190
+ runner
191
+ end
192
+ end
193
+
194
+ def run_maintenance(action)
195
+ case action
196
+ when :decay_cycle
197
+ apollo_maintenance_runner.run_decay_cycle
198
+ when :corroboration
199
+ apollo_maintenance_runner.check_corroboration
200
+ end
201
+ end
202
+
203
+ def apollo_graph_topology
204
+ return { error: 'Apollo runner unavailable' } unless apollo_runner_available?
205
+ unless apollo_runner.respond_to?(:graph_topology)
206
+ return { error: 'Apollo graph_topology not supported by runner' }
207
+ end
208
+
209
+ apollo_runner.graph_topology
210
+ rescue StandardError => e
211
+ Legion::Logging.debug("Apollo#apollo_graph_topology failed: #{e.message}") if defined?(Legion::Logging)
212
+ { error: 'apollo_graph_topology unavailable' }
213
+ end
214
+
215
+ def apollo_expertise_map
216
+ return { error: 'Apollo runner unavailable' } unless apollo_runner_available?
217
+ unless apollo_runner.respond_to?(:expertise_map)
218
+ return { error: 'Apollo expertise_map not supported by runner' }
219
+ end
220
+
221
+ apollo_runner.expertise_map
222
+ rescue StandardError => e
223
+ Legion::Logging.debug("Apollo#apollo_expertise_map failed: #{e.message}") if defined?(Legion::Logging)
224
+ { error: 'apollo_expertise_map unavailable' }
225
+ end
226
+
227
+ def apollo_stats
228
+ return { total_entries: 0, error: 'Apollo runner unavailable' } unless apollo_runner_available?
229
+ unless apollo_runner.respond_to?(:stats)
230
+ return { total_entries: 0,
231
+ error: 'Apollo stats not supported by runner' }
232
+ end
233
+
234
+ apollo_runner.stats
235
+ rescue StandardError => e
236
+ Legion::Logging.debug("Apollo#apollo_stats failed: #{e.message}") if defined?(Legion::Logging)
237
+ { total_entries: 0, error: 'apollo_stats unavailable' }
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Apollo
5
- VERSION = '0.3.2'
5
+ VERSION = '0.3.3'
6
6
  end
7
7
  end
data/lib/legion/apollo.rb CHANGED
@@ -5,6 +5,7 @@ require_relative 'apollo/version'
5
5
  require_relative 'apollo/settings'
6
6
  require_relative 'apollo/local'
7
7
  require_relative 'apollo/runners'
8
+ require_relative 'apollo/routes'
8
9
 
9
10
  module Legion
10
11
  # Apollo client library — query, ingest, and retrieve with smart routing.
@@ -23,6 +24,7 @@ module Legion
23
24
  @started = true
24
25
  Legion::Logging.info 'Legion::Apollo started' if defined?(Legion::Logging)
25
26
 
27
+ register_routes
26
28
  Legion::Apollo::Local.start
27
29
  seed_self_knowledge
28
30
  end
@@ -307,6 +309,15 @@ module Legion
307
309
  def not_started_error
308
310
  { success: false, error: :not_started }
309
311
  end
312
+
313
+ def register_routes
314
+ return unless defined?(Legion::API) && Legion::API.respond_to?(:register_library_routes)
315
+
316
+ Legion::API.register_library_routes('apollo', Legion::Apollo::Routes)
317
+ Legion::Logging.debug 'Legion::Apollo routes registered with API' if defined?(Legion::Logging)
318
+ rescue StandardError => e
319
+ Legion::Logging.warn "Legion::Apollo route registration failed: #{e.message}" if defined?(Legion::Logging)
320
+ end
310
321
  end
311
322
  end
312
323
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-apollo
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.2
4
+ version: 0.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -86,6 +86,7 @@ files:
86
86
  - lib/legion/apollo/messages/ingest.rb
87
87
  - lib/legion/apollo/messages/query.rb
88
88
  - lib/legion/apollo/messages/writeback.rb
89
+ - lib/legion/apollo/routes.rb
89
90
  - lib/legion/apollo/runners.rb
90
91
  - lib/legion/apollo/runners/request.rb
91
92
  - lib/legion/apollo/settings.rb