import { crypto } from "https://deno.land/std@0.190.0/crypto/mod.ts";
import { UMAP } from "https://esm.sh/umap-js@1.3.3";
const MAX_POINTS = 10000;
const MAX_DIMENSIONS = 1000;
const TIMEOUT_MS = 30000;
const resultCache = new Map();
export default async function server(request: Request): Promise<Response> {
if (request.method === "GET") {
return new Response(html, {
headers: { "Content-Type": "text/html" },
});
}
if (request.method !== "POST") {
return new Response("Method Not Allowed", { status: 405 });
}
try {
const { embeddings, config } = await request.json();
if (!Array.isArray(embeddings) || embeddings.length === 0) {
return new Response("Invalid input: embeddings must be a non-empty array", { status: 400 });
}
if (embeddings.length > MAX_POINTS) {
return new Response(`Input too large: maximum ${MAX_POINTS} points allowed`, { status: 413 });
}
if (embeddings[0].length > MAX_DIMENSIONS) {
return new Response(`Input too high-dimensional: maximum ${MAX_DIMENSIONS} dimensions allowed`, { status: 413 });
}
const encoder = new TextEncoder();
const data = encoder.encode(JSON.stringify({ embeddings, config }));
const hashBuffer = await crypto.subtle.digest("MD5", data);
const hashArray = Array.from(new Uint8Array(hashBuffer));
const cacheKey = hashArray.map(b => b.toString(16).padStart(2, "0")).join("");
if (resultCache.has(cacheKey)) {
return new Response(JSON.stringify(resultCache.get(cacheKey)), {
headers: { "Content-Type": "application/json" },
});
}
const umap = new UMAP({
nComponents: 2,
nEpochs: 400,
nNeighbors: config.nNeighbors || 15,
minDist: config.minDist || 0.1,
spread: config.spread || 1.0,
});
const result = await Promise.race([
umap.fit(embeddings),
new Promise((_, reject) => setTimeout(() => reject(new Error("Computation timed out")), TIMEOUT_MS)),
]);
resultCache.set(cacheKey, result);
return new Response(JSON.stringify(result), {
headers: { "Content-Type": "application/json" },
});
} catch (error) {
console.error("Error:", error);
return new Response(`Error: ${error.message}`, { status: 500 });
}
}