Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 57 additions & 33 deletions app/lib/modelClient.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,57 @@
// // lib/modelClient.ts
// "use client";

// import { pipeline } from "@xenova/transformers";

// /** Singleton references to loaded pipelines */
// let imageEmbedder: any = null;

// /**
// * loading a CLIP embedding pipeline for image search (example).
// * this will make it easy to use other models
// */
// export async function loadEmbeddingPipeline() {
// if (!imageEmbedder) {
// // Using CLIP for image embeddings, just as an example
// imageEmbedder = await pipeline(
// "feature-extraction",
// "Xenova/clip-vit-base-patch32"
// );
// }
// return imageEmbedder;
// }

// /**
// * Extract embeddings from an image.
// * Returns a vector (array of floats) we can then compare with our dataset.
// */
// export async function getImageEmbeddings(image: Blob | string) {
// const embedder = await loadEmbeddingPipeline();
// // The pipeline returns a nested array. We'll flatten or keep it nested as needed.
// const result = await embedder(image);
// return result;
// }
// lib/ModelClient.ts
"use client";

import { pipeline, Pipeline } from "@xenova/transformers";

/**
* Singleton reference to the loaded CLIP embedding pipeline
*/
let imageEmbedder: Pipeline | null = null;

/**
* Load the CLIP embedding pipeline (Xenova Transformers)
*/
export async function loadEmbeddingPipeline(): Promise<Pipeline> {
if (!imageEmbedder) {
imageEmbedder = await pipeline(
"feature-extraction",
"Xenova/clip-vit-base-patch32"
);
}
return imageEmbedder;
}

/**
* Extract embeddings from an image.
* @param image - A Blob, File, or URL string
* @returns A flattened array of floats (number[]) representing the image embedding
*/
export async function getImageEmbeddings(image: Blob | string): Promise<number[]> {
const embedder = await loadEmbeddingPipeline();
const result: unknown = await embedder(image);

let embedding: number[];

// Case 1: result is a nested array (number[][])
if (Array.isArray(result) && result.length > 0 && Array.isArray(result[0])) {
embedding = result[0] as number[];

// Case 2: result is an object with .data array (tensor-like)
} else if (
typeof result === "object" &&
result !== null &&
"data" in result &&
Array.isArray((result as { data?: unknown }).data)
) {
embedding = (result as { data: number[] }).data;

// Case 3: fallback: treat result directly as number[]
} else if (Array.isArray(result)) {
embedding = result as number[];

} else {
throw new Error("Unable to parse embeddings from pipeline output");
}

return Array.from(embedding);
}
Loading
Loading