# Other providers and stores

> Short indexing recipes for Ollama, Groq, Dashscope and FastEmbed models and for Redis, PgVector and LanceDB storage.

Each recipe is a runnable example from the swiftide repository that indexes files into one provider or store; the PgVector, LanceDB and Dashscope recipes also query what they stored. Each section points to the integration page that covers the client options in depth.

## Prerequisites

Run an example from the root of the swiftide repository, because the examples read `README.md` and other files by relative path:

```sh
# in the swiftide repository
cargo run -p swiftide-examples --example <name>
```

To copy a recipe into your own crate, add the `swiftide` line from its section, plus the runtime and logging crates every example uses:

```sh
# in your own crate
cargo add tokio --features full
cargo add tracing-subscriber
```

## Ollama

```sh
# in your own crate
cargo add swiftide@0.34 --features ollama,fastembed
```

```sh
# in the swiftide repository
ollama pull llama3.1
cargo run -p swiftide-examples --example index-ollama
```

```rust title="examples/index_ollama.rs"
indexing::Pipeline::from_loader(FileLoader::new("README.md"))
    .then_chunk(ChunkMarkdown::from_chunk_range(10..2048))
    .then(MetadataQAText::new(ollama_client.clone()))
    .then_in_batch(Embed::new(fastembed).with_batch_size(10))
    .then_store_with(memory_store.clone())
    .run()
    .await?;
```

