lex-apollo 0.4.28 → 0.4.44
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 +4 -4
- data/CHANGELOG.md +107 -0
- data/README.md +42 -5
- data/lib/legion/extensions/apollo/actors/contradiction_scanner.rb +63 -0
- data/lib/legion/extensions/apollo/actors/entity_watchdog.rb +1 -1
- data/lib/legion/extensions/apollo/actors/gas_subscriber.rb +4 -6
- data/lib/legion/extensions/apollo/api.rb +19 -9
- data/lib/legion/extensions/apollo/helpers/capability.rb +8 -20
- data/lib/legion/extensions/apollo/helpers/confidence.rb +11 -18
- data/lib/legion/extensions/apollo/helpers/entity_watchdog.rb +1 -1
- data/lib/legion/extensions/apollo/helpers/graph_query.rb +37 -51
- data/lib/legion/extensions/apollo/helpers/writeback.rb +9 -13
- data/lib/legion/extensions/apollo/runners/entity_extractor.rb +1 -1
- data/lib/legion/extensions/apollo/runners/expertise.rb +3 -3
- data/lib/legion/extensions/apollo/runners/gas.rb +95 -25
- data/lib/legion/extensions/apollo/runners/knowledge.rb +177 -113
- data/lib/legion/extensions/apollo/runners/maintenance.rb +135 -36
- data/lib/legion/extensions/apollo/runners/request.rb +4 -5
- data/lib/legion/extensions/apollo/transport.rb +20 -0
- data/lib/legion/extensions/apollo/version.rb +1 -1
- data/lib/legion/extensions/apollo.rb +2 -0
- data/spec/legion/extensions/apollo/actors/contradiction_scanner_spec.rb +38 -0
- data/spec/legion/extensions/apollo/actors/entity_watchdog_spec.rb +1 -1
- data/spec/legion/extensions/apollo/actors/gas_subscriber_spec.rb +4 -14
- data/spec/legion/extensions/apollo/api_spec.rb +11 -21
- data/spec/legion/extensions/apollo/contradiction_spec.rb +0 -9
- data/spec/legion/extensions/apollo/helpers/capability_spec.rb +2 -2
- data/spec/legion/extensions/apollo/helpers/writeback_spec.rb +31 -3
- data/spec/legion/extensions/apollo/runners/corroboration_integration_spec.rb +191 -0
- data/spec/legion/extensions/apollo/runners/entity_extractor_spec.rb +2 -2
- data/spec/legion/extensions/apollo/runners/gas_deposit_integration_spec.rb +92 -0
- data/spec/legion/extensions/apollo/runners/gas_spec.rb +27 -33
- data/spec/legion/extensions/apollo/runners/ingest_transaction_integration_spec.rb +114 -0
- data/spec/legion/extensions/apollo/runners/knowledge_spec.rb +129 -57
- data/spec/legion/extensions/apollo/runners/lifecycle_integration_spec.rb +165 -0
- data/spec/legion/extensions/apollo/runners/maintenance_spec.rb +22 -108
- data/spec/legion/extensions/apollo/runners/request_spec.rb +43 -26
- metadata +35 -2
- data/spec/legion/extensions/apollo/helpers/graph_query_spec.rb +0 -100
|
@@ -31,16 +31,10 @@ module Legion
|
|
|
31
31
|
content = response_content(response)
|
|
32
32
|
return false if content.nil? || content.length < min_content_length
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
apollo_results = enrichments['rag_context:apollo_results']
|
|
40
|
-
return true if apollo_results.nil? || (apollo_results[:count] || 0).zero?
|
|
41
|
-
|
|
42
|
-
# Apollo had results — only capture if LLM also did additional research
|
|
43
|
-
research_calls.any?
|
|
34
|
+
extract_tool_calls(response, enrichments).any? do |call|
|
|
35
|
+
name = call.respond_to?(:name) ? call.name : call[:name] || call['name']
|
|
36
|
+
RESEARCH_TOOLS.include?(name)
|
|
37
|
+
end
|
|
44
38
|
end
|
|
45
39
|
|
|
46
40
|
def build_payload(request:, response:, source_channel: nil)
|
|
@@ -147,9 +141,11 @@ module Legion
|
|
|
147
141
|
|
|
148
142
|
def extract_tool_calls(response, enrichments)
|
|
149
143
|
calls = []
|
|
150
|
-
calls
|
|
151
|
-
calls
|
|
152
|
-
calls.
|
|
144
|
+
calls.concat(Array(response.tools)) if response.respond_to?(:tools)
|
|
145
|
+
calls.concat(Array(response.tool_calls)) if response.respond_to?(:tool_calls)
|
|
146
|
+
calls.concat(Array(enrichments['tool_calls'])) if enrichments['tool_calls']
|
|
147
|
+
calls.uniq! { |call| call.respond_to?(:name) ? call.name : call[:name] || call['name'] }
|
|
148
|
+
calls
|
|
153
149
|
end
|
|
154
150
|
|
|
155
151
|
def derive_tags(query)
|
|
@@ -8,7 +8,7 @@ module Legion
|
|
|
8
8
|
DEFAULT_ENTITY_TYPES = %w[person service repository concept].freeze
|
|
9
9
|
DEFAULT_MIN_CONFIDENCE = 0.7
|
|
10
10
|
|
|
11
|
-
def extract_entities(text:, entity_types: nil, min_confidence:
|
|
11
|
+
def extract_entities(text:, entity_types: nil, min_confidence: Legion::Extensions::Apollo.settings[:entity_extractor][:min_confidence], **)
|
|
12
12
|
if text.to_s.strip.empty?
|
|
13
13
|
log.debug('Apollo EntityExtractor.extract_entities skipped reason=empty_text')
|
|
14
14
|
return { success: true, entities: [], source: :empty }
|
|
@@ -7,11 +7,11 @@ module Legion
|
|
|
7
7
|
module Apollo
|
|
8
8
|
module Runners
|
|
9
9
|
module Expertise
|
|
10
|
-
def get_expertise(domain:, min_proficiency:
|
|
10
|
+
def get_expertise(domain:, min_proficiency: Legion::Extensions::Apollo.settings[:expertise][:initial_proficiency], **)
|
|
11
11
|
{ action: :expertise_query, domain: domain, min_proficiency: min_proficiency }
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
def domains_at_risk(min_agents:
|
|
14
|
+
def domains_at_risk(min_agents: Legion::Extensions::Apollo.settings[:expertise][:min_agents_at_risk], **)
|
|
15
15
|
{ action: :domains_at_risk, min_agents: min_agents }
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -75,7 +75,7 @@ module Legion
|
|
|
75
75
|
|
|
76
76
|
def expertise_proficiency(confidences)
|
|
77
77
|
avg = confidences.sum / confidences.size
|
|
78
|
-
cap =
|
|
78
|
+
cap = Legion::Extensions::Apollo.settings[:expertise][:proficiency_cap]
|
|
79
79
|
[avg * Math.log2(confidences.size + 1), cap].min
|
|
80
80
|
end
|
|
81
81
|
|
|
@@ -27,11 +27,11 @@ module Legion
|
|
|
27
27
|
json_parse(str)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def relate_confidence_gate =
|
|
31
|
-
def synthesis_confidence_cap =
|
|
32
|
-
def max_anticipations =
|
|
33
|
-
def similar_entries_limit =
|
|
34
|
-
def fallback_confidence =
|
|
30
|
+
def relate_confidence_gate = Legion::Extensions::Apollo.settings[:gas][:relate_confidence_gate]
|
|
31
|
+
def synthesis_confidence_cap = Legion::Extensions::Apollo.settings[:gas][:synthesis_confidence_cap]
|
|
32
|
+
def max_anticipations = Legion::Extensions::Apollo.settings[:gas][:max_anticipations]
|
|
33
|
+
def similar_entries_limit = Legion::Extensions::Apollo.settings[:gas][:similar_entries_limit]
|
|
34
|
+
def fallback_confidence = Legion::Extensions::Apollo.settings[:gas][:fallback_confidence]
|
|
35
35
|
|
|
36
36
|
def process(audit_event)
|
|
37
37
|
unless processable?(audit_event)
|
|
@@ -139,31 +139,101 @@ module Legion
|
|
|
139
139
|
[]
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
-
# Phase 5: Deposit
|
|
143
|
-
def phase_deposit(facts, _entities,
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return
|
|
142
|
+
# Phase 5: Deposit entries, then link successfully stored facts.
|
|
143
|
+
def phase_deposit(facts, _entities, relations, synthesis, audit_event)
|
|
144
|
+
counts = { deposited: 0, failed: 0, facts: 0, synthesis: 0 }
|
|
145
|
+
unless defined?(Legion::Extensions::Apollo::Runners::Knowledge)
|
|
146
|
+
return counts.merge(failed: facts.size + synthesis.size, relations: 0,
|
|
147
|
+
relations_failed: relations.size, relations_skipped: 0)
|
|
147
148
|
end
|
|
148
149
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
150
|
+
fact_ids = Array.new(facts.size)
|
|
151
|
+
source_ids = {}
|
|
152
|
+
{ facts: facts, synthesis: synthesis }.each do |kind, entries|
|
|
153
|
+
entries.each_with_index do |item, index|
|
|
154
|
+
context = { source_request_id: audit_event[:request_id] }
|
|
155
|
+
if kind == :synthesis
|
|
156
|
+
context[:source_indices] = Array(item[:source_indices])
|
|
157
|
+
context[:source_entry_ids] = context[:source_indices].filter_map do |source_index|
|
|
158
|
+
fact_ids[source_index] if source_index.is_a?(Integer) && source_index.between?(0, facts.size - 1)
|
|
159
|
+
end.uniq
|
|
160
|
+
end
|
|
161
|
+
result = Legion::Extensions::Apollo::Runners::Knowledge.handle_ingest(
|
|
162
|
+
content: item[:content], content_type: item[:content_type].to_s,
|
|
163
|
+
tags: %w[gas auto_extracted], source_agent: 'gas_pipeline',
|
|
164
|
+
source_provider: audit_event.dig(:routing, :provider),
|
|
165
|
+
knowledge_domain: 'general', context: context
|
|
166
|
+
)
|
|
167
|
+
unless result.is_a?(Hash) && result[:success] == true && result[:entry_id]
|
|
168
|
+
counts[:failed] += 1
|
|
169
|
+
next
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
counts[:deposited] += 1
|
|
173
|
+
counts[kind] += 1
|
|
174
|
+
next unless kind == :facts
|
|
175
|
+
|
|
176
|
+
fact_ids[index] = result[:entry_id]
|
|
177
|
+
ids = (source_ids[item[:content]] ||= [])
|
|
178
|
+
ids << result[:entry_id] unless ids.include?(result[:entry_id])
|
|
179
|
+
rescue StandardError => e
|
|
180
|
+
counts[:failed] += 1
|
|
181
|
+
handle_exception(e, level: :warn, operation: 'apollo.gas.phase_deposit_entry')
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
counts.merge!(gas_deposit_relations(relations: relations, source_ids: source_ids))
|
|
185
|
+
log.info("GAS phase_deposit deposited=#{counts[:deposited]} failed=#{counts[:failed]} relations=#{counts[:relations]} relations_failed=#{counts[:relations_failed]}") # rubocop:disable Layout/LineLength
|
|
186
|
+
counts
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def gas_deposit_relations(relations:, source_ids:)
|
|
190
|
+
counts = { relations: 0, relations_failed: 0, relations_skipped: 0 }
|
|
191
|
+
return counts if relations.empty?
|
|
192
|
+
unless Legion::Extensions::Apollo::Helpers::DataModels.apollo_entry_available? &&
|
|
193
|
+
Legion::Extensions::Apollo::Helpers::DataModels.apollo_relation_available?
|
|
194
|
+
return counts.merge(relations_failed: relations.size)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
entry_model = Legion::Extensions::Apollo::Helpers::DataModels.apollo_entry
|
|
198
|
+
relation_model = Legion::Extensions::Apollo::Helpers::DataModels.apollo_relation
|
|
199
|
+
seen = {}
|
|
200
|
+
relations.each do |relation|
|
|
201
|
+
type = relation[:relation_type].to_s
|
|
202
|
+
weight = relation[:confidence]
|
|
203
|
+
unless gas_relation_accepted?(type: type, confidence: weight)
|
|
204
|
+
counts[:relations_skipped] += 1
|
|
205
|
+
next
|
|
206
|
+
end
|
|
207
|
+
ids = source_ids[relation[:from_content]]
|
|
208
|
+
source = entry_model[ids.first] if ids&.one?
|
|
209
|
+
target = entry_model[relation[:to_id]] if relation[:to_id]
|
|
210
|
+
unless source && target
|
|
211
|
+
counts[:relations_failed] += 1
|
|
212
|
+
next
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
key = [source.id, target.id, type]
|
|
216
|
+
if source.id == target.id || source[:access_scope] != target[:access_scope] || seen[key]
|
|
217
|
+
counts[:relations_skipped] += 1
|
|
218
|
+
next
|
|
219
|
+
end
|
|
220
|
+
seen[key] = true
|
|
221
|
+
relation_model.create(from_entry_id: source.id, to_entry_id: target.id, relation_type: type,
|
|
222
|
+
weight: weight, source_agent: 'gas_pipeline')
|
|
223
|
+
counts[:relations] += 1
|
|
161
224
|
rescue StandardError => e
|
|
162
|
-
|
|
225
|
+
counts[:relations_failed] += 1
|
|
226
|
+
handle_exception(e, level: :warn, operation: 'apollo.gas.phase_deposit_relation')
|
|
163
227
|
end
|
|
164
|
-
|
|
165
|
-
|
|
228
|
+
counts
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
def gas_relation_accepted?(type:, confidence:)
|
|
232
|
+
Legion::Extensions::Apollo::Helpers::Confidence::RELATION_TYPES.include?(type) &&
|
|
233
|
+
confidence.is_a?(Numeric) && confidence.real? && confidence.finite? &&
|
|
234
|
+
confidence.between?(relate_confidence_gate, 1.0)
|
|
166
235
|
end
|
|
236
|
+
private_class_method :gas_deposit_relations, :gas_relation_accepted?
|
|
167
237
|
|
|
168
238
|
# Phase 6: Anticipate - pre-cache likely follow-up questions
|
|
169
239
|
def phase_anticipate(facts, _synthesis)
|