Module: Legion::Apollo

Extended by:
Logging::Helper
Defined in:
lib/legion/apollo.rb,
lib/legion/apollo/local.rb,
lib/legion/apollo/routes.rb,
lib/legion/apollo/version.rb,
lib/legion/apollo/settings.rb,
lib/legion/apollo/local/graph.rb,
lib/legion/apollo/messages/query.rb,
lib/legion/apollo/messages/ingest.rb,
lib/legion/apollo/runners/request.rb,
lib/legion/apollo/helpers/confidence.rb,
lib/legion/apollo/helpers/similarity.rb,
lib/legion/apollo/messages/writeback.rb,
lib/legion/apollo/messages/access_boost.rb,
lib/legion/apollo/helpers/tag_normalizer.rb

Overview

Apollo client library — query, ingest, and retrieve with smart routing. Routes to a co-located lex-apollo service when available, falls back to RabbitMQ transport, and degrades gracefully when neither is present. Supports scope: :global (default), :local (SQLite only), :all (merged).

Defined Under Namespace

Modules: Helpers, Local, Messages, Routes, Runners, Settings

Constant Summary collapse

LIFECYCLE_MUTEX =

rubocop:disable Metrics/ModuleLength

Mutex.new
VERSION =
'0.5.0'

Class Method Summary collapse

Class Method Details

.data_available?Boolean

Returns:

  • (Boolean)


170
171
172
# File 'lib/legion/apollo.rb', line 170

def data_available?
  @data_available == true
end

.graph_query(entity_id:, relation_type: nil, depth: 3, direction: :outbound) ⇒ Hash

Graph traversal — delegates to Local::Graph for node-local SQLite store. Follows entity edges of the given relation_type up to depth hops.

Parameters:

  • entity_id (Integer)

    starting entity id

  • relation_type (String, nil) (defaults to: nil)

    edge filter (nil = any)

  • depth (Integer) (defaults to: 3)

    max traversal hops (1..10)

  • direction (Symbol) (defaults to: :outbound)

    :outbound (default) or :inbound

Returns:

  • (Hash)

    { success:, nodes:, edges:, count: }



135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/legion/apollo.rb', line 135

def graph_query(entity_id:, relation_type: nil, depth: 3, direction: :outbound) # rubocop:disable Metrics/MethodLength
  return not_started_error unless started?
  return { success: false, error: :local_not_started } unless Legion::Apollo::Local.started?

  log.info do
    "Apollo graph query requested entity_id=#{entity_id} relation_type=#{relation_type || 'any'} " \
      "depth=#{depth} direction=#{direction}"
  end
  log.debug do
    "Apollo graph_query entity_id=#{entity_id} relation_type=#{relation_type} " \
      "depth=#{depth} direction=#{direction}"
  end
  Legion::Apollo::Local::Graph.traverse(
    entity_id:     entity_id,
    relation_type: relation_type,
    depth:         depth,
    direction:     direction
  )
rescue StandardError => e
  handle_exception(
    e,
    level:         :error,
    operation:     'apollo.graph_query',
    entity_id:     entity_id,
    relation_type: relation_type,
    depth:         depth,
    direction:     direction
  )
  { success: false, error: e.message }
end

.ingest(content:, tags: [], scope: :global, **opts) ⇒ Object

rubocop:disable Metrics/MethodLength,Metrics/AbcSize



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/legion/apollo.rb', line 96

def ingest(content:, tags: [], scope: :global, **opts) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize
  return not_started_error unless started?

  normalized_tags = normalize_tags_input(tags)
  payload = { content: content, tags: normalized_tags, **opts }
  log.info do
    "Apollo ingest requested scope=#{scope} content_length=#{content.to_s.length} " \
      "tags=#{payload[:tags].size} source_channel=#{payload[:source_channel]}"
  end
  log.debug do
    "Apollo ingest scope=#{scope} tags=#{payload[:tags].size} source_channel=#{payload[:source_channel]}"
  end

  case scope
  when :local then ingest_local(payload)
  when :all   then ingest_all(payload)
  else
    if co_located_writer?
      direct_ingest(payload)
    elsif transport_available?
      publish_ingest(payload)
    else
      { success: false, error: :no_path_available }
    end
  end
end

.localObject



64
65
66
# File 'lib/legion/apollo.rb', line 64

def local
  Legion::Apollo::Local
end

.query(text:, limit: nil, min_confidence: nil, tags: nil, scope: :global, **opts) ⇒ Object

rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/ParameterLists



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/legion/apollo.rb', line 68

def query(text:, limit: nil, min_confidence: nil, tags: nil, scope: :global, **opts) # rubocop:disable Metrics/MethodLength,Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/ParameterLists
  return not_started_error unless started?

  text = normalize_text_input(text)
  normalized_tags = normalize_tags_input(tags)
  limit          ||= apollo_setting(:default_limit, 5)
  min_confidence ||= apollo_setting(:min_confidence, 0.3)
  log.info { "Apollo query requested scope=#{scope} text_length=#{text.to_s.length} limit=#{limit}" }
  log.debug do
    "Apollo query scope=#{scope} limit=#{limit} min_confidence=#{min_confidence} tags=#{normalized_tags.size}"
  end

  payload = { text: text, limit: limit, min_confidence: min_confidence, tags: normalized_tags, **opts }

  case scope
  when :local then query_local(payload)
  when :all   then query_merged(payload)
  else
    if co_located_reader?
      direct_query(payload)
    elsif transport_available?
      publish_query(payload)
    else
      { success: false, error: :no_path_available }
    end
  end
end

.retrieve(text:, limit: 5, scope: :global) ⇒ Object



123
124
125
# File 'lib/legion/apollo.rb', line 123

def retrieve(text:, limit: 5, scope: :global, **)
  query(text: text, limit: limit, scope: scope, **)
end

.shutdownObject



49
50
51
52
53
54
55
56
57
58
# File 'lib/legion/apollo.rb', line 49

def shutdown
  LIFECYCLE_MUTEX.synchronize do
    Legion::Apollo::Local.shutdown if defined?(Legion::Apollo::Local) && Legion::Apollo::Local.started?
    log.info 'Legion::Apollo shutdown'
    clear_state
  end
rescue StandardError => e
  handle_exception(e, level: :warn, operation: 'apollo.shutdown')
  clear_state
end

.startObject

rubocop:disable Metrics/MethodLength



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/legion/apollo.rb', line 23

def start # rubocop:disable Metrics/MethodLength
  LIFECYCLE_MUTEX.synchronize do
    return if @started

    merge_settings
    unless apollo_enabled?
      log.info 'Apollo start skipped because apollo.enabled is false'
      return
    end

    detect_transport
    detect_data
    register_routes
    Legion::Apollo::Local.start

    @started = true
    log.info 'Legion::Apollo started'

    seed_self_knowledge
    Legion::Apollo::Local.hydrate_from_global if Legion::Apollo::Local.started?
  end
rescue StandardError => e
  handle_exception(e, level: :error, operation: 'apollo.start')
  clear_state
end

.started?Boolean

Returns:

  • (Boolean)


60
61
62
# File 'lib/legion/apollo.rb', line 60

def started?
  @started == true
end

.transport_available?Boolean

Returns:

  • (Boolean)


166
167
168
# File 'lib/legion/apollo.rb', line 166

def transport_available?
  @transport_available == true
end