legion-data 1.7.4 → 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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/AGENTS.md +66 -13
  3. data/CHANGELOG.md +19 -0
  4. data/CLAUDE.md +44 -307
  5. data/README.md +100 -6
  6. data/lib/legion/data/migrations/077_create_llm_conversations.rb +32 -0
  7. data/lib/legion/data/migrations/078_create_llm_messages.rb +33 -0
  8. data/lib/legion/data/migrations/079_create_llm_message_inference_requests.rb +47 -0
  9. data/lib/legion/data/migrations/080_create_llm_message_inference_responses.rb +39 -0
  10. data/lib/legion/data/migrations/081_add_llm_message_inference_foreign_keys.rb +17 -0
  11. data/lib/legion/data/migrations/082_create_llm_route_attempts.rb +31 -0
  12. data/lib/legion/data/migrations/083_create_llm_message_inference_metrics.rb +36 -0
  13. data/lib/legion/data/migrations/084_create_llm_tool_calls.rb +32 -0
  14. data/lib/legion/data/migrations/085_add_llm_message_tool_call_foreign_key.rb +15 -0
  15. data/lib/legion/data/migrations/086_create_llm_tool_call_attempts.rb +30 -0
  16. data/lib/legion/data/migrations/087_create_llm_conversation_compactions.rb +31 -0
  17. data/lib/legion/data/migrations/088_create_llm_policy_evaluations.rb +33 -0
  18. data/lib/legion/data/migrations/089_create_llm_security_events.rb +33 -0
  19. data/lib/legion/data/migrations/090_create_llm_registry_events.rb +23 -0
  20. data/lib/legion/data/migrations/091_create_portable_identity_providers.rb +35 -0
  21. data/lib/legion/data/migrations/092_create_portable_identity_principals.rb +25 -0
  22. data/lib/legion/data/migrations/093_create_portable_identities.rb +31 -0
  23. data/lib/legion/data/migrations/094_create_portable_identity_groups.rb +21 -0
  24. data/lib/legion/data/migrations/095_create_portable_identity_group_memberships.rb +25 -0
  25. data/lib/legion/data/migrations/096_create_portable_identity_audit_log.rb +26 -0
  26. data/lib/legion/data/migrations/097_add_llm_dispatch_fields.rb +16 -0
  27. data/lib/legion/data/model.rb +11 -1
  28. data/lib/legion/data/models/apollo/access_log.rb +17 -0
  29. data/lib/legion/data/models/apollo/entries.rb +22 -0
  30. data/lib/legion/data/models/apollo/expertise.rb +16 -0
  31. data/lib/legion/data/models/apollo/model_helpers.rb +17 -0
  32. data/lib/legion/data/models/apollo/operation.rb +16 -0
  33. data/lib/legion/data/models/apollo/relation.rb +18 -0
  34. data/lib/legion/data/models/function.rb +1 -0
  35. data/lib/legion/data/models/identity/audit_log.rb +20 -0
  36. data/lib/legion/data/models/identity/group.rb +28 -0
  37. data/lib/legion/data/models/identity/group_memberships.rb +28 -0
  38. data/lib/legion/data/models/identity/identity.rb +24 -0
  39. data/lib/legion/data/models/identity/model_helpers.rb +86 -0
  40. data/lib/legion/data/models/identity/principal.rb +37 -0
  41. data/lib/legion/data/models/identity/providers.rb +34 -0
  42. data/lib/legion/data/models/identity.rb +8 -0
  43. data/lib/legion/data/models/identity_group.rb +13 -0
  44. data/lib/legion/data/models/identity_provider.rb +8 -0
  45. data/lib/legion/data/models/llm/conversation.rb +25 -0
  46. data/lib/legion/data/models/llm/conversation_compaction.rb +22 -0
  47. data/lib/legion/data/models/llm/message.rb +105 -0
  48. data/lib/legion/data/models/llm/message_inference_metric.rb +46 -0
  49. data/lib/legion/data/models/llm/message_inference_request.rb +80 -0
  50. data/lib/legion/data/models/llm/message_inference_response.rb +23 -0
  51. data/lib/legion/data/models/llm/model_helpers.rb +18 -0
  52. data/lib/legion/data/models/llm/policy_evaluation.rb +20 -0
  53. data/lib/legion/data/models/llm/registry_event.rb +15 -0
  54. data/lib/legion/data/models/llm/route_attempt.rb +18 -0
  55. data/lib/legion/data/models/llm/security_event.rb +66 -0
  56. data/lib/legion/data/models/llm/tool_call.rb +21 -0
  57. data/lib/legion/data/models/llm/tool_call_attempt.rb +18 -0
  58. data/lib/legion/data/models/node.rb +2 -1
  59. data/lib/legion/data/models/principal.rb +13 -0
  60. data/lib/legion/data/models/rbac/cross_team_grants.rb +25 -0
  61. data/lib/legion/data/models/rbac/model_helpers.rb +25 -0
  62. data/lib/legion/data/models/rbac/role_assignments.rb +25 -0
  63. data/lib/legion/data/models/rbac/runner_grants.rb +23 -0
  64. data/lib/legion/data/models/relationship.rb +1 -0
  65. data/lib/legion/data/models/runner.rb +24 -2
  66. data/lib/legion/data/models/task.rb +4 -0
  67. data/lib/legion/data/version.rb +1 -1
  68. metadata +52 -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 (76 numbered migrations), Sequel models for the full LegionIO control plane, and a parallel local SQLite database for on-node agentic cognitive state.
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.7.4 | **Ruby**: >= 3.4 | **License**: Apache-2.0
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 |
@@ -56,15 +73,20 @@ Legion::Data (singleton module)
56
73
  │ ├── .stats # Pool metrics, tuning snapshot, adapter-specific DB stats
