Agent and Mission GraphQL CRUD operations

This commit is contained in:
Eng. Elias
2024-02-20 17:06:45 +04:00
parent e6f08d85fc
commit 619f199abe
13 changed files with 1744 additions and 59 deletions

1382
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,7 +9,11 @@
"lint": "next lint"
},
"dependencies": {
"@apollo/client": "^3.9.5",
"@apollo/server": "^4.10.0",
"@as-integrations/next": "^3.0.0",
"@prisma/client": "^5.9.1",
"graphql": "^16.8.1",
"next": "14.1.0",
"react": "^18",
"react-dom": "^18",

View File

@@ -24,10 +24,10 @@ CREATE TABLE "Agent" (
CREATE TABLE "Mission" (
"id" SERIAL NOT NULL,
"name" TEXT NOT NULL,
"tasks" JSONB NOT NULL,
"verbose" BOOLEAN NOT NULL,
"process" "MissionProcess" NOT NULL,
"result" TEXT NOT NULL,
"tasks" JSONB[] DEFAULT ARRAY[]::JSONB[],
"verbose" BOOLEAN NOT NULL DEFAULT false,
"process" "MissionProcess" NOT NULL DEFAULT 'SEQUENTIAL',
"result" TEXT NOT NULL DEFAULT '',
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL,
@@ -35,16 +35,19 @@ CREATE TABLE "Mission" (
);
-- CreateTable
CREATE TABLE "AgentInMission" (
"agentId" INTEGER NOT NULL,
"missionId" INTEGER NOT NULL,
"assignedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "AgentInMission_pkey" PRIMARY KEY ("agentId","missionId")
CREATE TABLE "_AgentToMission" (
"A" INTEGER NOT NULL,
"B" INTEGER NOT NULL
);
-- AddForeignKey
ALTER TABLE "AgentInMission" ADD CONSTRAINT "AgentInMission_agentId_fkey" FOREIGN KEY ("agentId") REFERENCES "Agent"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
-- CreateIndex
CREATE UNIQUE INDEX "_AgentToMission_AB_unique" ON "_AgentToMission"("A", "B");
-- CreateIndex
CREATE INDEX "_AgentToMission_B_index" ON "_AgentToMission"("B");
-- AddForeignKey
ALTER TABLE "AgentInMission" ADD CONSTRAINT "AgentInMission_missionId_fkey" FOREIGN KEY ("missionId") REFERENCES "Mission"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
ALTER TABLE "_AgentToMission" ADD CONSTRAINT "_AgentToMission_A_fkey" FOREIGN KEY ("A") REFERENCES "Agent"("id") ON DELETE CASCADE ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "_AgentToMission" ADD CONSTRAINT "_AgentToMission_B_fkey" FOREIGN KEY ("B") REFERENCES "Mission"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@@ -35,7 +35,7 @@ model Agent {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
missions AgentInMission[]
missions Mission[]
}
enum MissionProcess {
@@ -44,24 +44,14 @@ enum MissionProcess {
}
model Mission {
id Int @id @default(autoincrement())
id Int @id @default(autoincrement())
name String
crew AgentInMission[]
tasks Json
verbose Boolean
process MissionProcess
result String
crew Agent[]
tasks Json[] @default([])
verbose Boolean @default(false)
process MissionProcess @default(SEQUENTIAL)
result String @default("")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model AgentInMission {
agent Agent @relation(fields: [agentId], references: [id])
agentId Int // relation scalar field (used in the `@relation` attribute above)
mission Mission @relation(fields: [missionId], references: [id])
missionId Int // relation scalar field (used in the `@relation` attribute above)
assignedAt DateTime @default(now())
@@id([agentId, missionId])
}

View File

@@ -0,0 +1,167 @@
import { Process } from "@/data/consts";
import { Agent } from "@/types/agent";
import { CreateMissionInput, Mission } from "@/types/mission";
import { Task } from "@/types/task";
import prisma from "@/utils/prisma";
import { GraphQLResolveInfo } from "graphql";
import { NextRequest, NextResponse } from "next/server";
const resolvers = {
Query: {
agents: () => {
return prisma.agent.findMany();
},
agent: (id: number) => {
return prisma.agent.findFirst({
where: {
id: id,
},
});
},
missions: async () => {
const missions = await prisma.mission.findMany({
include: {
crew: true,
},
});
return missions;
},
mission: (id: number) => {
return prisma.mission.findFirst({
where: {
id: id,
},
});
},
},
Mutation: {
createAgent: async (
parent: any,
body: Agent,
context: { req: NextRequest; res: NextResponse; datasource: any },
info: GraphQLResolveInfo
) => {
const agent = await prisma.agent.create({ data: body });
return agent;
},
updateAgent: async (
parent: any,
body: Agent,
context: { req: NextRequest; res: NextResponse; datasource: any },
info: GraphQLResolveInfo
) => {
const updatedAgent = await prisma.agent.update({
where: { id: body.id },
data: body,
});
return updatedAgent;
},
deleteAgent: async (
parent: any,
body: { id: number },
context: { req: NextRequest; res: NextResponse; datasource: any },
info: GraphQLResolveInfo
) => {
await prisma.agent.delete({ where: { id: body.id } });
return { deleted: true };
},
createMission: async (
parent: any,
body: CreateMissionInput,
context: { req: NextRequest; res: NextResponse; datasource: any },
info: GraphQLResolveInfo
) => {
const { name, verbose, process } = body;
const crew = await prisma.agent.findMany({
where: {
id: {
in: body.crew,
},
},
});
const tasks: Array<Task> = [];
for (let task of body.tasks) {
const agent = await prisma.agent.findFirst({
where: { id: task.agent },
});
tasks.push({
...task,
agent,
});
}
const mission = await prisma.mission.create({
data: {
name,
verbose: !!verbose,
process: process ?? Process.SEQUENTIAL,
crew: { create: crew },
tasks,
result: "",
},
});
mission.id;
return mission;
},
updateMission: async (
parent: any,
body: CreateMissionInput,
context: { req: NextRequest; res: NextResponse; datasource: any },
info: GraphQLResolveInfo
) => {
const { id, name, verbose, process } = body;
const crew = await prisma.agent.findMany({
where: {
id: {
in: body.crew,
},
},
});
const tasks: Array<Task> = [];
if (body.tasks) {
for (let task of body.tasks) {
const agent = await prisma.agent.findFirst({
where: { id: task.agent },
});
tasks.push({
...task,
agent,
});
}
}
const mission = await prisma.mission.update({
where: {
id,
},
include: {
crew: true,
},
data: {
name,
verbose: verbose,
process: process,
crew: { set: crew.map((agent) => ({ id: agent.id })) },
tasks,
result: "",
},
});
return mission;
},
deleteMission: async (
parent: any,
body: { id: number },
context: { req: NextRequest; res: NextResponse; datasource: any },
info: GraphQLResolveInfo
) => {
await prisma.mission.delete({ where: { id: body.id } });
return { deleted: true };
},
runMission: async (
parent: any,
body: { id: number },
context: { req: NextRequest; res: NextResponse; datasource: any },
info: GraphQLResolveInfo
) => {},
},
};
export default resolvers;

View File

@@ -0,0 +1,24 @@
import { startServerAndCreateNextHandler } from "@as-integrations/next";
import { ApolloServer } from "@apollo/server";
import { NextRequest, NextResponse } from "next/server";
import typeDefs from "./schema";
import resolvers from "./resolvers";
const server = new ApolloServer<object>({
resolvers,
typeDefs,
});
const handler = startServerAndCreateNextHandler<NextRequest>(server, {
context: async (req, res) => ({
req,
res,
dataSources: {},
}),
});
export async function GET(request: NextRequest) {
return handler(request);
}
export async function POST(request: NextRequest) {
return handler(request);
}

View File

@@ -0,0 +1,113 @@
const typeDefs = `#graphql
enum AgentTool {
DUCK_DUCK_GO_SEARCH
PUBMED
PYTHON_REPL
SEMANTIC_SCHOLER
STACK_EXCHANGE
WIKIDATA
WIKIPEDIA
YAHOO_FINANCE
YUOUTUBE_SEARCH
}
type Agent {
id: ID!
role: String!
goal: String!
backstory: String
tools: [AgentTool!]!
allowDelegation: Boolean!
verbose: Boolean!
image: String
missions: [Mission!]
}
input AgentInput {
id: ID!
}
type DeleteOutput {
deleted: Boolean!
}
type Task {
name: String!
description: String!
agent: Agent!
}
input TaskInput {
name: String!
description: String!
agent: Int
}
type Mission {
id: ID!
name: String!
crew: [Agent!]
tasks: [Task]
verbose: Boolean
process: MissionProcess
result: String
}
enum MissionProcess {
SEQUENTIAL
HIERARCHICAL
}
type Query {
agents(filter: String): [Agent!]!
agent(id: Int!): Agent
missions(filter: String): [Mission!]!
mission(id: Int!): Mission
}
type Mutation {
createAgent(
role: String!
goal: String!
backstory: String
tools: [AgentTool!] = []
allowDelegation: Boolean = false
verbose: Boolean = false
): Agent!
updateAgent(
id: Int!
role: String
goal: String
backstory: String
tools: [AgentTool!]
allowDelegation: Boolean
verbose: Boolean
): Agent!
deleteAgent(id: Int!): DeleteOutput
createMission(
name: String!
crew: [Int!] = []
tasks: [TaskInput!] = []
verbose: Boolean = false
process: MissionProcess = "SEQUENTIAL"
): Mission!
updateMission(
id: Int!
name: String
crew: [Int!]
tasks: [TaskInput!]
verbose: Boolean
process: MissionProcess
): Mission
deleteMission(id: Int!): DeleteOutput
runMission(id: Int!): Mission
}
`;
export default typeDefs;

4
src/data/consts.ts Normal file
View File

@@ -0,0 +1,4 @@
export enum Process {
SEQUENTIAL = "SEQUENTIAL",
HIERARCHICAL = "HIERARCHICAL",
}

View File

@@ -9,7 +9,7 @@ export const agents: Array<Agent> = [
Your expertise in programming in python. and do your best to
produce perfect code
`,
tools: ["tool1", "tool2"],
tools: ["DUCK_DUCK_GO_SEARCH", "PYTHON_REPL", "STACK_EXCHANGE"],
allowDelegation: false,
verbose: true,
image:

View File

@@ -1,9 +1,21 @@
type Tool =
| "DUCK_DUCK_GO_SEARCH"
| "PUBMED"
| "PYTHON_REPL"
| "SEMANTIC_SCHOLER"
| "STACK_EXCHANGE"
| "WIKIDATA"
| "WIKIPEDIA"
| "YAHOO_FINANCE"
| "YUOUTUBE_SEARCH";
export type Agent = {
id?: number;
role: string;
goal: string;
backstory: string;
tools: Array<string>;
backstory?: string | null;
tools: Array<Tool>;
allowDelegation: boolean;
verbose: boolean;
image: string;
image?: string | null;
};

View File

@@ -1,10 +1,23 @@
import { Task } from "./task";
import { Agent } from "./agent";
import { Task, TaskInput } from "./task";
type ProcessType = "SEQUENTIAL" | "HIERARCHICAL";
export type Mission = {
id?: number;
name: string;
crew: Array<string>;
crew: Array<Agent>;
tasks: Array<Task>;
verbose: boolean;
process: string;
result: string;
process: ProcessType;
result?: string;
};
export type CreateMissionInput = {
id?: number;
name: string;
crew: Array<number>;
tasks: Array<TaskInput>;
verbose: boolean;
process: ProcessType;
};

View File

@@ -1,5 +1,13 @@
import { Agent } from "./agent";
export type TaskInput = {
name: string;
description: string;
agent?: number;
};
export type Task = {
name: string;
description: string;
agent: string;
agent?: Agent | null;
};

5
src/utils/prisma.ts Normal file
View File

@@ -0,0 +1,5 @@
import { PrismaClient } from "@prisma/client";
const prisma: PrismaClient = new PrismaClient();
export default prisma;