priv/repo/migrations/20260823074000_create_continual_learning_jobs.exs

main at 58e6347eeb72 · 5 KB

defmodule OpenAgents.Repo.Migrations.CreateContinualLearningJobs do
  use Ecto.Migration

  def change do
    create table(:continual_learning_jobs, primary_key: false) do
      add :id, :uuid, primary_key: true
      add :buyer_ref, :text, null: false
      add :buyer_class, :text, null: false
      add :objective, :text, null: false
      add :objective_version, :integer, null: false
      add :base_model_ref, :text, null: false
      add :base_model_digest, :text, null: false
      add :training_code_digest, :text, null: false
      add :configuration, :map, null: false, default: fragment("'{}'::jsonb")
      add :configuration_digest, :text, null: false
      add :datasets, {:array, :map}, null: false, default: []
      add :evaluation, :map, null: false, default: fragment("'{}'::jsonb")
      add :budget, :map, null: false, default: fragment("'{}'::jsonb")
      add :runtime_class, :text, null: false
      add :capacity_receipt, :map, null: false, default: fragment("'{}'::jsonb")
      add :stopping_policy, :map, null: false, default: fragment("'{}'::jsonb")
      add :admission_digest, :text, null: false
      add :status, :text, null: false, default: "queued"
      add :error_code, :text
      add :rounds_completed, :integer, null: false, default: 0
      add :resume_count, :integer, null: false, default: 0
      add :usage, :map, null: false, default: fragment("'{}'::jsonb")
      add :work_job_id, references(:work_jobs, type: :uuid, on_delete: :restrict)
      add :replay_of_id, references(:continual_learning_jobs, type: :uuid, on_delete: :restrict)
      add :started_at, :utc_datetime_usec
      add :completed_at, :utc_datetime_usec

      timestamps(type: :utc_datetime_usec)
    end

    create constraint(:continual_learning_jobs, :continual_learning_jobs_status,
             check:
               "status IN ('queued', 'running', 'completed', 'failed', 'interrupted', 'budget_exhausted', 'cancelled')"
           )

    create constraint(:continual_learning_jobs, :continual_learning_jobs_rounds,
             check: "rounds_completed >= 0 AND resume_count >= 0"
           )

    create constraint(:continual_learning_jobs, :continual_learning_jobs_not_own_replay,
             check: "replay_of_id IS NULL OR replay_of_id <> id"
           )

    create index(:continual_learning_jobs, [:buyer_ref])
    create index(:continual_learning_jobs, [:status])
    create index(:continual_learning_jobs, [:admission_digest])
    create unique_index(:continual_learning_jobs, [:work_job_id])

    create table(:continual_learning_checkpoints, primary_key: false) do
      add :id, :uuid, primary_key: true

      add :job_id,
          references(:continual_learning_jobs, type: :uuid, on_delete: :delete_all),
          null: false

      add :round, :integer, null: false
      add :state, :map, null: false, default: fragment("'{}'::jsonb")
      add :state_digest, :text, null: false
      add :parent_digest, :text
      add :metrics, :map, null: false, default: fragment("'{}'::jsonb")
      add :usage, :map, null: false, default: fragment("'{}'::jsonb")
      add :energy, :map, null: false, default: fragment("'{}'::jsonb")
      add :lost, :boolean, null: false, default: false

      timestamps(type: :utc_datetime_usec)
    end

    create constraint(:continual_learning_checkpoints, :continual_learning_checkpoints_round,
             check: "round > 0"
           )

    create unique_index(:continual_learning_checkpoints, [:job_id, :round])
    create unique_index(:continual_learning_checkpoints, [:job_id, :state_digest])

    create table(:continual_learning_receipts, primary_key: false) do
      add :id, :uuid, primary_key: true

      add :job_id,
          references(:continual_learning_jobs, type: :uuid, on_delete: :delete_all),
          null: false

      add :kind, :text, null: false
      add :sequence, :integer, null: false
      add :receipt_ref, :text, null: false
      add :payload, :map, null: false, default: fragment("'{}'::jsonb")
      add :digest, :text, null: false

      timestamps(type: :utc_datetime_usec)
    end

    create constraint(:continual_learning_receipts, :continual_learning_receipts_kind,
             check:
               "kind IN ('admission', 'usage', 'energy', 'training', 'evaluation', 'artifact', 'settlement', 'resume', 'refusal')"
           )

    create unique_index(:continual_learning_receipts, [:job_id, :sequence])
    create unique_index(:continual_learning_receipts, [:receipt_ref])
    create index(:continual_learning_receipts, [:job_id, :kind])

    create table(:continual_learning_artifacts, primary_key: false) do
      add :id, :uuid, primary_key: true

      add :job_id,
          references(:continual_learning_jobs, type: :uuid, on_delete: :delete_all),
          null: false

      add :model_ref, :text, null: false
      add :model_digest, :text, null: false
      add :base_model_digest, :text, null: false
      add :training_code_digest, :text, null: false
      add :configuration_digest, :text, null: false
      add :dataset_bindings, {:array, :map}, null: false, default: []
      add :checkpoint_digests, {:array, :text}, null: false, default: []
      add :evaluation_result, :map, null: false, default: fragment("'{}'::jsonb")
      add :accepted_outcome, :map, null: false, default: fragment("'{}'::jsonb")
      add :settlement, :map, null: false, default: fragment("'{}'::jsonb")
      add :artifact_digest, :text, null: false

      timestamps(type: :utc_datetime_usec)
    end

    create unique_index(:continual_learning_artifacts, [:job_id])
    # Two jobs admitted under identical inputs must produce the same artifact
    # digest, so the digest is indexed for lookup and never made unique: a
    # replay that reproduces the digest is the evidence, not a conflict.
    create index(:continual_learning_artifacts, [:artifact_digest])
  end
end