57
74
  │ └── .shutdown # Disconnect and close query file logger
58
75
 
59
- ├── Migration # Auto-migration system (76 numbered Sequel DSL migrations)
76
+ ├── Migration # Auto-migration system (97 numbered Sequel DSL migrations)
60
77
 
61
78
  ├── Model # Sequel model autoloader
62
79
  │ └── Models: Extension, Function, Runner, Node, Task, TaskLog, Setting,
63
80
  │ DigitalWorker, Relationship, AuditLog, AuditRecord, Chain,
64
81
  │ RbacRoleAssignment, RbacRunnerGrant, RbacCrossTeamGrant,
65
82
  │ IdentityProvider, Principal, Identity, IdentityGroup,
66
- │ IdentityGroupMembership, IdentityAuditLog,
67
- │ 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
68
90
 
69
91
  ├── Local # Parallel local SQLite for agentic cognitive state
70
92
  │ ├── .setup # Lazy init — creates legionio_local.db on first access
@@ -127,6 +149,34 @@ Legion::Data::Connection.connection_info
127
149
  Legion::Data.shutdown
128
150
  ```
129
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
+
130
180
  ### Local Database (Agentic Cognitive State)
131
181
 
132
182
  Extensions register their own migration directories and create models bound to the local connection:
@@ -385,6 +435,37 @@ Legion::Data.reload_static_cache
385
435
 
386
436
  Apollo models require PostgreSQL with the `pgvector` extension. They are skipped silently on SQLite and MySQL.
387
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
+
388
469
  ---
389
470
 
390
471
  ## Dependencies
@@ -404,7 +485,7 @@ Apollo models require PostgreSQL with the `pgvector` extension. They are skipped
404
485
 
405
486
  ## Migrations
406
487
 
407
- 76 numbered Sequel DSL migrations run automatically on startup (`auto_migrate: true`). Key milestones:
488
+ 97 numbered Sequel DSL migrations run automatically on startup (`auto_migrate: true`). Key milestones:
408
489
 
409
490
  | Range | What was added |
410
491
  |-------|---------------|
@@ -422,6 +503,9 @@ Apollo models require PostgreSQL with the `pgvector` extension. They are skipped
422
503
  | 068–071 | Entity type on audit records, principal on nodes, approval queue resume, engine on relationships |
423
504
  | 072–073 | Identity audit log and multi-instance identity columns |
424
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 |
425
509
 
426
510
  Run migrations standalone:
427
511
 
@@ -429,6 +513,15 @@ Run migrations standalone:
429
513
  bundle exec legionio_migrate
430
514
  ```
431
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
+
432
525
  ---
433
526
 
434
527
  ## CLI Executable
@@ -458,6 +551,7 @@ bundle exec legionio_migrate
458
551
  11. Financial logging for UAIS cost recovery
459
552
  12. Global tool embedding cache (L4 tier for `Legion::Tools::EmbeddingCache`)
460
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
461
555
 
462
556
  ---
463
557
 
@@ -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
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:llm_registry_events) do
6
+ primary_key :id
7
+ String :uuid, size: 36, null: false, unique: true
8
+ String :provider, size: 128
9
+ String :model_key, size: 255
10
+ String :event_type, size: 128, null: false
11
+ String :status, size: 64, null: false
12
+ String :reason, text: true
13
+ DateTime :recorded_at
14
+ DateTime :inserted_at, null: false, default: Sequel::CURRENT_TIMESTAMP
15
+
16
+ index :uuid
17
+ index %i[provider model_key]
18
+ index :event_type
19
+ index :status
20
+ index :recorded_at
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:portable_identity_providers) do
6
+ primary_key :id
7
+ String :uuid, size: 36, null: false, unique: true
8
+ String :name, size: 255, null: false, unique: true
9
+ String :provider_type, size: 64, null: false
10
+ String :facing, size: 32, null: false
11
+ Integer :priority, null: false, default: 100
12
+ Integer :trust_weight, null: false, default: 50
13
+ String :source, size: 64, null: false, default: 'gem'
14
+ TrueClass :enabled, null: false, default: true
15
+ DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
16
+ DateTime :updated_at, null: false, default: Sequel::CURRENT_TIMESTAMP
17
+
18
+ index :uuid
19
+ index :name
20
+ index :provider_type
21
+ index :enabled
22
+ end
23
+
24
+ create_table(:portable_identity_provider_capabilities) do
25
+ primary_key :id
26
+ foreign_key :provider_id, :portable_identity_providers, null: false, on_delete: :cascade
27
+ String :capability_key, size: 128, null: false
28
+ DateTime :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
29
+
30
+ unique %i[provider_id capability_key]
31
+ index :provider_id
32
+ index :capability_key
33
+ end
34
+ end
35
+ end