Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Warnings:
- You are about to drop the column `downVotes` on the `aiWorkflowRunItem` table. All the data in the column will be lost.
- You are about to drop the column `upVotes` on the `aiWorkflowRunItem` table. All the data in the column will be lost.
- You are about to drop the column `downVotes` on the `aiWorkflowRunItemComment` table. All the data in the column will be lost.
- You are about to drop the column `upVotes` on the `aiWorkflowRunItemComment` table. All the data in the column will be lost.
*/
-- CreateEnum
CREATE TYPE "VoteType" AS ENUM ('UPVOTE', 'DOWNVOTE');

-- AlterTable
ALTER TABLE "aiWorkflowRunItem" DROP COLUMN "downVotes",
DROP COLUMN "upVotes";

-- AlterTable
ALTER TABLE "aiWorkflowRunItemComment" DROP COLUMN "downVotes",
DROP COLUMN "upVotes";

-- CreateTable
CREATE TABLE "aiWorkflowRunItemVote" (
"id" VARCHAR(14) NOT NULL DEFAULT nanoid(),
"workflowRunItemId" VARCHAR(14) NOT NULL,
"voteType" "VoteType" NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdBy" TEXT,

CONSTRAINT "aiWorkflowRunItemVote_pkey" PRIMARY KEY ("id")
);

