lex-apollo 0.3.3 → 0.3.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: e5f9f60cb9a67bc5d4cd35af051b1abe3251787d0da55ac092c1898942bd2144
4
- data.tar.gz: 8b0cd86443469ae7a0d5d857a127f855cd0e68f8d967c94a727e8f166372e483
3
+ metadata.gz: e7d785a9fed9656eb22b307620a384f64e52646bca14728dc0de3ef2b1adb5bd
4
+ data.tar.gz: c64bcf1b75ee0ed43a747722a610c5cee8de823995d5f7455e31ae5c9174c56d
5
5
  SHA512:
6
- metadata.gz: 3f239c19c6212f7c142aec1e0fe34aed651928f5a4c37acfcf54152d6719594f94b6ddb037bf48666eb93f9c747d341a477039e320e2e74fef17a8188519e0a9
7
- data.tar.gz: 6227ef5fffa37ad570940591d390ac594d71f10a49de1e75a6ce568b075ac6c0293ba13478130eebbc366c6195b383327dab2b7400398bc2fe6f1f274f4b61ca
6
+ metadata.gz: 0ff89d33e4993dd8c71e392202c66add619ab04045fa674b5f8f0c4d891b727ca7401865d349c68bdc3a0344e1773db4f86651ed7e96ecbcaf0098a9d05cdbc9
7
+ data.tar.gz: 29b69744b9870bee8e1fbfc030652aede63a51c3a12cd02a3bc69bfc805b6267cf8045346cb37dc2565e1e103ae6f3b559972536b02f3a62dd4b6f12223361a5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.3.4] - 2026-03-20
4
+
5
+ ### Added
6
+ - `Helpers::EntityWatchdog`: regex-based entity detection for persons, services, repos, and configurable concepts
7
+ - GAIA `post_tick_reflection` handler for passive entity detection (enabled via `apollo.entity_watchdog.enabled`)
8
+ - Deduplication by type+value, configurable type filtering, and `link_or_create` for Apollo integration
9
+
3
10
  ## [0.3.3] - 2026-03-20
4
11
 
