lex-apollo 0.4.18 → 0.4.19

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: 87724ea9011349f5f7c008a360728975567903827b5cdcf9a42e2678ca84b134
4
- data.tar.gz: ae10f48baf9522bd7087a20e1886fc26170c1f5e0a0c36a3ccac26c836f11885
3
+ metadata.gz: 1bf0ca8790d13fc3d262ede810ce50a47146dc37743e8806696de970b8a0385a
4
+ data.tar.gz: 1f1a9115e1a1bb36423150a7290a82352f07aef7aa41f0b4b7da8e0ff5b76f94
5
5
  SHA512:
6
- metadata.gz: b21c5413da8dfe9f60d9845a578bcfe8337982d128a5041df214004e144b9913dd34cd2da1514a30ea67e21d52604a11218c362a46f9c990c472ac4ec14a0fa9
7
- data.tar.gz: ae07a4aba39ce05f20b6a495310b6a20c1c468e0d627393ab931af76d9cd57da5b52075a33706a8b94fadae01c32b65d818d4f5c1018f9eac08a8ffdc10c7ace
6
+ metadata.gz: 3d92269898b53825fff831ef253fc44ac9e9a45602da25628f32fc95c1e6a53edfd86add2f02f7131c449cd1b940258d2c8fc4df3b90ca6306affbf8a76d4ccb
7
+ data.tar.gz: d6c98fc7a7f351dc929e2d2dac4c9d7d119e32328706c9880e19a6bc3edb53448b008d8a8fefd7f7e8b8a201084f4ec78006088954cb7e56ee72cb312f0d5ce6
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.4.19] - 2026-04-24
4
+
5
+ ### Fixed
6
+ - `store_knowledge` no longer rejects LLM-provided content_type values — normalizes free-form strings (`"reasoning"`, `"text"`, `"text/plain"`, `":fact"`, `"inference"`) to valid symbols via alias map with `:observation` fallback
7
+ - `GaiaIntegration.publish_insight` now passes `:observation` instead of the domain string as content_type (was sending `"general"` or domain names which failed validation)
8
+ - `llm_detects_conflict?` truncates content to 4000 chars before sending to LLM to prevent context overflow errors (was passing full entry content, hitting 65536-token limit)
9
+
3
10
  ## [0.4.18] - 2026-04-24
4
11
 
5
12
  ### Fixed
@@ -15,7 +15,7 @@ module Legion
15
15
  client = Legion::Extensions::Apollo::Client.new(agent_id: agent_id)
16
16
  client.store_knowledge(
17
17
  content: insight[:content],
18
- content_type: insight[:domain] || 'general',
18
+ content_type: :observation,
19
19
  source_agent: agent_id,
20
20
  tags: Array(insight[:tags])
21
21
  )
@@ -14,11 +14,16 @@ module Legion
14
14
  'general' => :all
15
15
  }.freeze
16
16
 
17
+ CONTENT_TYPE_ALIASES = {
18
+ reasoning: :concept, analysis: :concept, explanation: :concept,
19
+ text: :observation, general: :observation, note: :observation, summary: :observation,
20
+ rule: :procedure, step: :procedure, instruction: :procedure,
21
+ link: :association, relation: :association, connection: :association,
22
+ inference: :association, implication: :association
23
+ }.freeze
24
+
17
25
  def store_knowledge(content:, content_type:, tags: [], source_agent: nil, context: {}, **)
18
- content_type = content_type.to_sym
19
- unless Helpers::Confidence::CONTENT_TYPES.include?(content_type)
20
- raise ArgumentError, "invalid content_type: #{content_type}. Must be one of #{Helpers::Confidence::CONTENT_TYPES}"
21
- end
26
+ content_type = normalize_content_type(content_type)
22
27
 
23
28
  if defined?(Legion::Data::Model::ApolloEntry)
