legion-data 1.8.9 → 1.9.0

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: 18c9b2db6f7648fbad03b852d4565fe1c0ad0f46173a2130a6184d4b47b59d8a
4
- data.tar.gz: 2b31d060c165c7656967642d80760ff7e94968f2d069633f9b7b407ea19f8808
3
+ metadata.gz: 92fba520187d3940bae5ac79e43f2155b16a0c7e3949e664233dd012871d56e7
4
+ data.tar.gz: da5ff3d58c176ccb65fc03bdc6855318acba28b4974cf76e8b986eec2b70d5c3
5
5
  SHA512:
6
- metadata.gz: acce9df23ea4557560b5fc8b18bbb5a06acef0cdefbe75d9b9168371043575ffd8285b069b83b69d4b17f898f349702a47f780ebcd8de44fa9fd2dbdd60ff0a0
7
- data.tar.gz: 5f4c6c236e41ff258f5f2489435b0d6f76072e764d242f91d0c8e735ef6b369daa778fc3f1fcccf51192f2d9ad4006702e86be68354b0b84510eba60767e5471
6
+ metadata.gz: 748443ee5df5494f60e080a6ad6405df97ed4166ed71e58fc58159b540ab2c9748195447c2a05e697a5ecc00889b17d7ff84337d0b718644c27c1312a6c47ab5
7
+ data.tar.gz: 9417467c46a3a4b1651c68760ff682b07648cc021d51601bc844fb1afdccf68ae0a807f897823a818450658e8503feea7e409ecaacd2b80c757dd0847a037b22
data/.rubocop.yml CHANGED
@@ -1,31 +1,56 @@
1
1
  inherit_gem:
2
2
  rubocop-legion: config/core.yml
3
-
4
- Metrics/ParameterLists:
5
- Max: 8
6
-
7
- Metrics/BlockLength:
8
- Max: 40
3
+ AllCops:
4
+ TargetRubyVersion: 3.4
5
+ NewCops: enable
6
+ SuggestExtensions: false
7
+ Layout/LineLength:
8
+ Max: 160
9
+ Layout/SpaceAroundEqualsInParameterDefault:
10
+ EnforcedStyle: space
11
+ Layout/HashAlignment:
12
+ EnforcedHashRocketStyle: table
13
+ EnforcedColonStyle: table
14
+ Metrics/MethodLength:
15
+ Max: 50
16
+ Metrics/ClassLength:
17
+ Max: 1500
18
+ Metrics/ModuleLength:
19
+ Max: 1500
20
+ Naming/VariableNumber:
9
21
  Exclude:
10
22
  - 'spec/**/*'
11
- - 'lib/legion/data/migrations/**/*'
12
-
13
- Naming/VariableNumber:
23
+ - lib/legion/data/connection.rb
24
+ Legion/Framework/EagerSequelModel:
14
25
  Enabled: false
15
-
16
- Style/FileOpen:
26
+ Metrics/BlockLength:
27
+ Max: 100
17
28
  Exclude:
18
- - 'lib/legion/data/connection.rb'
19
-
20
- # Pre-existing patterns — suppress until addressed in a dedicated cleanup PR
29
+ - 'spec/**/*'
30
+ - 'lib/legion/data/migrations/*'
21
31
  ThreadSafety/ClassInstanceVariable:
22
32
  Enabled: false
23
-
24
33
  ThreadSafety/ClassAndModuleAttributes:
25
34
  Enabled: false
35
+ Metrics/AbcSize:
36
+ Max: 60
37
+ Exclude:
38
+ - 'spec/**/*'
39
+ Metrics/CyclomaticComplexity:
40
+ Max: 15
41
+ Exclude:
42
+ - 'spec/**/*'
43
+ Metrics/PerceivedComplexity:
44
+ Max: 17
45
+ Exclude:
46
+ - 'spec/**/*'
26
47
 
27
- Legion/RescueLogging/NoCapture:
48
+ Style/Documentation:
28
49
  Enabled: false
