---
title: dean151/swift-embeddings
framework: Swift Package Catalog
role: article
path: packages/dean151/swift-embeddings
---

# dean151/swift-embeddings

Text **embeddings and reranking** for Swift: two small protocols, multiple

## Installation

```swift .package(url: "https://github.com/tdurand/swift-embeddings.git", from: "0.1.0"), ```

```swift .target(name: "App", dependencies: [     .product(name: "Embeddings", package: "swift-embeddings"), ]) ```

## Embedding

```swift import Embeddings

let model = VoyageEmbeddingModel(model: .voyage3, apiKey: key) let embeddings = try await model.embed("how does Swift manage memory?") print(embeddings.vectors) // [[Double]] ```

Batch inputs and provider-specific options are supported:

```swift let model = JinaEmbeddingModel(model: .embeddingsV3, apiKey: key) let embeddings = try await model.embed(     ["query one", "query two"],     options: .jina(task: .retrievalQuery, dimensions: 256) ) ```

## Reranking

Reranking is a second-stage refinement for retrieval: fetch a broad candidate set cheaply (for example by embedding similarity), then score each candidate against the query for a sharper final ordering.

```swift let reranker = CohereRerankModel(model: .rerankV35, apiKey: key) let ranking = try await reranker.rerank(     "best fruit for a smoothie",     documents: ["apple pie", "ripe mango", "car engine"],     options: .init(topN: 2) ) for ranked in ranking.results {     print(ranked.index, ranked.relevanceScore) } ```

## On-device embeddings (NaturalLanguage)

On Apple platforms you can embed text entirely on-device — no API key and no network request — using `NaturalLanguageEmbeddingModel`, backed by Apple's NaturalLanguage framework.

```swift let model = NaturalLanguageEmbeddingModel() // .automatic let embeddings = try await model.embed("how does Swift manage memory?") print(embeddings.vectors) // [[Double]] ```

By default (`.automatic`) it uses the transformer-based `NLContextualEmbedding` on iOS 17 / macOS 14 and later — mean-pooling its per-token vectors into one vector per input — and falls back to `NLEmbedding`'s sentence embeddings on earlier systems or when no contextual asset is available for the input's language. Use `.contextual` or `.sentence` to pin a backend. The language is auto-detected per input; pass it explicitly to skip detection:

```swift try await model.embed("bonjour le monde", options: .naturalLanguage(language: .french)) ```

The result's `model` reports the backend used (e.g. `"contextual:en"`, `"sentence:fr"`), and `usage` is always `nil` since there is no token billing on-device.

## Providers

| Provider | Embedding | Reranking | |----------|:---------:|:---------:| | Voyage   | ✅ `VoyageEmbeddingModel` | ✅ `VoyageRerankModel` | | Jina     | ✅ `JinaEmbeddingModel`   | ✅ `JinaRerankModel`   | | Cohere   | ✅ `CohereEmbeddingModel` | ✅ `CohereRerankModel` | | OpenAI   | ✅ `OpenAIEmbeddingModel` | — | | Apple NaturalLanguage (on-device) | ✅ `NaturalLanguageEmbeddingModel` | — |

Each model takes a typed model identifier (e.g. `.voyage3`, with string-literal fallback for unlisted models), an `apiKey`, and optionally a custom `transport` and `baseURL` — handy for OpenAI-compatible gateways or tests.

## Custom transport

Every model issues requests through an `HTTPClientTransport`. The default is `URLSessionTransport`; conform your own type to plug in a different HTTP client or a mock for offline tests:

```swift struct MyTransport: HTTPClientTransport {     func execute(_ request: HTTPRequest, body: HTTPRequestBody?) async throws -> HTTPResponseData {         // ...     } }

let model = OpenAIEmbeddingModel(     model: .textEmbedding3Small,     apiKey: key,     transport: MyTransport() ) ```

## License

MIT © 2026 Thomas Durand

## Package Metadata

Repository: dean151/swift-embeddings

Default branch: main

README: README.md
