chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,166 @@
|
||||
import { createHash } from "crypto";
|
||||
import { prisma } from "~/db.server";
|
||||
import { BaseService, ServiceValidationError } from "./baseService.server";
|
||||
import { normalizePromptOverrideSource } from "./promptOverrideSource";
|
||||
|
||||
export class PromptService extends BaseService {
|
||||
async promoteVersion(promptId: string, versionId: string, options?: { sourceGuard?: boolean }) {
|
||||
const target = await this._prisma.promptVersion.findUnique({
|
||||
where: { id: versionId },
|
||||
});
|
||||
|
||||
if (!target) {
|
||||
throw new ServiceValidationError("Version not found", 404);
|
||||
}
|
||||
|
||||
if (target.promptId !== promptId) {
|
||||
throw new ServiceValidationError("Version does not belong to this prompt", 400);
|
||||
}
|
||||
|
||||
if (options?.sourceGuard && target.source !== "code") {
|
||||
throw new ServiceValidationError(
|
||||
"Only code-sourced versions can be promoted. Use the override API instead.",
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.$executeRaw`
|
||||
UPDATE "prompt_versions"
|
||||
SET "labels" = array_remove("labels", 'current')
|
||||
WHERE "promptId" = ${promptId} AND 'current' = ANY("labels")
|
||||
`;
|
||||
await tx.$executeRaw`
|
||||
UPDATE "prompt_versions"
|
||||
SET "labels" = array_append("labels", 'current')
|
||||
WHERE "id" = ${versionId} AND NOT ('current' = ANY("labels"))
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
async createOverride(
|
||||
promptId: string,
|
||||
data: {
|
||||
textContent: string;
|
||||
model?: string;
|
||||
commitMessage?: string;
|
||||
source?: string;
|
||||
createdBy?: string;
|
||||
}
|
||||
) {
|
||||
const contentHash = createHash("sha256").update(data.textContent).digest("hex").slice(0, 16);
|
||||
|
||||
const result = await prisma.$transaction(async (tx) => {
|
||||
const latest = await tx.promptVersion.findFirst({
|
||||
where: { promptId },
|
||||
orderBy: { version: "desc" },
|
||||
});
|
||||
const nextVersion = (latest?.version ?? 0) + 1;
|
||||
|
||||
await tx.$executeRaw`
|
||||
UPDATE "prompt_versions"
|
||||
SET "labels" = array_remove("labels", 'override')
|
||||
WHERE "promptId" = ${promptId} AND 'override' = ANY("labels")
|
||||
`;
|
||||
|
||||
// Defence in depth: reject the reserved `code` source regardless of
|
||||
// caller, in case a future caller skips the route-layer check.
|
||||
const safeSource = normalizePromptOverrideSource(data.source);
|
||||
|
||||
await tx.promptVersion.create({
|
||||
data: {
|
||||
promptId,
|
||||
version: nextVersion,
|
||||
textContent: data.textContent,
|
||||
model: data.model || null,
|
||||
source: safeSource,
|
||||
commitMessage: data.commitMessage || null,
|
||||
contentHash,
|
||||
labels: ["override"],
|
||||
createdBy: data.createdBy,
|
||||
},
|
||||
});
|
||||
|
||||
return { version: nextVersion };
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async updateOverride(
|
||||
promptId: string,
|
||||
data: {
|
||||
textContent?: string;
|
||||
model?: string;
|
||||
commitMessage?: string;
|
||||
}
|
||||
) {
|
||||
const overrideVer = await this._prisma.promptVersion.findFirst({
|
||||
where: { promptId, labels: { has: "override" } },
|
||||
orderBy: { version: "desc" },
|
||||
});
|
||||
|
||||
if (!overrideVer) {
|
||||
throw new ServiceValidationError("No active override to update", 400);
|
||||
}
|
||||
|
||||
const contentString = data.textContent ?? overrideVer.textContent ?? "";
|
||||
const contentHash = createHash("sha256").update(contentString).digest("hex").slice(0, 16);
|
||||
|
||||
await this._prisma.promptVersion.update({
|
||||
where: { id: overrideVer.id },
|
||||
data: {
|
||||
textContent: contentString,
|
||||
model: data.model ?? overrideVer.model,
|
||||
commitMessage: data.commitMessage ?? overrideVer.commitMessage,
|
||||
contentHash,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async removeOverride(promptId: string) {
|
||||
await this.#removeLabel(promptId, "override");
|
||||
}
|
||||
|
||||
async reactivateOverride(promptId: string, versionId: string) {
|
||||
const target = await this._prisma.promptVersion.findUnique({
|
||||
where: { id: versionId },
|
||||
});
|
||||
|
||||
if (!target) {
|
||||
throw new ServiceValidationError("Version not found", 404);
|
||||
}
|
||||
|
||||
if (target.promptId !== promptId) {
|
||||
throw new ServiceValidationError("Version does not belong to this prompt", 400);
|
||||
}
|
||||
|
||||
if (target.source === "code") {
|
||||
throw new ServiceValidationError(
|
||||
"Code-sourced versions cannot be reactivated as overrides",
|
||||
400
|
||||
);
|
||||
}
|
||||
|
||||
await prisma.$transaction(async (tx) => {
|
||||
await tx.$executeRaw`
|
||||
UPDATE "prompt_versions"
|
||||
SET "labels" = array_remove("labels", 'override')
|
||||
WHERE "promptId" = ${promptId} AND 'override' = ANY("labels")
|
||||
`;
|
||||
await tx.$executeRaw`
|
||||
UPDATE "prompt_versions"
|
||||
SET "labels" = array_append("labels", 'override')
|
||||
WHERE "id" = ${versionId} AND NOT ('override' = ANY("labels"))
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
async #removeLabel(promptId: string, label: string) {
|
||||
await this._prisma.$executeRaw`
|
||||
UPDATE "prompt_versions"
|
||||
SET "labels" = array_remove("labels", ${label})
|
||||
WHERE "promptId" = ${promptId} AND ${label} = ANY("labels")
|
||||
`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user