29
-
30
- Legion/Framework/EagerSequelModel:
50
+ Style/SymbolArray:
51
+ Enabled: true
52
+ Style/FrozenStringLiteralComment:
53
+ Enabled: true
54
+ EnforcedStyle: always
55
+ Naming/PredicateMethod:
31
56
  Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,15 @@
1
1
  # Legion::Data Changelog
2
2
 
3
+ ## [1.9.0] - 2026-06-01
4
+
5
+ ### Added
6
+
7
+ - Migration 123: audit columns on `llm_tool_calls` — `tool_arguments_json` (TEXT), `tool_result_json` (TEXT), `tool_category` (String(64)), `data_handling_classification` (String(32)), `policy_decision` (String(32)), `requires_human_approval` (Boolean) plus indexes on `tool_category`, `data_handling_classification`, `policy_decision`.
8
+ - Migration 124: audit columns on `llm_tool_call_attempts` — `attempt_input_json` (TEXT), `attempt_output_json` (TEXT), `error_details_json` (TEXT).
9
+ - Migration 125: audit columns on `llm_escalation_events` — `history_json` (TEXT), `outcome` (String(32)), `total_attempts` (Integer) plus index on `outcome`.
10
+ - Migration 126: audit columns on `llm_message_inference_responses` — `route_attempts` (Integer, default 0), `escalation_chain_ref` (String(128)) plus index on `escalation_chain_ref`. Skips `response_content_hash` (already exists since migration 080).
11
+ - Migration 127: audit columns on `llm_message_inference_requests` — `parent_request_id` (Integer, self-referencing FK on_delete: :set_null). Skips `request_content_hash`, `curation_strategy`, `tool_policy` (all already exist since migration 079).
12
+
3
13
  ## [1.8.9] - 2026-05-26
4
14
 
5
15
  ### Changed
@@ -198,7 +198,12 @@ module Legion
198
198
  connected: Legion::Settings[:data][:connected],
199
199
  fallback_active: @fallback_active || false,
200
200
  configured_adapter: Legion::Settings[:data][:adapter]&.to_sym || :sqlite,
201
- sequel_alive: (begin; !@sequel&.test_connection.nil?; rescue StandardError; false; end)
201
+ sequel_alive: (begin
202
+ !@sequel&.test_connection.nil?
203
+ rescue StandardError => e
204
+ log.debug("connection health check failed: #{e.message}")
205
+ false
206
+ end)
202
207
  }
203
208
  end
204
209
 
@@ -48,9 +48,11 @@ module Legion
48
48
  @connection.run('PRAGMA journal_mode=WAL')
49
49
  @connection.run('PRAGMA busy_timeout=30000')
50
50
  @connection.run('PRAGMA synchronous=NORMAL')
51
+ @connection.run('PRAGMA cache_size=-20000')
52
+ @connection.run('PRAGMA mmap_size=268435456')
51
53
  @connected = true
52
54
  run_migrations
53
- log.info "Legion::Data::Local connected to #{db_file} (WAL mode, 30s busy_timeout)"
55
+ log.info "Legion::Data::Local connected to #{db_file} (WAL mode, 30s busy_timeout, 20MB cache, 256MB mmap)"
54
56
  rescue StandardError => e
55
57
  handle_exception(e, level: :error, handled: false, operation: :local_setup, database: db_file)
56
58
  raise
@@ -99,7 +101,7 @@ module Legion
99
101
  stats[:file_size] = File.size(@db_path) if @db_path && File.exist?(@db_path)
100
102
 
101
103
  %w[page_size page_count freelist_count journal_mode
102
- wal_autocheckpoint cache_size busy_timeout].each do |pragma|
104
+ wal_autocheckpoint cache_size busy_timeout mmap_size].each do |pragma|
103
105
  val = begin
104
106
  @connection.fetch("PRAGMA #{pragma}").single_value
105
107
  rescue StandardError => e
@@ -3,28 +3,19 @@
3
3
  Sequel.migration do
