AI Providers
Vision and embedding are configured independently. Pick whichever combination suits your quality bar and budget — Spectacles handles the rest.
Provider matrix
| Provider | Vision | Text embedding | Image embedding |
|---|---|---|---|
| OpenAI | gpt-4o, gpt-4o-mini, gpt-4.1 | text-embedding-3-small/large | — |
| Anthropic | Claude Opus / Sonnet / Haiku 4.x | — | — |
| Google Gemini | gemini-2.5-flash / pro | text-embedding-004 | — |
| Voyage AI | — | voyage-multimodal-3 | voyage-multimodal-3 |
| Ollama (local) | llava, llama3.2-vision, bakllava | nomic-embed-text, mxbai-embed-large | — |
Recommended pairings
- Best quality: Anthropic Claude (vision) + Voyage
voyage-multimodal-3(embedding). Claude writes the richest descriptions; Voyage embeds the image bytes for the strongest visual similarity. - Best price/performance: OpenAI
gpt-4o-mini+text-embedding-3-small. Fast, cheap, reliable JSON mode. - Cheapest hosted: Gemini all the way through.
gemini-2.5-flashis fast and inexpensive;text-embedding-004is free at low volume. - Self-hosted, no API cost: Ollama with
llava+nomic-embed-text. Slower per image, but everything stays on your machine. - Strict similarity, descriptions less important: any vision provider + Voyage multimodal — the embedding does the heavy lifting.
About multimodal embeddings
Text embeddings turn the AI-generated description into a vector. They’re fine for “find me photos of beaches”-style queries, but they lose information: two beaches with very different compositions, lighting, or color palettes can end up with nearly identical descriptions.
Multimodal embeddings (Voyage voyage-multimodal-3) embed the raw image bytes directly. They capture texture, composition, palette, and overall visual style — the things you actually mean when you say “similar.”
When the embedding provider supports multimodal, Spectacles uses it automatically. When it doesn’t, it falls back to embedding the description text.
Adding a new provider
Implement VisionProvider and/or EmbeddingProvider in src/services/vision/ or src/services/embedding/, add a constant to Settings::PROVIDER_*, and wire it into Vision::visionProvider() or Vision::embeddingProvider(). PRs welcome.