Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [0.6.15] - 2026-05-07

### Fixed
- Knowledge ingest now sends Apollo chunk provenance through `context:` while retaining metadata compatibility, so source file, heading, section path, chunk index, and token count persist with document chunks.
- Batch embedding now falls back to per-chunk `Legion::LLM.embed` when `embed_batch` is unavailable.
- Retired corpus files now emit explicit observation entries tagged `retired` instead of using an Apollo-unknown `document_retired` content type.
- Retrieval and synthesis failures are logged through helper-based exception handling, and synthesis returns `nil` instead of presenting error strings as answers.
- Monitor-only installs now enable the maintenance actor without requiring `corpus_path`.
- Quality reports count Apollo query access logs with the `query` action recorded by lex-apollo.

## [0.6.14] - 2026-05-06

### Changed
Expand Down
10 changes: 7 additions & 3 deletions lib/legion/extensions/knowledge/actors/maintenance_runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@ def time
end

def enabled? # rubocop:disable Legion/Extension/ActorEnabledSideEffects
return false unless corpus_path && !corpus_path.empty?
return true if corpus_path && !corpus_path.empty?

true
Runners::Monitor.resolve_monitors.any?
rescue StandardError => e
handle_exception(e, level: :warn, operation: 'knowledge.maintenance_runner.enabled')
false
end

def args
{ path: corpus_path }
path = corpus_path
return { path: path } if path && !path.empty?

monitors = Runners::Monitor.resolve_monitors
monitors.any? ? { path: monitors.first[:path] } : { path: nil }
end

private
Expand Down
24 changes: 17 additions & 7 deletions lib/legion/extensions/knowledge/runners/ingest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ def build_exists_map(chunks)
private_class_method :build_exists_map

def llm_embed_available?
defined?(Legion::LLM) && Legion::LLM.respond_to?(:embed_batch)
defined?(Legion::LLM) && (Legion::LLM.respond_to?(:embed_batch) || Legion::LLM.respond_to?(:embed))
end
private_class_method :llm_embed_available?

Expand All @@ -250,9 +250,19 @@ def paired_without_embed(chunks, exists_map)
private_class_method :paired_without_embed

def build_embed_map(needs_embed)
results = Legion::LLM.embed_batch(needs_embed.map { |c| c[:content] }) # rubocop:disable Legion/HelperMigration/DirectLlm
results.each_with_object({}) do |r, h|
h[needs_embed[r[:index]][:content_hash]] = r[:vector] unless r[:error]
if Legion::LLM.respond_to?(:embed_batch)
results = Legion::LLM.embed_batch(needs_embed.map { |c| c[:content] }) # rubocop:disable Legion/HelperMigration/DirectLlm
results.each_with_object({}) do |r, h|
h[needs_embed[r[:index]][:content_hash]] = r[:vector] unless r[:error]
end
else
needs_embed.each_with_object({}) do |chunk, h|
result = Legion::LLM.embed(chunk[:content]) # rubocop:disable Legion/HelperMigration/DirectLlm
vector = result.is_a?(Hash) ? result[:vector] : result
h[chunk[:content_hash]] = vector if vector.is_a?(Array) && vector.any?
rescue StandardError => e
log.warn(e.message)
end
end
rescue StandardError => e
handle_exception(e, level: :warn, operation: 'knowledge.ingest.build_embed_map')
Expand Down Expand Up @@ -328,10 +338,10 @@ def retire_file(file_path:)
return unless Legion::Apollo.respond_to?(:ingest) && Legion::Apollo.started?

Legion::Apollo.ingest( # rubocop:disable Legion/HelperMigration/DirectKnowledge
content: file_path,
content_type: 'document_retired',
content: "Retired document: #{file_path}",
content_type: 'observation',
tags: [file_path, 'retired', 'document_chunk'].uniq,
metadata: { source_file: file_path, retired: true }
context: { source_file: file_path, retired: true }
)
rescue StandardError => e
handle_exception(e, level: :warn, operation: 'knowledge.ingest.retire_file', file_path: file_path)
Expand Down
2 changes: 1 addition & 1 deletion lib/legion/extensions/knowledge/runners/maintenance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ def quality_summary
def query_count
return 0 unless Helpers::ApolloModels.access_log_available?

Helpers::ApolloModels.access_log.where(action: 'knowledge_query').count
Helpers::ApolloModels.access_log.where(action: 'query').count
rescue StandardError => e
handle_exception(e, level: :warn, operation: 'knowledge.maintenance.query_count')
0
Expand Down
9 changes: 6 additions & 3 deletions lib/legion/extensions/knowledge/runners/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,14 @@ def synthesize_answer(question, chunks)
"Context:\n#{context_text}\n\nQuestion: #{question}\n\nAnswer:"
end

result = llm_chat(message: prompt, caller: { extension: 'lex-knowledge' })
result.is_a?(Hash) ? result[:content] : result
result = Legion::LLM.chat( # rubocop:disable Legion/HelperMigration/DirectLlm
message: prompt,
caller: { extension: 'lex-knowledge' }
)
result.is_a?(Hash) ? result[:content] : result.content
rescue StandardError => e
handle_exception(e, level: :warn, operation: 'knowledge.query.synthesize_answer')
"Error generating answer: #{e.message}"
nil
end
private_class_method :synthesize_answer

Expand Down
2 changes: 1 addition & 1 deletion lib/legion/extensions/knowledge/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Legion
module Extensions
module Knowledge
VERSION = '0.6.14'
VERSION = '0.6.15'
end
end
end
Loading