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 +4 -4
- data/.rubocop.yml +43 -18
- data/CHANGELOG.md +10 -0
- data/lib/legion/data/connection.rb +6 -1
- data/lib/legion/data/local.rb +4 -2
- data/lib/legion/data/migrations/116_make_tool_calls_response_id_nullable.rb +6 -15
- data/lib/legion/data/migrations/117_add_conversation_id_to_llm_tool_calls.rb +7 -2
- data/lib/legion/data/migrations/118_add_entity_type_to_audit_records.rb +29 -0
- data/lib/legion/data/migrations/119_create_missing_apollo_tables.rb +85 -0
- data/lib/legion/data/migrations/120_add_missing_apollo_indexes.rb +73 -0
- data/lib/legion/data/migrations/121_add_cache_token_metrics.rb +23 -0
- data/lib/legion/data/migrations/122_ensure_runtime_caller_columns.rb +36 -0
- data/lib/legion/data/migrations/123_add_llm_tool_calls_audit_columns.rb +17 -0
- data/lib/legion/data/migrations/124_add_llm_tool_call_attempts_audit_columns.rb +11 -0
- data/lib/legion/data/migrations/125_add_llm_escalation_events_audit_columns.rb +25 -0
- data/lib/legion/data/migrations/126_add_llm_message_inference_responses_audit_columns.rb +13 -0
- data/lib/legion/data/migrations/127_add_llm_message_inference_requests_audit_columns.rb +12 -0
- data/lib/legion/data/migrations/128_add_identity_columns_to_shared_tables.rb +29 -0
- data/lib/legion/data/models/apollo/model_helpers.rb +2 -1
- data/lib/legion/data/spool.rb +2 -1
- data/lib/legion/data/version.rb +1 -1
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 92fba520187d3940bae5ac79e43f2155b16a0c7e3949e664233dd012871d56e7
|
|
4
|
+
data.tar.gz: da5ff3d58c176ccb65fc03bdc6855318acba28b4974cf76e8b986eec2b70d5c3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
Max:
|
|
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
|
-
-
|
|
12
|
-
|
|
13
|
-
Naming/VariableNumber:
|
|
23
|
+
- lib/legion/data/connection.rb
|
|
24
|
+
Legion/Framework/EagerSequelModel:
|
|
14
25
|
Enabled: false
|
|
15
|
-
|
|
16
|
-
|
|
26
|
+
Metrics/BlockLength:
|
|
27
|
+
Max: 100
|
|
17
28
|
Exclude:
|
|
18
|
-
- '
|
|
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
|
-
|
|
48
|
+
Style/Documentation:
|
|
28
49
|
Enabled: false
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
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
|
|
data/lib/legion/data/local.rb
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
data/lib/legion/data/spool.rb
CHANGED
|
@@ -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)
|
data/lib/legion/data/version.rb
CHANGED
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.
|
|
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
|