-- CreateTable
CREATE TABLE "aiWorkflowRunItemCommentVote" (
"id" VARCHAR(14) NOT NULL DEFAULT nanoid(),
"workflowRunItemCommentId" VARCHAR(14) NOT NULL,
"voteType" "VoteType" NOT NULL,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"createdBy" TEXT,

CONSTRAINT "aiWorkflowRunItemCommentVote_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE INDEX "aiWorkflowRunItemVote_workflowRunItemId_idx" ON "aiWorkflowRunItemVote"("workflowRunItemId");

-- CreateIndex
CREATE INDEX "aiWorkflowRunItemCommentVote_workflowRunItemCommentId_idx" ON "aiWorkflowRunItemCommentVote"("workflowRunItemCommentId");

-- AddForeignKey
ALTER TABLE "aiWorkflowRunItemVote" ADD CONSTRAINT "aiWorkflowRunItemVote_workflowRunItemId_fkey" FOREIGN KEY ("workflowRunItemId") REFERENCES "aiWorkflowRunItem"("id") ON DELETE CASCADE ON UPDATE CASCADE;

-- AddForeignKey
ALTER TABLE "aiWorkflowRunItemCommentVote" ADD CONSTRAINT "aiWorkflowRunItemCommentVote_workflowRunItemCommentId_fkey" FOREIGN KEY ("workflowRunItemCommentId") REFERENCES "aiWorkflowRunItemComment"("id") ON DELETE CASCADE ON UPDATE CASCADE;
37 changes: 33 additions & 4 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,35 @@ model llmModel {
workflows aiWorkflow[]
}

model aiWorkflowRunItemVote {
id String @id @default(dbgenerated("nanoid()")) @db.VarChar(14)
workflowRunItemId String @db.VarChar(14)
voteType VoteType
createdAt DateTime @default(now()) @db.Timestamp(3)
createdBy String? @db.Text

item aiWorkflowRunItem @relation(fields: [workflowRunItemId], references: [id], onDelete: Cascade)

@@index([workflowRunItemId])
}

model aiWorkflowRunItemCommentVote {
id String @id @default(dbgenerated("nanoid()")) @db.VarChar(14)
workflowRunItemCommentId String @db.VarChar(14)
voteType VoteType
createdAt DateTime @default(now()) @db.Timestamp(3)
createdBy String? @db.Text

comment aiWorkflowRunItemComment @relation(fields: [workflowRunItemCommentId], references: [id], onDelete: Cascade)

@@index([workflowRunItemCommentId])
}

enum VoteType {
UPVOTE
DOWNVOTE
}

model aiWorkflow {
id String @id @default(dbgenerated("nanoid()")) @db.VarChar(14)
name String @unique @db.VarChar
Expand Down Expand Up @@ -690,24 +719,22 @@ model aiWorkflowRunItem {
workflowRunId String @db.VarChar(14)
scorecardQuestionId String @db.VarChar(14)
content String @db.Text
upVotes Int @default(0)
downVotes Int @default(0)
questionScore Float? @db.DoublePrecision
createdAt DateTime @db.Timestamp(3)
createdBy String? @db.Text

run aiWorkflowRun @relation(fields: [workflowRunId], references: [id])
question scorecardQuestion @relation(fields: [scorecardQuestionId], references: [id])
comments aiWorkflowRunItemComment[]

votes aiWorkflowRunItemVote[]
}

model aiWorkflowRunItemComment {
id String @id @default(dbgenerated("nanoid()")) @db.VarChar(14)
workflowRunItemId String @db.VarChar(14)
userId String @db.Text
content String @db.Text
upVotes Int @default(0)
downVotes Int @default(0)
parentId String? @db.VarChar(14)
createdAt DateTime @default(now()) @db.Timestamp(3)
createdBy String? @db.Text
Expand All @@ -717,4 +744,6 @@ model aiWorkflowRunItemComment {
item aiWorkflowRunItem @relation(fields: [workflowRunItemId], references: [id])
parent aiWorkflowRunItemComment? @relation("CommentHierarchy", fields: [parentId], references: [id])
replies aiWorkflowRunItemComment[] @relation("CommentHierarchy")

votes aiWorkflowRunItemCommentVote[]
}
109 changes: 95 additions & 14 deletions src/api/ai-workflow/ai-workflow.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { UserRole } from 'src/shared/enums/userRole.enum';
import { ChallengeStatus } from 'src/shared/enums/challengeStatus.enum';
import { LoggerService } from 'src/shared/modules/global/logger.service';
import { GiteaService } from 'src/shared/modules/global/gitea.service';
import { VoteType } from '@prisma/client';

@Injectable()
export class AiWorkflowService {
Expand Down Expand Up @@ -96,7 +97,49 @@ export class AiWorkflowService {
);
}

const allowedFields = ['content', 'upVotes', 'downVotes'];
// Handle vote updates
if (patchData.upVote !== undefined || patchData.downVote !== undefined) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ correctness]
Consider using a transaction for the vote update operations to ensure atomicity. If any part of the vote update process fails, it could leave the database in an inconsistent state.

if (!user.userId) {
throw new BadRequestException('User id is not available');
}

// Remove existing votes by this user for this comment
await this.prisma.aiWorkflowRunItemCommentVote.deleteMany({
where: {
workflowRunItemCommentId: commentId,
createdBy: user.userId.toString(),
},
});

// Add new vote if upVote or downVote is true
if (patchData.upVote) {
await this.prisma.aiWorkflowRunItemCommentVote.create({
data: {
workflowRunItemCommentId: commentId,
voteType: VoteType.UPVOTE,
createdBy: user.userId.toString(),
},
});
} else if (patchData.downVote) {
await this.prisma.aiWorkflowRunItemCommentVote.create({
data: {
workflowRunItemCommentId: commentId,
voteType: VoteType.DOWNVOTE,
createdBy: user.userId.toString(),
},
});
}

delete patchData.downVote;
delete patchData.upVote;
}

// No other fields to update apart from likes
if (Object.keys(patchData).length === 0) {
return;
}

const allowedFields = ['content'];
const updateData: any = {};
for (const key of allowedFields) {
if (key in patchData) {
Expand Down Expand Up @@ -388,8 +431,6 @@ export class AiWorkflowService {
workflowRunId: runId,
scorecardQuestionId: item.scorecardQuestionId,
content: item.content,
upVotes: item.upVotes ?? 0,
downVotes: item.downVotes ?? 0,
questionScore: item.questionScore ?? null,
createdAt: new Date(),
// TODO: Remove this once prisma middleware implementation is done
Expand Down Expand Up @@ -819,15 +860,6 @@ export class AiWorkflowService {
);
}

const updateData: any = {};

if (patchData.upVotes !== undefined) {
updateData.upVotes = patchData.upVotes;
}
if (patchData.downVotes !== undefined) {
updateData.downVotes = patchData.downVotes;
}

if (!user.isMachine) {
const keys = Object.keys(patchData);
const prohibitedKeys = ['content', 'questionScore'];
Expand All @@ -838,21 +870,70 @@ export class AiWorkflowService {
}
}

// Update properties which can be updated only via m2m
if (patchData.upVote !== undefined || patchData.downVote !== undefined) {
// Remove existing votes by this user for this item
if (!user.userId) {
throw new BadRequestException('User id is not available');
}

await this.prisma.aiWorkflowRunItemVote.deleteMany({
where: {
workflowRunItemId: itemId,
createdBy: user.userId.toString(),
},
});

// Add new vote if upVote or downVote is true
if (patchData.upVote) {
await this.prisma.aiWorkflowRunItemVote.create({
data: {
workflowRunItemId: itemId,
voteType: VoteType.UPVOTE,
createdBy: user.userId.toString(),
},
});
} else if (patchData.downVote) {
await this.prisma.aiWorkflowRunItemVote.create({
data: {
workflowRunItemId: itemId,
voteType: VoteType.DOWNVOTE,
createdBy: user.userId.toString(),
},
});
}

delete patchData.downVote;
delete patchData.upVote;
}

// Update other properties only allowed for machine users
const updateData: any = {};
if (user.isMachine) {
if (patchData.content) {
updateData.content = patchData.content;
}

if (patchData.questionScore) {
updateData.questionScore = patchData.questionScore;
}
}

// If there are no other fields to update
// just return the run item
if (Object.keys(updateData).length === 0) {
return this.prisma.aiWorkflowRunItem.findUnique({
where: { id: itemId },
include: {
comments: true,
votes: true,
},
});
}

return this.prisma.aiWorkflowRunItem.update({
where: { id: itemId },
include: {
comments: true,
votes: true,
},
data: updateData,
});
Expand Down
19 changes: 9 additions & 10 deletions src/dto/aiWorkflow.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
IsUUID,
Max,
IsEmpty,
IsBoolean,
} from 'class-validator';
import { Type, Transform } from 'class-transformer';

Expand Down Expand Up @@ -134,15 +135,13 @@ export class CreateAiWorkflowRunItemDto {

@ApiProperty({ required: false })
@IsOptional()
@IsInt()
@Min(0)
upVotes?: number;
@IsBoolean()

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[⚠️ design]
Changing upVotes from an integer to a boolean may limit future functionality, such as tracking the number of upvotes. Consider if this change aligns with the intended use case.

upVote?: boolean;

@ApiProperty({ required: false })
@IsOptional()
@IsInt()
@Min(0)
downVotes?: number;
@IsBoolean()
downVote?: boolean;

@ApiProperty({ required: false })
@IsOptional()
Expand Down Expand Up @@ -202,14 +201,14 @@ export class UpdateRunItemCommentDto {
content?: string;

@ApiProperty({ required: false })
@IsInt()
@IsBoolean()
@IsOptional()
upVotes?: number;
upVote?: boolean;

@ApiProperty({ required: false })
@IsInt()
@IsBoolean()
@IsOptional()
downVotes?: number;
downVote?: boolean;

@ApiHideProperty()
@IsEmpty({ message: 'parentId cannot be updated' })
Expand Down
Loading