mirror of
https://github.com/eliasstepanik/core.git
synced 2026-01-10 08:48:29 +00:00
* feat: space v3 * feat: connected space creation * fix: * fix: session_id for memory ingestion * chore: simplify gitignore patterns for agent directories --------- Co-authored-by: Manoj <saimanoj58@gmail.com>
160 lines
4.2 KiB
TypeScript
160 lines
4.2 KiB
TypeScript
import {
|
|
convertToModelMessages,
|
|
streamText,
|
|
validateUIMessages,
|
|
type LanguageModel,
|
|
experimental_createMCPClient as createMCPClient,
|
|
generateId,
|
|
stepCountIs,
|
|
} from "ai";
|
|
import { z } from "zod";
|
|
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
|
|
import { createHybridActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
|
|
import {
|
|
createConversationHistory,
|
|
getConversationAndHistory,
|
|
} from "~/services/conversation.server";
|
|
|
|
import { getModel } from "~/lib/model.server";
|
|
import { UserTypeEnum } from "@core/types";
|
|
import { nanoid } from "nanoid";
|
|
import {
|
|
deletePersonalAccessToken,
|
|
getOrCreatePersonalAccessToken,
|
|
} from "~/services/personalAccessToken.server";
|
|
import {
|
|
hasAnswer,
|
|
hasQuestion,
|
|
REACT_SYSTEM_PROMPT,
|
|
} from "~/lib/prompt.server";
|
|
import { enqueueCreateConversationTitle } from "~/lib/queue-adapter.server";
|
|
import { env } from "~/env.server";
|
|
|
|
const ChatRequestSchema = z.object({
|
|
message: z.object({
|
|
id: z.string().optional(),
|
|
parts: z.array(z.any()),
|
|
role: z.string(),
|
|
}),
|
|
id: z.string(),
|
|
});
|
|
|
|
const { loader, action } = createHybridActionApiRoute(
|
|
{
|
|
body: ChatRequestSchema,
|
|
allowJWT: true,
|
|
authorization: {
|
|
action: "conversation",
|
|
},
|
|
corsStrategy: "all",
|
|
},
|
|
async ({ body, authentication }) => {
|
|
const randomKeyName = `chat_${nanoid(10)}`;
|
|
const pat = await getOrCreatePersonalAccessToken({
|
|
name: randomKeyName,
|
|
userId: authentication.userId,
|
|
});
|
|
|
|
const message = body.message.parts[0].text;
|
|
const id = body.message.id;
|
|
const apiEndpoint = `${env.APP_ORIGIN}/api/v1/mcp?source=core`;
|
|
const url = new URL(apiEndpoint);
|
|
|
|
const mcpClient = await createMCPClient({
|
|
transport: new StreamableHTTPClientTransport(url, {
|
|
requestInit: {
|
|
headers: pat.token
|
|
? {
|
|
Authorization: `Bearer ${pat.token}`,
|
|
}
|
|
: {},
|
|
},
|
|
}),
|
|
});
|
|
|
|
const conversation = await getConversationAndHistory(
|
|
body.id,
|
|
authentication.userId,
|
|
);
|
|
|
|
const conversationHistory = conversation?.ConversationHistory ?? [];
|
|
|
|
if (conversationHistory.length === 0) {
|
|
// Trigger conversation title task
|
|
await enqueueCreateConversationTitle({
|
|
conversationId: body.id,
|
|
message,
|
|
});
|
|
}
|
|
|
|
if (conversationHistory.length > 1) {
|
|
await createConversationHistory(message, body.id, UserTypeEnum.User);
|
|
}
|
|
|
|
const messages = conversationHistory.map((history: any) => {
|
|
return {
|
|
parts: [{ text: history.message, type: "text" }],
|
|
role: "user",
|
|
id: history.id,
|
|
};
|
|
});
|
|
|
|
const tools = { ...(await mcpClient.tools()) };
|
|
|
|
const finalMessages = [
|
|
...messages,
|
|
{
|
|
parts: [{ text: message, type: "text" }],
|
|
role: "user",
|
|
id: id ?? generateId(),
|
|
},
|
|
];
|
|
|
|
const validatedMessages = await validateUIMessages({
|
|
messages: finalMessages,
|
|
});
|
|
|
|
const result = streamText({
|
|
model: getModel() as LanguageModel,
|
|
messages: [
|
|
{
|
|
role: "system",
|
|
content: REACT_SYSTEM_PROMPT,
|
|
},
|
|
...convertToModelMessages(validatedMessages),
|
|
],
|
|
tools,
|
|
stopWhen: [stepCountIs(10), hasAnswer, hasQuestion],
|
|
});
|
|
|
|
result.consumeStream(); // no await
|
|
await deletePersonalAccessToken(pat?.id);
|
|
|
|
return result.toUIMessageStreamResponse({
|
|
originalMessages: validatedMessages,
|
|
onFinish: async ({ messages }) => {
|
|
const lastMessage = messages.pop();
|
|
let message = "";
|
|
lastMessage?.parts.forEach((part) => {
|
|
if (part.type === "text") {
|
|
message += part.text;
|
|
}
|
|
});
|
|
|
|
await createConversationHistory(message, body.id, UserTypeEnum.Agent);
|
|
},
|
|
// async consumeSseStream({ stream }) {
|
|
// // Create a resumable stream from the SSE stream
|
|
// const streamContext = createResumableStreamContext({ waitUntil: null });
|
|
// await streamContext.createNewResumableStream(
|
|
// conversation.conversationHistoryId,
|
|
// () => stream,
|
|
// );
|
|
// },
|
|
});
|
|
},
|
|
);
|
|
|
|
export { loader, action };
|