4
4
  up do
5
5
  alter_table(:llm_tool_calls) do
6
+ drop_index :identity_principal_id, name: :idx_tool_calls_identity_principal_id, if_exists: true
6
7
  set_column_allow_null :message_inference_response_id
7
- end
8
-
9
- # SQLite's set_column_allow_null recreates the table internally, which
10
- # drops partial indexes invisible to Sequel's indexes() method. Restore
11
- # the partial index from migration 109 (no-op on PG where it survives).
12
- alter_table(:llm_tool_calls) do
13
- add_index :identity_principal_id, name: :idx_tool_calls_identity_principal_id,
14
- where: Sequel.negate(identity_principal_id: nil),
15
- ignore_errors: true
8
+ add_index :identity_principal_id, name: :idx_tool_calls_identity_principal_id,
9
+ where: Sequel.negate(identity_principal_id: nil)
16
10
  end
17
11
  end
18
12
 
19
13
  down do
20
14
  alter_table(:llm_tool_calls) do
15
+ drop_index :identity_principal_id, name: :idx_tool_calls_identity_principal_id, if_exists: true
21
16
  set_column_not_null :message_inference_response_id
22
- end
23
-
24
- alter_table(:llm_tool_calls) do
25
- add_index :identity_principal_id, name: :idx_tool_calls_identity_principal_id,
26
- where: Sequel.negate(identity_principal_id: nil),
27
- ignore_errors: true
17
+ add_index :identity_principal_id, name: :idx_tool_calls_identity_principal_id,
18
+ where: Sequel.negate(identity_principal_id: nil)
28
19
  end
29
20
  end
30
21
  end
@@ -10,8 +10,13 @@ Sequel.migration do
10
10
 
11
11
  down do
12
12
  alter_table(:llm_tool_calls) do
13
- drop_index :conversation_id
14
- drop_foreign_key :conversation_id
13
+ drop_column :conversation_id
14
+ # On SQLite, drop_column triggers table recreation which silently destroys
15
+ # partial indexes. Recreate the one from migration 109.
16
+ add_index :identity_principal_id,
17
+ name: :idx_tool_calls_identity_principal_id,
18
+ where: Sequel.negate(identity_principal_id: nil),
19
+ if_not_exists: true
15
20
  end
16
21
  end
