legion-data 1.3.8 → 1.4.2
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 +19 -0
- data/lib/legion/data/migrations/015_add_rbac_tables.rb +3 -3
- data/lib/legion/data/migrations/022_add_memory_traces.rb +29 -0
- data/lib/legion/data/migrations/023_add_data_archive.rb +20 -0
- data/lib/legion/data/migrations/024_add_tenant_partition_columns.rb +27 -0
- data/lib/legion/data/migrations/025_add_tenants_table.rb +23 -0
- data/lib/legion/data/storage_tiers.rb +65 -0
- data/lib/legion/data/vector.rb +60 -0
- data/lib/legion/data/version.rb +1 -1
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: eaf37e68acae5015ef50b01e6a5c7a4806e81b80f5cf74f3c71b72e6799f0bd4
|
|
4
|
+
data.tar.gz: 81b55083c6e8d59403b51a838935a4146b1d3f39eca8316dfd82ee4d2f6a0b05
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4a24d0a86df7d44669ff38d34667c4f0c63036f877d1453df2e1418670b8a8c59ad803f9c245f5895094465d994253e1b4eced1007f0724cd31f53c4ae804d37
|
|
7
|
+
data.tar.gz: 830b7fc8203eb8a339a5ad84d334e56dabf0e6e02084be99a3b27a3067b104d7b5ca973437f3f8577050983c0126a302903624bdcf6a2916e267a1e6a8558754
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# Legion::Data Changelog
|
|
2
2
|
|
|
3
|
+
## v1.4.2
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- Migration 015: use `create_table?` instead of `create_table` for idempotent RBAC table creation
|
|
7
|
+
|
|
8
|
+
## v1.4.1
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Migration 025: tenants table (tenant_id, name, status, quotas, token limits)
|
|
12
|
+
|
|
13
|
+
## v1.4.0
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- `Legion::Data::Vector`: reusable pgvector helpers (available?, cosine_search, l2_search, ensure_extension!)
|
|
17
|
+
- `Legion::Data::StorageTiers`: hot/warm/cold archival lifecycle (archive_to_warm, export_to_cold, stats)
|
|
18
|
+
- Migration 022: memory_traces table with optional pgvector embedding column (1536-dim, HNSW index)
|
|
19
|
+
- Migration 023: data_archive table for generic storage tier archival
|
|
20
|
+
- Migration 024: tenant_id partition columns on tasks, digital_workers, audit_log, memory_traces
|
|
21
|
+
|
|
3
22
|
## v1.3.8
|
|
4
23
|
|
|
5
24
|
### Added
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Sequel.migration do
|
|
4
4
|
up do
|
|
5
|
-
create_table(:rbac_role_assignments) do
|
|
5
|
+
create_table?(:rbac_role_assignments) do
|
|
6
6
|
primary_key :id
|
|
7
7
|
String :principal_type, null: false, size: 10
|
|
8
8
|
String :principal_id, null: false, size: 255
|
|
@@ -16,7 +16,7 @@ Sequel.migration do
|
|
|
16
16
|
index :team
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
create_table(:rbac_runner_grants) do
|
|
19
|
+
create_table?(:rbac_runner_grants) do
|
|
20
20
|
primary_key :id
|
|
21
21
|
String :team, null: false, size: 255
|
|
22
22
|
String :runner_pattern, null: false, size: 500
|
|
@@ -27,7 +27,7 @@ Sequel.migration do
|
|
|
27
27
|
index :team
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
create_table(:rbac_cross_team_grants) do
|
|
30
|
+
create_table?(:rbac_cross_team_grants) do
|
|
31
31
|
primary_key :id
|
|
32
32
|
String :source_team, null: false, size: 255
|
|
33
33
|
String :target_team, null: false, size: 255
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
create_table(:memory_traces) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :agent_id, null: false, size: 64, index: true
|
|
8
|
+
String :trace_type, null: false, size: 32
|
|
9
|
+
String :content, text: true, null: false
|
|
10
|
+
Float :significance, default: 0.5
|
|
11
|
+
Float :confidence, default: 1.0
|
|
12
|
+
String :associations, text: true
|
|
13
|
+
String :metadata, text: true
|
|
14
|
+
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
|
15
|
+
DateTime :accessed_at
|
|
16
|
+
DateTime :decayed_at
|
|
17
|
+
index %i[agent_id trace_type]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
next unless adapter_scheme == :postgres
|
|
21
|
+
|
|
22
|
+
run 'ALTER TABLE memory_traces ADD COLUMN IF NOT EXISTS embedding vector(1536)'
|
|
23
|
+
run 'CREATE INDEX IF NOT EXISTS idx_memory_traces_embedding ON memory_traces USING hnsw (embedding vector_cosine_ops)'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
down do
|
|
27
|
+
drop_table?(:memory_traces)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
create_table(:data_archive) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :source_table, null: false, size: 64, index: true
|
|
8
|
+
Integer :source_id, null: false
|
|
9
|
+
String :data, text: true, null: false
|
|
10
|
+
Integer :tier, default: 1
|
|
11
|
+
DateTime :archived_at, default: Sequel::CURRENT_TIMESTAMP
|
|
12
|
+
index %i[source_table source_id]
|
|
13
|
+
index :tier
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
down do
|
|
18
|
+
drop_table?(:data_archive)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
%i[tasks digital_workers audit_log memory_traces].each do |table|
|
|
6
|
+
next unless table_exists?(table)
|
|
7
|
+
next if schema(table).any? { |col, _| col == :tenant_id }
|
|
8
|
+
|
|
9
|
+
alter_table(table) do
|
|
10
|
+
add_column :tenant_id, String, size: 64
|
|
11
|
+
add_index :tenant_id
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
down do
|
|
17
|
+
%i[tasks digital_workers audit_log memory_traces].each do |table|
|
|
18
|
+
next unless table_exists?(table)
|
|
19
|
+
next unless schema(table).any? { |col, _| col == :tenant_id }
|
|
20
|
+
|
|
21
|
+
alter_table(table) do
|
|
22
|
+
drop_index :tenant_id
|
|
23
|
+
drop_column :tenant_id
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
create_table(:tenants) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :tenant_id, null: false, unique: true, size: 100
|
|
8
|
+
String :name, size: 255
|
|
9
|
+
String :status, default: 'active', size: 20
|
|
10
|
+
Integer :max_workers, default: 10
|
|
11
|
+
Integer :max_queue_depth, default: 10_000
|
|
12
|
+
Float :monthly_token_limit
|
|
13
|
+
Float :daily_token_limit
|
|
14
|
+
DateTime :created_at, default: Sequel::CURRENT_TIMESTAMP
|
|
15
|
+
DateTime :updated_at, default: Sequel::CURRENT_TIMESTAMP
|
|
16
|
+
index :status
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
down do
|
|
21
|
+
drop_table?(:tenants)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Data
|
|
5
|
+
module StorageTiers
|
|
6
|
+
TIERS = { hot: 0, warm: 1, cold: 2 }.freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
def archive_to_warm(table:, age_days: 90, batch_size: 1000)
|
|
10
|
+
return { archived: 0, reason: 'no_connection' } unless Legion::Data.connection
|
|
11
|
+
return { archived: 0, reason: 'no_archive_table' } unless Legion::Data.connection.table_exists?(:data_archive)
|
|
12
|
+
|
|
13
|
+
cutoff = Time.now - (age_days * 86_400)
|
|
14
|
+
records = Legion::Data.connection[table].where { created_at < cutoff }.limit(batch_size).all
|
|
15
|
+
return { archived: 0 } if records.empty?
|
|
16
|
+
|
|
17
|
+
Legion::Data.connection.transaction do
|
|
18
|
+
records.each do |record|
|
|
19
|
+
Legion::Data.connection[:data_archive].insert(
|
|
20
|
+
source_table: table.to_s, source_id: record[:id],
|
|
21
|
+
data: Legion::JSON.dump(record),
|
|
22
|
+
tier: TIERS[:warm],
|
|
23
|
+
archived_at: Time.now.utc
|
|
24
|
+
)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
ids = records.map { |r| r[:id] }
|
|
28
|
+
Legion::Data.connection[table].where(id: ids).delete
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
{ archived: records.size, table: table.to_s }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def export_to_cold(age_days: 365, batch_size: 5000)
|
|
35
|
+
return { exported: 0 } unless Legion::Data.connection&.table_exists?(:data_archive)
|
|
36
|
+
|
|
37
|
+
cutoff = Time.now - (age_days * 86_400)
|
|
38
|
+
records = Legion::Data.connection[:data_archive]
|
|
39
|
+
.where(tier: TIERS[:warm])
|
|
40
|
+
.where { archived_at < cutoff }
|
|
41
|
+
.limit(batch_size).all
|
|
42
|
+
return { exported: 0 } if records.empty?
|
|
43
|
+
|
|
44
|
+
ids = records.map { |r| r[:id] }
|
|
45
|
+
Legion::Data.connection[:data_archive].where(id: ids).update(tier: TIERS[:cold])
|
|
46
|
+
{ exported: records.size, data: records }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def stats
|
|
50
|
+
return {} unless Legion::Data.connection&.table_exists?(:data_archive)
|
|
51
|
+
|
|
52
|
+
{ warm: count_tier(:warm), cold: count_tier(:cold) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def count_tier(tier)
|
|
58
|
+
Legion::Data.connection[:data_archive].where(tier: TIERS[tier]).count
|
|
59
|
+
rescue StandardError
|
|
60
|
+
0
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Data
|
|
5
|
+
module Vector
|
|
6
|
+
class << self
|
|
7
|
+
def available?
|
|
8
|
+
return false unless Legion::Data.connection
|
|
9
|
+
return false unless Legion::Data.connection.adapter_scheme == :postgres
|
|
10
|
+
|
|
11
|
+
Legion::Data.connection.fetch("SELECT 1 FROM pg_extension WHERE extname = 'vector'").any?
|
|
12
|
+
rescue StandardError
|
|
13
|
+
false
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def ensure_extension!
|
|
17
|
+
return false unless Legion::Data.connection&.adapter_scheme == :postgres
|
|
18
|
+
|
|
19
|
+
Legion::Data.connection.run('CREATE EXTENSION IF NOT EXISTS vector')
|
|
20
|
+
true
|
|
21
|
+
rescue StandardError => e
|
|
22
|
+
Legion::Logging.warn("pgvector extension creation failed: #{e.message}") if defined?(Legion::Logging)
|
|
23
|
+
false
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def cosine_search(table:, column:, query_vector:, limit: 10, min_similarity: 0.0)
|
|
27
|
+
return [] unless available?
|
|
28
|
+
|
|
29
|
+
vec_literal = vector_literal(query_vector)
|
|
30
|
+
ds = Legion::Data.connection[table]
|
|
31
|
+
.select_all
|
|
32
|
+
.select_append(Sequel.lit("1 - (#{column} <=> ?)", vec_literal).as(:similarity))
|
|
33
|
+
.order(Sequel.lit("#{column} <=> ?", vec_literal))
|
|
34
|
+
.limit(limit)
|
|
35
|
+
|
|
36
|
+
ds = ds.where(Sequel.lit("1 - (#{column} <=> ?) >= ?", vec_literal, min_similarity)) if min_similarity.positive?
|
|
37
|
+
ds.all
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def l2_search(table:, column:, query_vector:, limit: 10)
|
|
41
|
+
return [] unless available?
|
|
42
|
+
|
|
43
|
+
vec_literal = vector_literal(query_vector)
|
|
44
|
+
Legion::Data.connection[table]
|
|
45
|
+
.select_all
|
|
46
|
+
.select_append(Sequel.lit("#{column} <-> ?", vec_literal).as(:distance))
|
|
47
|
+
.order(Sequel.lit("#{column} <-> ?", vec_literal))
|
|
48
|
+
.limit(limit)
|
|
49
|
+
.all
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def vector_literal(query_vector)
|
|
55
|
+
"[#{query_vector.join(',')}]"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
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.4.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -117,6 +117,10 @@ files:
|
|
|
117
117
|
- lib/legion/data/migrations/019_add_audit_hash_chain.rb
|
|
118
118
|
- lib/legion/data/migrations/020_add_webhooks.rb
|
|
119
119
|
- lib/legion/data/migrations/021_add_archive_tables.rb
|
|
120
|
+
- lib/legion/data/migrations/022_add_memory_traces.rb
|
|
121
|
+
- lib/legion/data/migrations/023_add_data_archive.rb
|
|
122
|
+
- lib/legion/data/migrations/024_add_tenant_partition_columns.rb
|
|
123
|
+
- lib/legion/data/migrations/025_add_tenants_table.rb
|
|
120
124
|
- lib/legion/data/model.rb
|
|
121
125
|
- lib/legion/data/models/apollo_access_log.rb
|
|
122
126
|
- lib/legion/data/models/apollo_entry.rb
|
|
@@ -136,6 +140,8 @@ files:
|
|
|
136
140
|
- lib/legion/data/models/task.rb
|
|
137
141
|
- lib/legion/data/models/task_log.rb
|
|
138
142
|
- lib/legion/data/settings.rb
|
|
143
|
+
- lib/legion/data/storage_tiers.rb
|
|
144
|
+
- lib/legion/data/vector.rb
|
|
139
145
|
- lib/legion/data/version.rb
|
|
140
146
|
- sonar-project.properties
|
|
141
147
|
homepage: https://github.com/LegionIO/legion-data
|