Search
Code165
}export async function generateEmbeddings(texts: string[]): Promise<number[][]> { try { const embeddings = await Promise.all( texts.map(text => generateEmbedding(text)) ); return embeddings; } catch (error) { console.error('Error generating batch embeddings:', error); throw error; }}// Utility function to normalize embeddings (optional, for better similarity search)export function normalizeEmbedding(embedding: number[]): number[] { const magnitude = Math.sqrt(embedding.reduce((sum, val) => sum + val * val, 0));}// Calculate cosine similarity between two embeddingsexport function cosineSimilarity(a: number[], b: number[]): number { if (a.length !== b.length) { throw new Error('Embeddings must have the same length'); } - **Database**: Supabase with PGVector for vector search- **AI**: Mistral API for LLM responses- **Embeddings**: Hugging Face sentence-transformers- **Animations**: Lottie- **PWA**: Service Worker + IndexedDB│ ├── routes/│ │ ├── chat.ts # Chat API endpoints│ │ └── embeddings.ts # Embedding generation│ └── services/│ ├── supabase.ts # Supabase client│ ├── mistral.ts # Mistral API client│ └── embeddings.ts # HuggingFace embeddings├── frontend/│ ├── index.html # Main HTML template- `VITE_SUPABASE_ANON_KEY`: Supabase anonymous key- `VITE_SUPABASE_SERVICE_ROLE_KEY`: Supabase service role key- `VITE_HUGGINFACE_API_KEY`: Hugging Face API key for embeddings## Getting Startedconst app = new Hono();// Hugging Face API for embeddingsconst HF_API_URL = "https://api-inference.huggingface.co/pipeline/feature-extraction/sentence-transformers/all-MiniLM-L6-v2";}// Generate embeddings endpointapp.post("/generate", async (c) => { try { console.error("Batch embedding error:", error); return c.json({ error: "Failed to generate batch embeddings", details: error.message }, 500); return c.json({ status: "healthy", service: "embeddings", model: "sentence-transformers/all-MiniLM-L6-v2", dimensions: testEmbedding?.length || 0, return c.json({ status: "unhealthy", service: "embeddings", error: error.message }, 500); getUserConversations} from "../database/queries.ts";import { generateEmbedding } from "./embeddings.ts";const app = new Hono();}// Fallback text search when embeddings are not availableexport async function textSearchMedicalContent( query: string, await sqlite.execute(` INSERT OR REPLACE INTO ${TABLES.EMBEDDINGS_CACHE} (query_hash, query_text, embedding_vector, last_accessed, access_count) VALUES (?, ?, ?, CURRENT_TIMESTAMP, COALESCE((SELECT access_count FROM ${TABLES.EMBEDDINGS_CACHE} WHERE query_hash = ?), 0) + 1) `, [queryHash, queryText, JSON.stringify(embedding), queryHash]); } catch (error) { const results = await sqlite.execute( `SELECT embedding_vector FROM ${TABLES.EMBEDDINGS_CACHE} WHERE query_hash = ?`, [queryHash] ); // Update access count await sqlite.execute( `UPDATE ${TABLES.EMBEDDINGS_CACHE} SET access_count = access_count + 1, last_accessed = CURRENT_TIMESTAMP WHERE query_hash = ?`, CONVERSATIONS: 'nelson_conversations_v1', USER_PREFERENCES: 'nelson_user_preferences_v1', EMBEDDINGS_CACHE: 'nelson_embeddings_cache_v1'}; `); // Embeddings cache for performance await sqlite.execute(` CREATE TABLE IF NOT EXISTS ${TABLES.EMBEDDINGS_CACHE} ( query_hash TEXT PRIMARY KEY, query_text TEXT NOT NULL, await sqlite.execute(` CREATE INDEX IF NOT EXISTS idx_embeddings_cache_access ON ${TABLES.EMBEDDINGS_CACHE}(last_accessed DESC) `);import { readFile, serveFile } from "https://esm.town/v/std/utils@85-main/index.ts";import { chatRoutes } from "./routes/chat.ts";import { embeddingRoutes } from "./routes/embeddings.ts";import { runMigrations } from "./database/migrations.ts";// API Routesapp.route("/api/chat", chatRoutes);app.route("/api/embeddings", embeddingRoutes);// Health check endpoint- **Supabase** with PGVector for vector search- **OpenAI/Mistral** for AI responses- **Hugging Face** for embeddings### Database Schema```sql-- Medical content chunks with embeddingsCREATE TABLE medical_content ( id SERIAL PRIMARY KEY,- Supabase project with PGVector extension- OpenAI or Mistral API key- Hugging Face API key (optional for server-side embeddings)### Environment Variables│ ├── routes/│ │ ├── chat.ts # Chat API endpoints│ │ ├── embeddings.ts # Embedding generation│ │ └── static.ts # Static file serving│ └── index.ts # Main API entry point try { // Get embedding const embResponse = await fetch("https://api.openai.com/v1/embeddings", { method: "POST", headers: { async getEmbedding(text: string, apiKey: string) { const response = await fetch("https://api.openai.com/v1/embeddings", { method: "POST", headers: { }, body: JSON.stringify({ model: "text-embedding-3-small", // IMPORTANT: Match your Pinecone embeddings input: text, }),