scira-chat / app /actions.ts
victor's picture
victor HF Staff
Switch generateTitle to use Hugging Face Llama model
a56be76
raw
history blame
5.53 kB
"use server";
import { openai } from "@/lib/openai-client";
import { hf } from "@/lib/hf-client";
import { z } from "zod";
import { startMcpSandbox } from "@/lib/mcp-sandbox";
// Use a global map to store active sandbox instances across requests
const activeSandboxes = (global as any).activeSandboxes || new Map();
(global as any).activeSandboxes = activeSandboxes;
// Helper to extract text content from a message regardless of format
function getMessageText(message: any): string {
// Check if the message has parts (new format)
if (message.parts && Array.isArray(message.parts)) {
const textParts = message.parts.filter(
(p: any) => p.type === "text" && p.text
);
if (textParts.length > 0) {
return textParts.map((p: any) => p.text).join("\n");
}
}
// Fallback to content (old format)
if (typeof message.content === "string") {
return message.content;
}
// If content is an array (potentially of parts), try to extract text
if (Array.isArray(message.content)) {
const textItems = message.content.filter(
(item: any) =>
typeof item === "string" || (item.type === "text" && item.text)
);
if (textItems.length > 0) {
return textItems
.map((item: any) => (typeof item === "string" ? item : item.text))
.join("\n");
}
}
return "";
}
export async function generateTitle(messages: any[]): Promise<string> {
const normalized = messages.map((m) => ({
role: m.role as "user" | "assistant" | "system",
content: Array.isArray(m.content) ? m.content.join("\n") : m.content,
}));
const response = await hf.chat.completions.create({
model: "meta-llama/Llama-3.1-8B-Instruct",
max_tokens: 30,
temperature: 0.4,
messages: [
{
role: "system",
content:
"You are a helpful assistant that writes very short, unique titles (≤30 characters) for chat conversations.",
},
...normalized,
{ role: "user", content: "Write the title only." },
],
});
const title = response.choices[0]?.message.content?.trim() ?? "New Chat";
// basic schema check
return z.string().min(1).max(100).parse(title);
}
export interface KeyValuePair {
key: string;
value: string;
}
/**
* Server action to start a sandbox
*/
export async function startSandbox(params: {
id: string;
command: string;
args: string[];
env?: KeyValuePair[];
}): Promise<{ url: string }> {
const { id, command, args, env } = params;
console.log(`[startSandbox] Starting sandbox for ID: ${id}`);
// Validate required fields
if (!id || !command || !args) {
throw new Error("Missing required fields");
}
// Check if we already have a sandbox for this ID
if (activeSandboxes.has(id)) {
// If we do, get the URL and return it without creating a new sandbox
const existingSandbox = activeSandboxes.get(id);
console.log(
`[startSandbox] Reusing existing sandbox for ${id}, URL: ${existingSandbox.url}`
);
// Re-fetch the URL to make sure it's current
try {
const freshUrl = await existingSandbox.sandbox.getUrl();
console.log(`[startSandbox] Updated sandbox URL for ${id}: ${freshUrl}`);
// Update the URL in the map
activeSandboxes.set(id, {
sandbox: existingSandbox.sandbox,
url: freshUrl,
});
return { url: freshUrl };
} catch (error) {
console.error(
`[startSandbox] Error refreshing sandbox URL for ${id}:`,
error
);
// Fall through to create a new sandbox if we couldn't refresh the URL
activeSandboxes.delete(id);
console.log(
`[startSandbox] Removed stale sandbox for ${id}, will create a new one`
);
}
}
// Build the command string
let cmd: string;
// Prepare the command based on the type of executable
if (command === "uvx") {
// For uvx, use the direct format
const toolName = args[0];
cmd = `uvx ${toolName} ${args.slice(1).join(" ")}`;
} else if (command.includes("python")) {
// For python commands
cmd = `${command} ${args.join(" ")}`;
} else {
// For node or other commands
cmd = `${command} ${args.join(" ")}`;
}
// Convert env array to object if needed
const envs: Record<string, string> = {};
if (env && env.length > 0) {
env.forEach((envVar) => {
if (envVar.key) envs[envVar.key] = envVar.value || "";
});
}
// Start the sandbox
console.log(
`[startSandbox] Creating new sandbox for ${id} with command: ${cmd}`
);
const sandbox = await startMcpSandbox({ cmd, envs });
const url = await sandbox.getUrl();
console.log(`[startSandbox] Sandbox created for ${id}, URL: ${url}`);
// Store the sandbox in our map
activeSandboxes.set(id, { sandbox, url });
return { url };
}
/**
* Server action to stop a sandbox
*/
export async function stopSandbox(id: string): Promise<{ success: boolean }> {
if (!id) {
throw new Error("Missing sandbox ID");
}
// Check if we have a sandbox with this ID
if (!activeSandboxes.has(id)) {
throw new Error(`No active sandbox found with ID: ${id}`);
}
// Stop the sandbox
const { sandbox } = activeSandboxes.get(id);
try {
await sandbox.stop();
console.log(`Stopped sandbox with ID: ${id}`);
} catch (stopError) {
console.error(`Error stopping sandbox ${id}:`, stopError);
// Continue to remove from the map even if stop fails
}
// Remove from our map
activeSandboxes.delete(id);
return { success: true };
}