quick question feature

This commit is contained in:
2026-05-02 23:48:01 -07:00
parent 6fbcaecbf8
commit 29e340fd08
8 changed files with 748 additions and 106 deletions

View File

@@ -327,10 +327,50 @@ export async function registerRoutes(app: FastifyInstance) {
app.post("/v1/chats", async (req) => {
requireAdmin(req);
const Body = z.object({ title: z.string().optional() });
const body = Body.parse(req.body ?? {});
const Body = z
.object({
title: z.string().optional(),
provider: z.enum(["openai", "anthropic", "xai"]).optional(),
model: z.string().trim().min(1).optional(),
messages: z.array(CompletionMessageSchema).optional(),
})
.superRefine((value, ctx) => {
if (value.provider && !value.model) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "model is required when provider is supplied",
path: ["model"],
});
}
if (!value.provider && value.model) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "provider is required when model is supplied",
path: ["provider"],
});
}
});
const parsed = Body.safeParse(req.body ?? {});
if (!parsed.success) return app.httpErrors.badRequest(parsed.error.message);
const body = parsed.data;
const chat = await prisma.chat.create({
data: { title: body.title },
data: {
title: body.title,
initiatedProvider: body.provider as any,
initiatedModel: body.model,
lastUsedProvider: body.provider as any,
lastUsedModel: body.model,
messages: body.messages?.length
? {
create: body.messages.map((message) => ({
role: message.role as any,
content: message.content,
name: message.name,
metadata: message.attachments?.length ? ({ attachments: message.attachments } as any) : undefined,
})),
}
: undefined,
},
select: {
id: true,
title: true,
@@ -838,7 +878,9 @@ export async function registerRoutes(app: FastifyInstance) {
});
const { chatId } = Params.parse(req.params);
const body = Body.parse(req.body);
const parsed = Body.safeParse(req.body);
if (!parsed.success) return app.httpErrors.badRequest(parsed.error.message);
const body = parsed.data;
const msg = await prisma.message.create({
data: {
@@ -866,7 +908,9 @@ export async function registerRoutes(app: FastifyInstance) {
maxTokens: z.number().int().positive().optional(),
});
const body = Body.parse(req.body);
const parsed = Body.safeParse(req.body);
if (!parsed.success) return app.httpErrors.badRequest(parsed.error.message);
const body = parsed.data;
// ensure chat exists if provided
if (body.chatId) {
@@ -891,16 +935,29 @@ export async function registerRoutes(app: FastifyInstance) {
app.post("/v1/chat-completions/stream", async (req, reply) => {
requireAdmin(req);
const Body = z.object({
chatId: z.string().optional(),
provider: z.enum(["openai", "anthropic", "xai"]),
model: z.string().min(1),
messages: z.array(CompletionMessageSchema),
temperature: z.number().min(0).max(2).optional(),
maxTokens: z.number().int().positive().optional(),
});
const Body = z
.object({
chatId: z.string().optional(),
persist: z.boolean().optional(),
provider: z.enum(["openai", "anthropic", "xai"]),
model: z.string().min(1),
messages: z.array(CompletionMessageSchema),
temperature: z.number().min(0).max(2).optional(),
maxTokens: z.number().int().positive().optional(),
})
.superRefine((value, ctx) => {
if (value.persist === false && value.chatId) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "chatId must be omitted when persist is false",
path: ["chatId"],
});
}
});
const body = Body.parse(req.body);
const parsed = Body.safeParse(req.body);
if (!parsed.success) return app.httpErrors.badRequest(parsed.error.message);
const body = parsed.data;
// ensure chat exists if provided
if (body.chatId) {
@@ -909,7 +966,7 @@ export async function registerRoutes(app: FastifyInstance) {
}
// Store only new non-assistant messages to avoid duplicate history entries.
if (body.chatId) {
if (body.persist !== false && body.chatId) {
await storeNonAssistantMessages(body.chatId, body.messages);
}