mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-11 09:28:40 +00:00
Feat: add conversation API
This commit is contained in:
parent
12453954b7
commit
158d26f7c2
@ -382,7 +382,6 @@ export const Graph = forwardRef<GraphRef, GraphProps>(
|
||||
|
||||
// Node click handler
|
||||
sigma.on("clickNode", (event) => {
|
||||
console.log(event);
|
||||
const { node } = event;
|
||||
// resetHighlights();
|
||||
if (onNodeClick) {
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
import { json } from "@remix-run/node";
|
||||
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
||||
|
||||
import { getWorkspaceByUser } from "~/models/workspace.server";
|
||||
import {
|
||||
createConversation,
|
||||
CreateConversationSchema,
|
||||
readConversation,
|
||||
} from "~/services/conversation.server";
|
||||
import { z } from "zod";
|
||||
|
||||
export const ConversationIdSchema = z.object({
|
||||
conversationId: z.string(),
|
||||
});
|
||||
|
||||
const { action, loader } = createActionApiRoute(
|
||||
{
|
||||
params: ConversationIdSchema,
|
||||
allowJWT: true,
|
||||
authorization: {
|
||||
action: "oauth",
|
||||
},
|
||||
corsStrategy: "all",
|
||||
},
|
||||
async ({ authentication, params }) => {
|
||||
const workspace = await getWorkspaceByUser(authentication.userId);
|
||||
|
||||
if (!workspace) {
|
||||
throw new Error("No workspace found");
|
||||
}
|
||||
|
||||
// Call the service to get the redirect URL
|
||||
const read = await readConversation(params.conversationId);
|
||||
|
||||
return json(read);
|
||||
},
|
||||
);
|
||||
|
||||
export { action, loader };
|
||||
@ -0,0 +1,44 @@
|
||||
import { json } from "@remix-run/node";
|
||||
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
||||
|
||||
import { getWorkspaceByUser } from "~/models/workspace.server";
|
||||
import {
|
||||
createConversation,
|
||||
CreateConversationSchema,
|
||||
getCurrentConversationRun,
|
||||
readConversation,
|
||||
stopConversation,
|
||||
} from "~/services/conversation.server";
|
||||
import { z } from "zod";
|
||||
|
||||
export const ConversationIdSchema = z.object({
|
||||
conversationId: z.string(),
|
||||
});
|
||||
|
||||
const { action, loader } = createActionApiRoute(
|
||||
{
|
||||
params: ConversationIdSchema,
|
||||
allowJWT: true,
|
||||
authorization: {
|
||||
action: "oauth",
|
||||
},
|
||||
corsStrategy: "all",
|
||||
},
|
||||
async ({ authentication, params }) => {
|
||||
const workspace = await getWorkspaceByUser(authentication.userId);
|
||||
|
||||
if (!workspace) {
|
||||
throw new Error("No workspace found");
|
||||
}
|
||||
|
||||
// Call the service to get the redirect URL
|
||||
const run = await getCurrentConversationRun(
|
||||
params.conversationId,
|
||||
workspace?.id,
|
||||
);
|
||||
|
||||
return json(run);
|
||||
},
|
||||
);
|
||||
|
||||
export { action, loader };
|
||||
@ -0,0 +1,40 @@
|
||||
import { json } from "@remix-run/node";
|
||||
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
||||
|
||||
import { getWorkspaceByUser } from "~/models/workspace.server";
|
||||
import {
|
||||
createConversation,
|
||||
CreateConversationSchema,
|
||||
readConversation,
|
||||
stopConversation,
|
||||
} from "~/services/conversation.server";
|
||||
import { z } from "zod";
|
||||
|
||||
export const ConversationIdSchema = z.object({
|
||||
conversationId: z.string(),
|
||||
});
|
||||
|
||||
const { action, loader } = createActionApiRoute(
|
||||
{
|
||||
params: ConversationIdSchema,
|
||||
allowJWT: true,
|
||||
authorization: {
|
||||
action: "oauth",
|
||||
},
|
||||
corsStrategy: "all",
|
||||
},
|
||||
async ({ authentication, params }) => {
|
||||
const workspace = await getWorkspaceByUser(authentication.userId);
|
||||
|
||||
if (!workspace) {
|
||||
throw new Error("No workspace found");
|
||||
}
|
||||
|
||||
// Call the service to get the redirect URL
|
||||
const stop = await stopConversation(params.conversationId, workspace?.id);
|
||||
|
||||
return json(stop);
|
||||
},
|
||||
);
|
||||
|
||||
export { action, loader };
|
||||
@ -0,0 +1,50 @@
|
||||
import { json } from "@remix-run/node";
|
||||
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
||||
|
||||
import { getWorkspaceByUser } from "~/models/workspace.server";
|
||||
import {
|
||||
getConversation,
|
||||
deleteConversation,
|
||||
} from "~/services/conversation.server";
|
||||
import { z } from "zod";
|
||||
|
||||
export const ConversationIdSchema = z.object({
|
||||
conversationId: z.string(),
|
||||
});
|
||||
|
||||
const { action, loader } = createActionApiRoute(
|
||||
{
|
||||
params: ConversationIdSchema,
|
||||
allowJWT: true,
|
||||
authorization: {
|
||||
action: "oauth",
|
||||
},
|
||||
corsStrategy: "all",
|
||||
},
|
||||
async ({ params, authentication, request }) => {
|
||||
const workspace = await getWorkspaceByUser(authentication.userId);
|
||||
|
||||
if (!workspace) {
|
||||
throw new Error("No workspace found");
|
||||
}
|
||||
|
||||
const method = request.method;
|
||||
|
||||
if (method === "GET") {
|
||||
// Get a conversation by ID
|
||||
const conversation = await getConversation(params.conversationId);
|
||||
return json(conversation);
|
||||
}
|
||||
|
||||
if (method === "DELETE") {
|
||||
// Soft delete a conversation
|
||||
const deleted = await deleteConversation(params.conversationId);
|
||||
return json(deleted);
|
||||
}
|
||||
|
||||
// Method not allowed
|
||||
return new Response("Method Not Allowed", { status: 405 });
|
||||
},
|
||||
);
|
||||
|
||||
export { action, loader };
|
||||
37
apps/webapp/app/routes/api.v1.conversation._index.tsx
Normal file
37
apps/webapp/app/routes/api.v1.conversation._index.tsx
Normal file
@ -0,0 +1,37 @@
|
||||
import { json } from "@remix-run/node";
|
||||
import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
||||
|
||||
import { getWorkspaceByUser } from "~/models/workspace.server";
|
||||
import {
|
||||
createConversation,
|
||||
CreateConversationSchema,
|
||||
} from "~/services/conversation.server";
|
||||
|
||||
const { action, loader } = createActionApiRoute(
|
||||
{
|
||||
body: CreateConversationSchema,
|
||||
allowJWT: true,
|
||||
authorization: {
|
||||
action: "oauth",
|
||||
},
|
||||
corsStrategy: "all",
|
||||
},
|
||||
async ({ body, authentication }) => {
|
||||
const workspace = await getWorkspaceByUser(authentication.userId);
|
||||
|
||||
if (!workspace) {
|
||||
throw new Error("No workspace found");
|
||||
}
|
||||
|
||||
// Call the service to get the redirect URL
|
||||
const conversation = await createConversation(
|
||||
workspace?.id,
|
||||
authentication.userId,
|
||||
body,
|
||||
);
|
||||
|
||||
return json(conversation);
|
||||
},
|
||||
);
|
||||
|
||||
export { action, loader };
|
||||
228
apps/webapp/app/services/conversation.server.ts
Normal file
228
apps/webapp/app/services/conversation.server.ts
Normal file
@ -0,0 +1,228 @@
|
||||
import { UserTypeEnum } from "@core/types";
|
||||
|
||||
import { auth, runs, tasks } from "@trigger.dev/sdk/v3";
|
||||
import { prisma } from "~/db.server";
|
||||
import { getOrCreatePersonalAccessToken } from "./personalAccessToken.server";
|
||||
import { createConversationTitle } from "~/trigger/conversation/create-conversation-title";
|
||||
|
||||
import { z } from "zod";
|
||||
|
||||
export const CreateConversationSchema = z.object({
|
||||
message: z.string(),
|
||||
title: z.string().optional(),
|
||||
conversationId: z.string().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;
|
||||
// Ensure PAT exists for the user
|
||||
await getOrCreatePersonalAccessToken({ name: "trigger", userId });
|
||||
|
||||
if (conversationId) {
|
||||
// Add a new message to an existing conversation
|
||||
const conversationHistory = await prisma.conversationHistory.create({
|
||||
data: {
|
||||
...otherData,
|
||||
userType: UserTypeEnum.User,
|
||||
...(userId && {
|
||||
user: {
|
||||
connect: { id: userId },
|
||||
},
|
||||
}),
|
||||
conversation: {
|
||||
connect: { id: conversationId },
|
||||
},
|
||||
},
|
||||
include: {
|
||||
conversation: true,
|
||||
},
|
||||
});
|
||||
|
||||
// No context logic here
|
||||
const handler = await tasks.trigger(
|
||||
"chat",
|
||||
{
|
||||
conversationHistoryId: conversationHistory.id,
|
||||
conversationId: conversationHistory.conversation.id,
|
||||
},
|
||||
{ tags: [conversationHistory.id, workspaceId, conversationId] },
|
||||
);
|
||||
|
||||
return {
|
||||
id: handler.id,
|
||||
token: handler.publicAccessToken,
|
||||
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: UserTypeEnum.User,
|
||||
...otherData,
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
ConversationHistory: true,
|
||||
},
|
||||
});
|
||||
|
||||
const conversationHistory = conversation.ConversationHistory[0];
|
||||
|
||||
// Trigger conversation title task
|
||||
await tasks.trigger<typeof createConversationTitle>(
|
||||
createConversationTitle.id,
|
||||
{
|
||||
conversationId: conversation.id,
|
||||
message: conversationData.message,
|
||||
},
|
||||
{ tags: [conversation.id, workspaceId] },
|
||||
);
|
||||
|
||||
const handler = await tasks.trigger(
|
||||
"chat",
|
||||
{
|
||||
conversationHistoryId: conversationHistory.id,
|
||||
conversationId: conversation.id,
|
||||
},
|
||||
{ tags: [conversationHistory.id, workspaceId, conversation.id] },
|
||||
);
|
||||
|
||||
return {
|
||||
id: handler.id,
|
||||
token: handler.publicAccessToken,
|
||||
conversationId: conversation.id,
|
||||
conversationHistoryId: conversationHistory.id,
|
||||
};
|
||||
}
|
||||
|
||||
// Get a conversation by ID
|
||||
export async function getConversation(conversationId: string) {
|
||||
return prisma.conversation.findUnique({
|
||||
where: { id: conversationId },
|
||||
});
|
||||
}
|
||||
|
||||
// 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 async function getCurrentConversationRun(
|
||||
conversationId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const conversationHistory = await prisma.conversationHistory.findFirst({
|
||||
where: {
|
||||
conversationId,
|
||||
conversation: {
|
||||
workspaceId,
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
updatedAt: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
if (!conversationHistory) {
|
||||
throw new Error("No run found");
|
||||
}
|
||||
|
||||
const response = await runs.list({
|
||||
tag: [conversationId, conversationHistory.id],
|
||||
status: ["QUEUED", "EXECUTING"],
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
const run = response.data[0];
|
||||
if (!run) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const publicToken = await auth.createPublicToken({
|
||||
scopes: {
|
||||
read: {
|
||||
runs: [run.id],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
id: run.id,
|
||||
token: publicToken,
|
||||
conversationId,
|
||||
conversationHistoryId: conversationHistory.id,
|
||||
};
|
||||
}
|
||||
|
||||
export async function stopConversation(
|
||||
conversationId: string,
|
||||
workspaceId: string,
|
||||
) {
|
||||
const conversationHistory = await prisma.conversationHistory.findFirst({
|
||||
where: {
|
||||
conversationId,
|
||||
conversation: {
|
||||
workspaceId,
|
||||
},
|
||||
},
|
||||
orderBy: {
|
||||
updatedAt: "desc",
|
||||
},
|
||||
});
|
||||
|
||||
if (!conversationHistory) {
|
||||
throw new Error("No run found");
|
||||
}
|
||||
|
||||
const response = await runs.list({
|
||||
tag: [conversationId, conversationHistory.id],
|
||||
status: ["QUEUED", "EXECUTING"],
|
||||
limit: 1,
|
||||
});
|
||||
|
||||
const run = response.data[0];
|
||||
if (!run) {
|
||||
await prisma.conversation.update({
|
||||
where: {
|
||||
id: conversationId,
|
||||
},
|
||||
data: {
|
||||
status: "failed",
|
||||
},
|
||||
});
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return await runs.cancel(run.id);
|
||||
}
|
||||
@ -397,8 +397,6 @@ export function createActionApiRoute<
|
||||
maxContentLength,
|
||||
} = options;
|
||||
|
||||
console.log(options);
|
||||
|
||||
async function loader({ request, params }: LoaderFunctionArgs) {
|
||||
if (corsStrategy !== "none" && request.method.toUpperCase() === "OPTIONS") {
|
||||
return apiCors(request, json({}));
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
import { ActionStatusEnum, LLMMappings } from "@core/types";
|
||||
import { ActionStatusEnum } from "@core/types";
|
||||
import { logger } from "@trigger.dev/sdk/v3";
|
||||
import {
|
||||
type CoreMessage,
|
||||
|
||||
@ -0,0 +1,62 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { LLMMappings } from "@core/types";
|
||||
import { logger, task } from "@trigger.dev/sdk/v3";
|
||||
import { generate } from "../chat/stream-utils";
|
||||
import { conversationTitlePrompt } from "./prompt";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
export const createConversationTitle = task({
|
||||
id: "create-conversation-title",
|
||||
run: async (payload: { conversationId: string; message: string }) => {
|
||||
let conversationTitleResponse = "";
|
||||
const gen = generate(
|
||||
[
|
||||
{
|
||||
role: "user",
|
||||
content: conversationTitlePrompt.replace(
|
||||
"{{message}}",
|
||||
payload.message,
|
||||
),
|
||||
},
|
||||
],
|
||||
false,
|
||||
() => {},
|
||||
undefined,
|
||||
"",
|
||||
LLMMappings.CLAUDESONNET,
|
||||
);
|
||||
|
||||
for await (const chunk of gen) {
|
||||
if (typeof chunk === "string") {
|
||||
conversationTitleResponse += chunk;
|
||||
} else if (chunk && typeof chunk === "object" && chunk.message) {
|
||||
conversationTitleResponse += chunk.message;
|
||||
}
|
||||
}
|
||||
|
||||
const outputMatch = conversationTitleResponse.match(
|
||||
/<output>(.*?)<\/output>/s,
|
||||
);
|
||||
|
||||
logger.info(`Conversation title data: ${JSON.stringify(outputMatch)}`);
|
||||
|
||||
if (!outputMatch) {
|
||||
logger.error("No output found in recurrence response");
|
||||
throw new Error("Invalid response format from AI");
|
||||
}
|
||||
|
||||
const jsonStr = outputMatch[1].trim();
|
||||
const conversationTitleData = JSON.parse(jsonStr);
|
||||
|
||||
if (conversationTitleData) {
|
||||
await prisma.conversation.update({
|
||||
where: {
|
||||
id: payload.conversationId,
|
||||
},
|
||||
data: {
|
||||
title: conversationTitleData.title,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
28
apps/webapp/app/trigger/conversation/prompt.ts
Normal file
28
apps/webapp/app/trigger/conversation/prompt.ts
Normal file
@ -0,0 +1,28 @@
|
||||
export const conversationTitlePrompt = `You are an AI assistant specialized in generating concise and informative conversation titles. Your task is to analyze the given message and context to create an appropriate title.
|
||||
|
||||
Here is the message:
|
||||
<message>
|
||||
{{message}}
|
||||
</message>
|
||||
|
||||
Please follow these steps:
|
||||
- Extract the core topic/intent from the message
|
||||
- Create a clear, concise title
|
||||
- Focus on the main subject or action
|
||||
- Avoid unnecessary words
|
||||
- Maximum length: 60 characters
|
||||
|
||||
Before providing output, analyze in <title_analysis> tags:
|
||||
- Key elements from message
|
||||
- Main topic/action
|
||||
- Relevant actors/context
|
||||
- Your title formation process
|
||||
|
||||
Provide final output in this format:
|
||||
<output>
|
||||
{
|
||||
"title": "Your generated title"
|
||||
}
|
||||
</output>
|
||||
|
||||
If message is empty or contains no meaningful content, return {"title": "New Conversation"}`;
|
||||
@ -1,38 +1,38 @@
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import { IntegrationPayloadEventType } from '@redplanethq/sol-sdk';
|
||||
import { logger, schedules, tasks } from '@trigger.dev/sdk/v3';
|
||||
// import { PrismaClient } from "@prisma/client";
|
||||
// import { IntegrationPayloadEventType } from "@core/types";
|
||||
// import { logger, schedules, tasks } from "@trigger.dev/sdk/v3";
|
||||
|
||||
import { integrationRun } from './integration-run';
|
||||
// import { integrationRun } from "./integration-run";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
// const prisma = new PrismaClient();
|
||||
|
||||
export const integrationRunSchedule = schedules.task({
|
||||
id: 'integration-run-schedule',
|
||||
run: async (payload) => {
|
||||
const { externalId } = payload;
|
||||
const integrationAccount = await prisma.integrationAccount.findUnique({
|
||||
where: { id: externalId },
|
||||
include: {
|
||||
integrationDefinition: true,
|
||||
workspace: true,
|
||||
},
|
||||
});
|
||||
// export const integrationRunSchedule = schedules.task({
|
||||
// id: "integration-run-schedule",
|
||||
// run: async (payload) => {
|
||||
// const { externalId } = payload;
|
||||
// const integrationAccount = await prisma.integrationAccount.findUnique({
|
||||
// where: { id: externalId },
|
||||
// include: {
|
||||
// integrationDefinition: true,
|
||||
// workspace: true,
|
||||
// },
|
||||
// });
|
||||
|
||||
if (!integrationAccount) {
|
||||
const deletedSchedule = await schedules.del(externalId);
|
||||
logger.info('Deleting schedule as integration account is not there');
|
||||
return deletedSchedule;
|
||||
}
|
||||
// if (!integrationAccount) {
|
||||
// const deletedSchedule = await schedules.del(externalId);
|
||||
// logger.info("Deleting schedule as integration account is not there");
|
||||
// return deletedSchedule;
|
||||
// }
|
||||
|
||||
const pat = await prisma.personalAccessToken.findFirst({
|
||||
where: { userId: integrationAccount.workspace.userId, name: 'default' },
|
||||
});
|
||||
// const pat = await prisma.personalAccessToken.findFirst({
|
||||
// where: { userId: integrationAccount.workspace.userId, name: "default" },
|
||||
// });
|
||||
|
||||
return await tasks.trigger<typeof integrationRun>('integration-run', {
|
||||
event: IntegrationPayloadEventType.SCHEDULED_SYNC,
|
||||
pat: pat.token,
|
||||
integrationAccount,
|
||||
integrationDefinition: integrationAccount.integrationDefinition,
|
||||
});
|
||||
},
|
||||
});
|
||||
// return await tasks.trigger<typeof integrationRun>("integration-run", {
|
||||
// event: IntegrationPayloadEventType.SCHEDULED_SYNC,
|
||||
// pat: pat.token,
|
||||
// integrationAccount,
|
||||
// integrationDefinition: integrationAccount.integrationDefinition,
|
||||
// });
|
||||
// },
|
||||
// });
|
||||
|
||||
@ -1,90 +1,87 @@
|
||||
import createLoadRemoteModule, {
|
||||
createRequires,
|
||||
} from '@paciolan/remote-module-loader';
|
||||
import {
|
||||
IntegrationAccount,
|
||||
IntegrationDefinition,
|
||||
} from '@redplanethq/sol-sdk';
|
||||
import { logger, task } from '@trigger.dev/sdk/v3';
|
||||
import axios from 'axios';
|
||||
// import createLoadRemoteModule, {
|
||||
// createRequires,
|
||||
// } from "@paciolan/remote-module-loader";
|
||||
|
||||
const fetcher = async (url: string) => {
|
||||
// Handle remote URLs with axios
|
||||
const response = await axios.get(url);
|
||||
// import { logger, task } from "@trigger.dev/sdk/v3";
|
||||
// import axios from "axios";
|
||||
|
||||
return response.data;
|
||||
};
|
||||
// const fetcher = async (url: string) => {
|
||||
// // Handle remote URLs with axios
|
||||
// const response = await axios.get(url);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const loadRemoteModule = async (requires: any) =>
|
||||
createLoadRemoteModule({ fetcher, requires });
|
||||
// return response.data;
|
||||
// };
|
||||
|
||||
function createAxiosInstance(token: string) {
|
||||
const instance = axios.create();
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// const loadRemoteModule = async (requires: any) =>
|
||||
// createLoadRemoteModule({ fetcher, requires });
|
||||
|
||||
instance.interceptors.request.use((config) => {
|
||||
// Check if URL starts with /api and doesn't have a full host
|
||||
if (config.url?.startsWith('/api')) {
|
||||
config.url = `${process.env.BACKEND_HOST}${config.url.replace('/api/', '/')}`;
|
||||
}
|
||||
// function createAxiosInstance(token: string) {
|
||||
// const instance = axios.create();
|
||||
|
||||
if (
|
||||
config.url.includes(process.env.FRONTEND_HOST) ||
|
||||
config.url.includes(process.env.BACKEND_HOST)
|
||||
) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
// instance.interceptors.request.use((config) => {
|
||||
// // Check if URL starts with /api and doesn't have a full host
|
||||
// if (config.url?.startsWith("/api")) {
|
||||
// config.url = `${process.env.BACKEND_HOST}${config.url.replace("/api/", "/")}`;
|
||||
// }
|
||||
|
||||
return config;
|
||||
});
|
||||
// if (
|
||||
// config.url.includes(process.env.FRONTEND_HOST) ||
|
||||
// config.url.includes(process.env.BACKEND_HOST)
|
||||
// ) {
|
||||
// config.headers.Authorization = `Bearer ${token}`;
|
||||
// }
|
||||
|
||||
return instance;
|
||||
}
|
||||
// return config;
|
||||
// });
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const getRequires = (axios: any) => createRequires({ axios });
|
||||
// return instance;
|
||||
// }
|
||||
|
||||
export const integrationRun = task({
|
||||
id: 'integration-run',
|
||||
run: async ({
|
||||
pat,
|
||||
eventBody,
|
||||
integrationAccount,
|
||||
integrationDefinition,
|
||||
event,
|
||||
}: {
|
||||
pat: string;
|
||||
// This is the event you want to pass to the integration
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
event: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
eventBody?: any;
|
||||
integrationDefinition: IntegrationDefinition;
|
||||
integrationAccount?: IntegrationAccount;
|
||||
}) => {
|
||||
const remoteModuleLoad = await loadRemoteModule(
|
||||
getRequires(createAxiosInstance(pat)),
|
||||
);
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// const getRequires = (axios: any) => createRequires({ axios });
|
||||
|
||||
logger.info(
|
||||
`${integrationDefinition.url}/${integrationDefinition.version}/backend/index.js`,
|
||||
);
|
||||
// export const integrationRun = task({
|
||||
// id: "integration-run",
|
||||
// run: async ({
|
||||
// pat,
|
||||
// eventBody,
|
||||
// integrationAccount,
|
||||
// integrationDefinition,
|
||||
// event,
|
||||
// }: {
|
||||
// pat: string;
|
||||
// // This is the event you want to pass to the integration
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// event: any;
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// eventBody?: any;
|
||||
// integrationDefinition: IntegrationDefinition;
|
||||
// integrationAccount?: IntegrationAccount;
|
||||
// }) => {
|
||||
// const remoteModuleLoad = await loadRemoteModule(
|
||||
// getRequires(createAxiosInstance(pat)),
|
||||
// );
|
||||
|
||||
const integrationFunction = await remoteModuleLoad(
|
||||
`${integrationDefinition.url}/${integrationDefinition.version}/backend/index.js`,
|
||||
);
|
||||
// logger.info(
|
||||
// `${integrationDefinition.url}/${integrationDefinition.version}/backend/index.js`,
|
||||
// );
|
||||
|
||||
// const integrationFunction = await remoteModuleLoad(
|
||||
// `${integrationDefinition.url}`,
|
||||
// );
|
||||
// const integrationFunction = await remoteModuleLoad(
|
||||
// `${integrationDefinition.url}/${integrationDefinition.version}/backend/index.js`,
|
||||
// );
|
||||
|
||||
return await integrationFunction.run({
|
||||
integrationAccount,
|
||||
integrationDefinition,
|
||||
event,
|
||||
eventBody: {
|
||||
...(eventBody ? eventBody : {}),
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
// // const integrationFunction = await remoteModuleLoad(
|
||||
// // `${integrationDefinition.url}`,
|
||||
// // );
|
||||
|
||||
// return await integrationFunction.run({
|
||||
// integrationAccount,
|
||||
// integrationDefinition,
|
||||
// event,
|
||||
// eventBody: {
|
||||
// ...(eventBody ? eventBody : {}),
|
||||
// },
|
||||
// });
|
||||
// },
|
||||
// });
|
||||
|
||||
@ -1,64 +1,64 @@
|
||||
import { PrismaClient } from "@prisma/client";
|
||||
import { logger, schedules, task } from "@trigger.dev/sdk/v3";
|
||||
// import { PrismaClient } from "@prisma/client";
|
||||
// import { logger, schedules, task } from "@trigger.dev/sdk/v3";
|
||||
|
||||
import { integrationRunSchedule } from "./integration-run-schedule";
|
||||
// import { integrationRunSchedule } from "./integration-run-schedule";
|
||||
|
||||
const prisma = new PrismaClient();
|
||||
// const prisma = new PrismaClient();
|
||||
|
||||
export const scheduler = task({
|
||||
id: "scheduler",
|
||||
run: async (payload: { integrationAccountId: string }) => {
|
||||
const { integrationAccountId } = payload;
|
||||
// export const scheduler = task({
|
||||
// id: "scheduler",
|
||||
// run: async (payload: { integrationAccountId: string }) => {
|
||||
// const { integrationAccountId } = payload;
|
||||
|
||||
const integrationAccount = await prisma.integrationAccount.findUnique({
|
||||
where: { id: integrationAccountId, deleted: null },
|
||||
include: {
|
||||
integrationDefinition: true,
|
||||
workspace: true,
|
||||
},
|
||||
});
|
||||
// const integrationAccount = await prisma.integrationAccount.findUnique({
|
||||
// where: { id: integrationAccountId, deleted: null },
|
||||
// include: {
|
||||
// integrationDefinition: true,
|
||||
// workspace: true,
|
||||
// },
|
||||
// });
|
||||
|
||||
if (!integrationAccount) {
|
||||
logger.error("Integration account not found");
|
||||
return null;
|
||||
}
|
||||
// if (!integrationAccount) {
|
||||
// logger.error("Integration account not found");
|
||||
// return null;
|
||||
// }
|
||||
|
||||
if (!integrationAccount.workspace) {
|
||||
return null;
|
||||
}
|
||||
// if (!integrationAccount.workspace) {
|
||||
// return null;
|
||||
// }
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const spec = integrationAccount.integrationDefinition.spec as any;
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// const spec = integrationAccount.integrationDefinition.spec as any;
|
||||
|
||||
if (spec.schedule && spec.schedule.frequency) {
|
||||
const createdSchedule = await schedules.create({
|
||||
// The id of the scheduled task you want to attach to.
|
||||
task: integrationRunSchedule.id,
|
||||
// The schedule in cron format.
|
||||
cron: spec.schedule.frequency,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
timezone: (integrationAccount.workspace.preferences as any).timezone,
|
||||
// this is required, it prevents you from creating duplicate schedules. It will update the schedule if it already exists.
|
||||
deduplicationKey: integrationAccount.id,
|
||||
externalId: integrationAccount.id,
|
||||
});
|
||||
// if (spec.schedule && spec.schedule.frequency) {
|
||||
// const createdSchedule = await schedules.create({
|
||||
// // The id of the scheduled task you want to attach to.
|
||||
// task: integrationRunSchedule.id,
|
||||
// // The schedule in cron format.
|
||||
// cron: spec.schedule.frequency,
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// timezone: (integrationAccount.workspace.preferences as any).timezone,
|
||||
// // this is required, it prevents you from creating duplicate schedules. It will update the schedule if it already exists.
|
||||
// deduplicationKey: integrationAccount.id,
|
||||
// externalId: integrationAccount.id,
|
||||
// });
|
||||
|
||||
await prisma.integrationAccount.update({
|
||||
where: {
|
||||
id: integrationAccount.id,
|
||||
},
|
||||
data: {
|
||||
settings: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
...(integrationAccount.settings as any),
|
||||
scheduleId: createdSchedule.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
// await prisma.integrationAccount.update({
|
||||
// where: {
|
||||
// id: integrationAccount.id,
|
||||
// },
|
||||
// data: {
|
||||
// settings: {
|
||||
// // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
// ...(integrationAccount.settings as any),
|
||||
// scheduleId: createdSchedule.id,
|
||||
// },
|
||||
// },
|
||||
// });
|
||||
|
||||
return createdSchedule;
|
||||
}
|
||||
// return createdSchedule;
|
||||
// }
|
||||
|
||||
return "No schedule for this task";
|
||||
},
|
||||
});
|
||||
// return "No schedule for this task";
|
||||
// },
|
||||
// });
|
||||
|
||||
@ -8,7 +8,8 @@
|
||||
"dev": "node ./server.mjs",
|
||||
"lint": "eslint --ignore-path .gitignore --cache --cache-location ./node_modules/.cache/eslint .",
|
||||
"start": "remix-serve ./build/server/index.js",
|
||||
"typecheck": "tsc"
|
||||
"typecheck": "tsc",
|
||||
"trigger:dev": "npx trigger.dev@latest dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "^1.2.12",
|
||||
@ -20,6 +21,7 @@
|
||||
"@core/database": "workspace:*",
|
||||
"@core/types": "workspace:*",
|
||||
"@mjackson/headers": "0.11.1",
|
||||
"@modelcontextprotocol/sdk": "1.13.2",
|
||||
"@nichtsam/remix-auth-email-link": "3.0.0",
|
||||
"@opentelemetry/api": "1.9.0",
|
||||
"@prisma/client": "*",
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { defineConfig } from "@trigger.dev/sdk/v3";
|
||||
import { env } from "~/env.server";
|
||||
|
||||
export default defineConfig({
|
||||
project: env.TRIGGER_PROJECT_ID,
|
||||
project: process.env.TRIGGER_PROJECT_ID as string,
|
||||
runtime: "node",
|
||||
logLevel: "log",
|
||||
// The max compute seconds a task is allowed to run. If the task run exceeds this duration, it will be stopped.
|
||||
|
||||
@ -17,7 +17,8 @@
|
||||
"db:seed": "dotenv -- turbo run db:seed",
|
||||
"db:studio": "dotenv -- turbo run db:studio",
|
||||
"db:populate": "dotenv -- turbo run db:populate",
|
||||
"generate": "dotenv -- turbo run generate"
|
||||
"generate": "dotenv -- turbo run generate",
|
||||
"trigger:dev": "dotenv -- turbo run trigger:dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"dotenv-cli": "^7.4.4",
|
||||
|
||||
9
packages/sdk/.eslintrc.js
Normal file
9
packages/sdk/.eslintrc.js
Normal file
@ -0,0 +1,9 @@
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
module.exports = {
|
||||
root: true,
|
||||
extends: ['@redplanethq/eslint-config/internal.js'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
rules: {
|
||||
'no-redeclare': 'off',
|
||||
},
|
||||
};
|
||||
4
packages/sdk/.prettierrc.json
Normal file
4
packages/sdk/.prettierrc.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
49
packages/sdk/package.json
Normal file
49
packages/sdk/package.json
Normal file
@ -0,0 +1,49 @@
|
||||
{
|
||||
"name": "@redplanethq/sol-sdk",
|
||||
"version": "0.2.18",
|
||||
"description": "Sol Node.JS SDK",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"module": "./dist/index.mjs",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "npm run clean && npm run build:tsup",
|
||||
"build:tsup": "tsup --dts-resolve",
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
},
|
||||
"devDependencies": {
|
||||
"@core/types": "workspace:*",
|
||||
"@types/configstore": "^6.0.2",
|
||||
"@types/debug": "^4.1.7",
|
||||
"@types/node": "18",
|
||||
"@types/slug": "^5.0.3",
|
||||
"@types/uuid": "^9.0.0",
|
||||
"encoding": "^0.1.13",
|
||||
"rimraf": "^6.0.1",
|
||||
"tsup": "^8.0.1",
|
||||
"typescript": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
"packageManager": "pnpm@10.3.0"
|
||||
}
|
||||
1
packages/sdk/src/index.ts
Normal file
1
packages/sdk/src/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from '@core/types';
|
||||
38
packages/sdk/tsconfig.json
Normal file
38
packages/sdk/tsconfig.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "https://json.schemastore.org/tsconfig",
|
||||
"include": ["./src/**/*.ts", "tsup.config.ts"],
|
||||
"compilerOptions": {
|
||||
"baseUrl": "src",
|
||||
|
||||
"moduleResolution": "node",
|
||||
|
||||
"strictNullChecks": false,
|
||||
"preserveConstEnums": true,
|
||||
"noUnusedParameters": true,
|
||||
"noUnusedLocals": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitThis": true,
|
||||
"noImplicitAny": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"useUnknownInCatchVariables": false,
|
||||
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"resolveJsonModule": true,
|
||||
"sourceMap": true,
|
||||
|
||||
"removeComments": true,
|
||||
"module": "commonjs",
|
||||
"target": "ES2022",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"lib": ["DOM", "DOM.Iterable"],
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"stripInternal": true
|
||||
},
|
||||
"exclude": ["node_modules", "dist"]
|
||||
}
|
||||
22
packages/sdk/tsup.config.ts
Normal file
22
packages/sdk/tsup.config.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Options, defineConfig as defineConfigTSUP } from 'tsup';
|
||||
|
||||
const options: Options = {
|
||||
name: 'main',
|
||||
config: 'tsconfig.json',
|
||||
entry: ['./src/index.ts'],
|
||||
outDir: './dist',
|
||||
platform: 'node',
|
||||
format: ['cjs', 'esm'],
|
||||
legacyOutput: false,
|
||||
sourcemap: true,
|
||||
clean: true,
|
||||
bundle: true,
|
||||
splitting: false,
|
||||
dts: true,
|
||||
treeshake: {
|
||||
preset: 'recommended',
|
||||
},
|
||||
external: ['axios'],
|
||||
};
|
||||
|
||||
export default defineConfigTSUP(options);
|
||||
@ -3,3 +3,4 @@ export * from "./graph";
|
||||
export * from "./conversation-execution-step";
|
||||
export * from "./oauth";
|
||||
export * from "./integration";
|
||||
export * from "./user";
|
||||
|
||||
5
packages/types/src/user/index.ts
Normal file
5
packages/types/src/user/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export enum UserTypeEnum {
|
||||
Agent = "Agent",
|
||||
User = "User",
|
||||
System = "System",
|
||||
}
|
||||
564
pnpm-lock.yaml
generated
564
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -32,6 +32,9 @@
|
||||
},
|
||||
"generate": {
|
||||
"dependsOn": [ "^generate" ]
|
||||
},
|
||||
"trigger:dev": {
|
||||
|
||||
}
|
||||
},
|
||||
"globalDependencies": [ ".env" ],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user