17
22
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The Great Convergence (part 1 of 2): add entity_type column to audit_records on all adapters.
4
+ # Migration 068 added this column on PostgreSQL only.
5
+ # Production is already at 117+, so this migration only runs on SQLite/MySQL
6
+ # deployments that missed it due to the postgres-only guard in migration 068.
7
+
8
+ Sequel.migration do
9
+ up do
10
+ return unless table_exists?(:audit_records)
11
+
12
+ existing = schema(:audit_records).map(&:first)
13
+ return if existing.include?(:entity_type)
14
+
15
+ alter_table(:audit_records) do
16
+ add_column :entity_type, String, size: 100, null: true
17
+ end
18
+
19
+ add_index :audit_records, :entity_type, name: :idx_audit_records_entity_type, if_not_exists: true
20
+ end
21
+
22
+ down do
23
+ return unless table_exists?(:audit_records)
24
+
25
+ alter_table(:audit_records) do
26
+ drop_column :entity_type if schema(:audit_records).any? { |col, _| col == :entity_type }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The Great Convergence (part 2): create apollo_relations, apollo_expertise,
4
+ # apollo_access_log, and apollo_operations on all adapters.
5
+ #
6
+ # Migration 012 (postgres-only) created apollo_relations, apollo_expertise,
7
+ # and apollo_access_log.
8
+ # Migration 047 (postgres-only) created apollo_operations.
9
+ # These tables were never created on SQLite/MySQL deployments.
10
+
11
+ Sequel.migration do
12
+ up do
13
+ # apollo_relations
14
+ unless table_exists?(:apollo_relations)
15
+ create_table(:apollo_relations) do
16
+ primary_key :id
17
+ String :from_entry_id, size: 36, null: false
18
+ String :to_entry_id, size: 36, null: false
19
+ String :relation_type, null: false, size: 50
20
+ Float :weight, default: 1.0
21
+ String :source_agent, size: 255
22
+ DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
23
+
24
+ index :from_entry_id, name: :idx_apollo_rel_from
25
+ index :to_entry_id, name: :idx_apollo_rel_to
26
+ index :relation_type, name: :idx_apollo_rel_type
27
+ index %i[from_entry_id relation_type], name: :idx_apollo_rel_composite
28
+ end
29
+ end
30
+
31
+ # apollo_expertise
32
+ unless table_exists?(:apollo_expertise)
33
+ create_table(:apollo_expertise) do
34
+ primary_key :id
35
+ String :agent_id, null: false, size: 255, index: { name: :idx_apollo_exp_agent }
36
+ String :domain, null: false, size: 255, index: { name: :idx_apollo_exp_domain }
37
+ Float :proficiency, default: 0.0
38
+ Integer :entry_count, default: 0
39
+ DateTime :last_active_at, default: Sequel::CURRENT_TIMESTAMP
40
+
41
+ index %i[agent_id domain], name: :idx_apollo_exp_composite
42
+ end
43
+ end
44
+
45
+ # apollo_access_log
46
+ unless table_exists?(:apollo_access_log)
47
+ create_table(:apollo_access_log) do
48
+ primary_key :id
49
+ String :entry_id, size: 36, index: { name: :idx_apollo_access_entry }
50
+ String :agent_id, null: false, size: 255
51
+ String :action, null: false, size: 20
52
+ DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
53
+ end
54
+ end
55
+
56
+ # apollo_operations
57
+ unless table_exists?(:apollo_operations)
58
+ create_table(:apollo_operations) do
59
+ primary_key :id
60
+ String :operation, size: 50, null: false
61
+ String :actor, size: 255, null: false
62
+ String :target_type, size: 50
63
+ String :target_ids, text: true # serialized array; PG uses INTEGER[]
64
+ String :summary, text: true
65
+ String :detail, text: true, default: '{}' # serialized json; PG uses JSONB
66
+ String :old_state, text: true
67
+ String :new_state, text: true
68
+ String :reason, text: true
69
+ String :principal_id, size: 255
70
+ DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
71
+
72
+ index :created_at, name: :idx_apollo_ops_created
73
+ index :operation, name: :idx_apollo_ops_operation
74
+ index :actor, name: :idx_apollo_ops_actor
75
+ end
76
+ end
77
+ end
78
+
79
+ down do
80
+ drop_table :apollo_operations if table_exists?(:apollo_operations)
81
+ drop_table :apollo_access_log if table_exists?(:apollo_access_log)
82
+ drop_table :apollo_expertise if table_exists?(:apollo_expertise)
83
+ drop_table :apollo_relations if table_exists?(:apollo_relations)
84
+ end
85
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The Great Convergence (part 3): add missing indexes on apollo_* tables.
4
+ #
5
+ # Migration 047 (postgres-only) created dozens of indexes on apollo_entries,
6
+ # apollo_relations, apollo_expertise, apollo_operations, and
7
+ # apollo_entries_archive. These were never created on SQLite/MySQL.
8
+ #
9
+ # Vector indexes (hnsw) and GIN indexes are postgres-specific and skipped.
10
+ #
11
+ # NOTE: Uses raw CREATE INDEX IF NOT EXISTS SQL because Sequel's add_index
12
+ # inside alter_table does not honor if_not_exists on SQLite (it triggers
13
+ # table recreation which fails if the index already exists).
14
+
15
+ Sequel.migration do
16
+ up do
17
+ return unless table_exists?(:apollo_entries)
18
+
19
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_submitted_by ON apollo_entries (submitted_by)'
20
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_submitted_from ON apollo_entries (submitted_from)'
21
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_status ON apollo_entries (status)'
22
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_confidence ON apollo_entries (confidence)'
23
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_created ON apollo_entries (created_at)'
24
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_updated ON apollo_entries (updated_at)'
25
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_domain ON apollo_entries (knowledge_domain)'
26
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_source_agent ON apollo_entries (source_agent)'
27
+ run "CREATE UNIQUE INDEX IF NOT EXISTS idx_apollo_content_hash ON apollo_entries (content_hash) WHERE status != 'archived'"
28
+ run "CREATE INDEX IF NOT EXISTS idx_apollo_active ON apollo_entries (id) WHERE status IN ('candidate', 'confirmed', 'disputed')"
29
+ run "CREATE INDEX IF NOT EXISTS idx_apollo_decay_target ON apollo_entries (updated_at) WHERE status != 'archived'"
30
+ run "CREATE INDEX IF NOT EXISTS idx_apollo_candidates ON apollo_entries (status, source_provider, source_channel) WHERE status = 'candidate'"
31
+
32
+ return unless table_exists?(:apollo_entries_archive)
33
+
34
+ run 'CREATE INDEX IF NOT EXISTS idx_archive_content_hash ON apollo_entries_archive (content_hash)'
35
+ run 'CREATE INDEX IF NOT EXISTS idx_archive_source_agent ON apollo_entries_archive (source_agent)'
36
+ run 'CREATE INDEX IF NOT EXISTS idx_archive_archived_at ON apollo_entries_archive (archived_at)'
37
+
38
+ return unless table_exists?(:apollo_relations)
39
+
40
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_rel_from ON apollo_relations (from_entry_id)'
41
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_rel_to ON apollo_relations (to_entry_id)'
42
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_rel_type ON apollo_relations (relation_type)'
43
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_rel_composite ON apollo_relations (from_entry_id, relation_type)'
44
+
45
+ return unless table_exists?(:apollo_expertise)
46
+
47
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_exp_agent ON apollo_expertise (agent_id)'
48
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_exp_domain ON apollo_expertise (domain)'
49
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_exp_composite ON apollo_expertise (agent_id, domain)'
50
+
51
+ return unless table_exists?(:apollo_operations)
52
+
53
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_ops_created ON apollo_operations (created_at)'
54
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_ops_operation ON apollo_operations (operation)'
55
+ run 'CREATE INDEX IF NOT EXISTS idx_apollo_ops_actor ON apollo_operations (actor)'
56
+ end
57
+
58
+ down do
59
+ %w[
60
+ idx_apollo_submitted_by idx_apollo_submitted_from idx_apollo_status
61
+ idx_apollo_confidence idx_apollo_created idx_apollo_updated
62
+ idx_apollo_domain idx_apollo_source_agent idx_apollo_content_hash
63
+ idx_apollo_active idx_apollo_decay_target idx_apollo_candidates
64
+ idx_archive_content_hash idx_archive_source_agent idx_archive_archived_at
65
+ idx_apollo_rel_from idx_apollo_rel_to idx_apollo_rel_type
66
+ idx_apollo_rel_composite
67
+ idx_apollo_exp_agent idx_apollo_exp_domain idx_apollo_exp_composite
68
+ idx_apollo_ops_created idx_apollo_ops_operation idx_apollo_ops_actor
69
+ ].each do |name|
70
+ run "DROP INDEX IF EXISTS #{name}"
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add cached_input_tokens and cache_creation_tokens to llm_message_inference_metrics.
4
+ # Tracks cache hit tokens (read from cache) and cache write tokens separately from
5
+ # standard input/output token counts.
6
+ #
7
+ # See: https://github.com/LegionIO/legion-data/issues/55
8
+
9
+ Sequel.migration do
10
+ up do
11
+ alter_table(:llm_message_inference_metrics) do
12
+ add_column :cached_input_tokens, Integer, null: false, default: 0
13
+ add_column :cache_creation_tokens, Integer, null: false, default: 0
14
+ end
15
+ end
16
+
17
+ down do
18
+ alter_table(:llm_message_inference_metrics) do
19
+ drop_column :cache_creation_tokens
20
+ drop_column :cached_input_tokens
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Migration 115 had a bug: it guarded the up block on the :definition column
4
+ # (added by migration 055) instead of :runtime_caller_class. This means on
5
+ # deployments where :definition existed but :runtime_caller_class did not,
6
+ # the columns were added correctly. But the guard was checking the wrong
7
+ # thing, and the down block has no guard at all.
8
+ #
9
+ # This migration ensures the columns exist on any deployment that might have
10
+ # skipped them due to the 115 bug.
11
+
12
+ Sequel.migration do
13
+ up do
14
+ if table_exists?(:llm_message_inference_requests)
15
+ cols = schema(:llm_message_inference_requests).map(&:first)
16
+ unless cols.include?(:runtime_caller_class) && cols.include?(:runtime_caller_client)
17
+ alter_table(:llm_message_inference_requests) do
18
+ add_column :runtime_caller_class, String, size: 255, null: true, index: true unless cols.include?(:runtime_caller_class)
19
+ add_column :runtime_caller_client, String, size: 255, null: true unless cols.include?(:runtime_caller_client)
20
+ end
21
+ end
22
+ end
23
+ end
24
+
25
+ down do
26
+ if table_exists?(:llm_message_inference_requests)
27
+ cols = schema(:llm_message_inference_requests).map(&:first)
28
+ if cols.include?(:runtime_caller_class) || cols.include?(:runtime_caller_client)
29
+ alter_table(:llm_message_inference_requests) do
30
+ drop_column :runtime_caller_client if cols.include?(:runtime_caller_client)
31
+ drop_column :runtime_caller_class if cols.include?(:runtime_caller_class)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ alter_table(:llm_tool_calls) do
6
+ add_column :tool_arguments_json, :text, null: true
7
+ add_column :tool_result_json, :text, null: true
8
+ add_column :tool_category, String, size: 64, null: true
9
+ add_column :data_handling_classification, String, size: 32, null: true
10
+ add_column :policy_decision, String, size: 32, null: true
11
+ add_column :requires_human_approval, TrueClass, null: true
12
+ add_index :tool_category, name: :idx_tool_calls_tool_category
13
+ add_index :data_handling_classification, name: :idx_tool_calls_data_handling_classification
14
+ add_index :policy_decision, name: :idx_tool_calls_policy_decision
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ alter_table(:llm_tool_call_attempts) do
6
+ add_column :attempt_input_json, :text, null: true
7
+ add_column :attempt_output_json, :text, null: true
8
+ add_column :error_details_json, :text, null: true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ next unless table_exists?(:llm_escalation_events)
6
+
7
+ alter_table(:llm_escalation_events) do
8
+ add_column :history_json, :text, null: true
9
+ add_column :outcome, String, size: 32, null: true
10
+ add_column :total_attempts, Integer, null: true
11
+ add_index :outcome, name: :idx_escalation_events_outcome
12
+ end
13
+ end
14
+
15
+ down do
16
+ next unless table_exists?(:llm_escalation_events)
17
+
18
+ alter_table(:llm_escalation_events) do
19
+ drop_index :outcome, name: :idx_escalation_events_outcome
20
+ drop_column :total_attempts
21
+ drop_column :outcome
22
+ drop_column :history_json
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE: response_content_hash already exists (migration 080) — skipped.
4
+
5
+ Sequel.migration do
6
+ change do
7
+ alter_table(:llm_message_inference_responses) do
8
+ add_column :route_attempts, Integer, null: true, default: 0
9
+ add_column :escalation_chain_ref, String, size: 128, null: true
10
+ add_index :escalation_chain_ref, name: :idx_inference_responses_escalation_chain_ref
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE: request_content_hash, curation_strategy, and tool_policy already exist
4
+ # (migration 079) — all skipped. Only parent_request_id is new.
5
+
6
+ Sequel.migration do
7
+ change do
8
+ alter_table(:llm_message_inference_requests) do
9
+ add_foreign_key :parent_request_id, :llm_message_inference_requests, null: true, on_delete: :set_null
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ up do
5
+ %i[apollo_access_log memory_traces memory_associations audit_log audit_records].each do |table|
6
+ next unless table_exists?(table)
7
+
8
+ cols = schema(table).map(&:first)
9
+ alter_table(table) do
10
+ add_column :identity_principal_id, Integer, null: true unless cols.include?(:identity_principal_id)
11
+ add_column :identity_id, Integer, null: true unless cols.include?(:identity_id)
12
+ add_column :identity_canonical_name, String, size: 255, null: true unless cols.include?(:identity_canonical_name)
13
+ end
14
+ end
15
+ end
16
+
17
+ down do
18
+ %i[apollo_access_log memory_traces memory_associations audit_log audit_records].each do |table|
19
+ next unless table_exists?(table)
20
+
21
+ cols = schema(table).map(&:first)
22
+ alter_table(table) do
23
+ drop_column :identity_canonical_name if cols.include?(:identity_canonical_name)
24
+ drop_column :identity_id if cols.include?(:identity_id)
25
+ drop_column :identity_principal_id if cols.include?(:identity_principal_id)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -7,7 +7,8 @@ module Legion
7
7
  module ModelHelpers
