Implementing AI Semantic Search with OpenAI Embeddings in prompts.chat
prompts.chat implements AI semantic search by generating OpenAI embeddings for prompts, computing cosine similarity against translated query vectors, and surfacing meaning-based results above a configurable 0.4 threshold.
The prompts.chat repository adds a sophisticated semantic layer that enables users to discover prompts by conceptual meaning rather than exact keyword matches. This functionality relies on OpenAI's embedding models and is controlled through a centralized feature flag system. The implementation spans embedding generation, query preprocessing, similarity scoring, and hybrid search integration across the Next.js application.
How Semantic Search Works in prompts.chat
The semantic search pipeline operates through a sequence of validation, translation, vectorization, and scoring steps. Every entry point first validates that the feature is enabled and that the required API credentials are present.
Feature Flag Configuration
Before any AI processing occurs, the system checks isAISearchEnabled() in src/lib/ai/embeddings.ts【L93-L96】. This function verifies both the features.aiSearch flag defined in the platform configuration and the presence of process.env.OPENAI_API_KEY. If either check fails, the pipeline returns graceful fallbacks to traditional text search.
// prompts.config.ts
export const config = {
features: {
aiSearch: true, // Enable AI-semantic search
},
};
Query Translation Pipeline
Non-English queries are automatically translated to maximize embedding accuracy. The containsNonEnglish utility detects non-ASCII characters, triggering translateQueryToEnglish to convert the input using gpt-4o-mini【L28-L68】. This ensures that the subsequent embedding generation operates on English keyword strings, matching the embedding space of the stored prompts.
// src/lib/ai/embeddings.ts
async function translateQueryToEnglish(text: string): Promise<string> {
// Uses gpt-4o-mini for cost-effective translation
// Returns English keyword string for embedding
}
Embedding Generation and Similarity Scoring
The (translated) query is vectorized using OpenAI's text-embedding-3-small model via generateEmbedding【L70-L78】. The system then retrieves all public prompts with pre-computed embeddings and calculates cosine similarity against the query vector. Only results exceeding the 0.4 similarity threshold are retained【L121-L132】.
The semanticSearch function sorts matches by similarity score and returns the top n results (default 20)【L72-L89】. The public API endpoint at src/app/api/search/ai/route.ts packages these results with metadata including the original query and result count【L24-L32】.
Core Implementation Files
Embedding Utilities (embeddings.ts)
The src/lib/ai/embeddings.ts file serves as the central nervous system for all embedding operations. It exports generatePromptEmbedding for single prompt processing and generateAllEmbeddings for bulk operations【L81-L119】【L150-L170】. The file also implements vector math utilities including cosine similarity calculations essential for the ranking algorithm.
Key functions include:
generateEmbedding- Creates 1536-dimensional vectors using OpenAI's APIsemanticSearch- Orchestrates the similarity computation and filteringfindAndSaveRelatedPrompts- Discovers semantically related content for the "related prompts" feature【L298-L364】
Search API Endpoint (route.ts)
The /api/search/ai route in src/app/api/search/ai/route.ts exposes semantic search to the frontend. It validates the feature flag, accepts query parameters for search terms and result limits, and returns a JSON response containing the ranked prompt objects.
Hybrid Search Integration (prompt-builder-tools.ts)
The Prompt Builder UI leverages a hybrid approach through src/lib/ai/prompt-builder-tools.ts. The executeToolCall function with the "search_prompts" tool runs traditional full-text search in parallel with semantic search, merging and deduplicating results【L54-L108】. If neither search yields results, the system falls back to recent random prompts to provide stylistic examples.
Database Schema and Maintenance
Storing Embeddings in Prisma
The Prisma schema defines the Prompt model with an embedding column storing the vector representation. A separate PromptConnection table maintains "related" relationships between prompts of the same type, populated by the semantic similarity engine.
// src/prisma/schema.prisma
model Prompt {
id String @id @default(uuid())
embedding Float[]? // Vector storage for semantic search
// ... other fields
}
model PromptConnection {
id String @id @default(uuid())
label String // "related" for semantic matches
// ... connection fields
}
Bulk Regeneration Strategy
Administrators can trigger bulk embedding generation via the /api/admin/embeddings route. The generateAllEmbeddings function processes the entire catalog with progress callbacks, reporting success and error counts for monitoring【L150-L170】.
// Triggered via /api/admin/embeddings?regenerate=true
await generateAllEmbeddings((current, total, ok, err) => {
console.log(`Progress: ${current}/${total} – ✅ ${ok} ❌ ${err}`);
}, true);
Related Prompt Discovery
Beyond search, the embedding system powers content recommendation through findAndSaveRelatedPrompts【L298-L364】. When a prompt's embedding is generated, this function identifies up to four nearest neighbors of the same type and persists them as PromptConnection records with the label "related". This creates a semantic graph that suggests conceptually similar prompts in the UI.
Practical Implementation Examples
Enabling AI Search in Configuration
// prompts.config.ts
export const config = {
features: {
aiSearch: true,
},
};
Generating Embeddings for New Prompts
import { generatePromptEmbedding } from "@/lib/ai/embeddings";
export async function createPrompt(data: PromptCreateInput) {
const prompt = await db.prompt.create({ data });
// Store embedding for public prompts only
await generatePromptEmbedding(prompt.id);
return prompt;
}
Client-Side Semantic Search
async function semanticSearch(query: string, limit = 20) {
const res = await fetch(
`/api/search/ai?q=${encodeURIComponent(query)}&limit=${limit}`
);
if (!res.ok) throw new Error("Search failed");
return res.json(); // { results, query, count }
}
Using the Hybrid Search Tool
import { executeToolCall } from "@/lib/ai/prompt-builder-tools";
const { result } = await executeToolCall(
"search_prompts",
{ query: "image generation", limit: 5, promptType: "IMAGE" },
currentState,
availableTags,
availableCategories
);
if (result.success) {
console.log("Found prompts:", result.data.prompts);
}
Summary
- Feature gating: All AI search functionality is controlled by
features.aiSearchand validated throughisAISearchEnabled()before processing. - Query preprocessing: Non-English queries are translated using
gpt-4o-minito ensure optimal embedding quality withtext-embedding-3-small. - Similarity threshold: Results are filtered at 0.4 cosine similarity and ranked before returning the top 20 matches.
- Hybrid architecture: The Prompt Builder combines traditional text search with semantic vectors for comprehensive result coverage.
- Maintenance automation: Bulk regeneration via
/api/admin/embeddingsand automatic related-prompt linking keep the semantic index current.
Frequently Asked Questions
What embedding model does prompts.chat use for semantic search?
The implementation uses OpenAI's text-embedding-3-small model as specified in src/lib/ai/embeddings.ts【L70-L78】. This model provides a balance between vector quality and API cost, generating 1536-dimensional embeddings that are stored directly in the PostgreSQL database via Prisma's Float array type.
How does the system handle non-English search queries?
When containsNonEnglish detects non-ASCII characters in the query, the system invokes translateQueryToEnglish which utilizes gpt-4o-mini to produce an English keyword string【L28-L68】. This translation step ensures the query embedding aligns with the English-based prompt embeddings in the database, maintaining search accuracy across languages without requiring multilingual embedding models.
What is the similarity threshold for returning semantic search results?
The system filters results using a 0.4 cosine similarity threshold defined in the semanticSearch function【L121-L132】. Only prompts with similarity scores exceeding this value are included in the final result set, ensuring that returned items demonstrate genuine semantic relevance rather than weak statistical associations.
How are related prompts discovered and stored?
The findAndSaveRelatedPrompts function in src/lib/ai/embeddings.ts computes vector similarity between a target prompt and all other prompts of the same type【L298-L364】. It selects up to four nearest neighbors and persists them in the PromptConnection table with the label "related", creating a semantic recommendation graph that the UI can query to suggest related content.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →