core/apps/webapp/app/services/conversation.server.ts
Harshith Mullapudi f39c7cc6d0
feat: remove trigger and run base on bullmq (#126)
* feat: remove trigger and run base on bullmq
* fix: telemetry and trigger deploymen
* feat: add Ollama container and update ingestion status for unchanged documents
* feat: add logger to bullmq workers
* 1. Remove chat and deep-search from trigger
2. Add ai/sdk for chat UI
3. Added a better model manager

* refactor: simplify clustered graph query and add stop conditions for AI responses

* fix: streaming

* fix: docker docs

---------

Co-authored-by: Manoj <saimanoj58@gmail.com>
2025-10-26 12:56:12 +05:30

221 lines
5.1 KiB
TypeScript

import { UserTypeEnum } from "@core/types";
import { prisma } from "~/db.server";
import { z } from "zod";
import { trackFeatureUsage } from "~/services/telemetry.server";
export const CreateConversationSchema = z.object({
message: z.string(),
title: z.string().optional(),
conversationId: z.string().optional(),
userType: z.nativeEnum(UserTypeEnum).optional(),
});
export type CreateConversationDto = z.infer<typeof CreateConversationSchema>;
// Create a new conversation
export async function createConversation(
workspaceId: string,
userId: string,
conversationData: CreateConversationDto,
) {
const { title, conversationId, ...otherData } = conversationData;
if (conversationId) {
// Add a new message to an existing conversation
const conversationHistory = await prisma.conversationHistory.create({
data: {
...otherData,
userType: otherData.userType || UserTypeEnum.User,
...(userId && {
user: {
connect: { id: userId },
},
}),
conversation: {
connect: { id: conversationId },
},
},
include: {
conversation: true,
},
});
// Track conversation message
trackFeatureUsage("conversation_message_sent", userId).catch(console.error);
return {
conversationId: conversationHistory.conversation.id,
conversationHistoryId: conversationHistory.id,
};
}
// Create a new conversation and its first message
const conversation = await prisma.conversation.create({
data: {
workspaceId,
userId,
title:
title?.substring(0, 100) ?? conversationData.message.substring(0, 100),
ConversationHistory: {
create: {
userId,
userType: otherData.userType || UserTypeEnum.User,
...otherData,
},
},
},
include: {
ConversationHistory: true,
},
});
const conversationHistory = conversation.ConversationHistory[0];
// Track new conversation creation
trackFeatureUsage("conversation_created", userId).catch(console.error);
return {
conversationId: conversation.id,
conversationHistoryId: conversationHistory.id,
};
}
// Get a conversation by ID
export async function getConversation(conversationId: string, userId: string) {
return prisma.conversation.findUnique({
where: { id: conversationId, userId },
});
}
// Delete a conversation (soft delete)
export async function deleteConversation(conversationId: string) {
return prisma.conversation.update({
where: { id: conversationId },
data: {
deleted: new Date().toISOString(),
},
});
}
// Mark a conversation as read
export async function readConversation(conversationId: string) {
return prisma.conversation.update({
where: { id: conversationId },
data: { unread: false },
});
}
export const getConversationAndHistory = async (
conversationId: string,
userId: string,
) => {
const conversation = await prisma.conversation.findFirst({
where: {
id: conversationId,
userId,
},
include: {
ConversationHistory: true,
},
});
return conversation;
};
export const createConversationHistory = async (
userMessage: string,
conversationId: string,
userType: UserTypeEnum,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
thoughts?: Record<string, any>,
) => {
return await prisma.conversationHistory.create({
data: {
conversationId,
message: userMessage,
thoughts,
userType,
},
});
};
export const GetConversationsListSchema = z.object({
page: z.string().optional().default("1"),
limit: z.string().optional().default("20"),
search: z.string().optional(),
});
export type GetConversationsListDto = z.infer<
typeof GetConversationsListSchema
>;
export async function getConversationsList(
workspaceId: string,
userId: string,
params: GetConversationsListDto,
) {
const page = parseInt(params.page);
const limit = parseInt(params.limit);
const skip = (page - 1) * limit;
const where = {
workspaceId,
userId,
deleted: null,
...(params.search && {
OR: [
{
title: {
contains: params.search,
mode: "insensitive" as const,
},
},
{
ConversationHistory: {
some: {
message: {
contains: params.search,
mode: "insensitive" as const,
},
},
},
},
],
}),
};
const [conversations, total] = await Promise.all([
prisma.conversation.findMany({
where,
include: {
ConversationHistory: {
take: 1,
orderBy: {
createdAt: "desc",
},
},
},
orderBy: {
updatedAt: "desc",
},
skip,
take: limit,
}),
prisma.conversation.count({ where }),
]);
return {
conversations,
pagination: {
page,
limit,
total,
totalPages: Math.ceil(total / limit),
hasNext: page < Math.ceil(total / limit),
hasPrev: page > 1,
},
};
}