super-harness / src /app /api /chat /actions.ts
Cyber Catalyst Team
Deploy to HF Spaces with LFS
4782147
Raw
History Blame Contribute Delete
6.6 kB
"use server";
import {
generateObject,
generateText,
jsonSchema,
LanguageModel,
type UIMessage,
} from "ai";
import {
CREATE_THREAD_TITLE_PROMPT,
generateExampleToolSchemaPrompt,
} from "lib/ai/prompts";
import type { ChatModel, ChatThread } from "app-types/chat";
import {
agentRepository,
chatExportRepository,
chatRepository,
mcpMcpToolCustomizationRepository,
mcpServerCustomizationRepository,
} from "lib/db/repository";
import { customModelProvider } from "lib/ai/models";
import { toAny } from "lib/utils";
import { McpServerCustomizationsPrompt, MCPToolInfo } from "app-types/mcp";
import { serverCache } from "lib/cache";
import { CacheKeys } from "lib/cache/cache-keys";
import { getSession } from "auth/server";
import logger from "logger";
import { JSONSchema7 } from "json-schema";
import { ObjectJsonSchema7 } from "app-types/util";
import { jsonSchemaToZod } from "lib/json-schema-to-zod";
import { Agent } from "app-types/agent";
export async function getUserId() {
const session = await getSession();
const userId = session?.user?.id;
if (!userId) {
throw new Error("User not found");
}
return userId;
}
export async function generateTitleFromUserMessageAction({
message,
model,
}: { message: UIMessage; model: LanguageModel }) {
const session = await getSession();
if (!session) {
throw new Error("Unauthorized");
}
const prompt = toAny(message.parts?.at(-1))?.text || "unknown";
const { text: title } = await generateText({
model,
system: CREATE_THREAD_TITLE_PROMPT,
prompt,
});
return title.trim();
}
export async function selectThreadWithMessagesAction(threadId: string) {
const session = await getSession();
if (!session) {
throw new Error("Unauthorized");
}
const thread = await chatRepository.selectThread(threadId);
if (!thread) {
logger.error("Thread not found", threadId);
return null;
}
if (thread.userId !== session?.user.id) {
return null;
}
const messages = await chatRepository.selectMessagesByThreadId(threadId);
return { ...thread, messages: messages ?? [] };
}
export async function deleteMessageAction(messageId: string) {
await chatRepository.deleteChatMessage(messageId);
}
export async function deleteThreadAction(threadId: string) {
await chatRepository.deleteThread(threadId);
}
export async function deleteMessagesByChatIdAfterTimestampAction(
messageId: string,
) {
"use server";
await chatRepository.deleteMessagesByChatIdAfterTimestamp(messageId);
}
export async function updateThreadAction(
id: string,
thread: Partial<Omit<ChatThread, "createdAt" | "updatedAt" | "userId">>,
) {
const userId = await getUserId();
await chatRepository.updateThread(id, { ...thread, userId });
}
export async function deleteThreadsAction() {
const userId = await getUserId();
await chatRepository.deleteAllThreads(userId);
}
export async function deleteUnarchivedThreadsAction() {
const userId = await getUserId();
await chatRepository.deleteUnarchivedThreads(userId);
}
export async function generateExampleToolSchemaAction(options: {
model?: ChatModel;
toolInfo: MCPToolInfo;
prompt?: string;
}) {
const model = customModelProvider.getModel(options.model);
const schema = jsonSchema(
toAny({
...options.toolInfo.inputSchema,
properties: options.toolInfo.inputSchema?.properties ?? {},
additionalProperties: false,
}),
);
const { object } = await generateObject({
model,
schema,
prompt: generateExampleToolSchemaPrompt({
toolInfo: options.toolInfo,
prompt: options.prompt,
}),
});
return object;
}
export async function rememberMcpServerCustomizationsAction(userId: string) {
const key = CacheKeys.mcpServerCustomizations(userId);
const cachedMcpServerCustomizations =
await serverCache.get<Record<string, McpServerCustomizationsPrompt>>(key);
if (cachedMcpServerCustomizations) {
return cachedMcpServerCustomizations;
}
const mcpServerCustomizations =
await mcpServerCustomizationRepository.selectByUserId(userId);
const mcpToolCustomizations =
await mcpMcpToolCustomizationRepository.selectByUserId(userId);
const serverIds: string[] = [
...mcpServerCustomizations.map(
(mcpServerCustomization) => mcpServerCustomization.mcpServerId,
),
...mcpToolCustomizations.map(
(mcpToolCustomization) => mcpToolCustomization.mcpServerId,
),
];
const prompts = Array.from(new Set(serverIds)).reduce(
(acc, serverId) => {
const sc = mcpServerCustomizations.find((v) => v.mcpServerId == serverId);
const tc = mcpToolCustomizations.filter(
(mcpToolCustomization) => mcpToolCustomization.mcpServerId === serverId,
);
const data: McpServerCustomizationsPrompt = {
name: sc?.serverName || tc[0]?.serverName || "",
id: serverId,
prompt: sc?.prompt || "",
tools: tc.reduce(
(acc, v) => {
acc[v.toolName] = v.prompt || "";
return acc;
},
{} as Record<string, string>,
),
};
acc[serverId] = data;
return acc;
},
{} as Record<string, McpServerCustomizationsPrompt>,
);
serverCache.set(key, prompts, 1000 * 60 * 30); // 30 minutes
return prompts;
}
export async function generateObjectAction({
model,
prompt,
schema,
}: {
model?: ChatModel;
prompt: {
system?: string;
user?: string;
};
schema: JSONSchema7 | ObjectJsonSchema7;
}) {
const result = await generateObject({
model: customModelProvider.getModel(model),
system: prompt.system,
prompt: prompt.user || "",
schema: jsonSchemaToZod(schema),
});
return result.object;
}
export async function rememberAgentAction(
agent: string | undefined,
userId: string,
) {
if (!agent) return undefined;
const key = CacheKeys.agentInstructions(agent);
let cachedAgent = await serverCache.get<Agent | null>(key);
if (!cachedAgent) {
cachedAgent = await agentRepository.selectAgentById(agent, userId);
await serverCache.set(key, cachedAgent);
}
return cachedAgent as Agent | undefined;
}
export async function exportChatAction({
threadId,
expiresAt,
}: {
threadId: string;
expiresAt?: Date;
}) {
const userId = await getUserId();
const isAccess = await chatRepository.checkAccess(threadId, userId);
if (!isAccess) {
return new Response("Unauthorized", { status: 401 });
}
return await chatExportRepository.exportChat({
threadId,
exporterId: userId,
expiresAt: expiresAt ?? undefined,
});
}