OpenAI models support for Spring AI, providing comprehensive integration for chat completion, embeddings, image generation, audio transcription, text-to-speech, and content moderation capabilities within Spring Boot applications.
Generate vector embeddings for text using OpenAI's embedding models for semantic search, clustering, recommendation systems, and similarity calculations.
Generates dense vector representations of text that capture semantic meaning, enabling similarity comparisons and vector-based operations.
/**
* OpenAI embedding model implementation
*/
public class OpenAiEmbeddingModel extends AbstractEmbeddingModel {
/**
* Generate embedding vector for a single document
* @param document Document to embed
* @return float array representing the embedding vector
*/
public float[] embed(Document document);
/**
* Generate embeddings for multiple inputs
* @param request Embedding request with one or more inputs
* @return EmbeddingResponse containing embeddings and metadata
*/
public EmbeddingResponse call(EmbeddingRequest request);
/**
* Set custom observation convention for observability
* @param observationConvention Custom observation convention
*/
public void setObservationConvention(EmbeddingModelObservationConvention observationConvention);
}Constructors:
// Basic constructor
public OpenAiEmbeddingModel(OpenAiApi openAiApi);
// With metadata mode
public OpenAiEmbeddingModel(
OpenAiApi openAiApi,
MetadataMode metadataMode
);
// With options
public OpenAiEmbeddingModel(
OpenAiApi openAiApi,
MetadataMode metadataMode,
OpenAiEmbeddingOptions options
);
// With retry support
public OpenAiEmbeddingModel(
OpenAiApi openAiApi,
MetadataMode metadataMode,
OpenAiEmbeddingOptions options,
RetryTemplate retryTemplate
);
// Full constructor with observability
public OpenAiEmbeddingModel(
OpenAiApi openAiApi,
MetadataMode metadataMode,
OpenAiEmbeddingOptions options,
RetryTemplate retryTemplate,
ObservationRegistry observationRegistry
);Note: OpenAiEmbeddingModel uses constructor-based initialization only. Unlike OpenAiChatModel, it does not provide a builder pattern. Use the appropriate constructor based on your configuration needs.
Usage Example:
import org.springframework.ai.openai.OpenAiEmbeddingModel;
import org.springframework.ai.openai.OpenAiEmbeddingOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingRequest;
// Create API client
var openAiApi = OpenAiApi.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();
// Create embedding model
var embeddingModel = new OpenAiEmbeddingModel(
openAiApi,
MetadataMode.EMBED,
OpenAiEmbeddingOptions.builder()
.model(OpenAiApi.EmbeddingModel.TEXT_EMBEDDING_3_SMALL.getValue())
.build()
);
// Generate single embedding
var document = new Document("The quick brown fox jumps over the lazy dog");
float[] embedding = embeddingModel.embed(document);
System.out.println("Embedding dimensions: " + embedding.length);
// Generate multiple embeddings
var request = new EmbeddingRequest(
List.of("First text", "Second text", "Third text"),
OpenAiEmbeddingOptions.builder()
.model(OpenAiApi.EmbeddingModel.TEXT_EMBEDDING_3_LARGE.getValue())
.dimensions(1024) // Reduce dimensions for efficiency
.build()
);
var response = embeddingModel.call(request);
response.getData().forEach(emb -> {
System.out.println("Embedding: " + Arrays.toString(emb.getEmbedding()));
});Calculating Similarity:
// Cosine similarity between two embeddings
public static double cosineSimilarity(float[] a, float[] b) {
double dotProduct = 0.0;
double normA = 0.0;
double normB = 0.0;
for (int i = 0; i < a.length; i++) {
dotProduct += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
}
// Compare documents
var doc1Embedding = embeddingModel.embed(new Document("Machine learning is fascinating"));
var doc2Embedding = embeddingModel.embed(new Document("AI and ML are interesting topics"));
var doc3Embedding = embeddingModel.embed(new Document("I love pizza"));
double similarity12 = cosineSimilarity(doc1Embedding, doc2Embedding);
double similarity13 = cosineSimilarity(doc1Embedding, doc3Embedding);
System.out.println("ML vs AI similarity: " + similarity12); // High similarity
System.out.println("ML vs Pizza similarity: " + similarity13); // Low similarityConfiguration options for embedding generation requests.
/**
* Configuration options for OpenAI embedding requests
*/
public class OpenAiEmbeddingOptions implements EmbeddingOptions {
/**
* Create a new builder for embedding options
* @return Builder instance
*/
public static Builder builder();
/**
* Get the embedding model identifier
* @return Model name
*/
public String getModel();
public void setModel(String model);
/**
* Get the encoding format for embeddings
* @return "float" or "base64"
*/
public String getEncodingFormat();
public void setEncodingFormat(String encodingFormat);
/**
* Get the number of dimensions for the embedding (v3 models only)
* @return Dimension count or null for default
*/
public Integer getDimensions();
public void setDimensions(Integer dimensions);
/**
* Get the user identifier for tracking/abuse prevention
* @return User ID
*/
public String getUser();
public void setUser(String user);
}Builder Pattern:
public static class Builder {
public Builder model(String model);
public Builder encodingFormat(String encodingFormat);
public Builder dimensions(Integer dimensions);
public Builder user(String user);
public OpenAiEmbeddingOptions build();
}Usage Example:
// text-embedding-ada-002 (legacy model, 1536 dimensions, fixed)
var adaOptions = OpenAiEmbeddingOptions.builder()
.model(OpenAiApi.EmbeddingModel.TEXT_EMBEDDING_ADA_002.getValue())
.build();
// text-embedding-3-small (default 1536 dimensions, adjustable)
var small3Options = OpenAiEmbeddingOptions.builder()
.model(OpenAiApi.EmbeddingModel.TEXT_EMBEDDING_3_SMALL.getValue())
.dimensions(512) // Reduce for efficiency, range: 1-1536
.build();
// text-embedding-3-large (default 3072 dimensions, adjustable)
var large3Options = OpenAiEmbeddingOptions.builder()
.model(OpenAiApi.EmbeddingModel.TEXT_EMBEDDING_3_LARGE.getValue())
.dimensions(1024) // Reduce for efficiency, range: 1-3072
.encodingFormat("float") // or "base64"
.user("user-123")
.build();Embedding model identifier:
Number of dimensions in the output embedding vector (v3 models only):
Format for the embedding vector:
Unique identifier for end-user for tracking and abuse monitoring
// High-level embedding request (from spring-ai-core)
public class EmbeddingRequest {
public EmbeddingRequest(List<String> inputs, EmbeddingOptions options);
public List<String> getInstructions();
public EmbeddingOptions getOptions();
}
// Low-level embedding request
public record EmbeddingRequest<T>(
T input, // String or List<String>
String model, // Model identifier
String encodingFormat, // "float" or "base64"
Integer dimensions, // Output dimensions (v3 models)
String user // User identifier
) {}// High-level embedding response (from spring-ai-core)
public interface EmbeddingResponse {
List<Embedding> getData();
EmbeddingResponseMetadata getMetadata();
}
public class Embedding {
public float[] getEmbedding();
public Integer getIndex();
public Object getObject();
}
// Low-level embedding response
public record EmbeddingList<T>(
String object, // "list"
List<Embedding> data, // Embedding results
String model, // Model used
Usage usage // Token usage
) {}
public record Embedding(
Integer index, // Result index
float[] embedding, // Embedding vector
String object // "embedding"
) {}public class EmbeddingResponseMetadata {
public String getModel();
public Usage getUsage();
}
public record Usage(
Integer promptTokens, // Input token count
Integer totalTokens // Total token count
) {}public enum OpenAiApi.EmbeddingModel {
TEXT_EMBEDDING_ADA_002("text-embedding-ada-002"),
TEXT_EMBEDDING_3_SMALL("text-embedding-3-small"),
TEXT_EMBEDDING_3_LARGE("text-embedding-3-large");
public String getValue();
}// From spring-ai-core, controls document metadata inclusion
public enum MetadataMode {
ALL, // Include all metadata in embedding
EMBED, // Include only embedding-relevant metadata
NONE // Exclude all metadata
}// Index documents
List<Document> documents = loadDocuments();
Map<Document, float[]> index = new HashMap<>();
for (Document doc : documents) {
float[] embedding = embeddingModel.embed(doc);
index.put(doc, embedding);
}
// Search query
String query = "How do I reset my password?";
float[] queryEmbedding = embeddingModel.embed(new Document(query));
// Find most similar documents
List<Map.Entry<Document, Double>> results = index.entrySet().stream()
.map(entry -> Map.entry(
entry.getKey(),
cosineSimilarity(queryEmbedding, entry.getValue())
))
.sorted(Map.Entry.<Document, Double>comparingByValue().reversed())
.limit(5)
.toList();// Generate embeddings for all documents
List<float[]> embeddings = documents.stream()
.map(embeddingModel::embed)
.toList();
// Use clustering algorithm (e.g., K-means) on embeddings
// to group semantically similar documents// Embed user preferences
float[] userProfile = embeddingModel.embed(
new Document(String.join(" ", userLikedItems))
);
// Find similar items
List<Item> recommendations = allItems.stream()
.map(item -> {
float[] itemEmbedding = embeddingModel.embed(new Document(item.getDescription()));
double similarity = cosineSimilarity(userProfile, itemEmbedding);
return new ScoredItem(item, similarity);
})
.sorted(Comparator.comparing(ScoredItem::score).reversed())
.limit(10)
.map(ScoredItem::item)
.toList();// Find near-duplicate documents
double threshold = 0.95; // Very high similarity
for (int i = 0; i < documents.size(); i++) {
for (int j = i + 1; j < documents.size(); j++) {
float[] emb1 = embeddingModel.embed(documents.get(i));
float[] emb2 = embeddingModel.embed(documents.get(j));
double similarity = cosineSimilarity(emb1, emb2);
if (similarity > threshold) {
System.out.println("Potential duplicate: " + i + " and " + j);
}
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-springframework-ai--spring-ai-openai