CosineSimilarity
Calculates the cosine similarity between two embedding vectors. Returns a value between -1 (opposite) and 1 (identical).
Signature
func CosineSimilarity(a, b []float64) (float64, error)
Parameters
| Parameter | Type | Description |
|---|---|---|
| a | []float64 | First embedding vector |
| b | []float64 | Second embedding vector |
Return Value
Returns a float64 between -1 and 1:
- 1.0: Vectors are identical
- 0.0: Vectors are orthogonal (no similarity)
- -1.0: Vectors are opposite
Zero and empty vectors
A zero vector (all-zero components) or an empty (len == 0) vector on either
side returns (0, nil) — not an error — matching the TypeScript AI SDK. Only
a length mismatch between a and b returns an error, a typed
*errors.InvalidArgumentError with message
"Vectors must have the same length (vector1Length=…, vector2Length=…)".
Examples
Basic Similarity Calculation
package main
import (
"context"
"fmt"
"log"
"github.com/digitallysavvy/go-ai/pkg/ai"
"github.com/digitallysavvy/go-ai/pkg/providers/openai"
)
func main() {
provider := openai.New(openai.Config{
APIKey: "your-api-key",
})
model, _ := provider.EmbeddingModel("text-embedding-3-small")
// Embed two texts
result1, _ := ai.Embed(context.Background(), ai.EmbedOptions{
Model: model,
Input: "machine learning",
})
result2, _ := ai.Embed(context.Background(), ai.EmbedOptions{
Model: model,
Input: "artificial intelligence",
})
// Calculate similarity
similarity, err := ai.CosineSimilarity(result1.Embedding, result2.Embedding)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Similarity: %.4f\n", similarity)
}
Semantic Search
// Query embedding
queryResult, _ := ai.Embed(ctx, ai.EmbedOptions{
Model: model,
Input: "neural networks",
})
// Document embeddings
documents := []string{
"Deep learning uses neural networks",
"The weather is sunny today",
"Artificial neural networks are computational models",
}
// Find most similar
var bestMatch string
var bestScore float64
for _, doc := range documents {
docResult, _ := ai.Embed(ctx, ai.EmbedOptions{
Model: model,
Input: doc,
})
similarity, _ := ai.CosineSimilarity(queryResult.Embedding, docResult.Embedding)
if similarity > bestScore {
bestScore = similarity
bestMatch = doc
}
}
fmt.Printf("Best match (%.4f): %s\n", bestScore, bestMatch)
Similarity Threshold
similarity, err := ai.CosineSimilarity(embedding1, embedding2)
if err != nil {
log.Fatal(err)
}
switch {
case similarity > 0.9:
fmt.Println("Highly similar")
case similarity > 0.7:
fmt.Println("Moderately similar")
case similarity > 0.5:
fmt.Println("Somewhat similar")
default:
fmt.Println("Not similar")
}
Pairwise Similarity Matrix
embeddings := [][]float64{
embedding1,
embedding2,
embedding3,
}
fmt.Println("Similarity Matrix:")
for i := 0; i < len(embeddings); i++ {
for j := 0; j < len(embeddings); j++ {
sim, _ := ai.CosineSimilarity(embeddings[i], embeddings[j])
fmt.Printf("%.3f ", sim)
}
fmt.Println()
}
Error Handling
CosineSimilarity only returns an error for a length mismatch; a zero or
empty vector is not an error condition.
similarity, err := ai.CosineSimilarity(a, b)
if err != nil {
var invalidArg *providererrors.InvalidArgumentError
if errors.As(err, &invalidArg) {
log.Printf("Dimension mismatch: %v", invalidArg)
} else {
log.Println("Unknown error:", err)
}
return
}
// similarity is 0 (not an error) if either vector is all-zero or empty.