Module: Legion::Gaia::Advisory

Extended by:
Logging::Helper
Defined in:
lib/legion/gaia/advisory.rb

Class Method Summary collapse

Class Method Details

.advise(caller:, conversation_id: nil, messages: nil) ⇒ Object

Fast in-memory advisory. NEVER makes LLM calls. Returns Hash with optional keys: system_prompt, routing_hint, context_window, tool_hint, suppress, valence



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/legion/gaia/advisory.rb', line 15

def advise(caller:, conversation_id: nil, messages: nil)
  log.debug "advise(caller: #{caller}, conversation_id: #{conversation_id}, messages: #{messages}) "
  return nil unless Gaia.started?

  advisory = {}
  advisory[:valence] = Gaia.last_valences if Gaia.last_valences
  merge_tick_data!(advisory, Gaia.registry&.tick_host&.last_tick_result)
  merge_observer_data!(advisory, caller)

  identity = caller&.dig(:requested_by, :identity)
  partner_prompt = build_partner_system_prompt(identity: identity) if identity
  if partner_prompt
    advisory[:system_prompt] = partner_prompt
    log.info("[gaia] advisory partner_prompt injected identity=#{identity} length=#{partner_prompt.length}")
  end

  result = advisory.compact
  if result.any?
    log.info("GAIA advisory generated conversation_id=#{conversation_id} keys=#{result.keys.join(',')}")
  end
  result
rescue StandardError => e
  handle_exception(e, level: :warn, operation: 'gaia.advisory.advise', conversation_id: conversation_id)
  {}
end

.apply_suppress!(advisory, results) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/legion/gaia/advisory.rb', line 64

def apply_suppress!(advisory, results)
  suppressed = results.dig(:sensory_processing, :suppressed)
  return unless suppressed

  arr = Array(suppressed)
  advisory[:suppress] = arr.empty? ? nil : arr
end

.apply_tool_hints!(advisory, results) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
# File 'lib/legion/gaia/advisory.rb', line 52

def apply_tool_hints!(advisory, results)
  predictions = results.dig(:prediction_engine, :predictions)
  return unless predictions

  tools = predictions.filter_map do |prediction|
    confidence = value_for(prediction, :confidence).to_f
    tool = value_for(prediction, :tool)
    tool if confidence >= 0.6 && tool
  end
  advisory[:tool_hint] = tools.empty? ? nil : tools
end

.build_epistemic_qualifier(identity:) ⇒ Object



152
153
154
155
156
# File 'lib/legion/gaia/advisory.rb', line 152

def build_epistemic_qualifier(identity:)
  return nil unless defined?(Legion::Gaia::VisibleGrowth)

  VisibleGrowth.epistemic_qualifier(identity: identity)
end

.build_partner_slots(identity:) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/legion/gaia/advisory.rb', line 127

def build_partner_slots(identity:)
  return nil unless defined?(Legion::Gaia::PartnerModel)

  slots = PartnerModel.build(identity: identity)
  return nil if slots.empty?

  lines = slots.map do |slot|
    domain = slot[:domain]
    content = slot[:content]
    content_str = content.is_a?(Hash) ? content.map { |k, v| "#{k}: #{v}" }.join(', ') : content.to_s
    "- #{domain}: #{content_str}"
  end

  "What I know about working with you:\n#{lines.join("\n")}"
end

.build_partner_system_prompt(identity:) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/legion/gaia/advisory.rb', line 105

def build_partner_system_prompt(identity:)
  log.unknown("[gaia] build_partner_system_prompt identity=#{identity} " \
              "bond_registry_defined=#{defined?(Legion::Gaia::BondRegistry)} " \
              "partner=#{defined?(Legion::Gaia::BondRegistry) && BondRegistry.partner?(identity.to_s)}")

  return nil unless defined?(Legion::Gaia::BondRegistry) && BondRegistry.partner?(identity.to_s)

  parts = ['|gaia:active|']

  slots = build_partner_slots(identity: identity)
  parts << slots if slots
  log.unknown("[gaia] partner_slots count=#{slots ? 'present' : 'empty'}")

  growth = drain_growth_content
  parts << growth if growth

  qualifier = build_epistemic_qualifier(identity: identity)
  parts << qualifier if qualifier

  parts.join("\n\n")
end

.drain_growth_contentObject



143
144
145
146
147
148
149
150
# File 'lib/legion/gaia/advisory.rb', line 143

def drain_growth_content
  return nil unless Gaia.respond_to?(:drain_growth_frames)

  frames = Gaia.drain_growth_frames
  return nil if frames.nil? || frames.empty?

  frames.join("\n")
end

.merge_observer_data!(advisory, caller) ⇒ Object



72
73
74
75
76
77
78
79
# File 'lib/legion/gaia/advisory.rb', line 72

def merge_observer_data!(advisory, caller)
  identity = caller&.dig(:requested_by, :identity)
  return unless identity

  learned = AuditObserver.instance.learned_data_for(identity)
  advisory[:routing_hint] ||= normalize_routing_hint(learned[:routing_preference]) if learned[:routing_preference]
  advisory[:tool_hint] ||= learned[:tool_predictions].keys.first(5) if learned[:tool_predictions]&.any?
end

.merge_tick_data!(advisory, tick_result) ⇒ Object



41
42
43
44
45
46
47
48
49
50
# File 'lib/legion/gaia/advisory.rb', line 41

def merge_tick_data!(advisory, tick_result)
  log.debug "merge_tick_data!(#{advisory.inspect}, #{tick_result})"
  return unless tick_result && tick_result[:results]

  results = tick_result[:results]
  apply_tool_hints!(advisory, results)
  apply_suppress!(advisory, results)
  advisory[:routing_hint] = normalize_routing_hint(results.dig(:post_tick_reflection, :routing_preference))
  advisory[:context_window] = results.dig(:memory_retrieval, :cross_conversation)
end

.normalize_routing_hint(value) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/legion/gaia/advisory.rb', line 81

def normalize_routing_hint(value)
  return nil if value.nil?

  if value.is_a?(Hash)
    provider = value_for(value, :provider)
    model = value_for(value, :model)
    return nil if provider.to_s.empty? && model.to_s.empty?

    return { provider: provider&.to_s, model: model&.to_s }
  end

  { provider: value.to_s, model: nil }
end

.value_for(hash, key) ⇒ Object



95
96
97
98
99
100
101
102
103
# File 'lib/legion/gaia/advisory.rb', line 95

def value_for(hash, key)
  return nil unless hash.respond_to?(:key?)

  string_key = key.to_s
  return hash[key] if hash.key?(key)
  return hash[string_key] if hash.key?(string_key)

  nil
end