182 lines
5.0 KiB
TypeScript
182 lines
5.0 KiB
TypeScript
import { json } from "@remix-run/server-runtime";
|
|
import { z } from "zod";
|
|
import { prisma } from "~/db.server";
|
|
import { PromptPresenter } from "~/presenters/v3/PromptPresenter.server";
|
|
import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstance.server";
|
|
import {
|
|
createActionApiRoute,
|
|
createLoaderApiRoute,
|
|
} from "~/services/routeBuilders/apiBuilder.server";
|
|
|
|
const ParamsSchema = z.object({
|
|
slug: z.string(),
|
|
});
|
|
|
|
const SearchParams = z.object({
|
|
label: z.string().optional(),
|
|
version: z.coerce.number().optional(),
|
|
});
|
|
|
|
// GET /api/v1/prompts/:slug — Get prompt + version
|
|
export const loader = createLoaderApiRoute(
|
|
{
|
|
params: ParamsSchema,
|
|
searchParams: SearchParams,
|
|
allowJWT: true,
|
|
corsStrategy: "all",
|
|
findResource: async (params, authentication) => {
|
|
return prisma.prompt.findUnique({
|
|
where: {
|
|
projectId_runtimeEnvironmentId_slug: {
|
|
projectId: authentication.environment.projectId,
|
|
runtimeEnvironmentId: authentication.environment.id,
|
|
slug: params.slug,
|
|
},
|
|
},
|
|
include: {
|
|
project: {
|
|
select: {
|
|
organizationId: true,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
},
|
|
authorization: {
|
|
action: "read",
|
|
resource: (_resource, params) => ({ type: "prompts", id: params.slug }),
|
|
},
|
|
},
|
|
async ({ searchParams, resource: prompt }) => {
|
|
if (!prompt) {
|
|
return json({ error: "Prompt not found" }, { status: 404 });
|
|
}
|
|
|
|
const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
|
|
prompt.project.organizationId,
|
|
"standard"
|
|
);
|
|
const presenter = new PromptPresenter(clickhouse);
|
|
const version = await presenter.resolveVersion(prompt.id, {
|
|
version: searchParams.version,
|
|
label: searchParams.label,
|
|
});
|
|
|
|
if (!version) {
|
|
return json({ error: "No version found" }, { status: 404 });
|
|
}
|
|
|
|
return json({
|
|
data: {
|
|
id: prompt.friendlyId,
|
|
slug: prompt.slug,
|
|
description: prompt.description,
|
|
type: prompt.type,
|
|
tags: prompt.tags,
|
|
defaultModel: prompt.defaultModel,
|
|
defaultConfig: prompt.defaultConfig,
|
|
variableSchema: prompt.variableSchema,
|
|
version: {
|
|
version: version.version,
|
|
textContent: version.textContent,
|
|
model: version.model,
|
|
config: version.config,
|
|
source: version.source,
|
|
labels: version.labels,
|
|
contentHash: version.contentHash,
|
|
commitMessage: version.commitMessage,
|
|
createdAt: version.createdAt,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
);
|
|
|
|
// POST /api/v1/prompts/:slug — Resolve prompt with variables
|
|
|
|
const ResolveBody = z.object({
|
|
variables: z.record(z.unknown()).default({}),
|
|
label: z.string().optional(),
|
|
version: z.number().optional(),
|
|
});
|
|
|
|
const { action } = createActionApiRoute(
|
|
{
|
|
params: ParamsSchema,
|
|
body: ResolveBody,
|
|
allowJWT: true,
|
|
corsStrategy: "all",
|
|
authorization: {
|
|
action: "read",
|
|
resource: (params) => ({ type: "prompts", id: params.slug }),
|
|
},
|
|
},
|
|
async ({ body, params, authentication }) => {
|
|
const prompt = await prisma.prompt.findUnique({
|
|
where: {
|
|
projectId_runtimeEnvironmentId_slug: {
|
|
projectId: authentication.environment.projectId,
|
|
runtimeEnvironmentId: authentication.environment.id,
|
|
slug: params.slug,
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!prompt) {
|
|
return json({ error: "Prompt not found" }, { status: 404 });
|
|
}
|
|
|
|
const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
|
|
authentication.environment.organizationId,
|
|
"standard"
|
|
);
|
|
const presenter = new PromptPresenter(clickhouse);
|
|
const version = await presenter.resolveVersion(prompt.id, {
|
|
version: body.version,
|
|
label: body.label,
|
|
});
|
|
|
|
if (!version) {
|
|
return json({ error: "No version found" }, { status: 404 });
|
|
}
|
|
|
|
if (!version.textContent) {
|
|
return json({ error: "Prompt has no content" }, { status: 404 });
|
|
}
|
|
|
|
const text = compileTemplate(version.textContent, body.variables);
|
|
return json({
|
|
data: {
|
|
promptId: prompt.friendlyId,
|
|
slug: prompt.slug,
|
|
version: version.version,
|
|
labels: version.labels,
|
|
template: version.textContent,
|
|
text,
|
|
model: version.model ?? prompt.defaultModel,
|
|
config: version.config ?? prompt.defaultConfig,
|
|
},
|
|
});
|
|
}
|
|
);
|
|
|
|
export { action };
|
|
|
|
function compileTemplate(template: string, variables: Record<string, unknown>): string {
|
|
let result = template.replace(/\{\{#(\w+)\}\}([\s\S]*?)\{\{\/\1\}\}/g, (_match, key, content) => {
|
|
const value = variables[key];
|
|
return value
|
|
? content.replace(/\{\{(\w+)\}\}/g, (_m: string, k: string) => {
|
|
return String(variables[k] ?? "");
|
|
})
|
|
: "";
|
|
});
|
|
|
|
result = result.replace(/\{\{\s*(\w+)\s*\}\}/g, (_match, key) => {
|
|
const value = variables[key];
|
|
return value !== undefined && value !== null ? String(value) : "";
|
|
});
|
|
|
|
return result;
|
|
}
|