mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-10 08:48:29 +00:00
151 lines
4.3 KiB
TypeScript
151 lines
4.3 KiB
TypeScript
import { ActionStatusEnum } from "@core/types";
|
|
import { metadata, task, queue } from "@trigger.dev/sdk";
|
|
|
|
import { run } from "./chat-utils";
|
|
import { MCP } from "../utils/mcp";
|
|
import { type HistoryStep } from "../utils/types";
|
|
import {
|
|
createConversationHistoryForAgent,
|
|
deductCredits,
|
|
deletePersonalAccessToken,
|
|
getPreviousExecutionHistory,
|
|
hasCredits,
|
|
InsufficientCreditsError,
|
|
init,
|
|
type RunChatPayload,
|
|
updateConversationHistoryMessage,
|
|
updateConversationStatus,
|
|
updateExecutionStep,
|
|
} from "../utils/utils";
|
|
|
|
const chatQueue = queue({
|
|
name: "chat-queue",
|
|
concurrencyLimit: 50,
|
|
});
|
|
|
|
/**
|
|
* Main chat task that orchestrates the agent workflow
|
|
* Handles conversation context, agent selection, and LLM interactions
|
|
*/
|
|
export const chat = task({
|
|
id: "chat",
|
|
maxDuration: 3000,
|
|
queue: chatQueue,
|
|
init,
|
|
run: async (payload: RunChatPayload, { init }) => {
|
|
await updateConversationStatus("running", payload.conversationId);
|
|
|
|
try {
|
|
// Check if workspace has sufficient credits before processing
|
|
if (init?.conversation.workspaceId) {
|
|
const hasSufficientCredits = await hasCredits(
|
|
init.conversation.workspaceId,
|
|
"chatMessage",
|
|
);
|
|
|
|
if (!hasSufficientCredits) {
|
|
throw new InsufficientCreditsError(
|
|
"Insufficient credits to process chat message. Please upgrade your plan or wait for your credits to reset.",
|
|
);
|
|
}
|
|
}
|
|
|
|
const { previousHistory, ...otherData } = payload.context;
|
|
|
|
// Initialise mcp
|
|
const mcpHeaders = { Authorization: `Bearer ${init?.token}` };
|
|
const mcp = new MCP();
|
|
await mcp.init();
|
|
await mcp.load(mcpHeaders);
|
|
|
|
// Prepare context with additional metadata
|
|
const context = {
|
|
// Currently this is assuming we only have one page in context
|
|
context: {
|
|
...(otherData.page && otherData.page.length > 0
|
|
? { page: otherData.page[0] }
|
|
: {}),
|
|
},
|
|
workpsaceId: init?.conversation.workspaceId,
|
|
resources: otherData.resources,
|
|
todayDate: new Date().toISOString(),
|
|
};
|
|
|
|
// Extract user's goal from conversation history
|
|
const message = init?.conversationHistory?.message;
|
|
// Retrieve execution history from previous interactions
|
|
const previousExecutionHistory = getPreviousExecutionHistory(
|
|
previousHistory ?? [],
|
|
);
|
|
|
|
let agentUserMessage = "";
|
|
let agentConversationHistory;
|
|
let stepHistory: HistoryStep[] = [];
|
|
// Prepare conversation history in agent-compatible format
|
|
agentConversationHistory = await createConversationHistoryForAgent(
|
|
payload.conversationId,
|
|
);
|
|
|
|
const llmResponse = run(
|
|
message as string,
|
|
context,
|
|
previousExecutionHistory,
|
|
mcp,
|
|
stepHistory,
|
|
);
|
|
|
|
const stream = await metadata.stream("messages", llmResponse);
|
|
|
|
let conversationStatus = "success";
|
|
for await (const step of stream) {
|
|
if (step.type === "STEP") {
|
|
const stepDetails = JSON.parse(step.message as string);
|
|
|
|
if (stepDetails.skillStatus === ActionStatusEnum.TOOL_REQUEST) {
|
|
conversationStatus = "need_approval";
|
|
}
|
|
|
|
if (stepDetails.skillStatus === ActionStatusEnum.QUESTION) {
|
|
conversationStatus = "need_attention";
|
|
}
|
|
|
|
await updateExecutionStep(
|
|
{ ...stepDetails },
|
|
agentConversationHistory.id,
|
|
);
|
|
|
|
agentUserMessage += stepDetails.userMessage;
|
|
|
|
await updateConversationHistoryMessage(
|
|
agentUserMessage,
|
|
agentConversationHistory.id,
|
|
);
|
|
} else if (step.type === "STREAM_END") {
|
|
break;
|
|
}
|
|
}
|
|
|
|
await updateConversationStatus(
|
|
conversationStatus,
|
|
payload.conversationId,
|
|
);
|
|
|
|
// Deduct credits for chat message
|
|
if (init?.conversation.workspaceId) {
|
|
await deductCredits(init.conversation.workspaceId, "chatMessage");
|
|
}
|
|
|
|
if (init?.tokenId) {
|
|
await deletePersonalAccessToken(init.tokenId);
|
|
}
|
|
} catch (e) {
|
|
console.log(e);
|
|
await updateConversationStatus("failed", payload.conversationId);
|
|
if (init?.tokenId) {
|
|
await deletePersonalAccessToken(init.tokenId);
|
|
}
|
|
throw new Error(e as string);
|
|
}
|
|
},
|
|
});
|