Source: [`examples/index_ollama.rs#L39-L45`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/index_ollama.rs#L39-L45)

FastEmbed computes the embeddings locally, so only metadata generation calls Ollama. `Ollama::default()` talks to `http://localhost:11434/v1` and sends no application programming interface (API) key.

Client options and gotchas: [OpenAI-compatible providers](/integrations/openai-compatible/).

## Groq

```sh
# in your own crate
cargo add swiftide@0.34 --features groq,fastembed
```

```sh
# in the swiftide repository
cargo run -p swiftide-examples --example index-groq
```

```rust title="examples/index_groq.rs"
indexing::Pipeline::from_loader(FileLoader::new("README.md"))
    .then_chunk(ChunkMarkdown::from_chunk_range(10..2048))
    .then(MetadataQAText::new(groq_client.clone()))
    .then_in_batch(Embed::new(fastembed).with_batch_size(10))
    .then_store_with(memory_store.clone())
    .run()
    .await?;
```

Source: [`examples/index_groq.rs#L39-L45`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/index_groq.rs#L39-L45)

The client reads `GROQ_API_KEY`. The example sets the model `llama3-8b-8192`, which Groq has retired. Change it in the example's setup to a current model, such as `llama-3.1-8b-instant`, before you run it.

Client options and gotchas: [OpenAI-compatible providers](/integrations/openai-compatible/).

## Dashscope

```sh
# in your own crate
cargo add swiftide@0.34 --features dashscope,lancedb
cargo add temp-dir anyhow
```

```sh
# in the swiftide repository
cargo run -p swiftide-examples --example dashscope
```

```rust title="examples/dashscope.rs"
indexing::Pipeline::from_loader(FileLoader::new(".").with_extensions(&["md"]))
    .with_default_llm_client(client.clone())
    .then_chunk(ChunkMarkdown::from_chunk_range(10..2048))
    .then(MetadataQAText::new(client.clone()))
    .then_in_batch(Embed::new(client.clone()).with_batch_size(10))
    .then_store_with(lancedb.clone())
    .run()
    .await?;
```

Source: [`examples/dashscope.rs#L38-L45`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/dashscope.rs#L38-L45)

`Dashscope` reads `QWEN_API_KEY`, or `DASHSCOPE_API_KEY` when that is unset. One client sets both an embed model and a prompt model, so it serves as large language model (LLM) and embedder.

`with_default_llm_client` gives a client to every transformer added after it that has none of its own, such as `MetadataQAText::default()`. A transformer built with `new(client)` keeps its own client.

The loader picks up every markdown file under the current directory. It uses the default filters of the `ignore` crate, so it skips hidden files and directories, such as `.github/`. It also skips what `.ignore` files, the global gitignore and `.git/info/exclude` exclude, and, inside a git repository, what `.gitignore` excludes.

```rust title="examples/dashscope.rs"
let pipeline = query::Pipeline::default()
    .then_transform_query(query_transformers::GenerateSubquestions::from_client(
        client.clone(),
    ))
    .then_transform_query(query_transformers::Embed::from_client(client.clone()))
    .then_retrieve(lancedb.clone())
    .then_transform_response(response_transformers::Summary::from_client(client.clone()))
    .then_answer(answers::Simple::from_client(client.clone()));

let result = pipeline
    .query("What is swiftide? Please provide an elaborate explanation")
    .await?;
```

Source: [`examples/dashscope.rs#L49-L60`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/dashscope.rs#L49-L60)

The query pipeline generates subquestions, embeds them, retrieves from LanceDB, summarizes the documents with the `Summary` response transformer, and answers with `answers::Simple`.

Client options and gotchas: [OpenAI-compatible providers](/integrations/openai-compatible/) and [LanceDB](/integrations/lancedb/).

## FastEmbed

```sh
# in your own crate
cargo add swiftide@0.34 --features fastembed,qdrant
```

```sh
# in the swiftide repository
docker run -p 6334:6334 qdrant/qdrant
cargo run -p swiftide-examples --example fastembed
```

```rust title="examples/fastembed.rs"
indexing::Pipeline::from_loader(FileLoader::new(".").with_extensions(&["rs"]))
    .then_in_batch(Embed::new(FastEmbed::builder().batch_size(10).build()?))
    .then_store_with(
        Qdrant::try_from_url(qdrant_url)?
            .batch_size(50)
            .vector_size(384)
            .collection_name("swiftide-examples-fastembed".to_string())
            .build()?,
    )
    .run()
    .await?;
```

Source: [`examples/fastembed.rs#L32-L42`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/fastembed.rs#L32-L42)

The recipe needs Qdrant on `QDRANT_URL`, which defaults to the gRPC port at `http://localhost:6334`. It loads, embeds and stores, with no chunker and no metadata.

FastEmbed runs embedding models in your process, with no API key. It downloads the default model from Hugging Face on first use. That model produces 384-dimensional vectors, so the Qdrant collection uses `vector_size(384)`.

`batch_size(10)` on the FastEmbed builder sets how many texts the model embeds at once (default 256). `batch_size(50)` on the Qdrant builder sets the nodes per write. [Embedding](/indexing/embedding/) explains how these relate to the pipeline's batch size.

Client options and gotchas: [FastEmbed](/integrations/fastembed/) and [Qdrant](/integrations/qdrant/).

## Redis

```sh
# in your own crate
cargo add swiftide@0.34 --features redis,tree-sitter
cargo add serde_json
```

```sh
# in the swiftide repository
cargo run -p swiftide-examples --example index-redis
```

```rust title="examples/index_into_redis.rs"
indexing::Pipeline::from_loader(FileLoader::new(".").with_extensions(&["rs"]))
    .then_chunk(ChunkCode::try_for_language_and_chunk_size(
        "rust",
        10..2048,
    )?)
    .then_store_with(
        // By default the value is the full node serialized to JSON.
        // We can customize this by providing a custom function.
        Redis::try_build_from_url(&redis_url)?
            .persist_value_fn(|node| Ok(serde_json::to_string(&node.metadata)?))
            .batch_size(50)
            .build()?,
    )
    .run()
    .await?;
```

Source: [`examples/index_into_redis.rs#L39-L53`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/index_into_redis.rs#L39-L53)

The recipe needs Redis on `REDIS_URL`, which defaults to `redis://localhost:6379`. It chunks Rust files with `ChunkCode` and stores the chunks in Redis, with no LLM or embedder.

Redis is storage only here. It does not implement retrieval, but it also works as a node cache and as a message history backend. Keys, values and the other backends: [Storage](/indexing/storage/).

## PgVector

```sh
# in your own crate
cargo add swiftide@0.34 --features pgvector,ollama,fastembed
```

```sh
# in the swiftide repository
ollama pull llama3.2
cargo run -p swiftide-examples --example index-md-pgvector
```

The example starts a `pgvector/pgvector` container through Docker with a test helper, so Docker must be running. In your application, pass your own database URL to `db_url`.

```rust title="examples/index_md_into_pgvector.rs"
tracing::info!("Starting indexing pipeline");

indexing::Pipeline::from_loader(FileLoader::new(test_dataset_path).with_extensions(&["md"]))
    .then_chunk(ChunkMarkdown::from_chunk_range(10..2048))
    .then(MetadataQAText::new(llm_client.clone()))
    .then_in_batch(Embed::new(fastembed.clone()).with_batch_size(100))
    .then_store_with(pgv_storage.clone())
    .run()
    .await?;
```

Source: [`examples/index_md_into_pgvector.rs#L103-L111`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/index_md_into_pgvector.rs#L103-L111)

```rust title="examples/index_md_into_pgvector.rs"
async fn ask_query(
    llm_client: impl SimplePrompt + Clone + 'static,
    embed: FastEmbed,
    vector_store: PgVector,
    questions: Vec<String>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
    // By default the search strategy is SimilaritySingleEmbedding
    // which takes the latest query, embeds it, and does a similarity search
    //
    // Pgvector will return an error if multiple embeddings are set
    //
    // The pipeline generates subquestions to increase semantic coverage, embeds these in a single
    // embedding, retrieves the default top_k documents, summarizes them and uses that as context
    // for the final answer.
    let pipeline = query::Pipeline::default()
        .then_transform_query(query_transformers::GenerateSubquestions::from_client(
            llm_client.clone(),
        ))
        .then_transform_query(query_transformers::Embed::from_client(embed))
        .then_retrieve(vector_store.clone())
        .then_transform_response(response_transformers::Summary::from_client(
            llm_client.clone(),
        ))
        .then_answer(answers::Simple::from_client(llm_client.clone()));

    let results: Vec<String> = pipeline
        .query_all(questions)
        .await?
        .iter()
        .map(|result| result.answer().to_string())
        .collect();

    Ok(results)
}
```

Source: [`examples/index_md_into_pgvector.rs#L17-L50`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/index_md_into_pgvector.rs#L17-L50)

`query_all` sends several questions through one pipeline and returns an answered query for each. The `SimilaritySingleEmbedding` search strategy needs exactly one vector field; with more, it fails with "Search strategy for multiple vector fields in the schema is not yet implemented". A `CustomStrategy` does not have this limit.

Table setup, columns and indexes: [PgVector](/integrations/pgvector/).

## LanceDB

```sh
# in your own crate
cargo add swiftide@0.34 --features lancedb,openai
cargo add temp-dir
```

```sh
# in the swiftide repository
cargo run -p swiftide-examples --example lancedb
```

```rust title="examples/lancedb.rs"
let openai_client = integrations::openai::OpenAI::builder()
    .default_embed_model("text-embedding-3-small")
    .default_prompt_model("gpt-4o-mini")
    .build()?;

let tempdir = TempDir::new().unwrap();

// Configure lancedb with a default vector size, a single embedding
// and in addition to embedding the text metadata, also store it in a field
let lancedb = LanceDB::builder()
    .uri(tempdir.child("lancedb").to_str().unwrap())
    .vector_size(1536)
    .with_vector(EmbeddedField::Combined)
    .with_metadata(METADATA_QA_TEXT_NAME)
    .table_name("swiftide_test")
    .build()
    .unwrap();
```

Source: [`examples/lancedb.rs#L20-L36`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/lancedb.rs#L20-L36)

```rust title="examples/lancedb.rs"
indexing::Pipeline::from_loader(FileLoader::new("README.md"))
    .then_chunk(ChunkMarkdown::from_chunk_range(10..2048))
    .then(MetadataQAText::new(openai_client.clone()))
    .then_in_batch(Embed::new(openai_client.clone()).with_batch_size(10))
    .then_store_with(lancedb.clone())
    .run()
    .await?;
```

Source: [`examples/lancedb.rs#L40-L46`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/lancedb.rs#L40-L46)

```rust title="examples/lancedb.rs"
// By default the search strategy is SimilaritySingleEmbedding
// which takes the latest query, embeds it, and does a similarity search
//
// LanceDB will return an error if multiple embeddings are set
//
// The pipeline generates subquestions to increase semantic coverage, embeds these in a single
// embedding, retrieves the default top_k documents, summarizes them and uses that as context
// for the final answer.
let pipeline = query::Pipeline::default()
    .then_transform_query(query_transformers::GenerateSubquestions::from_client(
        openai_client.clone(),
    ))
    .then_transform_query(query_transformers::Embed::from_client(
        openai_client.clone(),
    ))
    .then_retrieve(lancedb.clone())
    .then_transform_response(response_transformers::Summary::from_client(
        openai_client.clone(),
    ))
    .then_answer(answers::Simple::from_client(openai_client.clone()));
```

Source: [`examples/lancedb.rs#L50-L69`](https://github.com/bosun-ai/swiftide/blob/e377c4a2e76fe2559629ae0efdae3c6e0fb4d829/examples/lancedb.rs#L50-L69)

LanceDB is embedded: `uri` points at a local directory, and the table is created on the first run. The example uses a temporary directory, so the data is gone when it exits. The recipe needs `OPENAI_API_KEY`.

With `SimilaritySingleEmbedding`, retrieval also needs exactly one vector field, and fails with "Zero or multiple vector fields configured in schema" otherwise. A `CustomStrategy` does not have this limit.

Remote storage and vector indexes: [LanceDB](/integrations/lancedb/).

## Reference

- [`Ollama`](https://docs.rs/swiftide/0.34/swiftide/integrations/ollama/type.Ollama.html), [`Groq`](https://docs.rs/swiftide/0.34/swiftide/integrations/groq/type.Groq.html), [`Dashscope`](https://docs.rs/swiftide/0.34/swiftide/integrations/dashscope/type.Dashscope.html), [`GenericOpenAI`](https://docs.rs/swiftide/0.34/swiftide/integrations/openai/struct.GenericOpenAI.html)
- [`FastEmbed`](https://docs.rs/swiftide/0.34/swiftide/integrations/fastembed/struct.FastEmbed.html)
- [`Redis`](https://docs.rs/swiftide/0.34/swiftide/integrations/redis/struct.Redis.html), [`RedisBuilder`](https://docs.rs/swiftide/0.34/swiftide/integrations/redis/struct.RedisBuilder.html)
- [`PgVector`](https://docs.rs/swiftide/0.34/swiftide/integrations/pgvector/struct.PgVector.html), [`PgVectorBuilder`](https://docs.rs/swiftide/0.34/swiftide/integrations/pgvector/struct.PgVectorBuilder.html)
- [`LanceDB`](https://docs.rs/swiftide/0.34/swiftide/integrations/lancedb/struct.LanceDB.html), [`LanceDBBuilder`](https://docs.rs/swiftide/0.34/swiftide/integrations/lancedb/struct.LanceDBBuilder.html)
- [`Qdrant`](https://docs.rs/swiftide/0.34/swiftide/integrations/qdrant/struct.Qdrant.html)
- [`MetadataQAText`](https://docs.rs/swiftide/0.34/swiftide/indexing/transformers/struct.MetadataQAText.html), [`Embed`](https://docs.rs/swiftide/0.34/swiftide/indexing/transformers/struct.Embed.html), [`ChunkCode`](https://docs.rs/swiftide/0.34/swiftide/indexing/transformers/struct.ChunkCode.html)
- [indexing `Pipeline`](https://docs.rs/swiftide/0.34/swiftide/indexing/struct.Pipeline.html), [query `Pipeline`](https://docs.rs/swiftide/0.34/swiftide/query/struct.Pipeline.html)
- [`SimilaritySingleEmbedding`](https://docs.rs/swiftide/0.34/swiftide/query/search_strategies/struct.SimilaritySingleEmbedding.html), [`CustomStrategy`](https://docs.rs/swiftide/0.34/swiftide/query/search_strategies/struct.CustomStrategy.html), [`Summary`](https://docs.rs/swiftide/0.34/swiftide/query/response_transformers/struct.Summary.html)
- [OpenAI-compatible providers](/integrations/openai-compatible/), [FastEmbed](/integrations/fastembed/), [Qdrant](/integrations/qdrant/), [PgVector](/integrations/pgvector/), [LanceDB](/integrations/lancedb/), [Storage](/indexing/storage/)

## Next

[Feature flags](/reference/feature-flags/)