5
12
  ### Added
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Legion
4
+ module Extensions
5
+ module Apollo
6
+ module Helpers
7
+ module EntityWatchdog
8
+ ENTITY_PATTERNS = {
9
+ person: /\b[A-Z][a-z]+(?:\s[A-Z][a-z]+)+\b/,
10
+ service: %r{\bhttps?://[^\s]+\b},
11
+ repo: %r{\b[a-zA-Z0-9_-]+/[a-zA-Z0-9_.-]+\b}
12
+ }.freeze
13
+
14
+ class << self
15
+ def detect_entities(text:, types: nil)
16
+ return [] if text.nil? || text.empty?
17
+
18
+ types = (types || default_types).map(&:to_sym)
19
+ entities = []
20
+
21
+ types.each do |type_sym|
22
+ pattern = type_sym == :concept ? concept_pattern : ENTITY_PATTERNS[type_sym]
23
+ next unless pattern
24
+
25
+ text.scan(pattern).each do |match|
26
+ entities << { type: type_sym, value: match.strip, confidence: 0.5 }
27
+ end
28
+ end
29
+
30
+ entities.uniq { |e| [e[:type], e[:value].downcase] }
31
+ end
32
+
33
+ def link_or_create(entities:, source_context: nil)
34
+ return { success: true, linked: 0, created: 0 } if entities.nil? || entities.empty?
35
+
36
+ linked = 0
37
+ created = 0
38
+
39
+ entities.each do |entity|
40
+ existing = find_existing(entity)
41
+ if existing
42
+ bump_confidence(existing, source_context)
43
+ linked += 1
44
+ else
45
+ create_candidate(entity, source_context)
46
+ created += 1
47
+ end
48
+ end
49
+
50
+ { success: true, linked: linked, created: created }
51
+ end
52
+
53
+ def concept_pattern
54
+ keywords = if defined?(Legion::Settings)
55
+ Legion::Settings.dig(:apollo, :entity_watchdog, :concept_keywords) || []
56
+ else
57
+ []
58
+ end
59
+ return nil if keywords.empty?
60
+
61
+ Regexp.new("\\b(?:#{keywords.map { |k| Regexp.escape(k) }.join('|')})\\b", Regexp::IGNORECASE)
62
+ end
63
+
64
+ private
65
+
66
+ def default_types
67
+ if defined?(Legion::Settings)
68
+ Legion::Settings.dig(:apollo, :entity_watchdog, :types) || %w[person service repo concept]
69
+ else
70
+ %w[person service repo concept]
71
+ end
72
+ end
73
+
74
+ def find_existing(_entity)
75
+ return nil unless defined?(Runners::Knowledge) && respond_to?(:retrieve_relevant, true)
76
+
77
+ nil
78
+ end
79
+
80
+ def bump_confidence(_entry, _source_context)
81
+ # Increment retrieval confidence on existing Apollo entry
82
+ end
83
+
84
+ def create_candidate(entity, _source_context)
85
+ return unless defined?(Runners::Knowledge)
86
+
87
+ Legion::Logging.debug "[entity_watchdog] candidate: #{entity[:type]}=#{entity[:value]}" if defined?(Legion::Logging)
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Apollo
6
- VERSION = '0.3.3'
6
+ VERSION = '0.3.4'
7
7
  end
8
8
  end
9
9
  end
@@ -24,3 +24,20 @@ module Legion
24
24
  end
25
25
  end
26
26
  end
27
+
28
+ # Entity watchdog on post_tick_reflection
29
+ if defined?(Legion::Gaia::PhaseWiring) && begin
30
+ Legion::Settings.dig(:apollo, :entity_watchdog, :enabled)
31
+ rescue StandardError
32
+ false
33
+ end
34
+ require 'legion/extensions/apollo/helpers/entity_watchdog'
35
+ Legion::Gaia::PhaseWiring.register_handler(:post_tick_reflection) do |tick_results|
36
+ text = tick_results.is_a?(Hash) ? (tick_results[:content] || tick_results[:output] || '').to_s : tick_results.to_s
37
+ entities = Legion::Extensions::Apollo::Helpers::EntityWatchdog.detect_entities(text: text)
38
+ if entities.any?
39
+ Legion::Extensions::Apollo::Helpers::EntityWatchdog.link_or_create(entities: entities,
40
+ source_context: tick_results[:tick_id])
41
+ end
42
+ end
43
+ end
@@ -45,4 +45,29 @@ RSpec.describe Legion::Extensions::Apollo::GaiaIntegration do
45
45
  expect(result).to eq({ success: true })
46
46
  end
47
47
  end
48
+
49
+ describe 'entity watchdog phase handler' do
50
+ it 'detects entities from tick results' do
51
+ require 'legion/extensions/apollo/helpers/entity_watchdog'
52
+ tick_results = { content: 'Jane Doe deployed to https://api.example.com', tick_id: 'tick-1' }
53
+ entities = Legion::Extensions::Apollo::Helpers::EntityWatchdog.detect_entities(text: tick_results[:content])
54
+ expect(entities.size).to be >= 2
55
+ end
56
+
57
+ it 'links or creates entities from tick results' do
58
+ require 'legion/extensions/apollo/helpers/entity_watchdog'
59
+ tick_results = { content: 'Jane Doe at LegionIO/lex-mesh', tick_id: 'tick-2' }
60
+ entities = Legion::Extensions::Apollo::Helpers::EntityWatchdog.detect_entities(text: tick_results[:content])
61
+ result = Legion::Extensions::Apollo::Helpers::EntityWatchdog.link_or_create(
62
+ entities: entities, source_context: tick_results[:tick_id]
63
+ )
64
+ expect(result[:success]).to be true
65
+ end
66
+
67
+ it 'entry point has entity watchdog registration block' do
68
+ entry_point = File.read(File.expand_path('../../../../lib/legion/extensions/apollo.rb', __dir__))
69
+ expect(entry_point).to include('entity_watchdog')
70
+ expect(entry_point).to include('PhaseWiring.register_handler(:post_tick_reflection)')
71
+ end
72
+ end
48
73
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'legion/extensions/apollo/helpers/entity_watchdog'
5
+
6
+ RSpec.describe Legion::Extensions::Apollo::Helpers::EntityWatchdog do
7
+ describe '.detect_entities' do
8
+ it 'detects person names (capitalized multi-word)' do
9
+ entities = described_class.detect_entities(text: 'Talked to Jane Doe about the project')
10
+ person = entities.find { |e| e[:type] == :person }
11
+ expect(person).not_to be_nil
12
+ expect(person[:value]).to eq('Jane Doe')
13
+ end
14
+
15
+ it 'detects service URLs' do
16
+ entities = described_class.detect_entities(text: 'Deployed to https://api.example.com/v1')
17
+ service = entities.find { |e| e[:type] == :service }
18
+ expect(service).not_to be_nil
19
+ expect(service[:value]).to include('example.com')
20
+ end
21
+
22
+ it 'detects repo references' do
23
+ entities = described_class.detect_entities(text: 'Check LegionIO/lex-mesh for the code')
24
+ repo = entities.find { |e| e[:type] == :repo }
25
+ expect(repo).not_to be_nil
26
+ expect(repo[:value]).to eq('LegionIO/lex-mesh')
27
+ end
28
+
29
+ it 'detects concept keywords from settings' do
30
+ allow(described_class).to receive(:concept_pattern).and_return(/\b(?:kubernetes|terraform)\b/i)
31
+ entities = described_class.detect_entities(text: 'Using Terraform to deploy Kubernetes')
32
+ concepts = entities.select { |e| e[:type] == :concept }
33
+ expect(concepts.size).to eq(2)
34
+ end
35
+
36
+ it 'deduplicates entities by type and lowercase value' do
37
+ entities = described_class.detect_entities(text: 'Jane Doe met Jane Doe again')
38
+ persons = entities.select { |e| e[:type] == :person }
39
+ expect(persons.size).to eq(1)
40
+ end
41
+
42
+ it 'returns empty array for text with no entities' do
43
+ entities = described_class.detect_entities(text: 'nothing special here')
44
+ expect(entities).to be_empty
45
+ end
46
+
47
+ it 'filters by specified types' do
48
+ entities = described_class.detect_entities(
49
+ text: 'Jane Doe at https://example.com with LegionIO/lex-mesh',
50
+ types: [:person]
51
+ )
52
+ expect(entities.all? { |e| e[:type] == :person }).to be true
53
+ end
54
+ end
55
+
56
+ describe '.link_or_create' do
57
+ it 'returns counts for empty entities' do
58
+ result = described_class.link_or_create(entities: [])
59
+ expect(result[:success]).to be true
60
+ expect(result[:linked]).to eq(0)
61
+ expect(result[:created]).to eq(0)
62
+ end
63
+ end
64
+ 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.3.3
4
+ version: 0.3.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -58,6 +58,7 @@ files:
58
58
  - lib/legion/extensions/apollo/gaia_integration.rb
59
59
  - lib/legion/extensions/apollo/helpers/confidence.rb
60
60
  - lib/legion/extensions/apollo/helpers/embedding.rb
61
+ - lib/legion/extensions/apollo/helpers/entity_watchdog.rb
61
62
  - lib/legion/extensions/apollo/helpers/graph_query.rb
62
63
  - lib/legion/extensions/apollo/helpers/similarity.rb
63
64
  - lib/legion/extensions/apollo/runners/entity_extractor.rb
@@ -80,6 +81,7 @@ files:
80
81
  - spec/legion/extensions/apollo/gaia_integration_spec.rb
81
82
  - spec/legion/extensions/apollo/helpers/confidence_spec.rb
82
83
  - spec/legion/extensions/apollo/helpers/embedding_spec.rb
84
+ - spec/legion/extensions/apollo/helpers/entity_watchdog_spec.rb
83
85
  - spec/legion/extensions/apollo/helpers/graph_query_spec.rb
84
86
  - spec/legion/extensions/apollo/helpers/similarity_spec.rb
85
87
  - spec/legion/extensions/apollo/runners/decay_cycle_spec.rb