8
8
  def self.table_available?(table_name)
9
9
  Legion::Data::Connection.sequel&.table_exists?(table_name)
10
- rescue StandardError
10
+ rescue StandardError => e
11
+ log.error("table availability check failed for #{table_name}: #{e.message}")
11
12
  false
12
13
  end
13
14
  end
@@ -117,7 +117,8 @@ module Legion
117
117
 
118
118
  def load_event_file(path, sub_namespace)
119
119
  ::JSON.parse(File.binread(path), symbolize_names: true)
120
- rescue Errno::ENOENT
120
+ rescue Errno::ENOENT => e
121
+ log.debug("spool event file not found: #{path}: #{e.message}")
121
122
  nil
122
123
  rescue ::JSON::ParserError, EOFError, ArgumentError => e
123
124
  quarantine_corrupt_file(path, sub_namespace, e)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Legion
4
4
  module Data
5
- VERSION = '1.8.9'
5
+ VERSION = '1.9.0'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: legion-data
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.8.9
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esity
@@ -264,6 +264,17 @@ files:
264
264
  - lib/legion/data/migrations/115_add_runtime_caller_columns.rb
265
265
  - lib/legion/data/migrations/116_make_tool_calls_response_id_nullable.rb
266
266
  - lib/legion/data/migrations/117_add_conversation_id_to_llm_tool_calls.rb
267
+ - lib/legion/data/migrations/118_add_entity_type_to_audit_records.rb
268
+ - lib/legion/data/migrations/119_create_missing_apollo_tables.rb
269
+ - lib/legion/data/migrations/120_add_missing_apollo_indexes.rb
270
+ - lib/legion/data/migrations/121_add_cache_token_metrics.rb
271
+ - lib/legion/data/migrations/122_ensure_runtime_caller_columns.rb
272
+ - lib/legion/data/migrations/123_add_llm_tool_calls_audit_columns.rb
273
+ - lib/legion/data/migrations/124_add_llm_tool_call_attempts_audit_columns.rb
274
+ - lib/legion/data/migrations/125_add_llm_escalation_events_audit_columns.rb
275
+ - lib/legion/data/migrations/126_add_llm_message_inference_responses_audit_columns.rb
276
+ - lib/legion/data/migrations/127_add_llm_message_inference_requests_audit_columns.rb
277
+ - lib/legion/data/migrations/128_add_identity_columns_to_shared_tables.rb
267
278
  - lib/legion/data/model.rb
268
279
  - lib/legion/data/models/apollo/access_log.rb
269
280
  - lib/legion/data/models/apollo/entries.rb