24
29
  return handle_ingest(content: content, content_type: content_type,
@@ -324,8 +329,16 @@ module Legion
324
329
  { deleted: 0, redacted: 0, error: e.message }
325
330
  end
326
331
 
332
+ CONFLICT_CHECK_MAX_CHARS = 4000
333
+
327
334
  private
328
335
 
336
+ def normalize_content_type(raw)
337
+ sym = raw.to_s.delete_prefix(':').gsub(%r{[/\s]}, '_').strip.downcase.to_sym
338
+ sym = CONTENT_TYPE_ALIASES.fetch(sym, sym)
339
+ Helpers::Confidence::CONTENT_TYPES.include?(sym) ? sym : :observation
340
+ end
341
+
329
342
  def embed_text(text)
330
343
  text = normalize_text_input(text)
331
344
  result = Legion::LLM::Embeddings.generate(text: text)
@@ -399,10 +412,12 @@ module Legion
399
412
  def llm_detects_conflict?(content_a, content_b)
400
413
  return false unless defined?(Legion::LLM) && Legion::LLM.respond_to?(:structured)
401
414
 
415
+ a = content_a.to_s[0, CONFLICT_CHECK_MAX_CHARS]
416
+ b = content_b.to_s[0, CONFLICT_CHECK_MAX_CHARS]
402
417
  result = Legion::LLM.structured(
403
418
  messages: [
404
419
  { role: 'system', content: 'Do these two statements contradict each other? Return JSON.' },
405
- { role: 'user', content: "A: #{content_a}\n\nB: #{content_b}" }
420
+ { role: 'user', content: "A: #{a}\n\nB: #{b}" }
406
421
  ],
407
422
  schema: { type: 'object', properties: { contradicts: { type: 'boolean' } } },
408
423
  caller: { extension: 'lex-apollo', runner: 'knowledge' }
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Apollo
6
- VERSION = '0.4.18'
6
+ VERSION = '0.4.19'
7
7
  end
8
8
  end
9
9
  end
@@ -9,6 +9,27 @@ RSpec.describe 'Apollo Contradiction Detection' do
9
9
  it 'returns false when LLM unavailable' do
10
10
  expect(knowledge.send(:llm_detects_conflict?, 'sky is blue', 'sky is red')).to be false
11
11
  end
12
+
13
+ context 'when LLM is available' do
14
+ let(:llm_mod) do
15
+ Module.new do
16
+ def self.respond_to?(*) = true
17
+ def self.structured(**) = { data: { contradicts: true } }
18
+ end
19
+ end
20
+
21
+ before { stub_const('Legion::LLM', llm_mod) }
22
+
23
+ it 'truncates content longer than CONFLICT_CHECK_MAX_CHARS' do
24
+ long_text = 'x' * 10_000
25
+ allow(llm_mod).to receive(:structured).and_return({ data: { contradicts: false } })
26
+ knowledge.send(:llm_detects_conflict?, long_text, long_text)
27
+ expect(llm_mod).to have_received(:structured) do |**kwargs|
28
+ user_msg = kwargs[:messages].find { |m| m[:role] == 'user' }[:content]
29
+ expect(user_msg.length).to be < 10_000
30
+ end
31
+ end
32
+ end
12
33
  end
13
34
 
14
35
  describe '#detect_contradictions' do
@@ -44,6 +44,20 @@ RSpec.describe Legion::Extensions::Apollo::GaiaIntegration do
44
44
  )
45
45
  expect(result).to eq({ success: true })
46
46
  end
47
+
48
+ it 'passes :observation as content_type regardless of domain' do
49
+ client_double = instance_double(Legion::Extensions::Apollo::Client)
50
+ allow(Legion::Extensions::Apollo::Client).to receive(:new).and_return(client_double)
51
+ allow(client_double).to receive(:store_knowledge).and_return({ success: true })
52
+
53
+ described_class.publish_insight(
54
+ { confidence: 0.9, novelty: 0.5, content: 'insight', domain: 'clinical' },
55
+ agent_id: 'test-agent'
56
+ )
57
+ expect(client_double).to have_received(:store_knowledge).with(
58
+ hash_including(content_type: :observation)
59
+ )
60
+ end
47
61
  end
48
62
 
49
63
  describe 'entity watchdog phase handler' do
@@ -45,10 +45,41 @@ RSpec.describe Legion::Extensions::Apollo::Runners::Knowledge do
45
45
  expect(result[:source_agent]).to eq('worker-1')
46
46
  end
47
47
 
48
- it 'rejects invalid content_type' do
49
- expect do
50
- runner.store_knowledge(content: 'test', content_type: :invalid)
51
- end.to raise_error(ArgumentError, /content_type/)
48
+ it 'falls back to :observation for unrecognized content_type' do
49
+ result = runner.store_knowledge(content: 'test', content_type: 'invalid_type')
50
+ expect(result[:content_type]).to eq(:observation)
51
+ end
52
+
53
+ it 'normalizes LLM-provided content_type "reasoning" to :concept' do
54
+ result = runner.store_knowledge(content: 'test', content_type: 'reasoning')
55
+ expect(result[:content_type]).to eq(:concept)
56
+ end
57
+
58
+ it 'normalizes "text" to :observation' do
59
+ result = runner.store_knowledge(content: 'test', content_type: 'text')
60
+ expect(result[:content_type]).to eq(:observation)
61
+ end
62
+
63
+ it 'normalizes "text/plain" to :observation' do
64
+ result = runner.store_knowledge(content: 'test', content_type: 'text/plain')
65
+ expect(result[:content_type]).to eq(:observation)
66
+ end
67
+
68
+ it 'strips leading colon from ":fact"' do
69
+ result = runner.store_knowledge(content: 'test', content_type: ':fact')
70
+ expect(result[:content_type]).to eq(:fact)
71
+ end
72
+
73
+ it 'normalizes "inference" to :association' do
74
+ result = runner.store_knowledge(content: 'test', content_type: 'inference')
75
+ expect(result[:content_type]).to eq(:association)
76
+ end
77
+
78
+ it 'accepts all valid CONTENT_TYPES unchanged' do
79
+ %i[fact concept procedure association observation].each do |ct|
80
+ result = runner.store_knowledge(content: 'test', content_type: ct)
81
+ expect(result[:content_type]).to eq(ct)
82
+ end
52
83
  end
53
84
  end
54
85
 
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.4.18
4
+ version: 0.4.19
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity