55 lines
4.9 KiB
JavaScript
55 lines
4.9 KiB
JavaScript
// Durable jobs and private asset grants. No external calls or corpus changes.
|
|
exports.up = pgm => pgm.sql(`
|
|
CREATE TABLE generated_image_jobs (
|
|
id UUID PRIMARY KEY,
|
|
owner_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
|
workflow TEXT NOT NULL CHECK (workflow IN ('clinical_assistant', 'learning_hub')),
|
|
idempotency_key TEXT NOT NULL,
|
|
input_hash TEXT NOT NULL,
|
|
prompt_cipher TEXT NOT NULL CHECK (prompt_cipher LIKE 'enc1:%'),
|
|
model TEXT NOT NULL,
|
|
prompt_revision INTEGER NOT NULL,
|
|
budget INTEGER NOT NULL CHECK (budget BETWEEN 1000 AND 32000),
|
|
prompt_units INTEGER NOT NULL,
|
|
stage TEXT NOT NULL DEFAULT 'queued' CHECK (stage IN ('queued','generating','storing','done','error','interrupted')),
|
|
lease_token UUID, lease_until TIMESTAMPTZ,
|
|
staged_bytes BYTEA, mime TEXT, checksum TEXT, byte_length INTEGER,
|
|
error_code TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
UNIQUE(owner_id, workflow, idempotency_key)
|
|
);
|
|
CREATE INDEX generated_image_claim ON generated_image_jobs(stage, created_at);
|
|
CREATE TABLE generated_image_links (
|
|
asset_id UUID NOT NULL REFERENCES generated_image_jobs(id) ON DELETE CASCADE,
|
|
content_id INTEGER NOT NULL REFERENCES learning_content(id) ON DELETE CASCADE,
|
|
PRIMARY KEY(asset_id, content_id)
|
|
);
|
|
CREATE FUNCTION guard_generated_image_job() RETURNS trigger LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
IF (NEW.owner_id, NEW.workflow, NEW.idempotency_key, NEW.input_hash, NEW.prompt_cipher, NEW.model, NEW.prompt_revision, NEW.budget, NEW.prompt_units)
|
|
IS DISTINCT FROM (OLD.owner_id, OLD.workflow, OLD.idempotency_key, OLD.input_hash, OLD.prompt_cipher, OLD.model, OLD.prompt_revision, OLD.budget, OLD.prompt_units) THEN
|
|
RAISE EXCEPTION 'Image job input and ownership are immutable';
|
|
END IF;
|
|
RETURN NEW;
|
|
END; $$;
|
|
CREATE TRIGGER generated_image_job_immutable BEFORE UPDATE ON generated_image_jobs FOR EACH ROW EXECUTE FUNCTION guard_generated_image_job();
|
|
CREATE FUNCTION guard_generated_image_link() RETURNS trigger LANGUAGE plpgsql AS $$
|
|
BEGIN
|
|
IF NOT EXISTS (SELECT 1 FROM generated_image_jobs WHERE id = NEW.asset_id AND workflow = 'learning_hub' AND stage = 'done') THEN
|
|
RAISE EXCEPTION 'Only Learning assets may be attached';
|
|
END IF;
|
|
RETURN NEW;
|
|
END; $$;
|
|
CREATE TRIGGER generated_image_link_guard BEFORE INSERT OR UPDATE ON generated_image_links FOR EACH ROW EXECUTE FUNCTION guard_generated_image_link();
|
|
ALTER TABLE prompt_revisions DROP CONSTRAINT prompt_revisions_prompt_key_check;
|
|
ALTER TABLE prompt_revisions ADD CONSTRAINT prompt_revisions_prompt_key_check CHECK (prompt_key IN ('prompt.hpiEncounter','prompt.hpiDictation','prompt.hpiInpatient','prompt.hospitalCourseShort','prompt.hospitalCourseLong','prompt.hospitalCourseICU','prompt.hospitalCoursePsych','prompt.chartReviewOutpatient','prompt.chartReviewSubspecialty','prompt.chartReviewED','prompt.soapFull','prompt.soapSubjective','prompt.milestoneNarrative','prompt.milestoneList','prompt.milestoneSummary','prompt.peGuideNarrative','prompt.peGuideList','prompt.refine','prompt.shortenDocument','prompt.askClarification','prompt.shadessAssessment','prompt.wellVisitNote','prompt.wellVisitShort','prompt.sickVisitNote','prompt.edEncounterStaged','prompt.edConsolidate','prompt.edFinalize','prompt.dontMissTooltip','prompt.patientEducation','clinical_assistant.system_behavior','clinical_assistant.image_behavior','learning_hub.image_behavior'));
|
|
`);
|
|
// Down preserves append-only Learning prompt history: run only after explicit archival/removal of that history.
|
|
exports.down = pgm => pgm.sql(`
|
|
DO $$ BEGIN IF EXISTS(SELECT 1 FROM prompt_revisions WHERE prompt_key = 'learning_hub.image_behavior') THEN
|
|
RAISE EXCEPTION 'Learning image prompt history exists; retain migration rather than discard history'; END IF; END $$;
|
|
ALTER TABLE prompt_revisions DROP CONSTRAINT prompt_revisions_prompt_key_check;
|
|
ALTER TABLE prompt_revisions ADD CONSTRAINT prompt_revisions_prompt_key_check CHECK (prompt_key IN ('prompt.hpiEncounter','prompt.hpiDictation','prompt.hpiInpatient','prompt.hospitalCourseShort','prompt.hospitalCourseLong','prompt.hospitalCourseICU','prompt.hospitalCoursePsych','prompt.chartReviewOutpatient','prompt.chartReviewSubspecialty','prompt.chartReviewED','prompt.soapFull','prompt.soapSubjective','prompt.milestoneNarrative','prompt.milestoneList','prompt.milestoneSummary','prompt.peGuideNarrative','prompt.peGuideList','prompt.refine','prompt.shortenDocument','prompt.askClarification','prompt.shadessAssessment','prompt.wellVisitNote','prompt.wellVisitShort','prompt.sickVisitNote','prompt.edEncounterStaged','prompt.edConsolidate','prompt.edFinalize','prompt.dontMissTooltip','prompt.patientEducation','clinical_assistant.system_behavior','clinical_assistant.image_behavior'));
|
|
DROP TABLE generated_image_links; DROP TABLE generated_image_jobs;
|
|
DROP FUNCTION guard_generated_image_link(); DROP FUNCTION guard_generated_image_job();
|
|
`);
|