legion-data 1.7.3 → 1.8.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/.pre-commit-config.yaml +29 -0
- data/AGENTS.md +66 -13
- data/CHANGELOG.md +29 -0
- data/CLAUDE.md +44 -307
- data/README.md +119 -7
- data/lib/legion/data/connection.rb +3 -1
- data/lib/legion/data/migrations/077_create_llm_conversations.rb +32 -0
- data/lib/legion/data/migrations/078_create_llm_messages.rb +33 -0
- data/lib/legion/data/migrations/079_create_llm_message_inference_requests.rb +47 -0
- data/lib/legion/data/migrations/080_create_llm_message_inference_responses.rb +39 -0
- data/lib/legion/data/migrations/081_add_llm_message_inference_foreign_keys.rb +17 -0
- data/lib/legion/data/migrations/082_create_llm_route_attempts.rb +31 -0
- data/lib/legion/data/migrations/083_create_llm_message_inference_metrics.rb +36 -0
- data/lib/legion/data/migrations/084_create_llm_tool_calls.rb +32 -0
- data/lib/legion/data/migrations/085_add_llm_message_tool_call_foreign_key.rb +15 -0
- data/lib/legion/data/migrations/086_create_llm_tool_call_attempts.rb +30 -0
- data/lib/legion/data/migrations/087_create_llm_conversation_compactions.rb +31 -0
- data/lib/legion/data/migrations/088_create_llm_policy_evaluations.rb +33 -0
- data/lib/legion/data/migrations/089_create_llm_security_events.rb +33 -0
- data/lib/legion/data/migrations/090_create_llm_registry_events.rb +23 -0
- data/lib/legion/data/migrations/091_create_portable_identity_providers.rb +35 -0
- data/lib/legion/data/migrations/092_create_portable_identity_principals.rb +25 -0
- data/lib/legion/data/migrations/093_create_portable_identities.rb +31 -0
- data/lib/legion/data/migrations/094_create_portable_identity_groups.rb +21 -0
- data/lib/legion/data/migrations/095_create_portable_identity_group_memberships.rb +25 -0
- data/lib/legion/data/migrations/096_create_portable_identity_audit_log.rb +26 -0
- data/lib/legion/data/migrations/097_add_llm_dispatch_fields.rb +16 -0
- data/lib/legion/data/model.rb +11 -1
- data/lib/legion/data/models/apollo/access_log.rb +17 -0
- data/lib/legion/data/models/apollo/entries.rb +22 -0
- data/lib/legion/data/models/apollo/expertise.rb +16 -0
- data/lib/legion/data/models/apollo/model_helpers.rb +17 -0
- data/lib/legion/data/models/apollo/operation.rb +16 -0
- data/lib/legion/data/models/apollo/relation.rb +18 -0
- data/lib/legion/data/models/function.rb +1 -0
- data/lib/legion/data/models/identity/audit_log.rb +20 -0
- data/lib/legion/data/models/identity/group.rb +28 -0
- data/lib/legion/data/models/identity/group_memberships.rb +28 -0
- data/lib/legion/data/models/identity/identity.rb +24 -0
- data/lib/legion/data/models/identity/model_helpers.rb +86 -0
- data/lib/legion/data/models/identity/principal.rb +37 -0
- data/lib/legion/data/models/identity/providers.rb +34 -0
- data/lib/legion/data/models/identity.rb +8 -0
- data/lib/legion/data/models/identity_group.rb +13 -0
- data/lib/legion/data/models/identity_provider.rb +8 -0
- data/lib/legion/data/models/llm/conversation.rb +25 -0
- data/lib/legion/data/models/llm/conversation_compaction.rb +22 -0
- data/lib/legion/data/models/llm/message.rb +105 -0
- data/lib/legion/data/models/llm/message_inference_metric.rb +46 -0
- data/lib/legion/data/models/llm/message_inference_request.rb +80 -0
- data/lib/legion/data/models/llm/message_inference_response.rb +23 -0
- data/lib/legion/data/models/llm/model_helpers.rb +18 -0
- data/lib/legion/data/models/llm/policy_evaluation.rb +20 -0
- data/lib/legion/data/models/llm/registry_event.rb +15 -0
- data/lib/legion/data/models/llm/route_attempt.rb +18 -0
- data/lib/legion/data/models/llm/security_event.rb +66 -0
- data/lib/legion/data/models/llm/tool_call.rb +21 -0
- data/lib/legion/data/models/llm/tool_call_attempt.rb +18 -0
- data/lib/legion/data/models/node.rb +2 -1
- data/lib/legion/data/models/principal.rb +13 -0
- data/lib/legion/data/models/rbac/cross_team_grants.rb +25 -0
- data/lib/legion/data/models/rbac/model_helpers.rb +25 -0
- data/lib/legion/data/models/rbac/role_assignments.rb +25 -0
- data/lib/legion/data/models/rbac/runner_grants.rb +23 -0
- data/lib/legion/data/models/relationship.rb +1 -0
- data/lib/legion/data/models/runner.rb +24 -2
- data/lib/legion/data/models/task.rb +4 -0
- data/lib/legion/data/version.rb +1 -1
- data/scripts/pre-commit-rubocop.sh +16 -0
- metadata +54 -1
data/README.md
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
1
|
# legion-data
|
|
2
2
|
|
|
3
|
-
Persistent database storage for the [LegionIO](https://github.com/LegionIO/LegionIO) async job engine and AI coding assistant platform. Provides database connectivity via the [Sequel ORM](https://sequel.jeremyevans.net/), automatic schema migrations (
|
|
3
|
+
Persistent database storage for the [LegionIO](https://github.com/LegionIO/LegionIO) async job engine and AI coding assistant platform. Provides database connectivity via the [Sequel ORM](https://sequel.jeremyevans.net/), automatic schema migrations (97 numbered migrations), Sequel models for the full LegionIO control plane, and a parallel local SQLite database for on-node agentic cognitive state.
|
|
4
4
|
|
|
5
|
-
**Version**: 1.
|
|
5
|
+
**Version**: 1.8.0 | **Ruby**: >= 3.4 | **License**: Apache-2.0
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
+
## What It Owns
|
|
10
|
+
|
|
11
|
+
`legion-data` is the data contract for LegionIO. It owns database connectivity, migrations, model loading, and portable Sequel model definitions for shared platform state. HTTP routes, runtime orchestration, and extension behavior live in other LegionIO repos and call into these models.
|
|
12
|
+
|
|
13
|
+
Core responsibilities:
|
|
14
|
+
|
|
15
|
+
| Area | Tables and models |
|
|
16
|
+
|------|-------------------|
|
|
17
|
+
| Control plane | extensions, functions, runners, nodes, tasks, settings, workers, relationships, chains |
|
|
18
|
+
| Audit and governance | `audit_log`, `audit_records`, `governance_events`, archive manifests |
|
|
19
|
+
| Identity and RBAC | providers, principals, identities, groups, memberships, role grants, runner grants |
|
|
20
|
+
| LLM ledger | conversations, model-visible messages, inference requests/responses, routing, metrics, tool calls, policy/security events |
|
|
21
|
+
| Apollo knowledge | PostgreSQL `pgvector` knowledge entries, relations, expertise, access logs |
|
|
22
|
+
| Local state | on-node SQLite cognitive state, independent of the shared database |
|
|
23
|
+
|
|
24
|
+
The schema is portable by default across SQLite, MySQL, and PostgreSQL. PostgreSQL-only behavior is isolated to features that need PostgreSQL, such as Apollo vector columns.
|
|
25
|
+
|
|
9
26
|
## Supported Databases
|
|
10
27
|
|
|
11
28
|
| Database | Adapter | Gem | Default |
|
|
@@ -51,18 +68,25 @@ Legion::Data (singleton module)
|
|
|
51
68
|
│ ├── .adapter # Reads adapter from settings (:sqlite, :mysql2, :postgres)
|
|
52
69
|
│ ├── .setup # Establish connection (dev_mode fallback to SQLite if unreachable)
|
|
53
70
|
│ ├── .sequel # Raw Sequel::Database accessor
|
|
71
|
+
│ ├── .connection_info # Adapter, liveness, and fallback diagnostics
|
|
72
|
+
│ ├── .fallback_active? # True when dev fallback moved a network DB to SQLite
|
|
54
73
|
│ ├── .stats # Pool metrics, tuning snapshot, adapter-specific DB stats
|
|
55
74
|
│ └── .shutdown # Disconnect and close query file logger
|
|
56
75
|
│
|
|
57
|
-
├── Migration # Auto-migration system (
|
|
76
|
+
├── Migration # Auto-migration system (97 numbered Sequel DSL migrations)
|
|
58
77
|
│
|
|
59
78
|
├── Model # Sequel model autoloader
|
|
60
79
|
│ └── Models: Extension, Function, Runner, Node, Task, TaskLog, Setting,
|
|
61
80
|
│ DigitalWorker, Relationship, AuditLog, AuditRecord, Chain,
|
|
62
81
|
│ RbacRoleAssignment, RbacRunnerGrant, RbacCrossTeamGrant,
|
|
63
82
|
│ IdentityProvider, Principal, Identity, IdentityGroup,
|
|
64
|
-
│ IdentityGroupMembership,
|
|
65
|
-
│ ApolloEntry, ApolloRelation, ApolloExpertise, ApolloAccessLog (PG only)
|
|
83
|
+
│ IdentityGroupMembership, IdentityAuditLog, ExtractStepTiming,
|
|
84
|
+
│ ApolloEntry, ApolloRelation, ApolloExpertise, ApolloAccessLog (PG only),
|
|
85
|
+
│ LLM::Conversation, LLM::Message, LLM::MessageInferenceRequest,
|
|
86
|
+
│ LLM::MessageInferenceResponse, LLM::RouteAttempt,
|
|
87
|
+
│ LLM::MessageInferenceMetric, LLM::ToolCall, LLM::ToolCallAttempt,
|
|
88
|
+
│ LLM::ConversationCompaction, LLM::PolicyEvaluation,
|
|
89
|
+
│ LLM::SecurityEvent, LLM::RegistryEvent
|
|
66
90
|
│
|
|
67
91
|
├── Local # Parallel local SQLite for agentic cognitive state
|
|
68
92
|
│ ├── .setup # Lazy init — creates legionio_local.db on first access
|
|
@@ -117,10 +141,42 @@ Legion::Data.local.db_path # => "legionio_local.db"
|
|
|
117
141
|
Legion::Data.connected? # => true
|
|
118
142
|
Legion::Data.stats # => { shared: {...}, local: {...} }
|
|
119
143
|
|
|
144
|
+
# Inspect shared DB diagnostics, including dev fallback state
|
|
145
|
+
Legion::Data::Connection.connection_info
|
|
146
|
+
# => { adapter: :sqlite, connected: true, fallback_active: false, ... }
|
|
147
|
+
|
|
120
148
|
# Shut down both connections
|
|
121
149
|
Legion::Data.shutdown
|
|
122
150
|
```
|
|
123
151
|
|
|
152
|
+
### Model Associations
|
|
153
|
+
|
|
154
|
+
Models use Sequel associations as the public object graph. Prefer association methods and association datasets over hand-written foreign-key lookups when the relationship is part of the schema contract.
|
|
155
|
+
|
|
156
|
+
```ruby
|
|
157
|
+
task = Legion::Data::Model::Task.first(id: 42)
|
|
158
|
+
task.function # many_to_one :function
|
|
159
|
+
task.relationship # many_to_one :relationship
|
|
160
|
+
task.task_logs_dataset # further filter/order without losing the relationship
|
|
161
|
+
|
|
162
|
+
conversation = Legion::Data::Models::LLM::Conversation.first(uuid: conversation_uuid)
|
|
163
|
+
conversation.messages_dataset.order(:seq).all
|
|
164
|
+
conversation.security_incident_lineage
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Official LLM lifecycle data lives under `Legion::Data::Models::LLM`. `legion-llm` and `lex-llm-ledger` should use these models and the `llm_*` migration tables for conversations, model-visible messages, inference requests, responses, route attempts, metrics, tool calls, policy decisions, security events, and registry events. Legacy ledger-only tables are not the canonical schema.
|
|
168
|
+
|
|
169
|
+
Association rules used in this repo follow Sequel's own association model:
|
|
170
|
+
|
|
171
|
+
| Relationship | Use this Sequel association |
|
|
172
|
+
|--------------|-----------------------------|
|
|
173
|
+
| Current table has the foreign key | `many_to_one` |
|
|
174
|
+
| Associated table has the foreign key | `one_to_many` or `one_to_one` |
|
|
175
|
+
| Join table connects both sides | `many_to_many` |
|
|
176
|
+
| One associated record through a join table | `one_through_one` |
|
|
177
|
+
|
|
178
|
+
When Sequel cannot infer names from the schema, models must be explicit with `:class`, `:key`, `:primary_key`, `:join_table`, `:left_key`, and `:right_key`. Association names must not collide with real column names because Sequel creates methods with the association name.
|
|
179
|
+
|
|
124
180
|
### Local Database (Agentic Cognitive State)
|
|
125
181
|
|
|
126
182
|
Extensions register their own migration directories and create models bound to the local connection:
|
|
@@ -314,6 +370,8 @@ When `dev_mode: true` and a network database is unreachable, the shared connecti
|
|
|
314
370
|
{ "data": { "dev_mode": true, "dev_fallback": true } }
|
|
315
371
|
```
|
|
316
372
|
|
|
373
|
+
Fallback is intentionally loud. `Connection.setup` logs the degraded mode at error level, `Connection.fallback_active?` returns `true`, and `Connection.connection_info` reports the configured adapter, actual adapter, connection state, and Sequel liveness. Data written during fallback is local-only SQLite data and will not appear in the configured network database after reconnect.
|
|
374
|
+
|
|
317
375
|
### HashiCorp Vault Integration
|
|
318
376
|
|
|
319
377
|
When Vault is connected, credentials are fetched dynamically from `database/creds/legion`, overriding any static `creds` block.
|
|
@@ -377,6 +435,37 @@ Legion::Data.reload_static_cache
|
|
|
377
435
|
|
|
378
436
|
Apollo models require PostgreSQL with the `pgvector` extension. They are skipped silently on SQLite and MySQL.
|
|
379
437
|
|
|
438
|
+
The `Legion::Data::Model::Identity::*`, `Apollo::*`, and `RBAC::*` namespaces provide cleaner Sequel model names for API-facing code while preserving the legacy flat model classes. Official LLM lifecycle models live under `Legion::Data::Models::LLM`.
|
|
439
|
+
|
|
440
|
+
### Identity Namespace Models
|
|
441
|
+
|
|
442
|
+
| Model | Table | Description |
|
|
443
|
+
|-------|-------|-------------|
|
|
444
|
+
| `Identity::Provider` | `portable_identity_providers` | Portable provider records with integer primary keys and public UUIDs |
|
|
445
|
+
| `Identity::ProviderCapability` | `portable_identity_provider_capabilities` | Normalized provider capability declarations |
|
|
446
|
+
| `Identity::Principal` | `portable_identity_principals` | Human, service, worker, or system principals |
|
|
447
|
+
| `Identity::Identity` | `portable_identities` | Provider-bound identities for principals |
|
|
448
|
+
| `Identity::Group` | `portable_identity_groups` | Identity groups |
|
|
449
|
+
| `Identity::GroupMembership` | `portable_identity_group_memberships` | Principal and identity group membership rows |
|
|
450
|
+
| `Identity::AuditLog` | `portable_identity_audit_log` | Identity lifecycle and lookup audit events |
|
|
451
|
+
|
|
452
|
+
### LLM Lifecycle Models
|
|
453
|
+
|
|
454
|
+
| Model | Table | Description |
|
|
455
|
+
|-------|-------|-------------|
|
|
456
|
+
| `LLM::Conversation` | `llm_conversations` | Conversation container tied to the base user identity |
|
|
457
|
+
| `LLM::Message` | `llm_messages` | Model-visible conversation transcript messages |
|
|
458
|
+
| `LLM::MessageInferenceRequest` | `llm_message_inference_requests` | Provider request assembled from message, context, tools, policy, and routing inputs |
|
|
459
|
+
| `LLM::MessageInferenceResponse` | `llm_message_inference_responses` | Provider/runtime response for one inference request |
|
|
460
|
+
| `LLM::RouteAttempt` | `llm_route_attempts` | Provider/model/runner routing attempts, including failures and escalations |
|
|
461
|
+
| `LLM::MessageInferenceMetric` | `llm_message_inference_metrics` | Token, latency, cost, and finance usage metrics for an inference pair |
|
|
462
|
+
| `LLM::ToolCall` | `llm_tool_calls` | Tool calls requested by an LLM provider response |
|
|
463
|
+
| `LLM::ToolCallAttempt` | `llm_tool_call_attempts` | Execution attempts, retries, failures, and results for provider-requested tool calls |
|
|
464
|
+
| `LLM::ConversationCompaction` | `llm_conversation_compactions` | Conversation-scoped compaction events |
|
|
465
|
+
| `LLM::PolicyEvaluation` | `llm_policy_evaluations` | Policy, classification, RBAC, and enforcement decisions for inference requests |
|
|
466
|
+
| `LLM::SecurityEvent` | `llm_security_events` | Security-relevant events tied to conversation, inference, response, or tool attempts |
|
|
467
|
+
| `LLM::RegistryEvent` | `llm_registry_events` | Provider/model registry availability and health events |
|
|
468
|
+
|
|
380
469
|
---
|
|
381
470
|
|
|
382
471
|
## Dependencies
|
|
@@ -396,7 +485,7 @@ Apollo models require PostgreSQL with the `pgvector` extension. They are skipped
|
|
|
396
485
|
|
|
397
486
|
## Migrations
|
|
398
487
|
|
|
399
|
-
|
|
488
|
+
97 numbered Sequel DSL migrations run automatically on startup (`auto_migrate: true`). Key milestones:
|
|
400
489
|
|
|
401
490
|
| Range | What was added |
|
|
402
491
|
|-------|---------------|
|
|
@@ -412,7 +501,11 @@ Apollo models require PostgreSQL with the `pgvector` extension. They are skipped
|
|
|
412
501
|
| 050 | Critical indexes across 13 tables |
|
|
413
502
|
| 058–067 | Audit records, chains, knowledge tiers, tool embedding cache, identity system (providers, principals, identities, groups) |
|
|
414
503
|
| 068–071 | Entity type on audit records, principal on nodes, approval queue resume, engine on relationships |
|
|
415
|
-
| 072–
|
|
504
|
+
| 072–073 | Identity audit log and multi-instance identity columns |
|
|
505
|
+
| 074–076 | Apollo field width fixes, task idempotency columns, and Extract step timing rows |
|
|
506
|
+
| 077–090 | Portable LLM lifecycle schema: conversations, messages, inference requests/responses, route attempts, inference metrics, provider-requested tool calls, compactions, policy/security, and registry events |
|
|
507
|
+
| 091–096 | Portable identity companion schema with integer primary keys, public UUIDs, provider capabilities, principals, identities, groups, memberships, and audit log |
|
|
508
|
+
| 097 | LLM dispatch identifiers for fleet operation, correlation, idempotency, provider instance, and dispatch path |
|
|
416
509
|
|
|
417
510
|
Run migrations standalone:
|
|
418
511
|
|
|
@@ -420,6 +513,15 @@ Run migrations standalone:
|
|
|
420
513
|
bundle exec legionio_migrate
|
|
421
514
|
```
|
|
422
515
|
|
|
516
|
+
Migration rules:
|
|
517
|
+
|
|
518
|
+
- Do not edit published migrations.
|
|
519
|
+
- Do not guard migrations with `create_table?`, `table_exists?`, `if_not_exists`, or similar conditional schema logic.
|
|
520
|
+
- Add new migrations in the next available number and keep domains split by dependency and rollback risk.
|
|
521
|
+
- Use portable Sequel DSL unless a feature truly requires adapter-specific behavior.
|
|
522
|
+
- Prefer integer `id` primary keys for joins plus public `uuid` columns for APIs, logs, and external references.
|
|
523
|
+
- Avoid JSON columns unless the shape is genuinely provider-specific or dynamic evidence.
|
|
524
|
+
|
|
423
525
|
---
|
|
424
526
|
|
|
425
527
|
## CLI Executable
|
|
@@ -449,6 +551,7 @@ bundle exec legionio_migrate
|
|
|
449
551
|
11. Financial logging for UAIS cost recovery
|
|
450
552
|
12. Global tool embedding cache (L4 tier for `Legion::Tools::EmbeddingCache`)
|
|
451
553
|
13. Unified identity system (providers, principals, identities, groups)
|
|
554
|
+
14. LLM lifecycle ledger for audit, finance metrics, routing reconstruction, tool calls, and security incident lineage
|
|
452
555
|
|
|
453
556
|
---
|
|
454
557
|
|
|
@@ -462,6 +565,15 @@ bundle exec rspec # all tests must pass
|
|
|
462
565
|
bundle exec rubocop -A # zero offenses expected
|
|
463
566
|
```
|
|
464
567
|
|
|
568
|
+
This repo also includes a pre-commit configuration:
|
|
569
|
+
|
|
570
|
+
```bash
|
|
571
|
+
pre-commit install
|
|
572
|
+
pre-commit run --all-files
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
The local RuboCop hook auto-corrects staged Ruby files when RuboCop is available and fails the commit when RuboCop reports real offenses. The Ruby syntax hook checks every staged Ruby file.
|
|
576
|
+
|
|
465
577
|
Follow the [LegionIO contribution guide](https://github.com/LegionIO/.github/blob/main/CONTRIBUTING.md). Open a PR against `main`.
|
|
466
578
|
|
|
467
579
|
---
|
|
@@ -161,6 +161,7 @@ module Legion
|
|
|
161
161
|
end
|
|
162
162
|
|
|
163
163
|
def setup
|
|
164
|
+
@adapter = Legion::Settings[:data][:adapter]&.to_sym || :sqlite
|
|
164
165
|
opts = sequel_opts
|
|
165
166
|
log.info("Legion::Data::Connection setup adapter=#{adapter}")
|
|
166
167
|
@fallback_active = false
|
|
@@ -173,7 +174,7 @@ module Legion
|
|
|
173
174
|
rescue StandardError => e
|
|
174
175
|
raise unless dev_fallback?
|
|
175
176
|
|
|
176
|
-
log.error("Legion::Data FALLING BACK TO SQLITE — #{attempted_adapter} connection failed: #{e.message}")
|
|
177
|
+
log.error("Legion::Data FALLING BACK TO SQLITE — #{attempted_adapter} network DB connection failed: #{e.message}")
|
|
177
178
|
log.error("Legion::Data WARNING: Data written to SQLite will NOT be visible when #{attempted_adapter} reconnects. " \
|
|
178
179
|
'Apollo knowledge, audit logs, and other DB-backed services will use a local-only store.')
|
|
179
180
|
handle_exception(e, level: :error, handled: true, operation: :shared_connect, fallback: :sqlite)
|
|
@@ -261,6 +262,7 @@ module Legion
|
|
|
261
262
|
@sequel&.disconnect
|
|
262
263
|
@query_file_logger&.close
|
|
263
264
|
@query_file_logger = nil
|
|
265
|
+
@fallback_active = false
|
|
264
266
|
Legion::Settings[:data][:connected] = false
|
|
265
267
|
log.info 'Legion::Data connection closed'
|
|
266
268
|
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_conversations) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
Integer :principal_id
|
|
9
|
+
Integer :identity_id
|
|
10
|
+
String :title, size: 255
|
|
11
|
+
String :status, size: 32, null: false, default: 'active'
|
|
12
|
+
String :system_prompt_key, size: 255
|
|
13
|
+
String :system_prompt_hash, size: 128
|
|
14
|
+
String :classification_level, size: 64
|
|
15
|
+
TrueClass :contains_phi, null: false, default: false
|
|
16
|
+
TrueClass :contains_pii, null: false, default: false
|
|
17
|
+
String :retention_policy, size: 64, null: false, default: 'default'
|
|
18
|
+
DateTime :expires_at
|
|
19
|
+
DateTime :recorded_at
|
|
20
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
21
|
+
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
22
|
+
DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
23
|
+
|
|
24
|
+
index :uuid
|
|
25
|
+
index :principal_id
|
|
26
|
+
index :identity_id
|
|
27
|
+
index :status
|
|
28
|
+
index :retention_policy
|
|
29
|
+
index :expires_at
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_messages) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :conversation_id, :llm_conversations, null: false, on_delete: :cascade
|
|
9
|
+
foreign_key :parent_message_id, :llm_messages, null: true, on_delete: :set_null
|
|
10
|
+
Integer :message_inference_request_id
|
|
11
|
+
Integer :message_inference_response_id
|
|
12
|
+
Integer :tool_call_id
|
|
13
|
+
Integer :seq, null: false
|
|
14
|
+
String :role, size: 32, null: false
|
|
15
|
+
String :content_type, size: 64, null: false, default: 'text'
|
|
16
|
+
String :content, text: true
|
|
17
|
+
Integer :input_tokens, null: false, default: 0
|
|
18
|
+
Integer :output_tokens, null: false, default: 0
|
|
19
|
+
DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
20
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
21
|
+
|
|
22
|
+
unique %i[conversation_id seq]
|
|
23
|
+
index :uuid
|
|
24
|
+
index :conversation_id
|
|
25
|
+
index :parent_message_id
|
|
26
|
+
index :message_inference_request_id
|
|
27
|
+
index :message_inference_response_id
|
|
28
|
+
index :tool_call_id
|
|
29
|
+
index %i[conversation_id role]
|
|
30
|
+
index :created_at
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_message_inference_requests) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :conversation_id, :llm_conversations, null: false, on_delete: :cascade
|
|
9
|
+
foreign_key :latest_message_id, :llm_messages, null: true, on_delete: :set_null
|
|
10
|
+
Integer :caller_principal_id
|
|
11
|
+
Integer :caller_identity_id
|
|
12
|
+
String :runtime_caller_type, size: 64
|
|
13
|
+
String :request_ref, size: 128
|
|
14
|
+
String :correlation_ref, size: 128
|
|
15
|
+
String :exchange_ref, size: 128
|
|
16
|
+
String :request_type, size: 64, null: false, default: 'chat'
|
|
17
|
+
String :status, size: 64, null: false, default: 'created'
|
|
18
|
+
Integer :context_message_count, null: false, default: 0
|
|
19
|
+
Integer :context_tokens, null: false, default: 0
|
|
20
|
+
Integer :token_budget, null: false, default: 0
|
|
21
|
+
String :curation_strategy, size: 128
|
|
22
|
+
Integer :injected_tool_count, null: false, default: 0
|
|
23
|
+
String :tool_policy, size: 128
|
|
24
|
+
String :request_capture_mode, size: 64, null: false, default: 'metadata_only'
|
|
25
|
+
String :request_content_hash, size: 128
|
|
26
|
+
String :request_json, text: true
|
|
27
|
+
String :classification_level, size: 64
|
|
28
|
+
String :rbac_decision, size: 64
|
|
29
|
+
String :cost_center, size: 128
|
|
30
|
+
String :budget_key, size: 128
|
|
31
|
+
DateTime :requested_at
|
|
32
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
33
|
+
|
|
34
|
+
index :uuid
|
|
35
|
+
index :conversation_id
|
|
36
|
+
index :latest_message_id
|
|
37
|
+
index :caller_principal_id
|
|
38
|
+
index :caller_identity_id
|
|
39
|
+
index :request_ref
|
|
40
|
+
index :correlation_ref
|
|
41
|
+
index :exchange_ref
|
|
42
|
+
index :status
|
|
43
|
+
index %i[cost_center requested_at]
|
|
44
|
+
index :requested_at
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_message_inference_responses) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :message_inference_request_id, :llm_message_inference_requests, null: false, on_delete: :cascade
|
|
9
|
+
foreign_key :response_message_id, :llm_messages, null: true, on_delete: :set_null
|
|
10
|
+
String :provider, size: 128
|
|
11
|
+
String :model_key, size: 255
|
|
12
|
+
String :tier, size: 64
|
|
13
|
+
String :runner_ref, size: 128
|
|
14
|
+
String :provider_response_ref, size: 255
|
|
15
|
+
String :status, size: 64, null: false, default: 'created'
|
|
16
|
+
String :finish_reason, size: 128
|
|
17
|
+
String :error_category, size: 128
|
|
18
|
+
String :error_code, size: 128
|
|
19
|
+
String :error_message, text: true
|
|
20
|
+
Integer :latency_ms, null: false, default: 0
|
|
21
|
+
Integer :wall_clock_ms, null: false, default: 0
|
|
22
|
+
String :response_capture_mode, size: 64, null: false, default: 'metadata_only'
|
|
23
|
+
String :response_content_hash, size: 128
|
|
24
|
+
String :response_json, text: true
|
|
25
|
+
String :response_thinking_json, text: true
|
|
26
|
+
DateTime :responded_at
|
|
27
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
28
|
+
|
|
29
|
+
index :uuid
|
|
30
|
+
index :message_inference_request_id
|
|
31
|
+
index :response_message_id
|
|
32
|
+
index %i[provider model_key]
|
|
33
|
+
index :runner_ref
|
|
34
|
+
index :provider_response_ref
|
|
35
|
+
index :status
|
|
36
|
+
index :responded_at
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
alter_table(:llm_messages) do
|
|
6
|
+
add_foreign_key [:message_inference_request_id], :llm_message_inference_requests, key: :id, on_delete: :set_null
|
|
7
|
+
add_foreign_key [:message_inference_response_id], :llm_message_inference_responses, key: :id, on_delete: :set_null
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
down do
|
|
12
|
+
alter_table(:llm_messages) do
|
|
13
|
+
drop_foreign_key [:message_inference_response_id]
|
|
14
|
+
drop_foreign_key [:message_inference_request_id]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_route_attempts) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :message_inference_request_id, :llm_message_inference_requests, null: false, on_delete: :cascade
|
|
9
|
+
foreign_key :message_inference_response_id, :llm_message_inference_responses, null: true, on_delete: :set_null
|
|
10
|
+
Integer :attempt_no, null: false
|
|
11
|
+
String :provider, size: 128
|
|
12
|
+
String :model_key, size: 255
|
|
13
|
+
String :tier, size: 64
|
|
14
|
+
String :route_target, size: 255
|
|
15
|
+
String :status, size: 64, null: false
|
|
16
|
+
String :failure_reason, text: true
|
|
17
|
+
Integer :latency_ms, null: false, default: 0
|
|
18
|
+
DateTime :started_at
|
|
19
|
+
DateTime :ended_at
|
|
20
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
21
|
+
|
|
22
|
+
unique %i[message_inference_request_id attempt_no]
|
|
23
|
+
index :uuid
|
|
24
|
+
index :message_inference_request_id
|
|
25
|
+
index :message_inference_response_id
|
|
26
|
+
index %i[provider model_key]
|
|
27
|
+
index :status
|
|
28
|
+
index :started_at
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_message_inference_metrics) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :message_inference_request_id, :llm_message_inference_requests, null: false, on_delete: :cascade
|
|
9
|
+
foreign_key :message_inference_response_id, :llm_message_inference_responses, null: true, on_delete: :set_null
|
|
10
|
+
String :provider, size: 128
|
|
11
|
+
String :model_key, size: 255
|
|
12
|
+
String :tier, size: 64
|
|
13
|
+
Integer :input_tokens, null: false, default: 0
|
|
14
|
+
Integer :output_tokens, null: false, default: 0
|
|
15
|
+
Integer :thinking_tokens, null: false, default: 0
|
|
16
|
+
Integer :total_tokens, null: false, default: 0
|
|
17
|
+
Integer :latency_ms, null: false, default: 0
|
|
18
|
+
Integer :wall_clock_ms, null: false, default: 0
|
|
19
|
+
BigDecimal :cost_usd, size: [20, 8], null: false, default: 0
|
|
20
|
+
String :currency, size: 3, null: false, default: 'USD'
|
|
21
|
+
String :cost_center, size: 128
|
|
22
|
+
String :budget_key, size: 128
|
|
23
|
+
DateTime :recorded_at
|
|
24
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
25
|
+
|
|
26
|
+
index :uuid
|
|
27
|
+
index :message_inference_request_id
|
|
28
|
+
index :message_inference_response_id
|
|
29
|
+
index %i[provider model_key]
|
|
30
|
+
index :cost_center
|
|
31
|
+
index :budget_key
|
|
32
|
+
index :recorded_at
|
|
33
|
+
index %i[cost_center recorded_at]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_tool_calls) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :message_inference_response_id, :llm_message_inference_responses, null: false, on_delete: :cascade
|
|
9
|
+
foreign_key :requested_by_message_id, :llm_messages, null: true, on_delete: :set_null
|
|
10
|
+
foreign_key :result_message_id, :llm_messages, null: true, on_delete: :set_null
|
|
11
|
+
Integer :tool_call_index, null: false, default: 0
|
|
12
|
+
String :provider_tool_call_ref, size: 255
|
|
13
|
+
String :tool_name, size: 255, null: false
|
|
14
|
+
String :tool_source_type, size: 128
|
|
15
|
+
String :tool_source_server, size: 255
|
|
16
|
+
String :status, size: 64, null: false, default: 'requested'
|
|
17
|
+
DateTime :requested_at
|
|
18
|
+
DateTime :completed_at
|
|
19
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
20
|
+
|
|
21
|
+
unique %i[message_inference_response_id tool_call_index]
|
|
22
|
+
index :uuid
|
|
23
|
+
index :message_inference_response_id
|
|
24
|
+
index :requested_by_message_id
|
|
25
|
+
index :result_message_id
|
|
26
|
+
index :provider_tool_call_ref
|
|
27
|
+
index :tool_name
|
|
28
|
+
index :status
|
|
29
|
+
index :requested_at
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
up do
|
|
5
|
+
alter_table(:llm_messages) do
|
|
6
|
+
add_foreign_key [:tool_call_id], :llm_tool_calls, key: :id, on_delete: :set_null
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
down do
|
|
11
|
+
alter_table(:llm_messages) do
|
|
12
|
+
drop_foreign_key [:tool_call_id]
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_tool_call_attempts) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :tool_call_id, :llm_tool_calls, null: false, on_delete: :cascade
|
|
9
|
+
Integer :attempt_no, null: false
|
|
10
|
+
String :runner_ref, size: 128
|
|
11
|
+
String :status, size: 64, null: false
|
|
12
|
+
String :error_category, size: 128
|
|
13
|
+
String :error_code, size: 128
|
|
14
|
+
String :error_message, text: true
|
|
15
|
+
Integer :duration_ms, null: false, default: 0
|
|
16
|
+
String :arguments_ref, size: 255
|
|
17
|
+
String :result_ref, size: 255
|
|
18
|
+
DateTime :started_at
|
|
19
|
+
DateTime :ended_at
|
|
20
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
21
|
+
|
|
22
|
+
unique %i[tool_call_id attempt_no]
|
|
23
|
+
index :uuid
|
|
24
|
+
index :tool_call_id
|
|
25
|
+
index :runner_ref
|
|
26
|
+
index :status
|
|
27
|
+
index :started_at
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_conversation_compactions) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :conversation_id, :llm_conversations, null: false, on_delete: :cascade
|
|
9
|
+
foreign_key :triggered_by_message_inference_request_id, :llm_message_inference_requests, null: true,
|
|
10
|
+
on_delete: :set_null
|
|
11
|
+
foreign_key :replaces_message_from_id, :llm_messages, null: true, on_delete: :set_null
|
|
12
|
+
foreign_key :replaces_message_to_id, :llm_messages, null: true, on_delete: :set_null
|
|
13
|
+
String :strategy, size: 128
|
|
14
|
+
String :status, size: 64, null: false, default: 'created'
|
|
15
|
+
Integer :source_message_count, null: false, default: 0
|
|
16
|
+
Integer :source_token_count, null: false, default: 0
|
|
17
|
+
Integer :compacted_token_count, null: false, default: 0
|
|
18
|
+
String :content_hash, size: 128
|
|
19
|
+
String :summary, text: true
|
|
20
|
+
String :error_message, text: true
|
|
21
|
+
DateTime :compacted_at
|
|
22
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
23
|
+
|
|
24
|
+
index :uuid
|
|
25
|
+
index :conversation_id
|
|
26
|
+
index :triggered_by_message_inference_request_id
|
|
27
|
+
index :status
|
|
28
|
+
index :compacted_at
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_policy_evaluations) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :conversation_id, :llm_conversations, null: true, on_delete: :set_null
|
|
9
|
+
foreign_key :message_inference_request_id, :llm_message_inference_requests, null: true, on_delete: :set_null
|
|
10
|
+
foreign_key :message_inference_response_id, :llm_message_inference_responses, null: true, on_delete: :set_null
|
|
11
|
+
String :policy_key, size: 128, null: false
|
|
12
|
+
String :policy_version, size: 64
|
|
13
|
+
String :evaluation_type, size: 64, null: false
|
|
14
|
+
String :decision, size: 64, null: false
|
|
15
|
+
String :enforcement_action, size: 64
|
|
16
|
+
String :classification_level, size: 64
|
|
17
|
+
TrueClass :contains_phi, null: false, default: false
|
|
18
|
+
TrueClass :contains_pii, null: false, default: false
|
|
19
|
+
String :reason_code, size: 128
|
|
20
|
+
String :reason, text: true
|
|
21
|
+
DateTime :evaluated_at
|
|
22
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
23
|
+
|
|
24
|
+
index :uuid
|
|
25
|
+
index :conversation_id
|
|
26
|
+
index :message_inference_request_id
|
|
27
|
+
index :message_inference_response_id
|
|
28
|
+
index :policy_key
|
|
29
|
+
index :decision
|
|
30
|
+
index :evaluated_at
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
Sequel.migration do
|
|
4
|
+
change do
|
|
5
|
+
create_table(:llm_security_events) do
|
|
6
|
+
primary_key :id
|
|
7
|
+
String :uuid, size: 36, null: false, unique: true
|
|
8
|
+
foreign_key :conversation_id, :llm_conversations, null: true, on_delete: :set_null
|
|
9
|
+
foreign_key :message_inference_request_id, :llm_message_inference_requests, null: true, on_delete: :set_null
|
|
10
|
+
foreign_key :message_inference_response_id, :llm_message_inference_responses, null: true, on_delete: :set_null
|
|
11
|
+
foreign_key :tool_call_id, :llm_tool_calls, null: true, on_delete: :set_null
|
|
12
|
+
foreign_key :tool_call_attempt_id, :llm_tool_call_attempts, null: true, on_delete: :set_null
|
|
13
|
+
foreign_key :policy_evaluation_id, :llm_policy_evaluations, null: true, on_delete: :set_null
|
|
14
|
+
String :event_type, size: 128, null: false
|
|
15
|
+
String :severity, size: 32, null: false, default: 'info'
|
|
16
|
+
String :status, size: 64, null: false, default: 'open'
|
|
17
|
+
String :description, text: true
|
|
18
|
+
DateTime :detected_at
|
|
19
|
+
DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
|
|
20
|
+
|
|
21
|
+
index :uuid
|
|
22
|
+
index :conversation_id
|
|
23
|
+
index :message_inference_request_id
|
|
24
|
+
index :message_inference_response_id
|
|
25
|
+
index :tool_call_id
|
|
26
|
+
index :tool_call_attempt_id
|
|
27
|
+
index :policy_evaluation_id
|
|
28
|
+
index :event_type
|
|
29
|
+
index :severity
|
|
30
|
+
index :detected_at
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|