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 +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/legion/extensions/apollo/gaia_integration.rb +1 -1
- data/lib/legion/extensions/apollo/runners/knowledge.rb +20 -5
- data/lib/legion/extensions/apollo/version.rb +1 -1
- data/spec/legion/extensions/apollo/contradiction_spec.rb +21 -0
- data/spec/legion/extensions/apollo/gaia_integration_spec.rb +14 -0
- data/spec/legion/extensions/apollo/runners/knowledge_spec.rb +35 -4
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1bf0ca8790d13fc3d262ede810ce50a47146dc37743e8806696de970b8a0385a
|
|
4
|
+
data.tar.gz: 1f1a9115e1a1bb36423150a7290a82352f07aef7aa41f0b4b7da8e0ff5b76f94
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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:
|
|
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
|
|
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: #{
|
|
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' }
|
|
@@ -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 '
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|