The `generate` submodule uses a large language model to generate text. The submodule is dependent on the speed of the language model and any API that serves the language model.
Increase the timeout values to allow the client to wait longer for the language model to respond. ::: --- ### Weaviate/Concepts/Cluster (docs/weaviate/concepts/cluster.md) --- title: Horizontal Scaling sidebar_position: 30 description: "Multi-node cluster architecture and horizontal scaling strategies for high-availability Weaviate deployments." image: og/docs/concepts.jpg # tags: ['architecture', 'horizontal scaling', 'cluster', 'replication', 'sharding'] --- Weaviate can be scaled horizontally by being run on a set of multiple nodes in a cluster. This section lays out various ways in which Weaviate can be scaled, as well as factors to consider while scaling, and Weaviate's architecture in relation to horizontal scaling. ## Basic concepts ### Shards A collection in Weaviate comprises of one or more "shards", which are the basic units of data storage and retrieval. A shard will contain its own vector index, inverted indexes, and object store. Each shard can be hosted on a different node, allowing for distributed data storage and processing. The number of unique shards in a single-tenant collection can only be set at collection creation time. In most cases, letting Weaviate manage the number of shards is sufficient. But in some cases, you may want to manually configure the number of shards for performance or data distribution reasons. In a multi-tenant collection, each tenant consists of one shard. This means that the number of unique shards in a multi-tenant collection is equal to the number of tenants. ### Replicas Depending on the setup, each shard can have one or more "replicas", to be hosted on different nodes. This is referred to as a "high availability" setup, where the same data is available on multiple nodes. This allows for better read throughput and fault tolerance. You can set the desired number of replicas, also called a replication factor, in Weaviate. This can be set a global cluster-level default using the [`REPLICATION_MINIMUM_FACTOR` environment variable](/docs/deploy/configuration/env-vars/index.md). It can also be set [per collection](/docs/weaviate/manage-collections/multi-node-setup.mdx#replication-settings), which will override the global default. ## Motivation to scale Weaviate Generally there are (at least) three distinct motivations to scale out horizontally which all will lead to different setups. ### Motivation 1: Maximum Dataset Size Due to the [memory footprint of an HNSW graph](./resources.md#the-role-of-memory) it may be desirable to spread a dataset across multiple servers ("nodes"). In such a setup, a single collection may be split into shards and shards are spread across nodes. The disk-based [HFresh index](./indexing/vector-index.md#hfresh-index) can also reduce the need to shard purely for memory reasons. Weaviate does the required orchestration at import and query time fully automatically. See [Sharding vs Replication](#sharding-vs-replication) below for trade-offs involved when running multiple shards. **Solution: Sharding across multiple nodes in a cluster** :::note The ability to shard across a cluster was added in Weaviate `v1.8.0`. ::: ### Motivation 2: Higher Query Throughput When you receive more queries than a single Weaviate node can handle, it is desirable to add more Weaviate nodes which can help in responding to your users' queries. Instead of sharding across multiple nodes, you can replicate (the same data) across multiple nodes. This process also happens fully automatically and you only need to specify the desired replication factor. Sharding and replication can also be combined. **Solution: Replicate your classes across multiple nodes in a cluster** ### Motivation 3: High Availability When serving critical loads with Weaviate, it may be desirable to be able to keep serving queries even if a node fails completely. Such a failure could be either due to a software or OS-level crash or even a hardware issue. Other than unexpected crashes, a highly available setup can also tolerate zero-downtime updates and other maintenance tasks. To run a highly available setup, classes must be replicated among multiple nodes. **Solution: Replicate your classes across multiple nodes in a cluster** ## Sharding vs Replication The motivation sections above outline when it is desirable to shard your classes across multiple nodes and when it is desirable to replicate your classes - or both. This section highlights the implications of a sharded and/or replicated setup. :::note All of the scenarios below assume that - as sharding or replication is increased - the cluster size is adapted accordingly. If the number of shards or the replication factor is lower than the number of nodes in the cluster, the advantages no longer apply.* ::: ### Advantages when increasing sharding * Run larger datasets * Speed up imports. To use multiple CPUs efficiently, create multiple shards for your collection. For the fastest imports, create multiple shards even on a single node. ### Disadvantages when increasing sharding * Query throughput does not improve when adding more sharded nodes ### Advantages when increasing replication * System becomes highly available * Increased replication leads to near-linearly increased query throughput ### Disadvantages when increasing replication * Import speed does not improve when adding more replicated nodes ### Sharding Keys ("Partitioning Keys") Weaviate uses specific characteristics of an object to decide which shard it belongs to. As of `v1.8.0`, a sharding key is always the object's UUID. The sharding algorithm is a 64bit Murmur-3 hash. Other properties and other algorithms for sharding may be added in the future. Note that in a multi-tenant collection, each tenant consists of one shard. ## Shard replica movement import ReplicaMovement from '/_includes/feature-notes/replica-movement.mdx';
If you are a Kubernetes user, see the [`1.25 migration guide`](/deploy/migration/weaviate-1-25.md) before you upgrade. To upgrade, you have to delete your existing StatefulSet. * Adding a node to an existing cluster does not by itself change the ownership of existing shards. To rebalance data across nodes, or to drain a node before you remove it, move its shard replicas with [replica movement](/deploy/configuration/replica-movement.mdx) as described in [Shard replica movement](#shard-replica-movement) above.
Behavior before `v1.25` and `v1.32`
Prior to `v1.25`, schema changes were broadcast across the cluster with a form of two-phase transaction that could not tolerate node failures during the lifetime of the transaction. Raft replaced this mechanism. See [Replication architecture: Cluster metadata](/weaviate/concepts/replication-architecture/consistency.md#cluster-metadata) for the comparison. Prior to `v1.32`, shard replicas could not be moved between nodes, so a node that still held data could not be removed from a cluster. [Replica movement](/deploy/configuration/replica-movement.mdx) removes that limitation.In the client-side approach, **the Weaviate client library is responsible for grouping data into batches**. You define the batching mechanism and parameters, such as the size of each batch (e.g., 100 objects) using the appropriate [client library method](../manage-objects/import.mdx). The client then sends chunks to the Weaviate server accordingly. This method gives you direct control over the import process through manual tuning of parameters like the batch size and number of concurrent requests. However, the tuning must be done "blindly" on the client side, without knowledge of the server status. - **Server-side batching**
Server-side batching, or **automatic mode**, is a more robust and the recommended approach. Here, the client sends data at a rate based on **feedback from the Weaviate server**. Using an internal queue and a dynamic _[backpressure](https://en.wikipedia.org/wiki/Backpressure_routing)_ mechanism, the server tells the client how much data to send next based on its current workload. This simplifies your client code, eliminates the need for manual tuning, and results in a more efficient and resilient data import process. :::tip For **code examples**, check out the [How-to: Batch import](../manage-objects/import.mdx) guide. Server-side batch imports are supported by the Python, TypeScript, Java, and C# clients. The Go client does not yet support them; use client-side batching instead. ::: --- ## Server-side batching import SsbStatus from '/_includes/feature-notes/ssb-status.mdx';
For example, data may not be immediately available after reactivating an offloaded tenant. Similarly, data may not be immediately unavailable after offloading a tenant. This is because the [tenant states are eventually consistent](../concepts/replication-architecture/consistency.md#tenant-states-and-data-objects), and the change must be propagated to all nodes in the cluster. ::: #### Offloaded tenants import OffloadingLimitation from '/_includes/offloading-limitation.mdx';
For more information, see [Product Quantization](/weaviate/concepts/vector-quantization).
To configure PQ, see [Compression](../configuration/compression/pq-compression.md). - **Reduce the dimensionality of your vectors.** The most effective approach to reducing memory size, is to reduce the number of dimensions per vector. If you have high dimension vectors, consider using a model that uses fewer dimensions. For example, a model that has 384 dimensions uses far less memory than a model with 1536 dimensions. - **Reduce the number of [`maxConnections`](../config-refs/indexing/vector-index.mdx) in your HNSW index settings**. Each object in memory has up to `maxConnections` connections. Each of those connections uses 8-10B of memory. To reduce the overall memory footprint, reduce `maxConnections`. Reducing `maxConnections` adversely affects HNSW recall performance. To mitigate this effect, increase one or both of the `efConstruction` and `ef` parameters. - Increasing `efConstruction` increases import time without affecting query times. - Increasing `ef` increases query times without affecting import times. - **Use a vector cache that is smaller than the total amount of your vectors (not recommended)**. This strategy is described under [Vector Cache](#vector-cache) below. It has a significant performance impact, and is only recommended in specific, limited situations. ## Vector Cache For optimal search and import performance, all previously imported vectors need to be held in memory. The size of the vector cache is specified by the [`vectorCacheMaxObjects`](../config-refs/indexing/vector-index.mdx) parameter in the collection definition. By default this limit is set to one trillion (`1e12`) objects when you create a new collection. You can reduce the size of `vectorCacheMaxObjects`, but a disk lookup for a vector is orders of magnitudes slower than memory lookup. Only reduce the size of `vectorCacheMaxObjects` with care and as a last resort. Generally we recommend that: - During import set `vectorCacheMaxObjects` high enough that all vectors can be held in memory. Each import requires multiple searches. Import performance drop drastically when there isn't enough memory to hold all of the vectors in the cache. - After import, when your workload is mostly querying, experiment with vector cache limits that are less than your total dataset size. Vectors that aren't currently in cache are added to the cache if there is still room. If the cache fills, Weaviate drops the whole cache. All future vectors have to be read from disk for the first time. Then, subsequent queries runs against the cache, until it fills again and the procedure repeats. Note that the cache can be a very valuable tool if you have a large dataset, and a large percentage of users only query a specific subset of vectors. In this case you might be able to serve the largest user group from cache while requiring disk lookups for "irregular" queries. ### When to add more Memory to your Weaviate machine or cluster Consider adding more memory if: - You want to import a larger dataset (more common). - Exact lookups are disk-bound and more memory will improve page-caching (less common). ## The role of GPUs in Weaviate Weaviate Database itself does not make use of GPUs. However, some of the models that Weaviate includes as modules are meant to run with GPUs, for example `text2vec-transformers`, `qna-transformers`, and `ner-transformers`. These modules run in isolated containers, so you can run the module containers on GPU-accelerated hardware while running Weaviate Database on low-cost CPU-only hardware. ## Disks: SSD vs Spinning Disk Weaviate is optimized to work with Solid-State Disks (SSDs). However, spinning hard-disks can also be used with some performance penalties. ## File system For optimal performance and reliability, avoid using `NFS` or similar file systems for the Weaviate persistent volume ([`PERSISTENCE_DATA_PATH`](/deploy/configuration/env-vars/index.md)). Instead, use file systems like `Ext4` or `XFS` in combination with SAN storage (e.g. `EBS`) to ensure the best performance. ## Questions and feedback import DocsFeedback from '/_includes/docs-feedback.mdx';
Periodic snapshot conditions and memory requirements
Periodic snapshot creation is governed by three variables, and **all** of the following conditions must be met before Weaviate creates a snapshot: - `PERSISTENCE_HNSW_SNAPSHOT_INTERVAL_SECONDS` — the minimum time since the previous snapshot has elapsed (default `21600` seconds, or six hours). - `PERSISTENCE_HNSW_SNAPSHOT_MIN_DELTA_COMMITLOGS_NUMBER` — enough new commit log files have been created since the last snapshot (default `1`). - `PERSISTENCE_HNSW_SNAPSHOT_MIN_DELTA_COMMITLOGS_SIZE_PERCENTAGE` — the new commit logs are large enough, measured as a percentage of the previous snapshot's size (default `5`). This condition does not apply to the first snapshot, when there is no previous snapshot to measure against. Meeting these conditions makes a snapshot eligible rather than guaranteed. The background process that condenses and combines commit log files is also the one that writes the snapshot, so a snapshot can be created on a later pass than the one where the conditions are first met. In these versions, before creating a new snapshot, Weaviate loads the previous snapshot and the commit log difference into memory, so the node needs enough memory to accommodate both.You might be also interested in our blog post [How to Reduce Memory Requirements by up to 90%+ using Product Quantization](https://weaviate.io/blog/pq-rescoring). ::: ## Binary quantization **Binary quantization (BQ)** is a quantization technique that converts each vector embedding to a binary representation. The binary representation is much smaller than the original vector embedding. Usually each vector dimension requires 32 bits, but the binary representation only requires 1 bit, representing a 32x reduction in storage requirements. This works to speed up vector search by reducing the amount of data that needs to be read from disk, and simplifying the distance calculation. The tradeoff is that BQ is lossy. The binary representation by nature omits a significant amount of information, and as a result the distance calculation is not as accurate as the original vector embedding. Some vectorizers work better with BQ than others. Anecdotally, we have seen encouraging recall with Cohere's V3 models (e.g. `embed-multilingual-v3.0` or `embed-english-v3.0`), and OpenAI's `ada-002` model with BQ enabled. We advise you to test BQ with your own data and preferred vectorizer to determine if it is suitable for your use case. Note that when BQ is enabled, a vector cache can be used to improve query performance. The vector cache is used to speed up queries by reducing the number of disk reads for the quantized vector embeddings. Note that it must be balanced with memory usage considerations, with each vector taking up `n_dimensions` bits. ## Scalar quantization **Scalar quantization (SQ)** The dimensions in a vector embedding are usually represented as 32 bit floats. SQ transforms the float representation to an 8 bit integer. This is a 4x reduction in size. SQ compression, like BQ, is a lossy compression technique. However, SQ has a much greater range. The SQ algorithm analyzes your data and distributes the dimension values into 256 buckets (8 bits). SQ compressed vectors are more accurate than BQ compressed vectors. They are also significantly smaller than uncompressed vectors. The bucket boundaries are derived by determining the minimum and maximum values in a training set, and uniformly distributing the values between the minimum and maximum into 256 buckets. The 8 bit integer is then used to represent the bucket number. The size of the training set is configurable. The default is 100,000 objects per shard. When SQ is enabled, Weaviate boosts recall by over-fetching compressed results. After Weaviate retrieves the compressed results, it compares the original, uncompressed vectors that correspond to the compressed result against the query. The second search is very fast because it only searches a small number of vectors rather than the whole database. ## Rotational quantization **Rotational quantization (RQ)** provides significant compression while maintaining high recall. Unlike SQ, RQ requires no training phase and can be enabled immediately at index creation. RQ is available in: **8-bit** and **1-bit** variants. ### 8-bit RQ
Performance improvements added in Oct 2024
In Weaviate versions `v1.24.26`, `v1.25.20`, `v1.26.6` and `v1.27.0`, we introduced performance improvements and bugfixes for the BM25F scoring algorithm: - The BM25 segment merging algorithm was made faster - Improved WAND algorithm to remove exhausted terms from score computation and only do a full sort when necessary - Solved a bug in BM25F multi-prop search that could lead to not summing all the query term score for all segments - The BM25 scores are now calculated concurrently for multiple segments As always, we recommend upgrading to the latest version of Weaviate to benefit from improvements such as these.Example collection configuration without inverted indexes - JSON object
An example of a complete collection object without inverted indexes: ``` /* Detailed source-code truncated for AI context efficiency. */ ```Starting in `v1.28`, the async indexing feature has been expanded to include single object imports, deletions, and updates. Additionally, the in-memory queue has been replaced with a persistent, on-disk queue. This change allows for more robust handling of indexing operations, and improves performance though reduction of lock contention and memory usage.
The use of an on-disk queue may result in a slight increase in disk usage, however this is expected to be a small percentage of the total disk usage. ::: ## Vector indexing FAQ ### Can I use vector indexing with vector quantization? Yes, you can read more about it in [vector quantization (compression)](../vector-quantization.md). ### Which vector index is right for me? Here's a quick guide to choosing the right index: - **Flat index**: Best for SaaS products where each end user (tenant) has their own isolated, small dataset. Fast for small collections with a known size for minimal memory overhead. - **HNSW index**: Best for large collections requiring high query throughput and low latency. Requires more memory but provides excellent search performance. - **Dynamic index**: Best for collections that start small but may grow significantly over time. Automatically transitions from flat to HNSW as data scales. - **HFresh index**: Best when memory efficiency is the priority, especially with high-dimensional vectors. Suitable from small collections up to very large ones. #### Comparison between index types | Feature | Flat | HNSW | HFresh | | ----------------------------- | -------------------------------- | --------------------------- | --------------------------------------------------- | | Memory usage | Very low | High | Low | | Search speed (small datasets) | Fast | Very fast | Moderate | | Search speed (large datasets) | Slow | Very fast | Fast | | Disk usage | Low | Moderate | Moderate to high | | Maintenance | None | Costlier as the graph grows | Self-balancing in the background, no full rebuilds | | Best for | Small collections, multi-tenancy | Large collections, high QPS | Memory-constrained deployments, any size (disk-backed) | Note that the vector index type parameter only specifies how the vectors of data objects are _indexed_. The index is used for data retrieval and similarity search. The `vectorizer` parameter determines how the data vectors are created (which numbers the vectors contain). `vectorizer` specifies a [module](/weaviate/modules/index.md), such as `text2vec-contextionary`, that Weaviate uses to create the vectors. (You can also set to `vectorizer` to `none` if you want to import your own vectors). To learn more about configuring the collection, see [this how-to page](../../manage-collections/vector-config.mdx). ### Which distance metrics can I use with vector indexing? All of [the distance metrics](/weaviate/config-refs/distances.md), such as cosine similarity, can be used with most vector index types. The HFresh index only supports `cosine` and `l2-squared` distance metrics. ### How to configure the vector index type in Weaviate? The index type can be specified per data collection via the [collection definition](../../manage-collections/vector-config.mdx#set-vector-index-type) settings, according to available [vector index settings](../../config-refs/indexing/vector-index.mdx). ### When to skip indexing There are situations where it doesn't make sense to vectorize a collection. For example, if the collection consists solely of references between two other collections, or if the collection contains mostly duplicate elements. Importing duplicate vectors into HNSW is very expensive. The import algorithm checks early on if a candidate vector's distance is greater than the worst candidate's distance. When there are lots of duplicate vectors, this early exit condition is never met so each import or query results in an exhaustive search. To avoid indexing a collection, set `"skip"` to `"true"`. By default, collections are indexed. ### What ANN algorithms exist? There are different ANN algorithms, you can find a nice overview of them on this website. ### Are there indicative benchmarks for Weaviate's ANN performance? The [ANN benchmark page](/weaviate/benchmarks/ann.md) contains a wide variety of vector search use cases and relative benchmarks. This page is ideal for finding a dataset similar to yours and learning what the most optimal settings are. ## Further resources :::info Related pages - [Concepts: Vector quantization (compression)](../vector-quantization.md) - [Configuration: Vector index](../../config-refs/indexing/vector-index.mdx) - [Configuration: Schema (Configure semantic indexing)](../../config-refs/indexing/vector-index.mdx#configure-semantic-indexing) ::: ## Questions and feedback import DocsFeedback from '/\_includes/docs-feedback.mdx';





Pre-v1.25 cluster metadata consensus algorithm
Prior to using Raft, a cluster metadata update was done via a [Distributed Transaction](https://en.wikipedia.org/wiki/Distributed_transaction) algorithm. This is a set of operations that is done across databases on different nodes in the distributed network. Weaviate used a [two-phase commit (2PC)](https://en.wikipedia.org/wiki/Two-phase_commit_protocol) protocol, which replicates the cluster metadata updates in a short period of time (milliseconds).
A clean (without fails) execution has two phases:
1. The commit-request phase (or voting phase), in which a coordinator node asks each node whether they are able to receive and process the update.
2. The commit phase, in which the coordinator commits the changes to the nodes.




In a single datacenter with a replication factor of 3 and a read consistency level of ONE, the coordinator node will wait for a response from one replica node.

In a single datacenter with a replication factor of 3 and a read consistency level of `QUORUM`, the coordinator node will wait for n / 2 + 1 = 3 / 2 + 1 = 2 replicas nodes to return a response.

In a single datacenter with a replication factor of 3 and a read consistency level of `ALL`, the coordinator node will wait for all 3 replicas nodes to return a response.

As of `v1.36`, multi-tenant collections default to a hash tree height of `10` (~16KB per tenant per node), significantly reducing memory overhead compared to the single-tenant default of `16`.
To further reduce memory consumption, reduce the hash tree height. Keep in mind that this will result in slower hashing and potentially slower replication. ::: Use the following formulas and examples as a quick reference: ##### Memory calculation - **Total number of nodes in the hash tree:** For a hash tree with height `H`, the total number of nodes is: ``` Number of hash tree nodes = 2^(H+1) - 1 ≈ 2^(H+1) ``` - **Total memory required (per shard/tenant on each node):** Each hash tree node uses approximately **16 bytes** of memory. ``` Memory Required ≈ 2^(H+1) * 16 bytes ``` ##### Examples - Hash tree with height `16`: - `Total hash tree nodes ≈ 2^(16+1) = 131,072` - `Memory required ≈ 131072 * 16 bytes ≈ 2,097,152 bytes (~2 MB)` - Hash tree with height `20`: - `Total hash tree nodes ≈ 2^(20+1) = 2,097,152` - `Memory required ≈ 2,097,152 * 16 bytes ≈ 33,554,432 bytes (~33 MB)` ##### Performance Consideration: Number of Leaves The objects in a shard (e.g. tenant) are distributed among the leaves of the hash tree. A larger hash tree means less data for each leaf to hash, leading to faster comparisons and faster replication. - **Number of Leaves in the hash tree:** ``` Number of leaves = 2^H ``` ##### Examples - Hash tree with height `16`: - `Number of Leaves = 2^16 = 65,536` - Hash tree with height `20`: - `Number of Leaves = 2^20 = 1,048,576` :::note Default settings The default hash tree height is `16` for single-tenant collections and `10` for multi-tenant collections. These defaults balance memory consumption with replication performance. As of `v1.36`, these parameters can be configured per-collection via the [`asyncConfig`](/weaviate/config-refs/collections#async-config) object in `replicationConfig`. Worker concurrency is no longer a per-collection setting and as of `v1.38` the cluster shares a single async replication worker pool sized by [`ASYNC_REPLICATION_SCHEDULER_WORKERS`](/deploy/configuration/env-vars/index.md#async-replication). ::: ### Deletion resolution strategies When an object is present on some replicas but not others, this can be because a creation has not yet been propagated to all replicas, or because a deletion has not yet been propagated to all replicas. It is important to distinguish between these two cases. Deletion resolution works alongside async replication and repair-on-read to ensure consistent handling of deleted objects across the cluster. For each collection, [you can set one of the following](../../manage-collections/multi-node-setup.mdx#replication-settings) deletion resolution strategies: - `NoAutomatedResolution` - `DeleteOnConflict` - `TimeBasedResolution` Deletion resolution strategies are mutable. [Read more about how to update collection definitions](../../manage-collections/collection-operations.mdx#update-a-collection-definition). #### `NoAutomatedResolution` In this mode, Weaviate does not treat deletion conflicts as a special case. If an object is present on some replicas but not others, Weaviate may potentially restore the object on the replicas where it is missing. #### `DeleteOnConflict` A deletion conflict in `deleteOnConflict` is always resolved by deleting the object on all replicas. To do so, Weaviate updates an object as a deleted object on a replica upon receiving a deletion request, rather than removing all traces of the object. #### `TimeBasedResolution` This is the default setting from `v1.36` onwards. A deletion conflict in `timeBasedResolution` is resolved based on the timestamp of the deletion request, in comparison to any subsequent updates to the object such as a creation or an update. If the deletion request has a timestamp that is later than the timestamp of any subsequent updates, the object is deleted on all replicas. If the deletion request has a timestamp that is earlier than the timestamp of any subsequent updates, the later updates are applied to all replicas. For example: - If an object is deleted at timestamp 100 and then recreated at timestamp 110, the recreation wins - If an object is deleted at timestamp 100 and then recreated at timestamp 90, the deletion wins #### Choosing a strategy - Use `NoAutomatedResolution` when you want maximum control and handle conflicts manually - Use `DeleteOnConflict` when you want to ensure deletions are always honored - Use `TimeBasedResolution` when you want the most recent operation to take precedence ### Repair-on-read If your read consistency is set to `All` or `Quorum`, the read coordinator will receive responses from multiple replicas. If these responses differ, the coordinator can attempt to repair the inconsistency, as shown in the examples below. This process is called "repair-on-read", or "read repairs". | Problem | Action | | :- | :- | | Object never existed on some replicas. | Propagate the object to the missing replicas. | | Object is out of date. | Update the object on stale replicas. | | Object was deleted on some replicas. | Returns an error. Deletion may have failed, or the object may have been partially recreated. When using the `TimeBasedResolution` deletion strategy, the most recent version wins based on timestamps. | The read repair process also depends on the read and write consistency levels used. | Write consistency level | Read consistency level | Action | | :- | :- | | `ONE` | `ALL` | Weaviate has to verify all nodes to guarantee repair. | | `QUORUM` | `QUORUM` or `ALL` | Weaviate attempts to fix the sync issues. | | `ALL` | - | This situation should not occur. The write should have failed. | Repairs only happen on read, so they do not create a lot of background overhead. While nodes are in an inconsistent state, read operations with consistency level of `ONE` may return stale data. ## Replica movement import ReplicaMovement from '/_includes/feature-notes/replica-movement.mdx';
What is the cluster metadata?
Weaviate cluster `metadata` includes collection definitions and tenant activity statuses.
All cluster metadata is always replicated across all nodes, regardless of the replication factor.
Note that this is different to object metadata, such as the object creation time. Object metadata is stored alongside the object data according to the specified replication factor.


With a distributed (replicated) database structure, service will not be interrupted if one server node goes down. The database can still be available, read queries will just be (unnoticeably) redirected to an available node. 2. **Increased (read) throughput**
Adding extra server nodes to your database setup means that the throughput scales with it. The more server nodes, the more users (read operations) the system will be able to handle. When reading with consistency level of `ONE`, then scaling the replication factor (i.e. how many database server nodes) increases the throughput linearly. 3. **Zero downtime upgrades**
Without replication, there is a window of downtime when you update a Weaviate instance. This is because the single node needs to stop, update and restart before it's ready to serve again. With replication, upgrades are done using a rolling update, in which at most one node is unavailable at any point in time while the other nodes can still serve traffic. 4. **Regional proximity**
When users are located in different regional areas (e.g. Iceland and Australia as extreme examples), you cannot ensure low latency for all users due to the physical distance between the database server and the users. With a distributed database, you can place nodes in different local regions to decrease this latency. This depends on the Multi-Datacenter feature of replication. ## Replication vs. Sharding Replication is not the same as [sharding](../cluster.md). Sharding refers to horizontal scaling, and was introduced to Weaviate in v1.8. * **Replication** copies the data to different server nodes. For Weaviate, this increases data availability and provides redundancy in case a single node fails. Query throughput can be improved with replication. * **Sharding** handles horizontal scaling across servers by dividing the data and sending the pieces of data (shards) to multiple replica sets. The data is thus divided, and all shards together form the entire set of data. You can use sharding with Weaviate to run larger datasets and speed up imports.

Metadata replication pre-v1.25
Prior to Weaviate `v1.25`, each cluster metadata change was recorded via a distributed transaction with a two-phase commit.
This is a synchronous process, which means that the cluster metadata change is only committed when all nodes have acknowledged the change. In this architecture, any node downtime would temporarily prevent metadata operations. Additionally, only one such operation could be processed at a time. If you are using Weaviate `v1.24` or earlier, you can [upgrade to `v1.25`](/deploy/migration/weaviate-1-25.md) to benefit from the Raft consensus algorithm for cluster metadata changes.



All users will have relatively high latency, since data needs to travel between Iceland and India, and Australia and India.

Users from Iceland have very low latency while users from Australia experience relatively high latency since data needs to travel a long distance. Another option arises when you have the option to replicate your data cluster to two different geographical locations. This is called Multi-Datacenter (Multi-DC) replication. 3. Option 3 - Multi-DC replication with server clusters in both Iceland and Australia.
Users from Iceland and Australia now both experience low latency, because each user group is served from local clusters.


| Search Type | (id): score | (id): score | (id): score | (id): score | (id): score |
|---|---|---|---|---|---|
| Keyword | (1): 5 | (0): 2.6 | (2): 2.3 | (4): 0.2 | (3): 0.09 |
| Vector | (2): 0.6 | (4): 0.598 | (0): 0.596 | (1): 0.594 | (3): 0.009 |
| Search Type | (id): score | (id): score | (id): score | (id): score | (id): score |
|---|---|---|---|---|---|
| Keyword | (1): 0.0154 | (0): 0.0160 | (2): 0.0161 | (4): 0.0167 | (3): 0.0166 |
| Vector | (2): 0.016502 | (4): 0.016502 | (0): 0.016503 | (1): 0.016503 | (3): 0.016666 |
| Search Type | (id): score | (id): score | (id): score | (id): score | (id): score |
|---|---|---|---|---|---|
| Keyword | (1): 1.0 | (0): 0.511 | (2): 0.450 | (4): 0.022 | (3): 0.0 |
| Vector | (2): 1.0 | (4): 0.996 | (0): 0.993 | (1): 0.986 | (3): 0.0 |
[Search](#retrieval-search): Find the most relevant entries, using one of [keyword](#keyword-search), [vector](#vector-search) or [hybrid](#hybrid-search) search types
| Required | | 2. [Rerank](#rerank) | Reorder results using a different (e.g. more complex) model | Optional | | 3. [Retrieval augmented generation](#retrieval-augmented-generation-rag) | Send retrieved data and a prompt to a generative AI model. Also called retrieval augmented generation, or RAG. | Optional |
(BM25F)"] Vec["Vector Search
(Embeddings)"] Hyb["Hybrid Search
(Combined)"] Rerank["Rerank
(Optional)"] RAG["RAG
(Optional)"] Results[/"📊 Results"/] %% Main flow grouping subgraph retrieval ["Retrieval"] direction LR Filter search end subgraph search ["Search"] direction LR Key Vec Hyb end %% Connections Query --> retrieval Filter --> search retrieval --> Results retrieval --> Rerank Rerank --> RAG RAG --> Results %% Node styles style Query fill:#ffffff,stroke:#B9C8DF,color:#130C49 style Filter fill:#ffffff,stroke:#B9C8DF,color:#130C49 style Key fill:#ffffff,stroke:#B9C8DF,color:#130C49 style Vec fill:#ffffff,stroke:#B9C8DF,color:#130C49 style Hyb fill:#ffffff,stroke:#B9C8DF,color:#130C49 style Rerank fill:#ffffff,stroke:#B9C8DF,color:#130C49 style RAG fill:#ffffff,stroke:#B9C8DF,color:#130C49 style Results fill:#ffffff,stroke:#B9C8DF,color:#130C49 %% Subgraph styles style retrieval fill:#ffffff,stroke:#130C49,stroke-width:2px,color:#130C49 style search fill:#ffffff,stroke:#7AD6EB,stroke-width:2px,color:#130C49 ```
This ensures that search results overlap with the filter criteria to make sure that the right objects are retrieved. :::
Filter: Example
In a dataset such as `animal_objs` below, you could filter by a specific color to retrieve only objects that match this criterion.```json [ {"description": "brown dog"}, {"description": "small domestic black cat"}, {"description": "orange cheetah"}, {"description": "black bear"}, {"description": "large white seagull"}, {"description": "yellow canary"}, ] ``` A filter for `"black"` in the `"description"` would return only the objects with a black color. - `{'description': 'black bear'}` - `{'description': 'small domestic black cat'}`
In Weaviate, the order of these results are based on the UUIDs of the objects, if no other ranking is applied. As a result, the order of these objects would be essentially random, as the filter only passes or blocks objects based on the criteria.
Unlike filters, Search results will be **ranked** based on their relevance to the query. ::: Let's review these search types in more detail. #### Keyword Search Keyword search ranks results based on keyword match "scores". These scores are based on how often tokens in the query appear in each data object. These metrics are combined using the BM25 algorithm to produce a score.
Keyword Search: Example
In a dataset such as `animal_objs` below, you could perform keyword searches by a specific color to retrieve how significant they are.```json [ {"description": "brown dog"}, {"description": "small domestic black cat"}, {"description": "orange cheetah"}, {"description": "black bear"}, {"description": "large white seagull"}, {"description": "yellow canary"}, ] ``` A keyword search for `"black"` would return only the objects with a black color, as before. But here, the results are ranked based on the BM25 algorithm. 1. `{'description': 'black bear'}` 1. `{'description': 'small domestic black cat'}`
Here `{"description": "black bear"}` has a higher score than `{"description": "small domestic black cat"}` because the term "black" is a larger proportion of the text.
When to use keyword search
Keyword search is great where occurrences of certain words strongly indicate the text's relevance. For example: - Find medical, or legal literature containing specific terms. - Search for technical documentation or API references where exact terminology is crucial. - Locating specific product names or SKUs in an e-commerce database. - Finding code snippets or error messages in a programming context.Vector Search: Example
In a dataset such as `animal_objs` below, you could perform vector searches with words that are semantically similar to retrieve how significant they are.```json [ {"description": "brown dog"}, {"description": "small domestic black cat"}, {"description": "orange cheetah"}, {"description": "black bear"}, {"description": "large white seagull"}, {"description": "yellow canary"}, ] ``` A search for `"black"` here would work similarly to the keyword search. But, a vector search would also produce similar results for queries such as `"very dark"`, `"noir"`, or `"ebony"`.
This is because vector search is based on the extracted meaning of the text, rather than the exact words used. The vector embeddings capture the semantic meaning of the text, allowing for more flexible search queries.
As a result, the top 3 results are: 1. `{'description': 'black bear'}` 1. `{'description': 'small domestic black cat'}` 1. `{'description': 'orange cheetah'}`
When to use vector search
Vector search is best suited where a human-like concept of "similarity" can be a good measure of result quality. For example: - Semantic text search: Locating documents with similar meanings, even if they use different words. - Multi-lingual search: Finding relevant content across different languages. - Image similarity search: Finding visually similar images in a large database.Hybrid Search: Example
In a dataset such as `animal_objs` below, you could perform hybrid searches to robustly find relevant objects, taking a best-of-both-worlds approach.```json [ {"description": "brown dog"}, {"description": "small domestic black cat"}, {"description": "orange cheetah"}, {"description": "black bear"}, {"description": "large white seagull"}, {"description": "yellow canary"}, ] ``` A hybrid search for `"black canine"` would match well the objects with `"black"` in the description due to its match with the keyword search. So it would surface `{"description": "small domestic black cat"}` and `{"description": "black bear"}` towards the top.
But it would also boost objects with `"dog"` in the description, such as `{"description": "brown dog"}`. This is because the vector search would find a high similarity between the query and the word `"dog"`, even though the word `"dog"` is not in the query.
As a result, the top 3 results are: 1. `{"description": "black bear"}` 1. `{"description": "small domestic black cat"}` 1. `{"description": "brown dog"}`
When to use hybrid search
Hybrid search is great as a starting point, as it is a robust search type. It tends to boost results that perform well in at least one of the two searches. For example: - Academic paper search: Finding research papers based on both keyword relevance and semantic similarity to the query. - Job matching: Identifying suitable candidates by combining keyword matching of skills with semantic understanding of job descriptions. - Recipe search: Locating recipes that match specific ingredients (keywords) while also considering overall dish similarity (vector). - Customer support: Finding relevant support tickets or documentation using both exact term matching and conceptual similarity.When to use reranking
Reranking is useful when you want to improve the quality of search results by applying a more complex model to a smaller subset of results. This may be necessary when the object set is very subtle or specific, such as in particular industries or use cases. For example, searches in legal, medical, or scientific literature may require a more nuanced understanding of the text. Reranking can help to ensure that the most relevant results are surfaced.RAG: Example
In a dataset such as `animal_objs` below, you could combine retrieval augmented generation with any other search method to find relevant objects and then transform it.```json [ {"description": "brown dog"}, {"description": "small domestic black cat"}, {"description": "orange cheetah"}, {"description": "black bear"}, {"description": "large white seagull"}, {"description": "yellow canary"}, ] ``` Take an example of a keyword search for `"black"`, and a RAG request `"What do these animal descriptions have in common?"`.
The search results consist of `{"description": "black bear"}` and `{"description": "small domestic black cat"}` as you saw before. Then, the generative model would produce an output based on our query. In one example, it produced:
```text "What these descriptions have in common are: * **Color:** Both describe animals with a **black** color. * **Species:** One is an **animal**, the other describes a **breed** of animal (domesticated)." ```
In Weaviate, they are used interchangeably, as the BM25F algorithm is used to calculate the scores for keyword searches. Here we will refer to it generally as BM25. ::: A BM25 score is calculated based on the frequency of the query tokens in the object properties, as well as the length of the object properties and the query. When an input string such as `"A red Nike shoe"` is provided as the query, Weaviate will: 1. [Tokenize](#tokenization) the input (e.g. to `["a", "red", "nike", "shoe"]`) 2. Remove any [stopwords](#stopwords) (e.g. remove `a`, to produce `["red", "nike", "shoe"]`) 3. Determine the BM25 scores against [selected properties](#selected-properties) of the database objects, based on the [BM25 parameters](#bm25-parameters) and any [property boosting](#property-boosting). 4. Return the objects with the highest BM25 scores as the search results ### Tokenization Tokenization for keyword searches refers to how each source text is split up into individual "tokens" to be compared and matched. The default tokenization method is `word`. Other tokenization methods such as `whitespace`, `lowercase`, and `field` are available, as well as specialized ones such as `gse` or `kagome_kr` for other languages ([more details](../../config-refs/collections.mdx#tokenization)). Set the tokenization option [in the inverted index configuration](../../search/bm25.md#set-tokenization) for a collection. :::info Tokenization in different contexts The term "tokenization" is used in other contexts such as vectorization, or language generation. Note that each of these typically use different tokenizers to meet different requirements. This results in different sets of tokens, even from the same input text. ::: Text properties can also enable **accent folding** via `textAnalyzer.asciiFold`, which normalizes accented characters before tokens enter the inverted index. A document containing "Café Crème" becomes searchable as "cafe creme" (and vice versa), and the same rule applies to `Equal` and `Like` filters. See [Inverted index: Accent folding](../indexing/inverted-index.md#accent-folding) for details. ### Stopwords Stopwords are words that are filtered out before processing text. Weaviate uses configurable stopwords in calculating the BM25 score. Any tokens that are contained in the stopword list will be ignored from the BM25 score calculation. See the [reference page](../../config-refs/indexing/inverted-index.mdx#stopwords) for more details. Stopword lists are also configurable per collection **and** per property. You can define custom presets on `invertedIndexConfig.stopwordPresets` and assign them to individual text properties via `textAnalyzer.stopwordPreset`. This is useful for multilingual collections. For example, English and French properties can use different stopword lists. Stopwords are still indexed and only filtered at query time, so changing your stopword configuration does not require reindexing. See [Inverted index: Custom stopword presets](../indexing/inverted-index.md#custom-stopword-presets) for details. ### BM25 parameters BM25 is a scoring function used to rank documents based on the query terms appearing in them. It has two main parameters that control its behavior: - `k1` (default: 1.2): Controls term frequency saturation. Higher values mean that multiple occurrences of a term continue to increase the score more - `b` (default: 0.75): Controls document length normalization. Values closer to 1 mean more normalization for document length ``` /* Detailed source-code truncated for AI context efficiency. */ ``` Set custom `k1` and `b` values [for a collection](../../manage-collections/inverted-index.mdx#set-inverted-index-parameters). ### Keyword search operators import SearchOperators from '/_includes/feature-notes/search-operators.mdx';
vector store"] end %% User System subgraph user["🖥️ User System"] data["📄 Data"] end %% Connections with curved edges data --->|"1\. Insert objects
(with vectors)"| core %% Apply styles class user systemBox class weaviate weaviateBox class cloud cloudBox class provider providerBox class data,core,vectorizer,inference component %% Linkstyle for curved edges linkStyle default stroke:#718096,stroke-width:3px,fill:none,background-color:white ``` In this workflow, the user has the flexibility to use any vectorizer model and process independently of Weaviate. If using your own model, we recommend explicitly setting the vectorizer as `none` in the vectorizer configuration, such that you do not accidentally generate incompatible vectors with Weaviate. ### Named vectors A collections can be configured to allow each object to be represented by more than one vector embedding. Each such vector works as its distinct vector space that is independent of each other, referred to as a "named vector". A named vector can be configured with a [vectorizer model integration](#model-provider-integration), and may be provided using the ["bring your own vector"](#bring-your-own-vector) integration. ## Query vectors In Weaviate, you can specify the query vector using: - A query vector (called `nearVector`), - A query object (called `nearObject`), - A query text (called `nearText`), or - A query media (called `nearImage` or `nearVideo`). In each of these cases, the search will return the most similar objects to the query, based on the vector embeddings of the query and the stored objects. However, they differ in how the query vector is specified to Weaviate. ### `nearVector` In a `nearVector` query, the user provides a vector directly to Weaviate. This vector is compared to the vectors of the stored objects to find the most similar objects. ### `nearObject` In a `nearObject` query, the user provides an object ID to Weaviate. Weaviate retrieves the vector of the object and compares it to the vectors of the stored objects to find the most similar objects. ### `nearText` (and `nearImage`, `nearVideo`) In a `nearText` query, the user provides an input text to Weaviate. Weaviate uses the specified vectorizer model to generate a vector for the text, and compares it to the vectors of the stored objects to find the most similar objects. As a result, a `nearText` query is only available for collections where a vectorizer model is configured. A `nearImage` or `nearVideo` query works similarly to a `nearText` query, but with an image or video input instead of text. ## Multi-target vector search In a multi-target vector search, Weaviate performs multiple, concurrent, single-target vector searches. These searches will produce multiple sets of results, each with a vector distance score. Weaviat combines these result sets, using a ["join strategy"](#available-join-strategies) to produce final scores for each result. If an object is within the search limit or the distance threshold of any of the target vectors, it will be included in the search results. If an object does not contain vectors for any selected target vector, Weaviate ignores that object and does not include it in the search results. ### Available join strategies. - **minimum** (*default*) Use the minimum of all vector distances. - **sum** Use the sum of the vector distances. - **average** Use the average of the vector distances. - **manual weights** Use the sum of weighted distances, where the weight is provided for each target vector. - **relative score** Use the sum of weighted normalized distances, where the weight is provided for each target vector. ## Vector index and search Weaviate uses vector indexes to facilitate efficient vector searches. Like other types of indexes, a vector index organizes vector embeddings in a way that allows for fast retrieval while optimizing for other needs such as search quality (e.g. recall), search throughput, and resource use (e.g. memory). In Weaviate, multiple types of vector indexes are available such as `hnsw`, `flat` and `dynamic` indexes. Each [collection](../data.md#collections) or [tenant](../data.md#multi-tenancy) in Weaviate will have its own vector index. Additionally, each collection or tenant can have [multiple vector indexes](../data.md#multiple-vector-embeddings-named-vectors), each with different configurations. :::info Read more about: - [Collections](../data.md#collections) - [Multi-tenancy](../data.md#multi-tenancy) - [Vector indexes](../indexing/vector-index.md) - [Multiple named vectors](../data.md#multiple-vector-embeddings-named-vectors) ::: ### Distance metrics There are many ways to measure vector distances, such as cosine distance, dot product, and Euclidean distance. Weaviate supports a variety of these distance metrics, as listed on the [distance metrics](../../config-refs/distances.md) page. Each vectorizer model is trained with a specific distance metric, so it is important to use the same distance metric for search as was used for training the model. Weaviate uses cosine distance as the default distance metric for vector searches, as this is the typical distance metric for vectorizer models. :::tip Distance vs Similarity In a "distance", the lower the value, the closer the vectors are to each other. In a "similarity", or "certainty" score, the higher the value, the closer the vectors are to each other. Some metrics, such as cosine distance, can also be expressed as a similarity score. Others, such as Euclidean distance, are only expressable as a distance. ::: ## Diversity selection (MMR) import V137Preview from '/_includes/feature-notes/v137-preview.mdx';
\*\* [New named vectors can be added](../manage-collections/vector-config.mdx#add-new-named-vectors); some vector index settings are mutable
Example collection configuration - JSON object
An example of a complete collection object including properties: ``` /* Detailed source-code truncated for AI context efficiency. */ ```Example property configuration - JSON object
An example of a complete property object: ```json { "name": "title", // The name of the property "description": "title of the article", // A description for your reference "dataType": [ // The data type of the object as described above. When creating cross-references, a property can have multiple dataTypes. "text" ], "tokenization": "word", // Split field contents into word-tokens when indexing into the inverted index. See "Property Tokenization" below for more detail. "moduleConfig": { // Module-specific settings "text2vec-contextionary": { "skip": true, // If true, the whole property is NOT included in vectorization. Default is false, meaning that the object will be NOT be skipped. "vectorizePropertyName": true // Whether the name of the property is used in the calculation for the vector position of data objects. Default false. } }, "indexFilterable": true, // Optional, default is true. By default each property is indexed with a roaring bitmap index where available for efficient filtering. "indexSearchable": true // Optional, default is true. By default each property is indexed with a searchable index for BM25-suitable Map index for BM25 or hybrid searching. } ```Example property configuration - JSON object
```json { "classes": [ { "class": "Question", "properties": [ { "dataType": ["text"], "name": "question", // highlight-start "tokenization": "word" // highlight-end }, ], ... "vectorizer": "text2vec-openai" } ] } ````gse` and `trigram` tokenization methods
For Japanese and Chinese text, we recommend use of `gse` or `trigram` tokenization methods. These methods work better with these languages than the other methods as these languages are not easily able to be tokenized using whitespaces. The `gse` tokenizer is not loaded by default to save resources. To use it, set the environment variable `ENABLE_TOKENIZER_GSE` to `true` on the Weaviate instance. `gse` tokenization examples: - `"素早い茶色の狐が怠けた犬を飛び越えた"`: `["素早", "素早い", "早い", "茶色", "の", "狐", "が", "怠け", "けた", "犬", "を", "飛び", "飛び越え", "越え", "た", "素早い茶色の狐が怠けた犬を飛び越えた"]` - `"すばやいちゃいろのきつねがなまけたいぬをとびこえた"`: `["すばや", "すばやい", "やい", "いち", "ちゃ", "ちゃい", "ちゃいろ", "いろ", "のき", "きつ", "きつね", "つね", "ねが", "がな", "なま", "なまけ", "まけ", "けた", "けたい", "たい", "いぬ", "を", "とび", "とびこえ", "こえ", "た", "すばやいちゃいろのきつねがなまけたいぬをとびこえた"]` :::note `trigram` for fuzzy matching While originally designed for Asian languages, `trigram` tokenization is also highly effective for fuzzy matching and typo tolerance in other languages. :::`kagome_ja` tokenization method
For Japanese text, `kagome_ja` tokenization method is also available. This uses the [`Kagome` tokenizer](https://github.com/ikawaha/kagome?tab=readme-ov-file) with a Japanese [MeCab IPA](https://github.com/ikawaha/kagome-dict/) dictionary to split the property text. The `kagome_ja` tokenizer is not loaded by default to save resources. To use it, set the environment variable `ENABLE_TOKENIZER_KAGOME_JA` to `true` on the Weaviate instance. `kagome_ja` tokenization examples: - `"春の夜の夢はうつつよりもかなしき 夏の夜の夢はうつつに似たり 秋の夜の夢はうつつを超え 冬の夜の夢は心に響く 山のあなたに小さな村が見える 川の音が静かに耳に届く 風が木々を通り抜ける音 星空の下、すべてが平和である"`: - [`"春", "の", "夜", "の", "夢", "は", "うつつ", "より", "も", "かなしき", "\n\t", "夏", "の", "夜", "の", "夢", "は", "うつつ", "に", "似", "たり", "\n\t", "秋", "の", "夜", "の", "夢", "は", "うつつ", "を", "超え", "\n\t", "冬", "の", "夜", "の", "夢", "は", "心", "に", "響く", "\n\n\t", "山", "の", "あなた", "に", "小さな", "村", "が", "見える", "\n\t", "川", "の", "音", "が", "静か", "に", "耳", "に", "届く", "\n\t", "風", "が", "木々", "を", "通り抜ける", "音", "\n\t", "星空", "の", "下", "、", "すべて", "が", "平和", "で", "ある"`] - `"素早い茶色の狐が怠けた犬を飛び越えた"`: - `["素早い", "茶色", "の", "狐", "が", "怠け", "た", "犬", "を", "飛び越え", "た"]` - `"すばやいちゃいろのきつねがなまけたいぬをとびこえた"`: - `["すばやい", "ちゃ", "いろ", "の", "きつね", "が", "なまけ", "た", "いぬ", "を", "とびこえ", "た"]``kagome_kr` tokenization method
For Korean text, we recommend use of the `kagome_kr` tokenization method. This uses the [`Kagome` tokenizer](https://github.com/ikawaha/kagome?tab=readme-ov-file) with a Korean MeCab ([mecab-ko-dic](https://bitbucket.org/eunjeon/mecab-ko-dic/src/master/)) dictionary to split the property text. The `kagome_kr` tokenizer is not loaded by default to save resources. To use it, set the environment variable `ENABLE_TOKENIZER_KAGOME_KR` to `true` on the Weaviate instance. `kagome_kr` tokenization examples: - `"아버지가방에들어가신다"`: - `["아버지", "가", "방", "에", "들어가", "신다"]` - `"아버지가 방에 들어가신다"`: - `["아버지", "가", "방", "에", "들어가", "신다"]` - `"결정하겠다"`: - `["결정", "하", "겠", "다"]`Limit the number of `gse` and `Kagome` tokenizers
The `gse` and `Kagome` tokenizers can be resource intensive and affect Weaviate's performance. You can limit the combined number of `gse` and `Kagome` tokenizers running at the same time using the [`TOKENIZER_CONCURRENCY_COUNT` environment variable](/deploy/configuration/env-vars/index.md).Fuzzy matching with `trigram` tokenization
The `trigram` tokenization method provides fuzzy matching capabilities by breaking text into overlapping 3-character sequences. This enables BM25 searches to find matches even with spelling errors or variations. **Use cases for trigram fuzzy matching:** - **Typo tolerance**: Find matches despite spelling errors (e.g., "Reliace" matches "Reliance") - **Name reconciliation**: Match entity names with variations across datasets - **Search-as-you-type**: Build autocomplete functionality - **Partial matching**: Find objects with partial string matches **How it works:** When text is tokenized with `trigram`, it's broken into all possible 3-character sequences: - `"hello"` → `["hel", "ell", "llo"]` - `"world"` → `["wor", "orl", "rld"]` Similar strings share many trigrams, enabling fuzzy matching: - `"Morgan Stanley"` and `"Stanley Morgn"` share trigrams like `"sta", "tan", "anl", "nle", "ley"` **Performance considerations:** - Filtering behavior will change significantly, as text filtering will be done based on trigram-tokenized text, instead of whole words - Creates larger inverted indexes due to more tokens - May impact query performance for large datasets :::tip Use trigram tokenization selectively on fields where fuzzy matching is preferred. Keep exact-match fields with `word` or `field` tokenization for precision. :::Example module configuration - JSON object
An example of a complete `moduleConfig` object: ```json "moduleConfig": { "text2vec-contextionary": { "vectorizeClassName": true // Include the collection name in vector calculation (default true) } }, ```Example replication configuration - JSON object
An example of a complete `replicationConfig` object: ```json { "class": "Article", "vectorizer": "text2vec-openai", // highlight-start "replicationConfig": { "factor": 3, "deletionStrategy": "TimeBasedResolution", "asyncConfig": { "hashtreeHeight": 16, "frequency": 30000 } } // highlight-end } ```Example sharding configuration - JSON object
An example of a complete `shardingConfig` object: ```json "shardingConfig": { "virtualPerPhysical": 128, "desiredCount": 1, // defaults to the amount of Weaviate nodes in the cluster "actualCount": 1, "desiredVirtualCount": 128, "actualVirtualCount": 128, "key": "_id", "strategy": "hash", "function": "murmur3" } ```Mutable parameters
import RaftRFChangeWarning from "/_includes/1-25-replication-factor.mdx";string is deprecated
Prior to `v1.19`, Weaviate supported an additional datatype `string`, which was differentiated by tokenization behavior to `text`. As of `v1.19`, this type is deprecated and will be removed in a future release.
Use `text` instead of `string`. `text` supports the tokenization options that are available through `string`.
[See note 1 below] | `1 - cosine_sim(a,b)` | `0 <= d <= 2` | `0`: identical vectors
`2`: Opposing vectors. | | `dot` | A dot product-based indication of distance.
More precisely, the negative dot product.
[See note 2 below] | `-dot(a,b)` | `-∞ < d < ∞` | `-3`: more similar than `-2`
`2`: more similar than `5` | | `l2-squared` | The squared euclidean distance between two vectors. | `sum((a_i - b_i)^2)` | `0 <= d < ∞` | `0`: identical vectors | | `hamming` | Number of differences between vectors at each dimensions. |
sum(|a_i != b_i|) | `0 <= d < dims` | `0`: identical vectors |
| `manhattan` | The distance between two vector dimensions measured along axes at right angles. | sum(|a_i - b_i|) | `0 <= d < ∞` | `0`: identical vectors |
If you're missing your favorite distance type and would like to contribute it to Weaviate, we'd be happy to review your [PR](https://github.com/weaviate/weaviate).
:::note Additional notes
1. If `cosine` is chosen, all vectors are normalized to length 1 at read time and dot product is used to calculate the distance for computational efficiency.
2. Dot Product on its own is a similarity metric, not a distance metric. As a result, Weaviate returns the negative dot product to stick with the intuition that a smaller value of a distance indicates a more similar result and a higher distance value indicates a less similar result.
3. The [HFresh index](/weaviate/config-refs/indexing/vector-index.mdx#hfresh-index) only supports `cosine` and `l2-squared` distance metrics.
:::
## Distance implementations and optimizations
On a typical Weaviate use case the largest portion of CPU time is spent calculating vector distances. Even with an approximate nearest neighbor index - which leads to far fewer calculations - the efficiency of distance calculations has a major impact on [overall performance](/weaviate/benchmarks/ann.md).
Weaviate uses SIMD (Single Instruction, Multiple Data) instructions for the following distance metrics and architectures. The available optimizations are resolved in the shown order (e.g. SVE -> Neon).
| Distance | `arm64` | `amd64` |
| ----------------------------- | ----------- | --------------------------------------------- |
| `cosine`, `dot`, `l2-squared` | SVE or Neon | Sapphire Rapids with AVX512, or Any with AVX2 |
| `hamming`, `manhattan` | No SIMD | No SIMD |
If you like dealing with Assembly programming, SIMD, and vector instruction sets we would love to receive your contribution for one of the combinations that have not yet received an SIMD-specific optimization.
## Distance fields in the APIs
The `distance` is exposed in the APIs in two ways:
- Whenever a [vector search](../search/similarity.md#set-a-similarity-threshold) is involved, the distance can be displayed as part of the results, for example using `_additional { distance }`
- Whenever a [vector search](../search/similarity.md#set-a-similarity-threshold) is involved, the distance can be specified as a limiting criterion, for example using `nearVector({distance: 1.5, vector: ... })`
## Distance vs Certainty
Prior to version `v1.14` only `certainty` was available in the APIs. The
original ideas behind certainty was to normalize the distance score into a
value between `0 <= certainty <= 1`, where 1 would represent identical vectors
and 0 would represent opposite vectors.
This concept is however unique to `cosine` distance. With other distance
metrics, scores may be unbounded. As a result the preferred way is to use
`distance` in favor of `certainty`.
For backward compatibility, `certainty` can still be used when the distance is
`cosine`. If any other distance is selected `certainty` cannot be used.
See also [Search API: Additional properties (metadata)](../api/graphql/additional-properties.md).
## Further resources
- [How-to: Manage collections](../manage-collections/index.mdx)
- :::info Deployment documentation For deployment related topics like security, backups, replication, cluster information and advanced configuration options, visit the [deployment documentation](/docs/deploy/configuration/index.mdx). ::: --- ### Weaviate/Config Refs/Indexing/Inverted Index (docs/weaviate/config-refs/indexing/inverted-index.mdx) --- title: Inverted index description: Reference for inverted index parameters in Weaviate. --- import SkipLink from "/src/components/SkipValidationLink"; import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; import PyCode from "!!raw-loader!/_includes/code/config-refs/reference.collections.py"; import TSCode from "!!raw-loader!/_includes/code/howto/manage-data.collections.ts"; import GoCode from "!!raw-loader!/_includes/code/howto/go/docs/manage-data.classes_test.go"; import PyTokenizeEndpoint from "!!raw-loader!/_includes/code/tutorials/tokenization/tokenize_endpoint.py"; The **[inverted index](../../concepts/indexing/inverted-index.md)** maps values (like words or numbers) to the objects that contain them. It is the backbone for all attribute-based filtering (`where` filters) and keyword searching (`bm25`, `hybrid`). ## Inverted index types Multiple [inverted index types](../../concepts/indexing/inverted-index.md) are available in Weaviate. Not all inverted index types are available for all data types. The available inverted index types are: import InvertedIndexTypesSummary from "/_includes/inverted-index-types-summary.mdx";
Example `bm25` configuration - JSON object
An example of a complete collection object with `bm25` configuration: ```json { "class": "Article", // Configuration of the sparse index "invertedIndexConfig": { "bm25": { "b": 0.75, "k1": 1.2 } }, "properties": [ { "name": "title", "description": "title of the article", "dataType": ["text"], // Property-level settings override the collection-level settings "invertedIndexConfig": { "bm25": { "b": 0.75, "k1": 1.2 } }, "indexFilterable": true, "indexSearchable": true } ] } ```Example `stopwords` configuration - JSON object
An example of a complete collection object with `stopwords` configuration: ```json "invertedIndexConfig": { "stopwords": { "preset": "en", "additions": ["star", "nebula"], "removals": ["a", "the"] } } ```Example stopwordPresets configuration - JSON object
```json
"invertedIndexConfig": {
"stopwordPresets": {
"fr": ["le", "la", "les", "un", "une", "des", "du", "de", "et"],
"de": ["der", "die", "das", "und", "oder", "aber"]
}
}
```
Example textAnalyzer configuration - JSON object
```json
{
"name": "description",
"dataType": ["text"],
"tokenization": "word",
"textAnalyzer": {
"asciiFold": true,
"asciiFoldIgnore": ["é"],
"stopwordPreset": "fr"
}
}
```
How to select the index type
Generally, the `hnsw` index type is recommended for most use cases. The `flat` index type is recommended for use cases where the data the number of objects per index is low, such as in multi-tenancy cases. You can also opt for the `dynamic` index which will initially configure a `flat` index and once the object count exceeds a specified threshold it will automatically convert to an `hnsw` index. The `hfresh` index is a cluster-based index that uses HNSW for the centroid index. It can provide significant memory efficiency benefits while maintaining good search performance. See [this section](../../concepts/indexing/vector-index.md#which-vector-index-is-right-for-me) for more information about the different index types and how to choose between them.Dynamic `ef`. Weaviate automatically adjusts the `ef` value and creates a dynamic `ef` list when `ef` is set to -1. For more details, see [dynamic ef](../../concepts/indexing/vector-index.md#dynamic-ef). | -1 | Yes | | `efConstruction` | integer | Balance index search speed and build speed. A high `efConstruction` value means you can lower your `ef` settings, but importing is slower.
`efConstruction` must be greater than 0. | 128 | No | | `HNSWGeoIndexEF` | integer | Balance geo index search speed and recall. This value controls the search depth for geo-based queries. | 800 | Yes | | `maxConnections` | integer | Maximum number of connections per element. `maxConnections` is the connection limit per layer for layers above the zero layer. The zero layer can have (2 \* `maxConnections`) connections.
`maxConnections` must be greater than 0. | 32 | No | | `dynamicEfMin` | integer | Lower bound for [dynamic `ef`](../../concepts/indexing/vector-index.md#dynamic-ef). Protects against a creating search list that is too short.
This setting is only used when `ef` is -1. | 100 | Yes | | `dynamicEfMax` | integer | Upper bound for [dynamic `ef`](../../concepts/indexing/vector-index.md#dynamic-ef). Protects against creating a search list that is too long.
If `dynamicEfMax` is higher than the limit, `dynamicEfMax` does not have any effect. In this case, `ef` is the limit.
This setting is only used when `ef` is -1. | 500 | Yes | | `dynamicEfFactor` | integer | Multiplier for [dynamic `ef`](../../concepts/indexing/vector-index.md#dynamic-ef). Sets the potential length of the search list.
This setting is only used when `ef` is -1. | 8 | Yes | | `filterStrategy` | string | The filter strategy to use for filtering the search results. The filter strategy can be set to [`acorn`](../../concepts/filtering.md#acorn) (default as of `v1.34`) or [`sweeping`](../../concepts/filtering.md#sweeping). | `acorn` | Yes | | `flatSearchCutoff` | integer | Optional. Threshold for the [flat-search cutoff](/weaviate/concepts/filtering.md#flat-search-cutoff). To force a vector index search, set `"flatSearchCutoff": 0`. | 40000 | Yes | | `skip` | boolean | When true, do not index the collection.
Weaviate decouples vector creation and vector storage. If you skip vector indexing, but a vectorizer is configured (or a vector is provided manually), Weaviate logs a warning each import.
To skip indexing and vector generation, set `"vectorizer": "none"` when you set `"skip": true`.
See [When to skip indexing](../../concepts/indexing/vector-index.md#when-to-skip-indexing). | `false` | No | | `vectorCacheMaxObjects` | integer | Maximum number of objects in the memory cache. By default, this limit is set to one trillion (`1e12`) objects when a new collection is created. For sizing recommendations, see [Vector cache considerations](../../concepts/indexing/vector-index.md#vector-cache-considerations). | `1e12` | Yes | | `rq` | object | Enable and configure [rotational quantization (RQ)](/weaviate/concepts/indexing/vector-index.md) compression.
For RQ configuration details, see [RQ configuration parameters](#rq-parameters). | -- | Yes | | `pq` | object | Enable and configure [product quantization (PQ)](/weaviate/concepts/indexing/vector-index.md) compression.
PQ assumes some data has already been loaded. You should have 10,000 to 100,000 vectors per shard loaded before you enable PQ.
For PQ configuration details, see [PQ configuration parameters](#pq-parameters). | -- | Yes | | `bq` | object | Enable and configure [binary quantization (BQ)](/weaviate/concepts/indexing/vector-index.md) compression.
For BQ configuration details, see [BQ configuration parameters](#bq-parameters). | -- | Yes | | `sq` | object | Enable and configure [scalar quantization (SQ)](/weaviate/concepts/indexing/vector-index.md) compression.
For SQ configuration details, see [SQ configuration parameters](#sq-parameters). | -- | Yes | ### Database parameters for HNSW Note that some database-level parameters are available to configure HNSW indexing behavior. - [`PERSISTENCE_HNSW_MAX_LOG_SIZE`](/deploy/configuration/env-vars/index.md#PERSISTENCE_HNSW_MAX_LOG_SIZE) is a database-level parameter that sets the maximum size of the HNSW write-ahead-log. The default value is `500MiB`. Increase this value to improve efficiency of the compaction process, but be aware that this will increase the memory usage of the database. Conversely, decreasing this value will reduce memory usage but may slow down the compaction process. Preferably, the `PERSISTENCE_HNSW_MAX_LOG_SIZE` should set to a value close to the size of the HNSW graph. - [`DEFAULT_QUANTIZATION`](/deploy/configuration/env-vars/index.md#DEFAULT_QUANTIZATION) is a database-level parameter that defines which quantization technique will be used by default when creating new collections. ### Tombstone cleanup parameters :::info Environment variable availability - `TOMBSTONE_DELETION_CONCURRENCY` is available in `v1.24.0` and up. - `TOMBSTONE_DELETION_MIN_PER_CYCLE` and `TOMBSTONE_DELETION_MAX_PER_CYCLE` are available in `v1.24.15` / `v1.25.2` and up. ::: Tombstones are records that mark deleted objects. In an HNSW index, tombstones are regularly cleaned up, triggered periodically by the `cleanupIntervalSeconds` parameter. As the index grows in size, the cleanup process may take longer to complete and require more resources. For very large indexes, this may cause performance issues. To control the number of tombstones deleted per cleanup cycle and prevent performance issues, set the [`TOMBSTONE_DELETION_MAX_PER_CYCLE` and `TOMBSTONE_DELETION_MIN_PER_CYCLE` environment variables](/deploy/configuration/env-vars/index.md#general). - Set `TOMBSTONE_DELETION_MIN_PER_CYCLE` to prevent occurrences of unnecessary cleanup cycles. - Set `TOMBSTONE_DELETION_MAX_PER_CYCLE` to prevent the cleanup process from taking too long and consuming too many resources. As an example, for a cluster with 300 million objects per shard, a `TOMBSTONE_DELETION_MIN_PER_CYCLE` value of 1000000 (1 million) and a `TOMBSTONE_DELETION_MAX_PER_CYCLE` value of 10000000 (10 million) may be good starting points. You can also set the `TOMBSTONE_DELETION_CONCURRENCY` environment variable to limit the number of threads used for tombstone cleanup. This can help prevent prevent the cleanup process from unnecessarily consuming too many resources, or the cleanup process from taking too long. The default value for `TOMBSTONE_DELETION_CONCURRENCY` is set to half the number of CPU cores available to Weaviate. In a cluster with a large number of cores, you may want to set `TOMBSTONE_DELETION_CONCURRENCY` to a lower value to prevent the cleanup process from consuming too many resources. Conversely, in a cluster with a small number of cores and a large number of deletions, you may want to set `TOMBSTONE_DELETION_CONCURRENCY` to a higher value to speed up the cleanup process. ### HNSW Configuration tips To determine reasonable settings for your use case, consider the following questions and compare your answers in the table below: 1. How many queries do you expect per second? 1. Do you expect a lot of imports or updates? 1. How high should the recall be? | Number of queries | Many imports or updates | Recall level | Configuration suggestions | | ----------------- | ----------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | not many | no | low | This is the ideal scenario. Keep both the `ef` and `efConstruction` settings low. You don't need a big machine and you will still be happy with the results. | | not many | no | high | Here the tricky thing is that your recall needs to be high. Since you're not expecting a lot of requests or imports, you can increase both the `ef` and `efConstruction` settings. Keep increasing them until you are happy with the recall. In this case, you can get pretty close to 100%. | | not many | yes | low | Here the tricky thing is the high volume of imports and updates. Be sure to keep `efConstruction` low. Since you don't need a high recall, and you're not expecting a lot of queries, you can adjust the `ef` setting until you've reached the desired recall. | | not many | yes | high | The trade-offs are getting harder. You need high recall _and_ you're dealing with a lot of imports or updates. This means you need to keep the `efConstruction` setting low, but you can significantly increase your `ef` setting because your queries per second rate is low. | | many | no | low | Many queries per second means you need a low `ef` setting. Luckily you don't need high recall so you can significantly increase the `efConstruction` value. | | many | no | high | Many queries per second means a low `ef` setting. Since you need a high recall but you are not expecting a lot of imports or updates, you can increase your `efConstruction` until you've reached the desired recall. | | many | yes | low | Many queries per second means you need a low `ef` setting. A high number of imports and updates also means you need a low `efConstruction` setting. Luckily your recall does not have to be as close to 100% as possible. You can set `efConstruction` relatively low to support your input or update throughput, and you can use the `ef` setting to regulate the query per second speed. | | many | yes | high | Aha, this means you're a perfectionist _or_ you have a use case that needs the best of all three worlds. Increase your `efConstruction` value until you hit the time limit of imports and updates. Next, increase your `ef` setting until you reach your desired balance of queries per second versus recall.
While many people think they need maximize all three dimensions, in practice that's usually not the case. We leave it up to you to decide, and you can always ask for help in [our forum](https://forum.weaviate.io). | :::tip This set of values is a good starting point for many use cases. | Parameter | Value | | :--------------- | :---- | | `ef` | `64` | | `efConstruction` | `128` | | `maxConnections` | `32` | ::: ## Flat index Flat indexes are recommended for use cases where the number of objects per index is low, such as in multi-tenancy use cases. | Parameter | Type | Default | Changeable | Details | | :---------------------- | :------ | :------ | :--------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `vectorCacheMaxObjects` | integer | `1e12` | Yes | Maximum number of objects in the memory cache. By default, this limit is set to one trillion (`1e12`) objects when a new collection is created. For sizing recommendations, see [Vector cache considerations](../../concepts/indexing/vector-index.md#vector-cache-considerations). | | `bq` | object | -- | No | Enable and configure [binary quantization (BQ)](../../concepts/vector-quantization.md#binary-quantization) compression.
For BQ configuration details, see [BQ configuration parameters](#bq-parameters). | ## Dynamic index :::caution Experimental feature Available starting in `v1.25`. Dynamic indexing is an experimental feature. Use with caution. ::: import DynamicAsyncRequirements from "/_includes/dynamic-index-async-req.mdx";
Example Docker Compose configuration
```yaml --- services: weaviate: command: - --host - 0.0.0.0 - --port - "8080" - --scheme - http image: cr.weaviate.io/semitechnologies/weaviate:||site.weaviate_version|| restart: on-failure:0 ports: - 8080:8080 - 50051:50051 environment: QUERY_DEFAULTS_LIMIT: 25 QUERY_MAXIMUM_RESULTS: 10000 AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: "true" PERSISTENCE_DATA_PATH: "/var/lib/weaviate" CLUSTER_HOSTNAME: "node1" AUTOSCHEMA_ENABLED: "false" ASYNC_INDEXING: "true" ```Node status example usage
The `nodes/shards/vectorQueueLength` field shows the number of objects that still have to be indexed.
import Nodes from "/_includes/code/nodes.mdx";
The `vectorQueueLength` field will show the number of remaining objects to be indexed. In the example below, the vector index queue has 425 objects remaining to be indexed on the `TestArticle` shard, out of a total of 1000 objects. ```json { "nodes": [ { "batchStats": { "ratePerSecond": 0 }, "gitHash": "e6b37ce", "name": "weaviate-0", "shards": [ { "class": "TestArticle", "name": "nq1Bg9Q5lxxP", "objectCount": 1000, // highlight-start "vectorIndexingStatus": "INDEXING", "vectorQueueLength": 425 // highlight-end } ], "stats": { "objectCount": 1000, "shardCount": 1 }, "status": "HEALTHY", "version": "1.22.1" } ] } ```
Request"] AuthCheck{"AuthN
Enabled?"} AccessCheck{"Check
AuthZ"} Access["✅ Access
Granted"] Denied["❌ Access
Denied"] %% Define authentication method nodes subgraph auth ["AuthN"] direction LR API["API Key"] OIDC["OIDC"] AuthResult{"Success?"} end %% Define connections Request --> AuthCheck AuthCheck -->|"No"| AccessCheck AuthCheck -->|"Yes"| auth API --> AuthResult OIDC --> AuthResult AuthResult -->|"Yes"| AccessCheck AuthResult -->|"No"| Denied AccessCheck -->|"Pass"| Access AccessCheck -->|"Fail"| Denied %% Style nodes style Request fill:#ffffff,stroke:#B9C8DF,color:#130C49 style AuthCheck fill:#ffffff,stroke:#B9C8DF,color:#130C49 style AccessCheck fill:#ffffff,stroke:#B9C8DF,color:#130C49 style Access fill:#ffffff,stroke:#B9C8DF,color:#130C49 style Denied fill:#ffffff,stroke:#B9C8DF,color:#130C49 style API fill:#ffffff,stroke:#B9C8DF,color:#130C49 style OIDC fill:#ffffff,stroke:#B9C8DF,color:#130C49 style AuthResult fill:#ffffff,stroke:#B9C8DF,color:#130C49 %% Style subgraph style auth fill:#ffffff,stroke:#130C49,stroke-width:2px,color:#130C49 ``` For example, a user logging in with the API key `jane-secret` may be granted administrator permissions, while another user logging in with the API key `ian-secret` may be granted read-only permissions. API key and OIDC authentication can be both enabled at the same time. We recommend using a client library to authenticate against Weaviate. See [How-to: Connect](docs/weaviate/connections/index.mdx) pages for more information. :::info What about Weaviate Cloud (WCD)? For Weaviate Cloud (WCD) instances, authentication is pre-configured with API key access. You can [authenticate against Weaviate](../connections/connect-cloud.mdx) by [creating new API keys](/cloud/manage-clusters/connect.mdx). ::: ### API key API key authentication is the simplest method to authenticate against Weaviate. Each user is assigned a unique API key, which is passed in the request header. For details on configuring API key authentication, see the [authentication guide](/deploy/configuration/authentication.md#api-key-authentication). ### OpenID Connect (OIDC) [OpenID Connect (OIDC)](/deploy/configuration/authentication.md#oidc-authentication) enables authentication through an external identity provider (e.g., Okta, Azure AD, Google). OIDC supports multiple flows such as client credentials, resource owner password, and hybrid flow. For details on configuring OIDC and working with tokens, see the [OIDC configuration guide](/deploy/configuration/oidc). ### Anonymous access [Anonymous access](/deploy/configuration/authentication.md#anonymous-access) allows unauthenticated requests. This is **strongly discouraged** except for local development or evaluation purposes, as it bypasses all identity verification. ## Authorization Weaviate provides differentiated access through authorization levels, based on the user's [authentication](#authentication) status. The following authorization schemes are available: ### RBAC (recommended) [Role-Based Access Control (RBAC)](./rbac/index.mdx) provides fine-grained control over user permissions. With RBAC, you define roles with specific permissions and assign them to users. This is the **recommended authorization scheme** for production deployments. RBAC supports: - **Predefined roles**: `root` (full access) and `viewer` (read-only access) - **Custom roles**: Create roles with specific permissions for collections, objects, tenants, backups, and more - **Granular permissions**: Control access at the collection, tenant, and operation level using name filters and regex patterns See [RBAC Overview](./rbac/index.mdx) for the full permissions model, and [Configuring RBAC](/deploy/configuration/configuring-rbac.md) for setup instructions. ### Admin list (legacy) :::caution Prefer RBAC over Admin list The Admin list authorization scheme only provides coarse-grained access control (admin or read-only). Use [RBAC](#rbac-recommended) instead for production deployments, as it provides much more flexible and secure permission management. ::: The [Admin list](../../deploy/configuration/authorization.md#admin-list) scheme assigns users as either admin (full access) or read-only. [Anonymous users](../../deploy/configuration/authorization.md#anonymous-users) can optionally be granted permissions. ### Undifferentiated access With [undifferentiated access](../../deploy/configuration/authorization.md#undifferentiated-access), all authenticated users have full access. This is only suitable for development or trusted single-user environments. ## Further resources - [Configuration: Authentication](/deploy/configuration/authentication.md) - [Configuration: Authorization](/deploy/configuration/authorization.md) - [Configuration: OIDC](/deploy/configuration/oidc.md) - [Configuration: RBAC](/weaviate/configuration/rbac/index.mdx) - [Configuration: Environment variables - Authentication and Authorization](/deploy/configuration/env-vars/index.md#authentication-and-authorization) - [Weaviate MCP server](/weaviate/configuration/mcp-server.mdx) (authenticates via API key and respects RBAC permissions) ## Questions and feedback import DocsFeedback from '/\_includes/docs-feedback.mdx';
## Deployment configuration These guides cover server-level configuration for your Weaviate deployment: export const deployConfigData = [ { title: "Database configuration", description: "Configure environment variables, ports, and runtime settings.", link: "/deploy/configuration/env-vars", icon: "fas fa-cog", }, { title: "Monitoring and logging", description: "Collect metrics, configure logging, and monitor cluster health.", link: "/deploy/configuration/monitoring", icon: "fas fa-chart-bar", }, { title: "RBAC", description: "Configure Role-Based Access Control for fine-grained permissions.", link: "/weaviate/configuration/rbac", icon: "fas fa-user-shield", }, { title: "Replication and scaling", description: "Set up data replication and scaling across nodes for high availability.", link: "/deploy/configuration/replication", icon: "fas fa-copy", }, { title: "Storage and backups", description: "Configure backup, restore, and persistence for your Weaviate instance.", link: "/deploy/configuration/backups", icon: "fas fa-save", }, ];
## Questions and feedback import DocsFeedback from "/_includes/docs-feedback.mdx";
Per-tool permissions
| Tool | MCP permissions required | Additional collection permissions | | --------------------------------- | -------------------------- | --------------------------------- | | `weaviate-collections-get-config` | `read_mcp` | `read_collections` | | `weaviate-tenants-list` | `read_mcp` | `read_data` | | `weaviate-query-hybrid` | `read_mcp` | `read_data` | | `weaviate-objects-upsert` | `create_mcp`, `update_mcp` | `create_data`, `update_data` |Additional information
- How to [set the index type](../../manage-collections/vector-config.mdx#set-vector-index-type)| Resource type | Access levels | Optional resource‑specific constraints |
|---|---|---|
| Role Management |
Create roles
Read role info
Update role permissions
Delete roles
|
Role name filter:
Role scope:
|
| User Management |
Create users
Read user info
Update/rotate user API key
Delete users
Assign and revoke roles to and from users
|
User name filter:
|
|
Collections
(collection definitions only, data object permissions are separate) |
Create collections
Read collection definitions
Update collection definitions
Delete collections
|
Collection name filter:
|
| Tenants |
Create tenants
Read tenant info
Update tenants
Delete tenants
|
Collection name filter:
Tenant name filter:
|
| Data Objects |
Create objects
Read objects
Update objects
Delete objects
|
Collection name filter:
Tenant name filter:
|
| Backups | Manage backups |
Collection name filter:
|
| Cluster Data Access | Read cluster metadata | |
| Node Data Access | Read node metadata at a specified verbosity level |
Verbosity level:
Collection name filter (only for
|
| Collection aliases |
Create aliases
Read aliases
Update aliases
Delete aliases
|
Alias name filter:
|
| Replications |
Create replications
Read replications
Update replications
Delete replications
|
Collection name filter:
Shard name filter:
|
| Groups |
Read groups
Assign and revoke group membership
|
Group name filter:
Group type filter:
|
Example results
```text Roles assigned to '/admin-group': ['testRole', 'viewer'] ```Example results
```text Known OIDC groups (3): ['/viewer-group', '/admin-group', '/my-test-group'] ```Example results
```text Groups assigned to role 'testRole': - Group ID: /admin-group, Type: oidc ```Example results
```text [ UserDB(user_id='custom-user', role_names=['viewer', 'testRole'], user_type=Example results
```text RXF1dU1VcWM1Q3hvVndYT0F1OTBOTDZLZWx0ME5kbWVJRVdPL25EVW12QT1fMXlDUEhUNjhSMlNtazdHcV92MjAw ```Example results
```text SSs3WGVFbUxMVFhlOEsxVVMrQVBzM1VhQTJIM2xXWngwY01HaXFYVnM1az1fMXlDUEhUNjhSMlNtazdHcV92MjAw ```Example results
```text testRole viewer ```Example results
```text testRole viewer ```## Connection example To connect, use the `REST Endpoint` and the `Admin` API key stored as [environment variables](#environment-variables): import HostnameWarning from "/_includes/wcs/hostname-warning.mdx";
### Generate client code After running a query in the console, you can copy a Python or TypeScript snippet that reproduces the same call through the [`weaviate-agents`](/query-agent/index.md) client libraries. This makes it easy to move from exploration into application code. ### Limitations - The Cloud-console Query Agent does **not** support multi-tenant collections. To use the Query Agent with multi-tenancy, call it from a [client library](/query-agent/reference/advanced_collections.md). - Standard Query Agent [usage limits](/docs/cloud/tools/query-agent.mdx#usage-limits) apply. For a deeper walkthrough including additional screen recordings, see the [Query Agent tool docs](/docs/cloud/tools/query-agent.mdx). ## Query tool The [Query application (query tool)](/cloud/tools/query-tool) is a browser-based GraphQL IDE. The Query tool is available in the Weaviate Cloud (WCD) console. Use the query tool to work interactively with clusters hosted in Weaviate Cloud and self-hosted instances too. ### Open the query tool To use the query tool, open your [WCD Dashboard](/go/console?utm_content=howto). In the left-hand menu, click the Query tool icon.
Additional information
Use properties to configure additional parameters such as data type, index characteristics, or tokenization. For details, see: - [References: Configuration: Schema](../config-refs/collections.mdx) -Sample configuration: Text objects
This configuration for text objects defines the following: - The collection name (`Article`) - The vectorizer module (`text2vec-cohere`) and model (`embed-multilingual-v2.0`) - A set of properties (`title`, `body`) with `text` data types. ```json { "class": "Article", "vectorizer": "text2vec-cohere", "moduleConfig": { "text2vec-cohere": { "model": "embed-multilingual-v2.0" } }, "properties": [ { "name": "title", "dataType": ["text"] }, { "name": "body", "dataType": ["text"] } ] } ```Sample configuration: Nested objects
This configuration for nested objects defines the following: - The collection name (`Person`) - The vectorizer module (`text2vec-huggingface`) - A set of properties (`last_name`, `address`) - `last_name` has `text` data type - `address` has `object` data type - The `address` property has two nested properties (`street` and `city`) ```json { "class": "Person", "vectorizer": "text2vec-huggingface", "properties": [ { "dataType": ["text"], "name": "last_name" }, { "dataType": ["object"], "name": "address", "nestedProperties": [ { "dataType": ["text"], "name": "street" }, { "dataType": ["text"], "name": "city" } ] } ] } ``` To filter on values inside nested objects, see [Filter on nested object properties](../search/filters.md#filter-on-nested-object-properties).Sample configuration: Generative search
This configuration for [retrieval augmented generation](../search/generative.md) defines the following: - The collection name (`Article`) - The default vectorizer module (`text2vec-openai`) - The generative module (`generative-openai`) - A set of properties (`title`, `chunk`, `chunk_no` and `url`) - The tokenization option for the `url` property - The vectorization option (`skip` vectorization) for the `url` property ```json { "class": "Article", "vectorizer": "text2vec-openai", "vectorIndexConfig": { "distance": "cosine" }, "moduleConfig": { "generative-openai": {} }, "properties": [ { "name": "title", "dataType": ["text"] }, { "name": "chunk", "dataType": ["text"] }, { "name": "chunk_no", "dataType": ["int"] }, { "name": "url", "dataType": ["text"], "tokenization": "field", "moduleConfig": { "text2vec-openai": { "skip": true } } } ] } ```Sample configuration: Images
This configuration for image search defines the following: - The collection name (`Image`) - The vectorizer module (`img2vec-neural`) - The `image` property configures collection to store image data. - The vector index distance metric (`cosine`) - A set of properties (`image`), with the `image` property set as `blob`. For image searches, see [Image search](../search/image.md). ```json { "class": "Image", "vectorizer": "img2vec-neural", "vectorIndexConfig": { "distance": "cosine" }, "moduleConfig": { "img2vec-neural": { "imageFields": ["image"] } }, "properties": [ { "name": "image", "dataType": ["blob"] } ] } ```Indexing limitations after data import
There are no index limitations when you add collection properties before you import data. If you add a new property after you import data, there is an impact on indexing.Property indexes are built at import time. If you add a new property after importing some data, pre-existing objects index aren't automatically updated to add the new property. This means pre-existing objects aren't added to the new property index. Queries may return unexpected results because the index only includes new objects.
To create an index that includes all of the objects in a collection, do one of the following: - New collections: Add all of the collection's properties before importing objects. - Existing collections: Export the existing data from the collection. Re-create it with the new property. Import the data into the updated collection. We are working on a re-indexing API to allow you to re-index the data after adding a property. This will be available in a future release.
Additional information
Notes: - Cross-references does not affect object vectors of the source or the target objects. - For multi-tenancy collection, you can establish a cross-reference from a multi-tenancy collection object to: - A non-multi-tenancy collection object, or - A multi-tenancy collection object belonging to the same tenant.What happens if the target object is deleted?
What happens if the `to` object is deleted? If an object is deleted, cross-references to it will be left intact. A [Get query using the inline fragment syntax](../search/basics.md#retrieve-cross-referenced-properties) will correctly retrieve only fields in the existing cross-references objects, but [getting the object by ID](../manage-objects/read.mdx#get-an-object-by-id) will show all cross-references, whether the objects they point to exist or not.## How collections work A Weaviate collection is defined by several core components and settings that enable data storage and vector search. Key elements include: import CollectionsSimplified from "/docs/weaviate/manage-collections/img/weaviate-collections-simplified.png"; import Link from "@docusaurus/Link"; export const textColumnContent = (
-
Objects:
- The fundamental units stored within a collection. Each object contains data properties and includes vector embeddings representing its meaning. (See the{" "} 'How-to: Manage objects' guide {" "} for more details on manipulating objects).
-
AI Model Integrations:
- Vectorizers (Embedding Models) : Generate vector embeddings from object properties to enable semantic search.
- Generative models : Used to perform RAG (Retrieval-Augmented Generation), combining retrieved data with generative AI capabilities.
- Reranker models : Refine search relevance by re-ordering the initial results from a search query.
-
Configuration Settings:{" "}
- Vector index : The vector index is used to speed up vector searches.
- Inverted index : Optimizes keyword searches by indexing textual object properties for faster lookups.
- Multi-tenancy : Enables securely storing data for multiple distinct tenants (users or groups) within the same collection instance.
- Aliases : Use aliases to re-point to different collections without changing your application code.
Additional information
In Weaviate, the inverted index supports search capabilities such as keyword search, filtering, and range queries. An inverted index maps from terms (_tokens_) back to the objects that contain them. This mapping allows Weaviate to quickly identify which objects contain specific terms or match certain criteria during search queries. You can [enable inverted indexes](#enable-inverted-index-for-keyword-searches-and-filtering) on properties and adjust various parameters that control [indexing behavior](#set-inverted-index-parameters) and [tokenization strategies](#set-tokenization-type-for-property). Proper configuration of these parameters is crucial for optimizing both search performance and storage efficiency.Enabling inverted index
The inverted index in Weaviate can be enabled through parameters at the property level. The names below are the REST and camelCase client spellings; the Python client uses the snake_case equivalent, so `indexFilterable` is `index_filterable`, `indexSearchable` is `index_searchable` and `indexRangeFilters` is `index_range_filters`. **`indexFilterable`** - Controls whether a property can be used in where filters. When set to `true`, the property values are indexed for efficient filtering operations. Disable this for properties that don't need filtering to save storage space. **`indexSearchable`** - Determines whether a property participates in keyword search queries. When `true`, the property's text content is tokenized and indexed for search. Set to `false` for properties that shouldn't be searchable to improve performance. **`indexRangeFilters`** - Enables range filtering capabilities (greater than, less than, etc.) for numerical and date properties. When enabled, additional indexing structures are created to support efficient range queries.Inverted index parameters
The inverted index in Weaviate can be configured through various parameters at the collection level. The names below are the REST and camelCase client spellings. In REST, `b` and `k1` are members of the `bm25` object. The Python client uses the snake_case equivalent, so they are `bm25_b`, `bm25_k1`, `index_null_state`, `index_property_length` and `index_timestamps`. **`bm25`: `b`** - Controls the degree of normalization by document length in the BM25 ranking algorithm. Values range from 0 to 1, where 0 means no length normalization and 1 means full normalization. Higher values favor shorter documents. **`bm25`: `k1`** - Controls term frequency saturation in BM25. Higher values make term frequency more important, while lower values reduce the impact of term frequency on scoring. **`indexNullState`** - Determines whether null values are indexed. When enabled, you can filter for objects that have null values in specific properties. **`indexPropertyLength`** - Controls whether the length of text properties is indexed. When enabled, allows filtering based on text length and can improve certain ranking algorithms. **`indexTimestamps`** - Enables indexing of creation and update timestamps for objects, allowing filtering and sorting operations.Tokenization methods
Tokenization determines how text content is broken down into individual terms that can be indexed and searched. Weaviate supports several tokenization strategies: **`word`** - The default tokenization that splits text on whitespace and punctuation, converting to lowercase. Best for general text search where you want to match individual words. **`lowercase`** - Splits text on whitespace only, then lowercases each token. Preserves symbols (like `&`, `@`, `_`) that `word` tokenization would strip. Good for case-insensitive matching where punctuation is meaningful, such as code snippets or email addresses. **`whitespace`** - Splits text only on whitespace characters, preserving punctuation and case. Good when punctuation is meaningful for search. **`field`** - Treats the entire property value as a single token without any processing. Use for exact matching of complete field values like IDs, email addresses, or URLs. **`trigram`** - Breaks text into overlapping 3-character sequences. Enables fuzzy matching and is useful for handling typos or partial matches. **`gse`** - Language-aware tokenization for Chinese and Japanese text. Disabled by default. Enable with the `ENABLE_TOKENIZER_GSE` environment variable. For Korean text, see the `kagome_kr` option. For the full list of supported tokenizers (including `kagome_ja`, `kagome_kr`, and the per-property text-analyzer options), see the [tokenization reference](../config-refs/collections.mdx#tokenization).Additional information
The examples use two different Weaviate instances, exposed through different ports. The same process can be used for two different instances as well. Cross-references in Weaviate are properties. As such, you can [retrieve cross-reference](./cross-references.mdx#read-cross-references) as a part of the object.What about cross-references?
These scripts should migrate cross-references as well.Cross-references are properties. As such, these cursor-based exports will include them. During restoration, restore the cross-referenced (i.e. "to") object first, then the object that contains the cross-reference (i.e. "from" object).
Additional information
To use replication factors greater than one, use a [multi-node deployment](/deploy/installation-guides/docker-installation.md#multi-node-configuration). For details on the configuration parameters, see the following: - [Replication](/weaviate/config-refs/collections.mdx#replication)Additional information
For details on the configuration parameters, see the following: - [Sharding](/weaviate/config-refs/collections.mdx#sharding)Additional information
import TenantNameFormat from "/_includes/tenant-names.mdx"; Tenant status is available from Weaviate `1.21` onwards.Manage vector index resource temperature
The vector index type affects its default resource type. * [`HNSW` index (default)](/weaviate/config-refs/indexing/vector-index.mdx#hnsw-index) - uses the vector index in RAM, a **Hot** resource. * [`Flat` index](/weaviate/config-refs/indexing/vector-index.mdx#flat-index) - uses the vector index on disk, a **Warm** resource. * [`Dynamic` index](/weaviate/config-refs/indexing/vector-index.mdx#dynamic-index) - starts as a flat index (using a **Warm** resource), then switches to an HNSW index (a **Hot** resource) at a predetermined threshold.
### Inactive
An `Inactive` tenant is not available for queries nor CRUD operations.
The tenant's object data, vector index and inverted index are stored on disk, using `warm` resources. This can lower Weaviate's memory requirements compared to active tenants that use `hot` resources.
Since the tenant is stored locally, inactive tenants can be activated quickly.
### Offloaded
import OffloadingLimitation from '/_includes/offloading-limitation.mdx';
## Activate tenant
To activate an `INACTIVE` tenant from disk, or to onload and activate an `OFFLOADED` tenant from cloud, call:
Additional information
Collection level settings override default values and general configuration parameters such as [environment variables](/deploy/configuration/env-vars/index.md). - [Available model integrations](../model-providers/index.md) - [Vectorizer configuration references](/weaviate/config-refs/collections.mdx#vector-configuration)Additional information
Read more about index types & compression in: - [References: Vector index](../config-refs/indexing/vector-index.mdx) - [Concepts: Vector index](../concepts/indexing/vector-index.md)Additional information
Read more about index types & compression in: - [References: Vector index](../config-refs/indexing/vector-index.mdx) - [Concepts: Vector index](../concepts/indexing/vector-index.md)Additional information
For details on the configuration parameters, see the following: - [Distances](../config-refs/distances.md) - [Vector indexes](../config-refs/indexing/vector-index.mdx)Additional information
To create an object, specify the following: - The object data you want to add - The target collection - If [multi-tenancy](../concepts/data.md#multi-tenancy) is enabled, [specify the tenant](../manage-collections/multi-tenancy.mdx) By default, [auto-schema](/weaviate/config-refs/collections.mdx#auto-schema) creates new collections and adds new properties.Weaviate throws an error if you provide a duplicate ID. Use deterministic IDs to avoid inserting duplicate objects. :::
Additional information
To generate deterministic IDs, use one of these methods: - `generate_uuid5` (Python) - `generateUuid5` (TypeScript)Additional information
- To delete objects, you must provide the collection name as well as identifying criteria (e.g. object id or filters). - For [multi-tenancy](../concepts/data.md#multi-tenancy) collections, you will also need to specify the tenant name when deleting objects. See [Manage data: multi-tenancy operations](../manage-collections/multi-tenancy.mdx) for details on how.{" "}
Additional information
- There is a configurable [maximum limit (QUERY_MAXIMUM_RESULTS)](/deploy/configuration/env-vars/index.md#general) on the number of objects that can be deleted in a single query (default 10,000). To delete more objects than the limit, re-run the query.Limitations
There is an upper limit (`QUERY_MAXIMUM_RESULTS`) to how many objects can be deleted using a single query. This protects against unexpected memory surges and very-long-running requests which would be prone to client-side timeouts or network interruptions. Objects are deleted in the same order that they would be fetched, by order of UUID. To delete more objects than the limit, run the same query multiple times until no objects are matched anymore. The default `QUERY_MAXIMUM_RESULTS` value is 10,000. This may be configurable, e.g. in [the environment variables](/deploy/configuration/env-vars/index.md).Example response
It should produce a response like the one below:## Questions and feedback import DocsFeedback from "/_includes/docs-feedback.mdx";
Additional information
Additional information
- Partial updates use{" "}
Server not showing up
- Check that the file path is correct for your OS - Restart the app completely - Ensure the URL is entered correctly - Verify the config file syntax is valid JSONConnection errors
If you see connection errors: 1. Verify the server URL is accessible: ```bash curl https://weaviate-docs.mcp.kapa.ai ``` 2. Check your internet connection 3. Try the MCP Inspector to debug: ```bash npx @modelcontextprotocol/inspector ```No results returned
If queries return empty results: - Check that your question is relevant to Weaviate documentation - Try rephrasing your query - Verify the MCP server is properly configured (test with curl)Rate limiting
The public Weaviate MCP server may have rate limits. If you experience issues: - Wait a few minutes before trying againLocal Model"] end %% Weaviate section (middle) subgraph weaviate["Weaviate"] vectorizer["🔌 Model Provider
Integration"] core["⚡️ Data & vector store"] end %% User System (bottom) subgraph user["User System"] data["📄 Data"] end %% Connections data -->|"1\. Insert objects"| core core -->|"2\. Request vector"| vectorizer vectorizer -->|"3\. Request vector"| inference inference -->|"4\. Vector"| vectorizer vectorizer -->|"5\. Vector"| core %% Apply styles class user systemBox class weaviate weaviateBox class provider providerBox class data,core,vectorizer,inference component ``` Weaviate generates embeddings for objects as follows: - Selects properties with `text` or `text[]` data types unless they are configured to be skipped - Sorts properties in alphabetical (a-z) order before concatenating values - Prepends the collection name if configured :::note Case sensitivity For Weaviate versions before `v1.27`, the string created above is lowercased before being sent to the model provider. Starting in `v1.27`, the string is sent as is. If you prefer the text to be lowercased, you can do so by setting the `LOWERCASE_VECTORIZATION_INPUT` environment variable. The text is always lowercased for the `text2vec-contextionary` integration. ::: ## Questions and feedback import DocsFeedback from '/\_includes/docs-feedback.mdx';
API key headers
From `v1.27.7`, `v1.26.12` and `v1.25.27`, `X-Goog-Vertex-Api-Key` and `X-Goog-Studio-Api-Key` headers are supported for Vertex AI users and Gemini API respectively. We recommend these headers for highest compatibility.Consider `X-Google-Vertex-Api-Key`, `X-Google-Studio-Api-Key`, `X-Google-Api-Key` and `X-PaLM-Api-Key` deprecated.
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
Deprecated models
The following models are available, but deprecated: - `multilingual-22-12` - `large` - `medium` - `small`For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate. - To enable the module, include it in the `ENABLE_MODULES` environment variable available to Weaviate, e.g. `ENABLE_MODULES="generative-deepseek"` (add it to your existing comma-separated list if other modules are enabled).For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
Deprecated models
The following models have been deprecated by Google and are no longer supported. They may not function as expected. - `text-embedding-004` - `embedding-001` - `textembedding-gecko@001` - `textembedding-gecko@002` - `textembedding-gecko@003` - `textembedding-gecko@latest` - `textembedding-gecko-multilingual@001` - `textembedding-gecko-multilingual@latest` - `text-embedding-preview-0409` - `text-multilingual-embedding-preview-0409`For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Deprecated models
The following models have been deprecated by Google. They may not function as expected. **Vertex AI:** - `chat-bison` - `chat-bison-32k` - `chat-bison@002` - `chat-bison-32k@002` - `chat-bison@001` - `gemini-1.0-pro-002` - `gemini-1.0-pro-001` - `gemini-1.0-pro` - `gemini-1.5-pro-preview-0514` - `gemini-1.5-pro-preview-0409` - `gemini-1.5-flash-preview-0514` **Gemini API:** - `chat-bison-001` - `gemini-pro`Search locations for Google Vertex AI credentials
Once `USE_GOOGLE_AUTH` is set to `true`, Weaviate will look for credentials in the following places, preferring the first location found: 1. A JSON file whose path is specified by the `GOOGLE_APPLICATION_CREDENTIALS` environment variable. For workload identity federation, refer to [this link](https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation) on how to generate the JSON configuration file for on-prem/non-Google cloud platforms. 2. A JSON file in a location known to the `gcloud` command-line tool. On Windows, this is `%APPDATA%/gcloud/application_default_credentials.json`. On other systems, `$HOME/.config/gcloud/application_default_credentials.json`. 3. On Google App Engine standard first generation runtimes (<= Go 1.9) it uses the appengine.AccessToken function. 4. On Google Compute Engine, Google App Engine standard second generation runtimes (>= Go 1.11), and Google App Engine flexible environment, it fetches credentials from the metadata server.For Weaviate Cloud (WCD) users
This integration is not available for Weaviate Cloud (WCD) instances, as it requires a locally running GPT4All instance.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.#### Docker Option 2: Add the configuration manually Alternatively, add the configuration to the `docker-compose.yml` file manually as in the example below. ```yaml services: weaviate: # Other Weaviate configuration environment: GPT4ALL_INFERENCE_API: http://text2vec-gpt4all:8080 # Set the inference API endpoint text2vec-gpt4all: # Set the name of the inference container image: cr.weaviate.io/semitechnologies/gpt4all-inference:all-MiniLM-L6-v2 ``` - `GPT4ALL_INFERENCE_API` environment variable sets the inference API endpoint - `text2vec-gpt4all` is the name of the inference container - `image` is the container image
Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is not available for Weaviate Cloud (WCD) instances, as it requires spinning up a container with the ImageBind model.#### Docker Option 2: Add the configuration manually Alternatively, add the configuration to the `docker-compose.yml` file manually as in the example below. ```yaml services: weaviate: # Other Weaviate configuration environment: BIND_INFERENCE_API: http://multi2vec-bind:8080 # Set the inference API endpoint multi2vec-bind: # Set the name of the inference container mem_limit: 12g image: cr.weaviate.io/semitechnologies/multi2vec-bind:imagebind environment: ENABLE_CUDA: 0 # Set to 1 to enable ``` - `BIND_INFERENCE_API` environment variable sets the inference API endpoint - `multi2vec-bind` is the name of the inference container - `image` is the container image - `ENABLE_CUDA` environment variable enables GPU usage
Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is not available for Weaviate Cloud (WCD) instances, as it requires a locally running Model2Vec instance.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.#### Docker Option 2: Add the configuration manually Alternatively, add the configuration to the `docker-compose.yml` file manually as in the example below. ```yaml services: weaviate: # Other Weaviate configuration environment: MODEL2VEC_INFERENCE_API: http://text2vec-model2vec:8080 # Set the inference API endpoint text2vec-model2vec: # Set the name of the inference container image: cr.weaviate.io/semitechnologies/model2vec-inference:minishlab-potion-base-32M ``` - `MODEL2VEC_INFERENCE_API` environment variable sets the inference API endpoint - `text2vec-model2vec` is the name of the inference container - `image` is the container image
Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.From Weaviate `v1.25.22`, `v1.26.8`, and `v1.27.1`, the `text2vec-octoai` and `generative-octoai` modules are inactive. Every vectorization and generation request they receive fails server-side with the error `OctoAI is permanently shut down`. The configuration, import, search, and RAG examples on these pages therefore no longer run against OctoAI, and are kept only as a record of how the integrations used to be configured. Of the options below, only "bring your own vectors" (Option 1) keeps an existing OctoAI collection usable.
If you have a collection that is using an OctoAI integration, consider your options depending on whether you are using OctoAI's embedding models ([your options](#for-collections-with-octoai-embedding-integrations)) or generative models ([your options](#for-collections-with-octoai-generative-ai-integrations)). #### For collections with OctoAI embedding integrations OctoAI provided `thenlper/gte-large` as the embedding model. This model is also available through the [Hugging Face API](../../huggingface/embeddings.md).
After the shutdown date, this model will no longer be available through OctoAI. If you are using this integration, you have the following options:
**Option 1: Use the existing collection, and provide your own vectors**
You can continue to use the existing collection, provided that you rely on some other method to generate the required embeddings yourself for any new data, and for queries. If you are unfamiliar with the "bring your own vectors" approach, [refer to this starter guide](../../../starter-guides/custom-vectors.mdx).
**Option 2: Migrate to a new collection with another model provider** Alternatively, you can migrate your data to a new collection ([read how](#how-to-migrate)). At this point, you can re-use the existing embeddings or choose a new model.
- **Re-using the existing embeddings** will save on time and inference costs. - **Choosing a new model** will allow you to explore new models and potentially improve the performance of your application. If you would like to re-use the existing embeddings, you must select a model provider (e.g. [Hugging Face API](../../huggingface/embeddings.md)) that offers the same embedding model.
You can also select a new model with any embedding model provider. This will require you to re-generate the embeddings for your data, as the existing embeddings will not be compatible with the new model.
#### For collections with OctoAI generative AI integrations If you are only using the generative AI integration, you do not need to migrate your data to a new collection.
Follow [this how-to](../../../manage-collections/generative-reranker-models.mdx#update-the-generative-model-integration) to re-configure your collection with a new generative AI model provider. Note this requires Weaviate `v1.25.23`, `v1.26.8`, `v1.27.1`, or later.
You can select any model provider that offers generative AI models.
If you would like to continue to use the same model that you used with OctoAI, providers such as [Anyscale](../../anyscale/generative.md), [FriendliAI](../../friendliai/generative.md), [Mistral](../../mistral/generative.md) or local models with [Ollama](../../ollama/generative.md) each offer some of the suite of models that OctoAI provided.
#### How to migrate An outline of the migration process is as follows:
- Create a new collection with the desired model provider integration(s). - Export the data from the existing collection. - (Optional) To re-use the existing embeddings, export the data with the existing embeddings. - Import the data into the new collection. - (Optional) To re-use the existing embeddings, import the data with the existing embeddings. - Update your application to use the new collection.
See [How-to manage data: migrate data](../../../manage-collections/migrate.mdx) for examples on migrating data objects between collections. ::: --- ### Weaviate/Model Providers/Ollama/ Category .Json (docs/weaviate/model-providers/ollama/_category_.json) { "label": "Ollama (locally hosted)", "position": 370 } --- ### Weaviate/Model Providers/Ollama/Embeddings (docs/weaviate/model-providers/ollama/embeddings.md) --- title: Text Embeddings description: Ollama Embedding Provider sidebar_position: 20 image: og/docs/integrations/provider_integrations_ollama.jpg # tags: ['model providers', 'ollama', 'embeddings'] --- # Ollama Embeddings with Weaviate import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; import FilteredTextBlock from '@site/src/components/Documentation/FilteredTextBlock'; import PyConnect from '!!raw-loader!../_includes/provider.connect.local.py'; import TSConnect from '!!raw-loader!../_includes/provider.connect.local.ts'; import PyCode from '!!raw-loader!../_includes/provider.vectorizer.py'; import TSCode from '!!raw-loader!../_includes/provider.vectorizer.ts'; Weaviate's integration with Ollama's models allows you to access their models' capabilities directly from Weaviate. [Configure a Weaviate vector index](#configure-the-vectorizer) to use an Ollama embedding model, and Weaviate will generate embeddings for various operations using the specified model via your local Ollama instance. This feature is called the *vectorizer*. At [import time](#data-import), Weaviate generates text object embeddings and saves them into the index. For [vector](#vector-near-text-search) and [hybrid](#hybrid-search) search operations, Weaviate converts text queries into embeddings. ## Requirements ### Ollama This integration requires a locally running Ollama instance with your selected model available. Refer to the [Ollama documentation](https://ollama.com/) for installation and model download instructions. ### Weaviate configuration Your Weaviate instance must be configured with the Ollama vectorizer integration (`text2vec-ollama`) module.
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.To use Ollama with Weaviate Cloud, make sure your Ollama server is running and accessible from the Weaviate Cloud instance. If you are running Ollama on your own machine, you may need to expose it to the internet. Carefully consider the security implications of exposing your Ollama server to the internet.
For use cases such as this, consider using a self-hosted Weaviate instance, or another API-based integration method.
For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.To use Ollama with Weaviate Cloud, make sure your Ollama server is running and accessible from the Weaviate Cloud instance. If you are running Ollama on your own machine, you may need to expose it to the internet. Carefully consider the security implications of exposing your Ollama server to the internet.
For use cases such as this, consider using a self-hosted Weaviate instance, or another API-based integration method.
For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
Deprecated models
The following models are available, but deprecated: * Codex * babbage-001 * davinci-001 * curie [Source](https://platform.openai.com/docs/deprecations)For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Older models
The following models are available, but not recommended: * [davinci 002](https://platform.openai.com/docs/models/overview) * [davinci 003](https://platform.openai.com/docs/models/overview)For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.To build an image with a model from the Hugging Face Hub, create a new `Dockerfile` similar to the following.
Save the `Dockerfile` as `my-inference-image.Dockerfile`. (You can name it anything you like.)
```yaml FROM semitechnologies/transformers-inference:custom RUN MODEL_NAME=distilroberta-base ./download.py ```
To build an image with a local, custom model, create a new `Dockerfile` similar to the following, replacing `./my-model` with the path to your model folder.
Save the `Dockerfile` as `my-inference-image.Dockerfile`. (You can name it anything you like.)
This will create a custom image for a model stored in a local folder `my-model` on your machine.
```yaml FROM semitechnologies/transformers-inference:custom COPY ./my-model /app/models/model ```
Do not modify `/app/models/model`, as this is the path where the application expects to find the model.
To build an image with a model from the Hugging Face Hub, create a new `Dockerfile` similar to the following.
Save the `Dockerfile` as `my-inference-image.Dockerfile`. (You can name it anything you like.)
```yaml FROM semitechnologies/multi2vec-clip:custom RUN CLIP_MODEL_NAME=clip-ViT-B-32 TEXT_MODEL_NAME=clip-ViT-B-32 ./download.py ```
To build an image with a local, custom model, create a new `Dockerfile` similar to the following, replacing `./my-text-model` and `./my-clip-model` with the paths to your model folders.
Save the `Dockerfile` as `my-inference-image.Dockerfile`. (You can name it anything you like.)
This will create a custom image for the models stored in the local folders `my-text-model` and `my-clip-model` on your machine.
```yaml FROM semitechnologies/multi2vec-clip:custom COPY ./my-text-model /app/models/text COPY ./my-clip-model /app/models/clip ```
Do not modify `/app/models/text` or `/app/models/clip`, as these are the paths where the application expects to find the model files.
For Weaviate Cloud (WCD) users
This integration is not available for Weaviate Cloud (WCD) instances, as it requires spinning up a container with the Hugging Face model.#### Docker Option 2: Add the configuration manually Alternatively, add the configuration to the `docker-compose.yml` file manually as in the example below. ```yaml services: weaviate: # Other Weaviate configuration environment: CLIP_INFERENCE_API: http://multi2vec-clip:8080 # Set the inference API endpoint multi2vec-clip: # Set the name of the inference container image: cr.weaviate.io/semitechnologies/multi2vec-clip:sentence-transformers-clip-ViT-B-32-multilingual-v1 environment: ENABLE_CUDA: 0 # Set to 1 to enable ``` - `CLIP_INFERENCE_API` environment variable sets the inference API endpoint - `multi2vec-clip` is the name of the inference container - `image` is the container image - `ENABLE_CUDA` environment variable enables GPU usage Set `image` from a [list of available models](#available-models) to specify a particular model to be used.
Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is not available for Weaviate Cloud (WCD) instances, as it requires spinning up a container with the Hugging Face model.#### Docker Option 2: Add the configuration manually Alternatively, add the configuration to the `docker-compose.yml` file manually as in the example below. ```yaml services: weaviate: # Other Weaviate configuration environment: ENABLE_MODULES: text2vec-transformers # Enable this module TRANSFORMERS_INFERENCE_API: http://text2vec-transformers:8080 # Set the inference API endpoint text2vec-transformers: # Set the name of the inference container image: cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-multi-qa-MiniLM-L6-cos-v1 environment: ENABLE_CUDA: 0 # Set to 1 to enable ``` - `TRANSFORMERS_INFERENCE_API` environment variable sets the inference API endpoint - `text2vec-transformers` is the name of the inference container - `image` is the container image - `ENABLE_CUDA` environment variable enables GPU usage Set `image` from a [list of available models](#available-models) to specify a particular model to be used.
Vectorization behavior
See the full list
|Model Name|Image Name| |---|---| |`distilbert-base-uncased` ([Info](https://huggingface.co/distilbert-base-uncased))|`cr.weaviate.io/semitechnologies/transformers-inference:distilbert-base-uncased`| |`sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2` ([Info](https://huggingface.co/sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-paraphrase-multilingual-MiniLM-L12-v2`| |`sentence-transformers/multi-qa-MiniLM-L6-cos-v1` ([Info](https://huggingface.co/sentence-transformers/multi-qa-MiniLM-L6-cos-v1))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-multi-qa-MiniLM-L6-cos-v1`| |`sentence-transformers/multi-qa-mpnet-base-cos-v1` ([Info](https://huggingface.co/sentence-transformers/multi-qa-mpnet-base-cos-v1))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-multi-qa-mpnet-base-cos-v1`| |`sentence-transformers/all-mpnet-base-v2` ([Info](https://huggingface.co/sentence-transformers/all-mpnet-base-v2))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-all-mpnet-base-v2`| |`sentence-transformers/all-MiniLM-L12-v2` ([Info](https://huggingface.co/sentence-transformers/all-MiniLM-L12-v2))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-all-MiniLM-L12-v2`| |`sentence-transformers/paraphrase-multilingual-mpnet-base-v2` ([Info](https://huggingface.co/sentence-transformers/paraphrase-multilingual-mpnet-base-v2))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-paraphrase-multilingual-mpnet-base-v2`| |`sentence-transformers/all-MiniLM-L6-v2` ([Info](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-all-MiniLM-L6-v2`| |`sentence-transformers/multi-qa-distilbert-cos-v1` ([Info](https://huggingface.co/sentence-transformers/multi-qa-distilbert-cos-v1))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-multi-qa-distilbert-cos-v1`| |`sentence-transformers/gtr-t5-base` ([Info](https://huggingface.co/sentence-transformers/gtr-t5-base))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-gtr-t5-base`| |`sentence-transformers/gtr-t5-large` ([Info](https://huggingface.co/sentence-transformers/gtr-t5-large))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-gtr-t5-large`| |`google/flan-t5-base` ([Info](https://huggingface.co/google/flan-t5-base))|`cr.weaviate.io/semitechnologies/transformers-inference:google-flan-t5-base`| |`google/flan-t5-large` ([Info](https://huggingface.co/google/flan-t5-large))|`cr.weaviate.io/semitechnologies/transformers-inference:google-flan-t5-large`| |`BAAI/bge-small-en-v1.5` ([Info](https://huggingface.co/BAAI/bge-small-en-v1.5))|`cr.weaviate.io/semitechnologies/transformers-inference:baai-bge-small-en-v1.5`| |`BAAI/bge-base-en-v1.5` ([Info](https://huggingface.co/BAAI/bge-base-en-v1.5))|`cr.weaviate.io/semitechnologies/transformers-inference:baai-bge-base-en-v1.5`|See the full list
|Model Name|Image Name| |---|---| |`facebook/dpr-ctx_encoder-single-nq-base` ([Info](https://huggingface.co/facebook/dpr-ctx_encoder-single-nq-base))|`cr.weaviate.io/semitechnologies/transformers-inference:facebook-dpr-ctx_encoder-single-nq-base`| |`facebook/dpr-question_encoder-single-nq-base` ([Info](https://huggingface.co/facebook/dpr-question_encoder-single-nq-base))|`cr.weaviate.io/semitechnologies/transformers-inference:facebook-dpr-question_encoder-single-nq-base`| |`vblagoje/dpr-ctx_encoder-single-lfqa-wiki` ([Info](https://huggingface.co/vblagoje/dpr-ctx_encoder-single-lfqa-wiki))|`cr.weaviate.io/semitechnologies/transformers-inference:vblagoje-dpr-ctx_encoder-single-lfqa-wiki`| |`vblagoje/dpr-question_encoder-single-lfqa-wiki` ([Info](https://huggingface.co/vblagoje/dpr-question_encoder-single-lfqa-wiki))|`cr.weaviate.io/semitechnologies/transformers-inference:vblagoje-dpr-question_encoder-single-lfqa-wiki`| |Bar-Ilan University NLP Lab Models| |`biu-nlp/abstract-sim-sentence` ([Info](https://huggingface.co/biu-nlp/abstract-sim-sentence))|`cr.weaviate.io/semitechnologies/transformers-inference:biu-nlp-abstract-sim-sentence`| |`biu-nlp/abstract-sim-query` ([Info](https://huggingface.co/biu-nlp/abstract-sim-query))|`cr.weaviate.io/semitechnologies/transformers-inference:biu-nlp-abstract-sim-query`|See the full list
|Model Name|Image Name| |---|---| |`Snowflake/snowflake-arctic-embed-xs` ([Info](https://huggingface.co/Snowflake/snowflake-arctic-embed-xs))|`cr.weaviate.io/semitechnologies/transformers-inference:snowflake-snowflake-arctic-embed-xs`| |`Snowflake/snowflake-arctic-embed-s` ([Info](https://huggingface.co/Snowflake/snowflake-arctic-embed-s))|`cr.weaviate.io/semitechnologies/transformers-inference:snowflake-snowflake-arctic-embed-s`| |`Snowflake/snowflake-arctic-embed-m` ([Info](https://huggingface.co/Snowflake/snowflake-arctic-embed-m))|`cr.weaviate.io/semitechnologies/transformers-inference:snowflake-snowflake-arctic-embed-m`| |`Snowflake/snowflake-arctic-embed-l` ([Info](https://huggingface.co/Snowflake/snowflake-arctic-embed-l))|`cr.weaviate.io/semitechnologies/transformers-inference:snowflake-snowflake-arctic-embed-l`|See the full list
|Model Name|Image Name| |---|---| |`sentence-transformers/all-MiniLM-L6-v2` ([Info](https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2))|`cr.weaviate.io/semitechnologies/transformers-inference:sentence-transformers-all-MiniLM-L6-v2-onnx`| |`BAAI/bge-small-en-v1.5` ([Info](https://huggingface.co/BAAI/bge-small-en-v1.5))|`cr.weaviate.io/semitechnologies/transformers-inference:baai-bge-small-en-v1.5-onnx`| |`BAAI/bge-base-en-v1.5` ([Info](https://huggingface.co/BAAI/bge-base-en-v1.5))|`cr.weaviate.io/semitechnologies/transformers-inference:baai-bge-base-en-v1.5-onnx`| |`BAAI/bge-m3` ([Info](https://huggingface.co/BAAI/bge-m3))|`cr.weaviate.io/semitechnologies/transformers-inference:baai-bge-m3-onnx`| |`Snowflake/snowflake-arctic-embed-xs` ([Info](https://huggingface.co/Snowflake/snowflake-arctic-embed-xs))|`cr.weaviate.io/semitechnologies/transformers-inference:snowflake-snowflake-arctic-embed-xs-onnx`| |`Snowflake/snowflake-arctic-embed-s` ([Info](https://huggingface.co/Snowflake/snowflake-arctic-embed-s))|`cr.weaviate.io/semitechnologies/transformers-inference:snowflake-snowflake-arctic-embed-s-onnx`| |`Snowflake/snowflake-arctic-embed-m` ([Info](https://huggingface.co/Snowflake/snowflake-arctic-embed-m))|`cr.weaviate.io/semitechnologies/transformers-inference:snowflake-snowflake-arctic-embed-m-onnx`| |`Snowflake/snowflake-arctic-embed-l` ([Info](https://huggingface.co/Snowflake/snowflake-arctic-embed-l))|`cr.weaviate.io/semitechnologies/transformers-inference:snowflake-snowflake-arctic-embed-l-onnx`|For Weaviate Cloud (WCD) users
This integration is not available for Weaviate Cloud (WCD) instances, as it requires spinning up a container with the Hugging Face model.#### Docker Option 2: Add the configuration manually Alternatively, add the configuration to the `docker-compose.yml` file manually as in the example below. ```yaml services: weaviate: # Other Weaviate configuration environment: RERANKER_INFERENCE_API: http://reranker-transformers:8080 # Set the inference API endpoint reranker-transformers: # Set the name of the inference container image: cr.weaviate.io/semitechnologies/reranker-transformers:cross-encoder-ms-marco-MiniLM-L-6-v2 environment: ENABLE_CUDA: 0 # Set to 1 to enable ``` - `RERANKER_INFERENCE_API` environment variable sets the inference API endpoint - `reranker-transformers` is the name of the inference container - `image` is the container image - `ENABLE_CUDA` environment variable enables GPU usage Set `image` from a [list of available models](#available-models) to specify a particular model to be used.
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Vectorization behavior
Model support history
- `v1.36`: - Added `voyage-4`, `voyage-4-lite`, `voyage-4-large` - Added `voyage-3.5`, `voyage-3.5-lite`, `voyage-context-3` - `v1.24.25`, `v1.25.18`, `v1.26.5`: - Added `voyage-3`, `voyage-3-lite` - Default model changed to `voyage-3` from `voyage-large-2` - `v1.24.14`, `v1.25.1`: - Added `voyage-large-2-instruct` - Removed `voyage-lite-02-instruct` - `v1.24.9`: - Added `voyage-law-2`, `voyage-lite-02-instruct` - `v1.24.2`: - Introduced `text2vec-voyage`, with `voyage-large-2`, `voyage-code-2`, `voyage-2` supportFor Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.Model support history
- Added `rerank-2.5`, `rerank-2.5-lite` - `v1.24.25`, `v1.25.18`, `v1.26.5`: - Added `rerank-2`, `rerank-2-lite` - `v1.24.18`, `v1.25.3`: - Added `rerank-1` - `1.24.7`: - Introduced `reranker-voyageai`, with `rerank-lite-1` supportBasic configuration (without MUVERA)
If you prefer to store the raw multi-vector embeddings without MUVERA compression, use this configuration. Note that this will consume more memory.Vectorization behavior
For Weaviate Cloud (WCD) users
This integration is enabled by default on Weaviate Cloud (WCD) instances.For self-hosted users
- Check the [cluster metadata](/deploy/configuration/status.md#cluster-metadata) to verify if the module is enabled. - Follow the [how-to configure modules](../../configuration/modules.md) guide to enable the module in Weaviate.
- [The Cohere reranker integration page](../model-providers/cohere/reranker.md) shows how to use Cohere's reranker models in Weaviate.
- [The Anthropic generative AI integration page](../model-providers/anthropic/generative.md) shows how to use Anthropic's generative AI models in Weaviate.
### Module characteristics - Naming convention: - Vectorizer (Retriever module): `
See original text
> *The Loch Ness Monster (Scottish Gaelic: Uilebheist Loch Nis), affectionately known as Nessie, is a creature in Scottish folklore that is said to inhabit Loch Ness in the Scottish Highlands. It is often described as large, long-necked, and with one or more humps protruding from the water. Popular interest and belief in the creature has varied since it was brought to worldwide attention in 1933. Evidence of its existence is anecdotal, with a number of disputed photographs and sonar readings.* > *The scientific community explains alleged sightings of the Loch Ness Monster as hoaxes, wishful thinking, and the misidentification of mundane objects. The pseudoscience and subculture of cryptozoology has placed particular emphasis on the creature.*Answer
> Our goal is three-folded. Firstly, we want to make it as easy as possible for others to create their own semantic systems or vector search engines (hence, our APIs are GraphQL based). Secondly, we have a strong focus on the semantic element (the "knowledge" in "vector databases," if you will). Our ultimate goal is to have Weaviate help you manage, index, and "understand" your data so that you can build newer, better, and faster applications. And thirdly, we want you to be able to run it everywhere. This is the reason why Weaviate comes containerized.Answer
> Yes. Weaviate works well as a vector store for agent memory. If you'd rather not build and operate the memory layer yourself, we also offer [Engram](/engram/), a dedicated managed memory service built on Weaviate that automatically extracts, stores, and retrieves memories for your agents and applications.Answer
> Other database systems like Elasticsearch rely on inverted indexes, which makes search super fast. Weaviate also uses inverted indexes to store data and values. But additionally, Weaviate is also a vector-native search database, which means that data is stored as vectors, which enables semantic search. This combination of data storage is unique, and enables fast, filtered and semantic search from end-to-end.Answer
> Yes, we do - check out [Weaviate Cloud](https://weaviate.io/pricing).Answer
> You can find this in the [architecture section](/weaviate/concepts/resources.md#an-example-calculation) of the docs.Answer
> Weaviate uses Docker images as a means to distribute releases and uses Docker Compose to tie a module-rich runtime together. If you are new to those technologies, we recommend reading the [Docker Introduction for Weaviate Users](https://medium.com/semi-technologies/what-weaviate-users-should-know-about-docker-containers-1601c6afa079).Answer
> There are three levels: > 1. You have no volume configured (the default in our `Docker Compose` files), if the container restarts (e.g. due to a crash, or because of `docker stop/start`) your data is kept > 2. You have no volume configured (the default in our `Docker Compose` files), if the container is removed (e.g. from `docker compose down` or `docker rm`) your data is gone > 3. If a volume is configured, your data is persisted regardless of what happens to the container. They can be completely removed or replaced, next time they start up with a volume, all your data will be thereAnswer
> Role-based access control (RBAC) can be enabled when configuring Weaviate via the `AUTHORIZATION_RBAC_ENABLED` environment variable. > For more info visit the [RBAC: Configuration](/deploy/configuration/configuring-rbac.md) guide.Answer
> As a rule of thumb, the smaller the units, the more accurate the search will be. Two objects of e.g. a sentence would most likely contain more information in their vector embedding than a common vector (which is essentially just the mean of sentences). At the same time more objects leads to a higher import time and (since each vector also makes up some data) more space. (E.g. when using transformers, a single vector is 768xfloat32 = 3KB. This can easily make a difference if you have millions, etc.) of vectors. As a rule of thumb, the more vectors you have the more memory you're going to need. > > So, basically, it's a set of tradeoffs. Personally we've had great success with using paragraphs as individual units, as there's little benefit in going even more granular, but it's still much more precise than whole chapters, etc. > > You can use cross-references to link e.g. chapters to paragraphs. Note that resolving a cross-references takes a performance penalty. Essentially resolving A1->B1 is the same cost as looking up both A1 and B1 indvidually. But at scale, this can add up. > > So, consider denormalizing your data, i.e. storing the data in a way that you can resolve the cross-references without actually looking them up. This is a common pattern in databases, and it's also a common pattern in Weaviate.Answer
> In short: for convenience you can add relations to your data schema, because you need less code and queries to get data. But resolving references in queries takes some of the performance. > > 1. If your ultimate goal is performance, references probably don't add any value, as resolving them adds a cost. > 2. If your goal is represent complex relationships between your data items, they can help a lot. You can resolve references in a single query, so if you have collections with multiple links, it could definitely be helpful to resolve some of those connections in a single query. On the other hand, if you have a single (bi-directional) reference in your data, you could also just denormalize the links (e.g. with an ID field) and resolve them during search.Answer
> Yes, it is possible to reference to one or more objects (Class -> one or more Classes) through cross-references. Referring to lists or arrays of primitives, this will be available [soon](https://github.com/weaviate/weaviate/issues/1611).Answer
> The `text` and `string` datatypes differ in tokenization behavior. Note that `string` is now deprecated. Read more in [this section](../config-refs/collections.mdx#tokenization) on the differences.Answer
Yes. Each collection itself acts like namespaces. Additionally, you can use the [multi-tenancy](../concepts/data.md#multi-tenancy) feature to create isolated storage for each tenant. This is especially useful for use cases where one cluster might be used to store data for multiple customers or users.Answer
> The UUID must be presented as a string matching the [Canonical Textual representation](https://en.wikipedia.org/wiki/Universally_unique_identifier#Format). If you don't specify a UUID, Weaviate will generate a `v4` i.e. a random UUID. If you generate them yourself you could either use random ones or deterministically determine them based on some fields that you have. For this you'll need to use [`v3` or `v5`](https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)).Answer
> Yes, Weaviate creates a UUID if one is not specified.Answer
> We use a schema because it focusses on the representation of your data (in our case in the GraphQL API) but you can use a Weaviate schema to express an ontology. One of Weaviate's core features is that it semantically interprets your schema (and with that your ontology) so that you can search for concepts rather than formally defined entities.Answer
> Read about how taxonomies, ontologies and schemas are related to Weaviate in [this blog post](https://medium.com/semi-technologies/taxonomies-ontologies-and-schemas-how-do-they-relate-to-weaviate-9f76739fc695).Answer
> Sometimes, users work with custom terminology, which often comes in the form of abbreviations or jargon. You can find more information on how to use the endpoint [here](/weaviate/modules/text2vec-contextionary.md#extending-the-contextionary)Answer
> Every data object gets its vector embedding based on its semantic meaning. In a nutshell, we calculate the vector position of the data object based on the words and concepts used in the data object. The existing model in the contextionary gives already enough context. If you want to get in the nitty-gritty, you can [browse the code here](https://github.com/weaviate/contextionary/tree/master/server), but you can also ask a [specific question on Stackoverflow](https://stackoverflow.com/tags/weaviate/) and tag it with Weaviate.Answer
> Because you are probably one of the first that needs one! Ping us [here on GitHub](https://github.com/weaviate/weaviate/issues), and we will make sure in the next iteration it will become available (unless you want it in [Silbo Gomero](https://en.wikipedia.org/wiki/Silbo_Gomero) or another language which is whistled).Answer
> How can Weaviate interpret that you mean a company, as in business, and not as the division of the army? We do this based on the structure of the schema and the data you add. A schema in Weaviate might contain a company collection with the property name and the value Apple. This simple representation (company, name, apple) is already enough to gravitate the vector position of the data object towards businesses or the iPhone. You can read [here](../) how we do this, or you can ask a specific question on [Stackoverflow](https://stackoverflow.com/tags/weaviate/) and tag it with Weaviate.Answer
> You can create multiple collections in the Weaviate schema, where one collection will act like a namespace in Kubernetes or an index in Elasticsearch. So the spaces will be completely independent, this allows space 1 to use completely different embeddings from space 2. The configured vectorizer is always scoped only to a single collection. You can also use Weaviate's Cross-Reference features to make a graph-like connection between an object of Class 1 to the corresponding object of Class 2 to make it easy to see the equivalent in the other space.Answer
import HowToGetObjectCount from '/_includes/how.to.get.object.count.mdx'; > This `Aggregate` query returns the total object count in a collection.Answer
> To obtain the [cosine similarity](https://en.wikipedia.org/wiki/Cosine_similarity) from weaviate's `certainty`, you can do `cosine_sim = 2*certainty - 1`Answer
Weaviate makes use of ANN indexes to serve vector searches. An ANN index is an approximate nearest neighbor index. The "approximate" part refers to an explicit recall-query-speed tradeoff. This trade-off is presented in detail in the [ANN benchmarks section](/weaviate/benchmarks/ann.md#benchmark-results). For example, a 98% recall for a given set of HNSW parameters means that 2% of results will not match the true nearest neighbors. What build parameters lead to what recall depends on the dataset used. The benchmark pages shows 4 different example datasets. Based on the characteristic of each dataset you can pick the one closest to your production load and draw conclusions about the expected recall for the respective build and query-time parameters. Generally if you need a higher recall than the default parameters provide you with, you can use stronger parameters. This can either be done at build time (`efConstruction`, `maxConnections`) or at query time (`ef`). Roughly speaking, a higher `ef` value at query time means a more thorough search. It will have a slightly higher latency, but also lead to a slightly better recall. By changing the specified limit, you are implicitly changing the `ef` parameter. This is because the default `ef` value is set to `-1`, indicating that Weaviate should pick the parameter based on the limit. The dynamic `ef` value is controlled using the configuration fields `dynamicEfMin` which acts as a lower boundary, `dynamicEfMax` which acts as an upper boundary and `dynamicEfFactor` which is the factor to derive the target `ef` based on the limit within the lower and upper boundary. Example: Using the default parameters `ef=-1`, `dynamicEfMin=100`, `dynamicEfMax=500`, `dynamicEfFactor=8`, you will end up with the following `ef` values based on the limit: * `limit=1`, dynamically calculated: `ef=1*8=8`. This value is below the lower boundary, so `ef` is set to `100`. * `limit=20`, dynamically calculated: `ef=20*8=160`. This value is within the boundaries, so `ef` is `160`. * `limit=100`, dynamically calculated: `ef=100*8=800`. This value is above the upper boundary, so `ef` is set to `500`. If you need a higher search quality for a given limit you can consider the following options: 1. Instead of using a dynamic `ef` value, use a fixed one that provides the desired recall. 1. If your search quality varies a lot depending on the query-time `ef` values, you should also consider choosing stronger build parameters. The [ANN benchmarks section](/weaviate/benchmarks/ann.md#benchmark-results) present a combination of many different parameter combination for various datasets.Answer
> For user experience. We want to make it as simple as possible to integrate Weaviate into your stack, and we believe that GraphQL is the answer to this. The community and client libraries around GraphQL are enormous, and you can use almost all of them with Weaviate.Answer
> Yes, Weaviate supports cursor-based iteration as well as pagination through a result set. > > To iterate through all objects, you can use the [`after` operator](../manage-objects/read-all-objects.mdx). > > For pagination through a result set, you can use the `offset` and `limit` operators for GraphQL API calls. Take a look at [this page](../api/graphql/filters.md) which describes how to use these operators, including tips on performance and limitations.Answer
> Here are top 3 best practices for updating data: > 1. Use the [batch API](../manage-objects/import.mdx) > 2. Start with a small-ish batch size e.g. 100 per batch. Adjust up if it is very fast, adjust down if you run into timeouts > 3. If you have unidirectional relationships (e.g. `Foo -> Bar`.) it's easiest to first import all `Bar` objects, then import all `Foo` objects with the refs already set. If you have more complex relationships, you can also import the objects without references, then [add references](../manage-objects/import.mdx#import-with-references) to set links between collections in arbitrary directions.Answer
> [Yes!](/weaviate/modules/custom-modules.md)Answer
> Not at the moment. You can currently use the [available contextionaries](/weaviate/modules/text2vec-contextionary.md) in a variety of languages and use the transfer learning feature to add custom concepts if needed.Answer
> No > > Weaviate uses a custom implementation of HNSW that overcomes certain limitations of [hnswlib](https://github.com/nmslib/hnswlib), such as durability requirements, CRUD support, pre-filtering, etc. > > Custom HNSW implementation in Weaviate references: > > - [HNSW plugin (GitHub)](https://github.com/weaviate/weaviate/tree/master/adapters/repos/db/vector/hnsw) > - [vector dot product ASM](https://github.com/weaviate/weaviate/blob/master/adapters/repos/db/vector/hnsw/distancer/asm/dot_amd64.s) > > More information: > > - [Weaviate, an ANN Database with CRUD support – DB-Engines.com](https://db-engines.com/en/blog_post/87) ⬅️ best resource on the topic > - [Weaviate's HNSW implementation in the docs](/weaviate/concepts/indexing/vector-index.md#hierarchical-navigable-small-world-hnsw-index) > > _Note I: HNSW is just one implementation in Weaviate, but Weaviate can support multiple indexing algoritmns as outlined [here](/weaviate/concepts/indexing/vector-index.md)_Answer
> No > > Some algorithms (e.g., Annoy or ScaNN) are entirely immutable once built, they can neither be changed nor built up incrementally. Instead, they require you to have all of your vectors present, then you build the algorithm once. After a build, you can only query them, but cannot add more elements or change existing elements. Thus, they aren't capable of the CRUD operations we want to support in Weaviate.Answer
> Weaviate currently uses pre-filtering exclusively on filtered ANN search. > See "How does Weaviate's vector and scalar filtering work" for more details.Answer
> It's a 2-step process: > > 1. The inverted index (which is [built at import time](#q-does-weaviate-use-hnswlib)) queries to produce an allowed list of the specified document ids. Then the ANN index is queried with this allow list (the list being one of the reasons for our custom implementation). > 2. If we encounter a document id which would be a close match, but isn't on the allow list the id is treated as a candidate (i.e. we add it to our list of links to evaluate), but is never added to the result set. Since we only add allowed IDs to the set, we don't exit early, i.e. before the top `k` elements are reached. > > For more information on the technical implementations, see [this video](https://www.youtube.com/watch?v=6hdEJdHWXRE).Answer
> As the embedding is currently stored using `uint16`, the maximum possible length is currently 65535.Answer
> This is a very difficult to answer 100% correctly, because there are several factors in play: > * **The vector search itself**. This part is CPU-bound, however only with regards to throughput: A single search is single-threaded. Multiple parallel searches can use multiple threads. So if you measure the time of a single request (otherwise idle), it will be the same whether the machine has 1 core or 100. However, if your QPS approach the throughput of a CPU, you'll see massive benefits by adding more Cores > * **The retrieval of the objects**. Once the vector search part is done, we are essentially left with a list of n IDs which need to be resolved to actual objects. This is IO-bound in general. However, all disk files are memory-mapped. So generally, more mem will allow you to hold more of the disk state in memory. In real life however, it's not that simple. Searches are rarely evenly distributed. So let's pretend that 90% of searches will return just 10% of objects (because these are more popular search results). Then if those 10% of the disk objects are already cached in mem, there's no benefit in adding more memory. > > Taking the above in mind: we can carefully say: If throughput is the problem, increase CPU, if response time is the problem increase mem. However, note that the latter only adds value if there are more things that can be cached. If you have enough mem to cache your entire disk state (or at least the parts that are relevant for most queries), additional memory won't add any additional benefit. > If we are talking about imports on the other hand, they are almost always CPU-bound because of the cost of creating the HNSW index. So, if you can resize between import and query, my recommendation would be roughly prefer CPUs while importing and then gradually replace CPU with memory at query time - until you see no more benefits. (This assumes that there is a separation between importing and querying which might not always be the case in real life).Answer
> HNSW is super fast at query time, but slower on vectorization. This means that adding and updating data objects costs relatively more time. You could try [asynchronous indexing](../config-refs/indexing/vector-index.mdx#asynchronous-indexing), which separates data ingestion from vectorization.Answer
> Queries containing deeply nested references that need to be filtered or resolved can take some time. Read on optimization strategies [here](./performance.md#costs-of-queries-and-operations).Answer
> The mixed structured vector searches in Weaviate are pre-filter. There is an inverted index which is queried first to basically form an allow-list, in the HNSW search the allow list is then used to treat non-allowed doc ids only as nodes to follow connections, but not to add to the result set.Answer
> Essentially the list ids uses the internal doc id which is a `uint64` or 8 bytes per ID. The list can grow as long as you have memory available. So for example with 2GB of free memory, it could hold 250M ids, with 20GB it could hold 2.5B ids, etc. > > Performance wise there are two things to consider: > 1. Building the lookup list > 2. Filtering the results when vector searching > > Building the list is a typical inverted index look up, so depending on the operator this is just a single read on == (or a set of range reads, e.g. for >7, we'd read the value rows from 7 to infinity). This process is pretty efficient, similar to how the same thing would happen in a traditional search engine, such as elasticsearch > > Performing the filtering during the vector search depends on whether the filter is very restrictive or very loose. In the case you mentioned where a lot of IDs are included, it will be very efficient. Because the equivalent of an unfiltered search would be the one where your ID list contains all possible IDs. So the HNSW index would behave normally. There is however, a small penalty whenever a list is present: We need to check if the current ID is contained an the allow-list. This is essentially a hashmap lookup, so it should be O(1) per object. Nevertheless, there is a slight performance penalty. > > Now the other extreme, a very restrictive list, i.e few IDs on the list, actually takes considerably more time. Because the HNSW index will find neighboring IDs, but since they're not contained, they cannot be added as result candidates, meaning that all we can do with them is evaluating their connections, but not the points themselves. In the extreme case of a list that is very, very restrictive, say just 10 objects out of 1B in the worst case the search would become exhaustive if you the filtered ids are very far from the query. In this extreme case, it would actually be much more efficient to just skip the index and do a brute-force indexless vector search on the 10 ids. So, there is a cut-off when a brute-force search becomes more efficient than a heavily-restricted vector search with HNSW. We do not yet have any optimization to discovery such a cut-off point and skip the index, but this should be fairly simple to implement if this ever becomes an actual problem.Answer
> Check that your import uses the latest version of Weaviate. `v1.12.0` and `v1.12.1` fix an [issue](https://github.com/weaviate/weaviate/issues/1868) where excessive amounts of data are written to disk, resulting in unreasonable memory consumption after restarts. If upgrading does not fix the issue, see this post on [how to profile memory use](https://stackoverflow.com/a/71793178/5322199).Answer
You can do this by sending a `SIGQUIT` signal to the process. This will print a stack trace to the console. The logging level and debugging variables can be set with `LOG_LEVEL` and `DEBUG` [environment variables](/deploy/configuration/env-vars/index.md). Read more on SIGQUIT [here](https://en.wikipedia.org/wiki/Signal_(IPC)#SIGQUIT) and this [StackOverflow answer](https://stackoverflow.com/questions/19094099/how-to-dump-goroutine-stacktraces/35290196#35290196).Answer
In Weaviate Python client versions `4.16.0` to `4.16.3`, the following pattern when creating a collection with a text2vec_xxx vectorizer will result in an error: ```python client.collections.create( "CollectionName", vector_config=Configure.Vectorizer.text2vec_cohere(), # also applies to other vectorizers ) ``` The error message will look like this: ```text UnexpectedStatusCodeError: Collection may not have been created properly.! Unexpected status code: 422, with response body: {'error': [{'message': "module 'text2vec-cohere': invalid properties: didn't find a single property which is of type string or text and is not excluded from indexing.... ``` This is a known issue, which will occur when setting a vectorizer definition without defining any `TEXT` or `TEXT_ARRAY` properties in the collection, in order to rely on AutoSchema to create the data schema for you. **This issue is addressed in Weaviate Python client patch release `4.16.4`. So, we recommend updating to the version `4.16.4` of the Weaviate Python client, or later.** If you are unable to change your Weaviate Python client version from the affected ones, you can work around this issue in one of two ways: 1. By explicitly defining at least one `TEXT` or `TEXT_ARRAY` property in the collection schema, like this: ```python client.collections.create( "CollectionName", properties=[ Property(name="Answer
`insert_many` (Python) and `insertMany` (TypeScript, Java) send all objects in a **single gRPC request**. Requests larger than the server's [`GRPC_MAX_MESSAGE_SIZE`](/deploy/configuration/env-vars/index.md#GRPC_MAX_MESSAGE_SIZE) limit are rejected with the gRPC status `RESOURCE_EXHAUSTED`, so a sufficiently large list fails as a whole with an error similar to: ```text WeaviateBatchError: Query call with protocol GRPC batch failed with message CLIENT: Sent message larger than max (3002340 vs. 1000000). ``` The two numbers are the size of your request and the server's limit (the values shown here are from a test with a 1 MB limit). Recent Python clients read the server's limit at connect time and reject oversized requests before sending them. With older clients, the server-side variant `grpc: received message larger than max` may appear instead. For large lists, use [server-side batching](../manage-objects/import.mdx#server-side-batching) instead, which splits the data into server-paced batches: - **Python**: Use `collection.data.ingest(objects)`, a drop-in replacement for `insert_many` that returns the same return object. Alternatively, use the `collection.batch.stream()` context manager. - **TypeScript**: `collection.data.ingest(objects)`. - **Java** (`v6`): the `collection.batch.start()` streaming context. - **C#**: `collection.Batch.InsertMany(items)` (already uses server-side batching). Alternatively, you can raise the `GRPC_MAX_MESSAGE_SIZE` [environment variable](/deploy/configuration/env-vars/index.md#GRPC_MAX_MESSAGE_SIZE) on the server, but batching is the recommended solution.Answer
> Sure (also, feel free to [issue a pull request](https://github.com/weaviate/weaviate/pulls) 😉) you can [add those requests here](https://github.com/weaviate/weaviate/issues). The only thing you need is a GitHub account, and while you're there, make sure to give us a star 😇.Answer
> Weaviate is generally modeled to prefer Availability over Consistency (AP over CP). It is designed to deliver low search latencies under high throughput in situations where availability is more business-critical than consistency. If strict serializability is required on your data, we generally recommend storing your data in a different primary data store, use Weaviate as an auxiliary data store, and set up replication between the two. If you do not need serializability and eventual consistency is enough for your use case, Weaviate can be used as a primary datastore. > > Weaviate has no notion of transactions, operations always affect exactly a single key, therefore Serializability is not applicable. In a distributed setup (under development) Weaviate's consistency model is eventual consistency. When a cluster is healthy, all changes are replicated to all affected nodes by the time the write is acknowledged by the user. Objects will immediately be present in search results on all nodes after the import request completes. If a search query occurs concurrently with an import operation nodes may not be in sync yet. This means some nodes might already include the newly added or updated objects, while others don't yet. In a healthy cluster, all nodes will have converged by the time the import request has been completed successfully. If a node is temporarily unavailable and rejoins a cluster it may temporarily be out of sync. It will then sync the missed changes from other replica nodes and eventually serve the same data again.Answer
> At the moment, we cannot aggregate over timeseries into time buckets yet, but architecturally there's nothing in the way. If there is demand, this seems like a nice feature request, you can submit an [issue here](https://github.com/weaviate/weaviate/issues). (We're a very small company though and the priority is on Horizontal Scaling at the moment.)Answer
> You can run Weaviate with `Docker Compose`, you can build your own container off the [`master`](https://github.com/weaviate/weaviate) branch. Note that this is not an officially released Weaviate version, so this might contain bugs. > > ```sh > git clone https://github.com/weaviate/weaviate.git > cd weaviate > docker build --target weaviate -t name-of-your-weaviate-image . > ``` > > Then, make a `docker-compose.yml` file with this new image. For example: > > ```yml > > services: > weaviate: > image: name-of-your-weaviate-image > ports: > - 8080:8080 > environment: > CONTEXTIONARY_URL: contextionary:9999 > QUERY_DEFAULTS_LIMIT: 25 > AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: 'true' > PERSISTENCE_DATA_PATH: './data' > ENABLE_MODULES: 'text2vec-contextionary' > AUTOSCHEMA_ENABLED: 'false' > contextionary: > environment: > OCCURRENCE_WEIGHT_LINEAR_FACTOR: 0.75 > EXTENSIONS_STORAGE_MODE: weaviate > EXTENSIONS_STORAGE_ORIGIN: http://weaviate:8080 > NEIGHBOR_OCCURRENCE_IGNORE_PERCENTILE: 5 > ENABLE_COMPOUND_SPLITTING: 'false' > image: cr.weaviate.io/semitechnologies/contextionary:en0.16.0-v1.0.2 > ``` > > After the build is complete, you can run this Weaviate build with docker compose: ```bash docker compose up ```Answer
Weaviate can be used on Windows via containerized environments like [Docker](/deploy/installation-guides/docker-installation.md) or [WSL](https://learn.microsoft.com/en-us/windows/wsl/), Keep in mind that we don't offer native Windows support at this time and deployment options like [Weaviate Embedded](/docs/deploy/installation-guides/embedded.md) should be avoided.Answer
Weaviate Academy is a full-fledged learning platform available at [academy.weaviate.io](https://academy.weaviate.io). :::note If you need resources from the previous version of Weaviate Academy, check out the [documentation archive](https://archive.docs.weaviate.io/academy) :::Answer
> The Weaviate Community Slack has been decommissioned. We've moved community discussions to the [Weaviate Community Forum](https://forum.weaviate.io/), which offers better long-term discoverability: conversations are indexed and searchable, so valuable answers don't get lost over time. > > Join us at [forum.weaviate.io](https://forum.weaviate.io/) to ask questions, share ideas, and connect with the community. For private support inquiries, you can reach us at [support@weaviate.io](mailto:support@weaviate.io).Answer
> Yes, Weaviate provides two MCP (Model Context Protocol) servers: > > - **[Weaviate MCP server](/weaviate/configuration/mcp-server.mdx)**: Built into Weaviate itself. Exposes tools for inspecting schemas, searching data (vector/hybrid), and modifying objects. Runs on the same port as the REST API at `/v1/mcp`. Disabled by default. Enable it with `MCP_SERVER_ENABLED=true`. > - **[Weaviate Docs MCP server](/weaviate/mcp/docs-mcp-server.mdx)**: A standalone server that gives LLMs access to Weaviate's documentation. Useful for AI-assisted development with Weaviate. > > Both servers use the Streamable HTTP transport and work with MCP clients like Claude Code, Claude Desktop, Cursor, and VS Code.(recommended) > ), description: "Import objects and vectorize them with the Weaviate Embeddings service.", link: "?import=vectorization#create-a-collection", icon: "fas fa-arrows-spin", groupId: "import", activeTab: "vectorization", }, { title: "Import vectors", description: "Import pre-computed vector embeddings along with your data.", link: "?import=custom-embeddings#create-a-collection", icon: "fas fa-circle-nodes", groupId: "import", activeTab: "custom-embeddings", }, ]; Weaviate is an open-source vector database built to power AI applications. This quickstart guide will show you how to: 1. **Set up a collection** - Create a collection and import data into it. 2. **Search** - Perform a similarity (vector) search on your data. 3. **RAG** - Perform Retrieval Augmented Generation (RAG) with a generative model. 4. **Query Agent** - Get answers from your data by using a natural language prompt/question.
How to set up a Weaviate Cloud free cluster
Go to the [Weaviate Cloud console](https://console.weaviate.cloud) and create a free cluster as shown in the interactive example below.:::note - Cluster provisioning typically takes 1-3 minutes. - When the cluster is ready, Weaviate Cloud displays a checkmark (`✔️`) next to the cluster name. - Note that Weaviate Cloud may add a random suffix to cluster names to ensure uniqueness. :::
How to retrieve Weaviate Cloud credentials (`WEAVIATE_API_KEY` and `WEAVIATE_URL`)
After you create a Weaviate Cloud instance, you will need the: - **REST Endpoint URL** and the - **Administrator API Key**. You can retrieve them both from the [WCD console](/go/console?utm_content=quickstart) as shown in the interactive example below.:::info REST vs gRPC endpoints Weaviate supports both REST and gRPC protocols. For Weaviate Cloud deployments, you only need to provide the REST endpoint URL - the client will automatically configure gRPC. ::: Once you have the **REST Endpoint URL** and the **admin API key**, you can connect to your cluster, and work with Weaviate.
Example response
```json { "genre": "Science Fiction", "title": "The Matrix", "description": "A computer hacker learns about the true nature of reality and his role in the war against its controllers." } { "genre": "Fantasy", "title": "The Lord of the Rings: The Fellowship of the Ring", "description": "A meek Hobbit and his companions set out on a perilous journey to destroy a powerful ring and save Middle-earth." } ```Example response
```json 🕶️ Unplug from the system & join Neo's journey 💊🐰 "The Matrix" will blow your mind 🤯 as reality unravels 🌀 Kung-fu, slow-mo & mind-bending sci-fi 🥋🕴️ Are you ready to see how deep the rabbit hole goes? 🔴🔵 #TheMatrix #WakeUp ```(recommended) > ), description: "Import objects and vectorize them with the Ollama embedding model.", link: "?import=vectorization#create-a-collection", icon: "fas fa-arrows-spin", groupId: "import", activeTab: "vectorization", }, { title: "Import vectors", description: "Import pre-computed vector embeddings along with your data.", link: "?import=custom-embeddings#create-a-collection", icon: "fas fa-circle-nodes", groupId: "import", activeTab: "custom-embeddings", }, ]; Weaviate is an open-source vector database built to power AI applications. This quickstart guide will show you how to: 1. **Set up a collection** - Create a collection and import data into it. 2. **Search** - Perform a similarity (vector) search on your data. 3. **RAG** - Perform Retrieval Augmented Generation (RAG) with a generative model. import KapaAI from "/src/components/KapaAI"; If you encounter any issues along the way or have additional questions, use the
Example response
```json { "genre": "Science Fiction", "title": "The Matrix", "description": "A computer hacker learns about the true nature of reality and his role in the war against its controllers." } { "genre": "Fantasy", "title": "The Lord of the Rings: The Fellowship of the Ring", "description": "A meek Hobbit and his companions set out on a perilous journey to destroy a powerful ring and save Middle-earth." } ```Example response
```json 🕶️ Unplug from the system & join Neo's journey 💊🐰 "The Matrix" will blow your mind 🤯 as reality unravels 🌀 Kung-fu, slow-mo & mind-bending sci-fi 🥋🕴️ Are you ready to see how deep the rabbit hole goes? 🔴🔵 #TheMatrix #WakeUp ```
First, the ingestion pipeline processes the PDF documents as images with the multimodal late-interaction model. The multi-vector embeddings are stored in a vector database.
Then at query time, the text query is processed by the same multimodal late-interaction model to retrieve the relevant documents.
The retrieved PDF files are then passed as visual context together with the original user query to the vision language model, which generates a response based on this information.
## Prerequisites
To run this notebook, you will need a machine capable of running neural networks using 5-10 GB of memory.
The demonstration uses two different vision language models that both require several gigabytes of memory.
See the documentation for each individual model and the general PyTorch docs to figure out how to best run the models on your hardware.
For example, you can run it on:
- Google Colab (using the free-tier T4 GPU)
- or locally (tested on an M2 Pro Mac).
Furthermore, you will need an instance of Weaviate version >= `1.29.0`.
## Step 1: Install required libraries
Let's begin by installing and importing the required libraries.
Note that you'll need Python `3.13`.
```python
%%capture
%pip install colpali_engine weaviate-client qwen_vl_utils
%pip install -q -U "colpali-engine[interpretability]>=0.3.2,<0.4.0"
```
```python
import os
import torch
import numpy as np
from google.colab import userdata
from datasets import load_dataset
from transformers.utils.import_utils import is_flash_attn_2_available
from transformers import Qwen2_5_VLForConditionalGeneration, AutoTokenizer, AutoProcessor
from colpali_engine.models import ColQwen2, ColQwen2Processor
#from colpali_engine.models import ColPali, ColPaliProcessor # uncomment if you prefer to use ColPali models instead of ColQwen2 models
import weaviate
from weaviate.classes.init import Auth
import weaviate.classes.config as wc
from weaviate.classes.config import Configure
from weaviate.classes.query import MetadataQuery
from qwen_vl_utils import process_vision_info
import base64
from io import BytesIO
import matplotlib.pyplot as plt
from colpali_engine.interpretability import (
get_similarity_maps_from_embeddings,
plot_all_similarity_maps,
plot_similarity_map,
)
```
## Step 2: Load the PDF dataset
Let's start with the data.
We're going to first load a PDF document dataset of the [top-40 most
cited AI papers on arXiv](https://arxiv.org/abs/2412.12121) from Hugging Face from the period 2023-01-01 to 2024-09-30.
```python
dataset = load_dataset("weaviate/arXiv-AI-papers-multi-vector", split="train")
```
Python output:
```text
README.md: 0%| | 0.00/530 [00:00<?, ?B/s]
n40_p10_images.parquet: 0%| | 0.00/201M [00:00<?, ?B/s]
Generating train split: 0%| | 0/399 [00:00<?, ? examples/s]
```
```python
dataset
```
Python output:
```text
Dataset({
features: ['page_id', 'paper_title', 'paper_arxiv_id', 'page_number', 'colqwen_embedding', 'page_image'],
num_rows: 399
})
```
```python
dataset[398]
```
Let's take a look at a sample document page from the loaded PDF dataset.
```python
display(dataset[289]["page_image"])
```
## Step 3: Load the ColVision (ColPali or ColQwen2) model
The approach to generate embeddings for this tutorial is outlined in the paper [ColPali: Efficient Document Retrieval with Vision Language Models](https://arxiv.org/abs/2407.01449). The paper demonstrates that it is possible to simplify traditional approaches to preprocessing PDF documents for retrieval:
Traditional PDF processing in RAG systems involves using OCR (Optical Character Recognition) and layout detection software, and separate processing of text, tables, figures, and charts. Additionally, after text extraction, text processing also requires a chunking step. Instead, the ColPali method feeds images (screenshots) of entire PDF pages to a Vision Language Model that produces a ColBERT-style multi-vector embedding.
There are different ColVision models, such as ColPali or ColQwen2, available, which mainly differ in the used encoders (Contextualized Late Interaction over Qwen2 vs. PaliGemma-3B). You can read more about the differences between ColPali and ColQwen2 in our [overview of late-interaction models](https://weaviate.io/blog/late-interaction-overview).
Let's load the [ColQwen2-v1.0](https://huggingface.co/vidore/colqwen2-v1.0) model for this tutorial.
```python
# Get rid of process forking deadlock warnings.
os.environ["TOKENIZERS_PARALLELISM"] = "false"
```
```python
if torch.cuda.is_available(): # If GPU available
device = "cuda:0"
elif torch.backends.mps.is_available(): # If Apple Silicon available
device = "mps"
else:
device = "cpu"
if is_flash_attn_2_available():
attn_implementation = "flash_attention_2"
else:
attn_implementation = "eager"
print(f"Using device: {device}")
print(f"Using attention implementation: {attn_implementation}")
```
Python output:
```text
Using device: cuda:0
Using attention implementation: eager
```
```python
model_name = "vidore/colqwen2-v1.0"
# About a 5 GB download and similar memory usage.
model = ColQwen2.from_pretrained(
model_name,
torch_dtype=torch.bfloat16,
device_map=device,
attn_implementation=attn_implementation,
).eval()
# Load processor
processor = ColQwen2Processor.from_pretrained(model_name)
```
Python output:
```text
adapter_config.json: 0%| | 0.00/728 [00:00<?, ?B/s]
config.json: 0.00B [00:00, ?B/s]
model.safetensors.index.json: 0.00B [00:00, ?B/s]
Fetching 2 files: 0%| | 0/2 [00:00<?, ?it/s]
model-00001-of-00002.safetensors: 0%| | 0.00/4.98G [00:00<?, ?B/s]
model-00002-of-00002.safetensors: 0%| | 0.00/3.85G [00:00<?, ?B/s]
Loading checkpoint shards: 0%| | 0/2 [00:00<?, ?it/s]
adapter_model.safetensors: 0%| | 0.00/74.0M [00:00<?, ?B/s]
preprocessor_config.json: 0%| | 0.00/619 [00:00<?, ?B/s]
Using a slow image processor as `use_fast` is unset and a slow processor was saved with this model. `use_fast=True` will be the default behavior in v4.52, even if the model was saved with a slow processor. This will result in minor differences in outputs. You'll still be able to use a slow processor with `use_fast=False`.
tokenizer_config.json: 0.00B [00:00, ?B/s]
vocab.json: 0.00B [00:00, ?B/s]
merges.txt: 0.00B [00:00, ?B/s]
tokenizer.json: 0%| | 0.00/11.4M [00:00<?, ?B/s]
added_tokens.json: 0%| | 0.00/392 [00:00<?, ?B/s]
special_tokens_map.json: 0%| | 0.00/613 [00:00<?, ?B/s]
video_preprocessor_config.json: 0%| | 0.00/54.0 [00:00<?, ?B/s]
chat_template.json: 0.00B [00:00, ?B/s]
```
This notebook uses the ColQwen2 model because it has a permissive Apache 2.0 license.
Alternatively, you can also use [ColPali](https://huggingface.co/vidore/colpali-v1.2), which has a Gemma license, or check out other available [ColVision models](https://github.com/illuin-tech/colpali). For a detailed comparison, you can also refer to [ViDoRe: The Visual Document Retrieval Benchmark](https://huggingface.co/spaces/vidore/vidore-leaderboard)
If you want to use ColPali instead of ColQwen2, you can comment out the above code cell and uncomment the code cell below.
```python
#model_name = "vidore/colpali-v1.2"
# Load model
#colpali_model = ColPali.from_pretrained(
# model_name,
# torch_dtype=torch.bfloat16,
# device_map=device,
# attn_implementation=attn_implementation,
#).eval()
# Load processor
#colpali_processor = ColPaliProcessor.from_pretrained(model_name)
```
Before we go further, let's familiarize ourselves with the ColQwen2 model. It can create multi-vector embeddings from both images and text queries. Below you can see examples of each.
```python
# Sample image inputs
images = [
dataset[0]["page_image"],
dataset[1]["page_image"],
]
# Process the inputs
batch_images = processor.process_images(images).to(model.device)
# Forward pass
with torch.no_grad():
query_embedding = model(**batch_images)
print(query_embedding)
print(query_embedding.shape)
```
Python output:
```text
tensor([[[ 2.0630e-02, -8.6426e-02, -7.1289e-02, ..., 5.1758e-02,
-3.0365e-03, 1.1084e-01],
[ 1.9409e-02, -1.0840e-01, -2.6245e-02, ..., 7.6172e-02,
-4.4922e-02, -1.3965e-01],
[-1.7242e-03, -9.8145e-02, -1.9653e-02, ..., 7.5684e-02,
-3.3936e-02, -1.2891e-01],
...,
[ 5.0537e-02, -1.0205e-01, -8.6426e-02, ..., 4.9561e-02,
3.1982e-02, 8.0078e-02],
[ 3.9795e-02, -1.3477e-01, -5.0537e-02, ..., 3.8330e-02,
-6.1523e-02, -1.2012e-01],
[ 9.3384e-03, -2.2168e-01, -1.4746e-01, ..., -8.1177e-03,
-5.2246e-02, -3.1128e-02]],
[[ 2.0630e-02, -8.6426e-02, -7.1289e-02, ..., 5.1758e-02,
-3.0365e-03, 1.1084e-01],
[ 1.9409e-02, -1.0840e-01, -2.6245e-02, ..., 7.6172e-02,
-4.4922e-02, -1.3965e-01],
[-1.7242e-03, -9.8145e-02, -1.9653e-02, ..., 7.5684e-02,
-3.3936e-02, -1.2891e-01],
...,
[ 7.2266e-02, -9.3750e-02, -7.9102e-02, ..., 5.7373e-02,
1.0803e-02, 7.1777e-02],
[ 5.2490e-02, -1.2207e-01, -4.9072e-02, ..., 3.2471e-02,
-6.4453e-02, -1.1084e-01],
[ 1.7480e-01, -1.8457e-01, -7.2937e-03, ..., 6.4392e-03,
-1.3828e-04, -5.7617e-02]]], device='cuda:0', dtype=torch.bfloat16)
torch.Size([2, 755, 128])
```
```python
# Sample query inputs
queries = [
"A table with LLM benchmark results.",
"A figure detailing the architecture of a neural network.",
]
# Process the inputs
batch_queries = processor.process_queries(queries).to(model.device)
# Forward pass
with torch.no_grad():
query_embedding = model(**batch_queries)
print(query_embedding)
print(query_embedding.shape)
```
Python output:
```text
tensor([[[ 0.0000, -0.0000, -0.0000, ..., -0.0000, 0.0000, 0.0000],
[ 0.0000, -0.0000, -0.0000, ..., -0.0000, 0.0000, 0.0000],
[ 0.0238, -0.0835, -0.0752, ..., 0.0549, 0.0076, 0.0903],
...,
[ 0.0559, -0.0457, -0.1118, ..., -0.1621, 0.1758, 0.1011],
[ 0.0525, -0.0376, -0.1172, ..., -0.1572, 0.1787, 0.0938],
[ 0.0486, -0.0294, -0.1250, ..., -0.1494, 0.1797, 0.0918]],
[[ 0.0238, -0.0835, -0.0752, ..., 0.0549, 0.0076, 0.0903],
[-0.0086, -0.1021, -0.0198, ..., 0.0708, -0.0310, -0.1367],
[-0.0864, -0.1230, -0.0222, ..., 0.0776, 0.1040, -0.0128],
...,
[-0.0544, 0.0310, -0.1318, ..., -0.2236, -0.1445, 0.0381],
[-0.0679, 0.0292, -0.1484, ..., -0.2178, -0.1387, 0.0439],
[-0.0742, 0.0291, -0.1553, ..., -0.2109, -0.1289, 0.0452]]],
device='cuda:0', dtype=torch.bfloat16)
torch.Size([2, 22, 128])
```
Let's write a class to wrap the multimodal late-interaction model and its embedding functionalities for convenience.
```python
# A convenience class to wrap the embedding functionality
# of ColVision models like ColPali and ColQwen2
class ColVision:
def __init__(self, model, processor):
"""Initialize with a loaded model and processor."""
self.model = model
self.processor = processor
# A batch size of one appears to be most performant when running on an M4.
# Note: Reducing the image resolution speeds up the vectorizer and produces
# fewer multi-vectors.
def multi_vectorize_image(self, img):
"""Return the multi-vector image of the supplied PIL image."""
image_batch = self.processor.process_images([img]).to(self.model.device)
with torch.no_grad():
image_embedding = self.model(**image_batch)
return image_embedding[0]
def multi_vectorize_text(self, query):
"""Return the multi-vector embedding of the query text string."""
query_batch = self.processor.process_queries([query]).to(self.model.device)
with torch.no_grad():
query_embedding = self.model(**query_batch)
return query_embedding[0]
# Instantiate the model to be used below.
colvision_embedder = ColVision(model, processor) # This will be instantiated after loading the model and processor
```
Let's verify that the embedding of images and queries works as intended.
```python
# Sample image inputs
images = dataset[0]["page_image"]
page_embedding = colvision_embedder.multi_vectorize_image(images)
print(page_embedding.shape) # torch.Size([755, 128])
queries = [
"A table with LLM benchmark results.",
"A figure detailing the architecture of a neural network.",
]
query_embeddings = [colvision_embedder.multi_vectorize_text(q) for q in queries]
print(query_embeddings[0].shape) # torch.Size([20, 128])
```
Python output:
```text
torch.Size([755, 128])
torch.Size([20, 128])
```
## Step 4: Connect to a Weaviate vector database instance
Now, you will need to connect to a running Weaviate vector database cluster.
You can choose one of the following options:
1. **Option 1:** You can create a free cluster on the managed service [Weaviate Cloud (WCD)](/go/console?utm_content=recipe/)
2. **Option 2:** [Embedded Weaviate](https://docs.weaviate.io/deploy/installation-guides/embedded)
3. **Option 3:** [Local deployment](https://docs.weaviate.io/deploy/installation-guides/docker-installation)
4. [Other options](https://docs.weaviate.io/deploy)
```python
# Option 1: Weaviate Cloud
WCD_URL = os.environ["WEAVIATE_URL"] # Replace with your Weaviate cluster URL
WCD_AUTH_KEY = os.environ["WEAVIATE_API_KEY"] # Replace with your cluster auth key
# Uncomment if you are working in a Google Colab environment
#WCD_URL = userdata.get("WEAVIATE_URL")
#WCD_AUTH_KEY = userdata.get("WEAVIATE_API_KEY")
# Weaviate Cloud Deployment
client = weaviate.connect_to_weaviate_cloud(
cluster_url=WCD_URL,
auth_credentials=weaviate.auth.AuthApiKey(WCD_AUTH_KEY),
)
# Option 2: Embedded Weaviate instance
# use if you want to explore Weaviate without any additional setup
#client = weaviate.connect_to_embedded()
# Option 3: Locally hosted instance of Weaviate via Docker or Kubernetes
#!docker run --detach -p 8080:8080 -p 50051:50051 cr.weaviate.io/semitechnologies/weaviate:1.29.0
#client = weaviate.connect_to_local()
print(client.is_ready())
```
Python output:
```text
True
```
This tutorial requires Weaviate `v1.29.0` and later.
Let's make sure we have the required version:
```python
client.get_meta()['version']
```
Python output:
```text
'1.32.4'
```
## Step 5: Create a collection
Next, we will create a collection that will hold the embeddings of the images of the PDF document pages.
We will not define a built-in vectorizer but use the [Bring Your Own Vectors (BYOV) approach](https://docs.weaviate.io/weaviate/starter-guides/custom-vectors), where we manually embed queries and PDF documents at ingestions and query stage.
Additionally, if you are interested in using the [MUVERA encoding algorithm](https://weaviate.io/blog/muvera) for multi-vector embeddings, you can uncomment it in the code below.
```python
collection_name = "PDFDocuments"
```
```python
# Delete the collection if it already exists
# Note: in practice, you shouldn't rerun this cell, as it deletes your data
# in "PDFDocuments", and then you need to re-import it again.
#if client.collections.exists(collection_name):
# client.collections.delete(collection_name)
# Create a collection
collection = client.collections.create(
name=collection_name,
properties=[
wc.Property(name="page_id", data_type=wc.DataType.INT),
wc.Property(name="dataset_index", data_type=wc.DataType.INT),
wc.Property(name="paper_title", data_type=wc.DataType.TEXT),
wc.Property(name="paper_arxiv_id", data_type=wc.DataType.TEXT),
wc.Property(name="page_number", data_type=wc.DataType.INT),
],
vector_config=[
Configure.MultiVectors.self_provided(
name="colqwen",
#encoding=Configure.VectorIndex.MultiVector.Encoding.muvera(),
vector_index_config=Configure.VectorIndex.hnsw(
multi_vector=Configure.VectorIndex.MultiVector.multi_vector()
)
)]
)
```
## Step 6: Uploading the vectors to Weaviate
In this step, we're indexing the vectors into our Weaviate Collection in batches.
For each batch, the images are processed and encoded using the ColPali model, turning them into multi-vector embeddings.
These embeddings are then converted from tensors into lists of vectors, capturing key details from each image and creating a multi-vector representation for each document.
This setup works well with Weaviate's multivector capabilities.
After processing, the vectors and any metadata are uploaded to Weaviate, gradually building up the index.
You can lower or increase the `batch_size` depending on your available GPU resources.
```python
# Map of page ids to images to support displaying the image corresponding to a
# particular page id.
page_images = {}
with collection.batch.dynamic() as batch:
for i in range(len(dataset)):
p = dataset[i]
page_images[p["page_id"]] = p["page_image"]
batch.add_object(
properties={
"page_id": p["page_id"],
"paper_title": p["paper_title"],
"paper_arxiv_id": p["paper_arxiv_id"],
"page_number": p["page_number"],
},
vector={"colqwen": colvision_embedder.multi_vectorize_image(p["page_image"]).cpu().float().numpy().tolist()})
if i % 25 == 0:
print(f"Added {i+1}/{len(dataset)} Page objects to Weaviate.")
batch.flush()
# Delete dataset after creating page_images dict to hold the images
del dataset
```
Python output:
```text
Added 1/399 Page objects to Weaviate.
Added 26/399 Page objects to Weaviate.
Added 51/399 Page objects to Weaviate.
Added 76/399 Page objects to Weaviate.
Added 101/399 Page objects to Weaviate.
Added 126/399 Page objects to Weaviate.
Added 151/399 Page objects to Weaviate.
Added 176/399 Page objects to Weaviate.
Added 201/399 Page objects to Weaviate.
Added 226/399 Page objects to Weaviate.
Added 251/399 Page objects to Weaviate.
Added 276/399 Page objects to Weaviate.
Added 301/399 Page objects to Weaviate.
Added 326/399 Page objects to Weaviate.
Added 351/399 Page objects to Weaviate.
Added 376/399 Page objects to Weaviate.
```
```python
len(collection)
```
Python output:
```text
399
```
## Step 7: Multimodal Retrieval Query
As an example of what we are going to build, consider the following actual demo query and resulting PDF page from our collection (nearest neighbor):
- Query: "How does DeepSeek-V2 compare against the LLaMA family of LLMs?"
- Nearest neighbor: "DeepSeek-V2: A Strong Economical and Efficient Mixture-of-Experts Language Model" (arXiv: 2405.04434), Page: 1.
```python
query = "How does DeepSeek-V2 compare against the LLaMA family of LLMs?"
```
By inspecting the first page of the [DeepSeek-V2 paper](https://arxiv.org/abs/2405.04434), we see that it does indeed contain a figure that is relevant for answering our query:
Note: To avoid `OutOfMemoryError` on freely available resources like Google Colab, we will only retrieve a single document. If you have resources with more memory available, you can set the `limit`parameter to a higher value, like e.g., `limit=3` to increase the number of retrieved PDF pages.
```python
response = collection.query.near_vector(
near_vector=colvision_embedder.multi_vectorize_text(query).cpu().float().numpy(),
target_vector="colqwen",
limit=1,
return_metadata=MetadataQuery(distance=True), # Needed to return MaxSim score
)
print(f"The most relevant documents for the query \"{query}\" by order of relevance:\n")
result_images = []
for i, o in enumerate(response.objects):
p = o.properties
print(
f"{i+1}) MaxSim: {-o.metadata.distance:.2f}, "
+ f"Title: \"{p['paper_title']}\" "
+ f"(arXiv: {p['paper_arxiv_id']}), "
+ f"Page: {int(p['page_number'])}"
)
result_images.append(page_images[p["page_id"]])
```
Python output:
```text
The most relevant documents for the query "How does DeepSeek-V2 compare against the LLaMA family of LLMs?" by order of relevance:
1) MaxSim: 23.12, Title: "DeepSeek-V2: A Strong Economical and Efficient Mixture-of-Experts Language Model" (arXiv: 2405.04434), Page: 1
```
The retrieved page with the highest MaxSim score is indeed the page with the figure we mentioned earlier.
```python
closest_page_id = response.objects[0].properties['page_id']
image = page_images[closest_page_id]
display(image)
```
Let's visualize the similarity maps for the retrieved PDF document page to see the semantic similarity between each token in the user query and the image patches. This is an optional step.
```python
# Preprocess inputs
batch_images = processor.process_images([image]).to(device)
batch_queries = processor.process_queries([query]).to(device)
# Forward passes
with torch.no_grad():
image_embeddings = model.forward(**batch_images)
query_embeddings = model.forward(**batch_queries)
# Get the number of image patches
n_patches = processor.get_n_patches(
image_size=image.size,
spatial_merge_size=model.spatial_merge_size,
)
# Get the tensor mask to filter out the embeddings that are not related to the image
image_mask = processor.get_image_mask(batch_images)
# Generate the similarity maps
batched_similarity_maps = get_similarity_maps_from_embeddings(
image_embeddings=image_embeddings,
query_embeddings=query_embeddings,
n_patches=n_patches,
image_mask=image_mask,
)
# Get the similarity map for our (only) input image
similarity_maps = batched_similarity_maps[0] # (query_length, n_patches_x, n_patches_y)
print(f"Similarity map shape: (query_length, n_patches_x, n_patches_y) = {tuple(similarity_maps.shape)}")
```
```python
# Remove the padding tokens and the query augmentation tokens
query_content = processor.decode(batch_queries.input_ids[0])
query_content = query_content.replace(processor.tokenizer.pad_token, "")
query_content = query_content.replace(processor.query_augmentation_token, "").strip()
# Retokenize the cleaned query
query_tokens = processor.tokenizer.tokenize(query_content)
# Use this cell output to choose a token using its index
for idex, val in enumerate(query_tokens):
print(f"{idex}: {val}")
```
Let's check the similarity plot for the token "MA" in "LLaMA". (Note that similarity maps are created for each token separately.)
```python
token_idx = 13
fig, ax = plot_similarity_map(
image=image,
similarity_map=similarity_maps[token_idx],
figsize=(18, 18),
show_colorbar=False,
)
max_sim_score = similarity_maps[token_idx, :, :].max().item()
ax.set_title(f"Token #{token_idx}: `{query_tokens[token_idx]}`. MaxSim score: {max_sim_score:.2f}", fontsize=14)
plt.show()
```
```python
# Delete variables used for visualization
del batched_similarity_maps, similarity_maps, n_patches, query_content, query_tokens, token_idx
```
## Step 8: Extension to Multimodal RAG using Qwen2.5
The above example gives us the most relevant pages to begin looking at to answer our query. Let's extend this multimodal document retrieval pipeline to a multimodal RAG pipeline.
Vision language models (VLMs) are Large Language Models with vision capabilities. They are now powerful enough that we can give the query and relevant pages to such a model and have it produce an answer to our query in plain text.
To accomplish this we are going to feed the top results into the
state-of-the-art VLM [Qwen/Qwen2.5-VL-3B-Instruct](https://huggingface.co/Qwen/Qwen2.5-VL-3B-Instruct).
```
/* Detailed source-code truncated for AI context efficiency. */
```
Python output:
```text
config.json: 0.00B [00:00, ?B/s]
model.safetensors.index.json: 0.00B [00:00, ?B/s]
Fetching 2 files: 0%| | 0/2 [00:00<?, ?it/s]
model-00002-of-00002.safetensors: 0%| | 0.00/3.53G [00:00<?, ?B/s]
model-00001-of-00002.safetensors: 0%| | 0.00/3.98G [00:00<?, ?B/s]
Loading checkpoint shards: 0%| | 0/2 [00:00<?, ?it/s]
generation_config.json: 0%| | 0.00/216 [00:00<?, ?B/s]
preprocessor_config.json: 0%| | 0.00/350 [00:00<?, ?B/s]
tokenizer_config.json: 0.00B [00:00, ?B/s]
vocab.json: 0.00B [00:00, ?B/s]
merges.txt: 0.00B [00:00, ?B/s]
tokenizer.json: 0.00B [00:00, ?B/s]
You have video processor config saved in `preprocessor.json` file which is deprecated. Video processor configs should be saved in their own `video_preprocessor.json` file. You can rename the file or load and save the processor back which renames it automatically. Loading from `preprocessor.json` will be removed in v5.0.
chat_template.json: 0.00B [00:00, ?B/s]
```
The response from `Qwen2.5-VL-3B-Instruct` based on the retrieved PDF pages:
```python
qwenvl.query_images(query, result_images)
```
Python output:
```text
'DeepSeek-V2 achieves significantly stronger performance than the LLaMA family of LLMs, while also saving 42.5% of training costs and boosting the maximum generation throughput to 5.76 times.'
```
As you can see, the multimodal RAG pipeline was able to answer the original query: "How does DeepSeek-V2 compare against the LLaMA family of LLMs?". For this, the ColQwen2 retrieval model retrieved the correct PDF page from the
"DeepSeek-V2: A Strong Economical and Efficient Mixture-of-Experts Language Model" paper and used both the text and visual from the retrieved PDF page to answer the question.
## Summary
This notebook demonstrates a multimodal RAG pipeline over PDF documents using ColQwen2 for multi-vector embeddings, a Weaviate vector database for storage and retrieval, and Qwen2.5-VL-3B-Instruct for generating answers.
## References
- Faysse, M., Sibille, H., Wu, T., Omrani, B., Viaud, G., Hudelot, C., Colombo, P. (2024). ColPali: Efficient Document Retrieval with Vision Language Models. arXiv. https://doi.org/10.48550/arXiv.2407.01449
- [ColPali GitHub repository](https://github.com/illuin-tech/colpali)
- [ColPali Cookbook](https://github.com/tonywu71/colpali-cookbooks)
---
### Weaviate/Recipes/Rag Llama 3.1 Nemotron 51b Instruct (docs/weaviate/recipes/rag_llama_3.1_nemotron_51b_instruct.md)
---
layout: recipe
toc: True
title: "Generate new content with NVIDIA models and RAG"
featured: False
integration: False
agent: False
tags: ["Generative Search", "RAG", "NVIDIA"]
---
[](https://colab.research.google.com/github/weaviate/recipes/blob/main/weaviate-features/model-providers/nvidia/rag_llama_3.1_nemotron_51b_instruct.ipynb)
# Generative Search with NVIDIA
In this demo, we will use an embedding and generative model on NVIDIA to generate embeddings for the blog posts and use a generative model to create new content!
## Requirements
1. Weaviate cluster
1. You can create a free cluster on [WCD](/go/console?utm_content=recipe/)
2. [Embedded Weaviate](https://docs.weaviate.io/deploy/installation-guides/embedded)
3. [Local deployment](https://docs.weaviate.io/deploy/installation-guides/docker-installation)
4. [Other options](https://docs.weaviate.io/deploy)
2. NVIDIA NIM API key. Grab one [here](https://build.nvidia.com/models).
3. Weaviate client version `4.11.0` or newer
4. Weaviate database version `1.28.5`, `1.29.0`, or newer.
```python
import weaviate
from weaviate.embedded import EmbeddedOptions
import weaviate.classes as wvc
import weaviate.classes.config as wc
import requests, json
import weaviate.classes.query as wq
from weaviate.classes.config import Property, DataType
import os
import re
from weaviate.util import get_valid_uuid
from uuid import uuid4
```
## Connect to Weaviate
Only choose one option from the below.
**Weaviate Cloud Deployment**
```python
WCD_URL = os.environ["WEAVIATE_URL"] # Replace with your Weaviate cluster URL
WCD_AUTH_KEY = os.environ["WEAVIATE_AUTH"] # Replace with your cluster auth key
NVIDIA_KEY = os.environ["NVIDIA_API_KEY"] # Replace with your NVIDIA key
# Weaviate Cloud Deployment
client = weaviate.connect_to_wcs(
cluster_url=WCD_URL,
auth_credentials=weaviate.auth.AuthApiKey(WCD_AUTH_KEY),
headers={ "X-Nvidia-Api-Key": NVIDIA_KEY}
)
print(client.is_ready())
```
Python output:
```text
True
```
**Embedded Weaviate**
```python
# NVIDIA_KEY = os.environ["NVIDIA_API_KEY"] # Replace with your NVIDIA key
# client = weaviate.WeaviateClient(
# embedded_options=EmbeddedOptions(
# version="1.29.0",
# additional_env_vars={
# "ENABLE_MODULES": "text2vec-nvidia, generative-nvidia"
# }),
# additional_headers={
# "X-Nvidia-Api-Key": NVIDIA_KEY
# }
# )
# client.connect()
```
**Local Deployment**
```python
# NVIDIA_KEY = os.environ["NVIDIA_API_KEY"] # Replace with your NVIDIA key
# client = weaviate.connect_to_local(
# headers={
# "X-NVIDIA-Api-Key": NVIDIA_KEY
# }
# )
# print(client.is_ready())
```
## Create a collection
Collection stores your data and vector embeddings.
Full list of [generative models](https://weaviate.io/developers/weaviate/model-providers/octoai/generative#available-models)
```python
# Note: in practice, you shouldn't rerun this cell, as it deletes your data
# in "BlogChunks", and then you need to re-import it again.
# Delete the collection if it already exists
if (client.collections.exists("BlogChunks")):
client.collections.delete("BlogChunks")
client.collections.create(
name="BlogChunks",
vector_config=wc.Configure.Vectors.text2vec_nvidia( # specify the vectorizer and model
model="nvidia/nv-embed-v1", # optional, default is nvidia/nv-embed-v1
),
generative_config=wc.Configure.Generative.nvidia( # specify the generative model
model="nvidia/llama-3.1-nemotron-51b-instruct" # optional, default is nvidia/llama-3.1-nemotron-51b-instruct
),
properties=[
Property(name="content", data_type=DataType.TEXT) # We only have one property for our collection. It is the content within the blog posts
]
)
print("Successfully created collection: BlogChunks.")
```
Python output:
```text
Successfully created collection: BlogChunks.
```
## Chunk and Import Data
We need to break our blog posts into smaller chunks
```python
def chunk_list(lst, chunk_size):
"""Break a list into chunks of the specified size."""
return [lst[i:i + chunk_size] for i in range(0, len(lst), chunk_size)]
def split_into_sentences(text):
"""Split text into sentences using regular expressions."""
sentences = re.split(r'(? pure BM25
`alpha`= 0.5 --> half BM25, half vector search
`alpha`= 1 --> pure vector search
```python
import json
blogs = client.collections.use("BlogChunks")
response = blogs.query.hybrid(
query="What is Ref2Vec",
alpha=0.5,
limit=3
)
for o in response.objects:
print(json.dumps(o.properties, indent=2))
```
Python output:
```
/* Detailed source-code truncated for AI context efficiency. */
```
### Generative Search Query
Here is what happens in the below:
1. We will retrieve 3 relevant chunks from our vector database
2. We will pass the 3 chunks to NVIDIA to generate the short paragraph about Ref2Vec
The first line in the output is the generated text, and the `content` pieces below it, are what was retrieved from Weaviate and passed to NVIDIA.
```python
blogs = client.collections.use("BlogChunks")
response = blogs.generate.near_text(
query="What is Ref2Vec?",
single_prompt="Write a short paragraph about ref2vec with this content: {content}",
limit=3
)
for o in response.objects:
print(o.generated)
print(json.dumps(o.properties, indent=2))
```
Python output:
```
/* Detailed source-code truncated for AI context efficiency. */
```
---
### Weaviate/Recipes/Rag Titan Text Express V1 Bedrock (docs/weaviate/recipes/rag_titan-text-express-v1_bedrock.md)
---
layout: recipe
toc: True
title: "Generative search (RAG) with AWS Bedrock"
featured: False
integration: False
agent: False
tags: ['Generative Search', 'RAG', 'AWS']
---
[](https://colab.research.google.com/github/weaviate/recipes/blob/main/weaviate-features/model-providers/aws/rag_titan-text-express-v1_bedrock.ipynb)
## Dependencies
```python
!pip install weaviate-client
```
## Configuration
```python
import weaviate, os
# Connect to your local Weaviate instance deployed with Docker
client = weaviate.connect_to_local(
headers={
"X-AWS-Access-Key": os.getenv("AWS_ACCESS_KEY"), # Replace with your AWS access key - recommended: use env var
"X-AWS-Secret-Key": os.getenv("AWS_SECRET_KEY"), # Replace with your AWS secret key - recommended: use env var
}
)
# Option 2
# Connect to your Weaviate Client Service cluster
# client = weaviate.connect_to_wcs(
# cluster_url="WCS-CLUSTER-ID", # Replace with your WCS cluster ID
# auth_credentials=weaviate.auth.AuthApiKey("WCS-API-KEY"), # Replace with your WCS API KEY - recommended: use env var
# headers={
# "X-AWS-Access-Key": os.getenv("AWS_ACCESS_KEY"), # Replace with your AWS access key - recommended: use env var
# "X-AWS-Secret-Key": os.getenv("AWS_SECRET_KEY"), # Replace with your AWS secret key - recommended: use env var
# }
# )
client.is_ready()
```
## Create a collection
> Collection stores your data and vector embeddings.
```python
# Note: in practice, you shouldn"t rerun this cell, as it deletes your data
# in "JeopardyQuestion", and then you need to re-import it again.
import weaviate.classes.config as wc
# Delete the collection if it already exists
if (client.collections.exists("JeopardyQuestion")):
client.collections.delete("JeopardyQuestion")
client.collections.create(
name="JeopardyQuestion",
vector_config=wc.Configure.Vectors.text2vec_aws(
service="bedrock", #this is crucial
model="cohere.embed-english-v3", # select the model, make sure it is enabled for your account
# model="amazon.titan-embed-text-v1", # select the model, make sure it is enabled for your account
region="eu-west-2" # select your region
),
# Enable generative model from AWS
generative_config=wc.Configure.Generative.aws(
service="bedrock", #this is crucial
model="amazon.titan-text-express-v1", # select the model, make sure it is enabled for your account
region="eu-west-2" # select your region
),
properties=[ # defining properties (data schema) is optional
wc.Property(name="Question", data_type=wc.DataType.TEXT),
wc.Property(name="Answer", data_type=wc.DataType.TEXT),
wc.Property(name="Category", data_type=wc.DataType.TEXT, skip_vectorization=True),
]
)
print("Successfully created collection: JeopardyQuestion.")
```
## Import the Data
```python
import requests, json
url = "https://raw.githubusercontent.com/weaviate/weaviate-examples/main/jeopardy_small_dataset/jeopardy_tiny.json"
resp = requests.get(url)
data = json.loads(resp.text)
# Get a collection object for "JeopardyQuestion"
jeopardy = client.collections.get("JeopardyQuestion")
# Insert data objects
response = jeopardy.data.insert_many(data)
# Note, the `data` array contains 10 objects, which is great to call insert_many with.
# However, if you have a milion objects to insert, then you should spit them into smaller batches (i.e. 100-1000 per insert)
if (response.has_errors):
print(response.errors)
else:
print("Insert complete.")
```
## Generative Search Queries
### Single Result
Single Result makes a generation for each individual search result.
In the below example, I want to create a Facebook ad from the Jeopardy question about Elephants.
```python
generatePrompt = "Turn the following Jeogrady question into a Facebook Ad: {question}"
jeopardy = client.collections.get("JeopardyQuestion")
response = jeopardy.generate.near_text(
query="Elephants",
limit=2,
single_prompt=generatePrompt
)
for item in response.objects:
print(json.dumps(item.properties, indent=1))
print("-----vvvvvv-----")
print(item.generated)
print("-----^^^^^^-----")
```
### Grouped Result
Grouped Result generates a single response from all the search results.
The below example is creating a Facebook ad from the 2 retrieved Jeoprady questions about animals.
```python
generateTask = "Explain why these Jeopardy questions are under the Animals category."
jeopardy = client.collections.get("JeopardyQuestion")
response = jeopardy.generate.near_text(
query="Animals",
limit=3,
grouped_task=generateTask
)
print(response.generated)
```
---
### Weaviate/Recipes/Similarity Search Embed Multilingual V2.0 (docs/weaviate/recipes/similarity_search_embed_multilingual_v2.0.md)
---
layout: recipe
toc: True
title: "Similarity Search with Cohere"
featured: False
integration: False
agent: False
tags: ['Similarity Search', 'Cohere']
---
[](https://colab.research.google.com/github/weaviate/recipes/blob/main/weaviate-features/model-providers/cohere/similarity_search_embed_multilingual_v2.0.ipynb)
## Dependencies
```python
!pip install weaviate-client
```
## Connect to Weaviate
```python
import weaviate, os
# Connect to your local Weaviate instance deployed with Docker
client = weaviate.connect_to_local(
headers={
"X-COHERE-Api-Key": os.environ["COHERE_API_KEY"] # Replace with your Cohere key - recommended: use env var
}
)
# Option 2
# Connect to your Weaviate Client Service cluster
# client = weaviate.connect_to_wcs(
# cluster_url="WCS-CLUSTER-ID", # Replace with your WCS cluster ID
# auth_credentials=weaviate.auth.AuthApiKey("WCS-API-KEY"), # Replace with your WCS API KEY - recommended: use env var
# headers={
# "X-Cohere-Api-Key": os.getenv("COHERE_API_KEY"), # Replace with your inference API key - recommended: use env var
# }
# )
client.is_ready()
```
## Create a collection
> Collection stores your data and vector embeddings.
```python
# Note: in practice, you shouldn"t rerun this cell, as it deletes your data
# in "JeopardyQuestion", and then you need to re-import it again.
import weaviate.classes.config as wc
# Delete the collection if it already exists
if (client.collections.exists("JeopardyQuestion")):
client.collections.delete("JeopardyQuestion")
client.collections.create(
name="JeopardyQuestion",
vector_config=wc.Configure.Vectors.text2vec_cohere( # specify the vectorizer and model type you"re using
model="embed-multilingual-v2.0", # defaults to embed-multilingual-v2.0 if not set
),
properties=[ # defining properties (data schema) is optional
wc.Property(name="Question", data_type=wc.DataType.TEXT),
wc.Property(name="Answer", data_type=wc.DataType.TEXT),
wc.Property(name="Category", data_type=wc.DataType.TEXT, skip_vectorization=True),
]
)
print("Successfully created collection: JeopardyQuestion.")
```
## Import the Data
```python
import requests, json
url = "https://raw.githubusercontent.com/weaviate/weaviate-examples/main/jeopardy_small_dataset/jeopardy_tiny.json"
resp = requests.get(url)
data = json.loads(resp.text)
# Get a collection object for "JeopardyQuestion"
jeopardy = client.collections.use("JeopardyQuestion")
# Insert data objects
response = jeopardy.data.insert_many(data)
# Note, the `data` array contains 10 objects, which is great to call insert_many with.
# However, if you have a milion objects to insert, then you should spit them into smaller batches (i.e. 100-1000 per insert)
if (response.has_errors):
print(response.errors)
else:
print("Insert complete.")
```
## Query Weaviate: Similarity Search (Text objects)
Similarity search options for text objects in **Weaviate**:
1. [near_text](https://docs.weaviate.io/weaviate/search/similarity#an-input-medium)
2. [near_object](https://docs.weaviate.io/weaviate/search/similarity#an-object)
3. [near_vector](https://docs.weaviate.io/weaviate/search/similarity#a-vector)
### nearText Example
Find a `JeopardyQuestion` about "animals in movies". Limit it to only 4 responses.
```python
# note, you can reuse the collection object from the previous cell.
# Get a collection object for "JeopardyQuestion"
jeopardy = client.collections.use("JeopardyQuestion")
response = jeopardy.query.near_text(
query="african beasts",
limit=4
)
for item in response.objects:
print("ID:", item.uuid)
print("Data:", json.dumps(item.properties, indent=2), "\n")
```
Return vector embeddings.
```python
response = jeopardy.query.near_text(
query="african beasts",
include_vector=True,
limit=4
)
for item in response.objects:
print("ID:", item.uuid)
print("Data:", json.dumps(item.properties, indent=2))
print("Vector:", item.vector, "\n")
```
Now, also request the `distance` for each returned item.
```python
import weaviate.classes.query as wq
response = jeopardy.query.near_text(
query="african beasts",
return_metadata=wq.MetadataQuery(distance=True),
limit=4
)
for item in response.objects:
print("ID:", item.uuid)
print("Distance:", item.metadata.distance)
print("Data:", item.properties, "\n")
```
### nearObject Example
Search through the `JeopardyQuestion` class to find the top 4 objects closest to id `a1dd67f9-bfa7-45e1-b45e-26eb8c52e9a6`. (The id was taken from the query above)
```python
response = jeopardy.query.near_object(
near_object="a1dd67f9-bfa7-45e1-b45e-26eb8c52e9a6", # replace with your id of interest
limit=4
)
for item in response.objects:
print("ID:", item.uuid)
print("Data:", item.properties, "\n")
```
### nearVector Example
Search through the `JeopardyQuestion` class to find the top 2 objects closest to the query vector `[-0.0125526935, -0.021168863, ... ]`
```python
response = jeopardy.query.near_vector(
near_vector=[-0.0125526935, -0.021168863, ... ], # your vector object goes here
limit=4
)
for item in response.objects:
print("ID:", item.uuid)
print("Data:", item.properties, "\n")
```
---
### Weaviate/Recipes/Weaviate Embeddings Service (docs/weaviate/recipes/weaviate_embeddings_service.md)
---
layout: recipe
toc: True
title: "How to Use Weaviate Embedding Service"
featured: True
integration: False
agent: False
tags: ["Weaviate Embeddings", "Weaviate Cloud"]
---
[](https://colab.research.google.com/github/weaviate/recipes/blob/main/weaviate-services/embedding-service/weaviate_embeddings_service.ipynb)
# Weaviate Embedding Service
[Weaviate Embeddings](https://docs.weaviate.io/cloud/embeddings) enables you to generate embeddings directly from a [Weaviate Cloud](/go/console?utm_content=recipe/) database instance.
_Please note this service is part of Weaviate Cloud and cannot be accessed through open-source. Additionally, this service is currently under technical preview, and you can request access [here](https://events.weaviate.io/embeddings-preview)._
This notebook will show you how to:
1. Define a Weaviate Collection
1. Run a vector search query
1. Run a hybrid search query
1. Run a hybrid search query with metadata filters
1. Run a generative search query (RAG)
## Requirements
1. Weaviate Cloud (WCD) account: You can register [here](/go/console?utm_content=recipe/)
1. Create a cluster on WCD: A free or Shared Cloud cluster is fine. You will need to grab the cluster URL and admin API key
1. OpenAI key to access `GPT-4o mini`
```python
!pip install --q weaviate-client
```
```python
!pip show weaviate-client # you need to have the Python client version 4.9.5 or higher
```
## Import Libraries and Keys
```python
import weaviate
from weaviate.classes.init import Auth
import os
import weaviate.classes.config as wc
from weaviate.classes.query import Filter
import requests, json
import pandas as pd
from io import StringIO
```
```python
WCD_CLUSTER_URL = os.getenv("WCD_CLUSTER_URL")
WCD_CLUSTER_KEY = os.getenv("WCD_CLUSTER_KEY")
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
```
## Connect to Weaviate
```python
client = weaviate.connect_to_weaviate_cloud(
cluster_url=WCD_CLUSTER_URL,
auth_credentials=Auth.api_key(WCD_CLUSTER_KEY),
headers={
"X-OpenAI-Api-Key": OPENAI_API_KEY,
}
)
print(client.is_ready())
```
Python output:
```text
True
```
## Define Collection
```python
# Note: This will delete your data stored in "JeopardyQuestion".and
# It will require you to re-import again.
# Delete the collection if it already exists
if (client.collections.exists("JeopardyQuestion")):
client.collections.delete("JeopardyQuestion")
client.collections.create(
name="JeopardyQuestion",
vector_config=wc.Configure.Vectors.text2vec_weaviate( # specify the vectorizer and model type you're using
model="Snowflake/snowflake-arctic-embed-l-v2.0", # default model
),
generative_config=wc.Configure.Generative.openai(
model="gpt-4o-mini" # select model, default is gpt-3.5-turbo
),
properties=[ # defining properties (data schema) is optional
wc.Property(name="Question", data_type=wc.DataType.TEXT),
wc.Property(name="Answer", data_type=wc.DataType.TEXT, skip_vectorization=True),
wc.Property(name="Category", data_type=wc.DataType.TEXT, skip_vectorization=True),
wc.Property(name="Value", data_type=wc.DataType.TEXT, skip_vectorization=True)
]
)
print("Successfully created collection: JeopardyQuestion.")
```
Python output:
```text
Successfully created collection: JeopardyQuestion.
```
## Import Data
We will use the small jeopardy dataset as an example. It has 1,000 objects.
```python
url = 'https://raw.githubusercontent.com/weaviate/weaviate-examples/main/jeopardy_small_dataset/jeopardy_small.csv'
resp = requests.get(url)
df = pd.read_csv(StringIO(resp.text))
```
```python
# Get a collection object for "JeopardyQuestion"
collection = client.collections.use("JeopardyQuestion")
# Insert data objects with batch import
with collection.batch.dynamic() as batch:
for _, row in df.iterrows():
properties = {
"question": row['Question'],
"answer": row['Answer'],
"category": row["Category"],
"value": row["Value"]
}
batch.add_object(properties)
failed_objects = collection.batch.failed_objects
if failed_objects:
print(f"Number of failed imports: {len(failed_objects)}")
else:
print("Insert complete.")
```
Python output:
```text
Insert complete.
```
```python
# count the number of objects
collection = client.collections.use("JeopardyQuestion")
response = collection.aggregate.over_all(total_count=True)
print(response.total_count)
```
Python output:
```text
1000
```
## Query Time
### Vector Search
```python
collection = client.collections.use("JeopardyQuestion")
response = collection.query.near_text(
query="marine mamal with tusk",
limit=2 # limit to only 2
)
for item in response.objects:
print("Data:", json.dumps(item.properties, indent=2), "\n")
```
Python output:
```text
Data: {
"value": "NaN",
"answer": "the narwhal",
"question": "A part of this marine mammal was prized by medieval folk, who thought it belonged to a unicorn",
"category": "THE ANIMAL KINGDOM"
}
Data: {
"value": "$400",
"answer": "the walrus",
"question": "You could say this Arctic mammal, Odobenus rosmarus, has a Wilford Brimley mustache",
"category": "MAMMALS"
}
```
### Hybrid Search
The goal of this notebook is to show you how to use the embedding service. For more information on hybrid search, check out [this folder](https://github.com/weaviate/recipes/tree/main/weaviate-features/hybrid-search) and/or the [documentation](https://docs.weaviate.io/weaviate/search/hybrid).
The `alpha` parameter determines the weight given to the sparse and dense search methods. `alpha = 0` is pure sparse (bm25) search, whereas `alpha = 1` is pure dense (vector) search.
Alpha is an optional parameter. The default is set to `0.75`.
```python
collection = client.collections.use("JeopardyQuestion")
response = collection.query.hybrid(
query="unicorn-like artic animal",
alpha=0.7,
limit=2
)
for item in response.objects:
print("Data:", json.dumps(item.properties, indent=2), "\n")
```
Python output:
```text
Data: {
"value": "NaN",
"answer": "the narwhal",
"question": "A part of this marine mammal was prized by medieval folk, who thought it belonged to a unicorn",
"category": "THE ANIMAL KINGDOM"
}
Data: {
"value": "$400",
"answer": "the walrus",
"question": "You could say this Arctic mammal, Odobenus rosmarus, has a Wilford Brimley mustache",
"category": "MAMMALS"
}
```
### Fetch Objects with Metadata Filters
Learn more about the different filter operators [here](https://docs.weaviate.io/weaviate/search/filters).
```python
collection = client.collections.use("JeopardyQuestion")
response = collection.query.fetch_objects(
limit=2,
filters=Filter.by_property("category").equal("BUSINESS & INDUSTRY")
)
for item in response.objects:
print("Data:", json.dumps(item.properties, indent=2), "\n")
```
Python output:
```text
Data: {
"value": "$200",
"answer": "Disney",
"question": "This company operates the 4 most popular theme parks in North America",
"category": "BUSINESS & INDUSTRY"
}
Data: {
"value": "$400",
"answer": "Yamaha",
"question": "This firm began in 1897 as Nippon Gakki Company, an organ manufacturer; electronic organs came along in 1959",
"category": "BUSINESS & INDUSTRY"
}
```
### Generative Search (RAG)
```python
collection = client.collections.use("JeopardyQuestion")
response = collection.generate.hybrid(
query="unicorn-like artic animal",
alpha=0.7,
grouped_task="Explain why people thought these animals were unicorn-like",
limit=2
)
print(f"Generated output: {response.generated}")
```
Python output:
```text
Generated output: People thought these animals were unicorn-like for a few reasons:
1. **Narwhal**: The narwhal is a marine mammal known for its long, spiral tusk, which can reach lengths of up to 10 feet. In medieval times, this tusk was often sold as a "unicorn horn" and was believed to possess magical properties. The resemblance of the narwhal's tusk to the mythical unicorn's horn led to the association between the two, as people were fascinated by the idea of unicorns and sought to find evidence of their existence in the natural world.
2. **Walrus**: While the walrus does not have a direct connection to unicorns like the narwhal, its large tusks and unique appearance may have contributed to some fantastical interpretations. The walrus's tusks, which can be quite prominent, might have sparked the imagination of those who were already inclined to believe in mythical creatures. Additionally, the walrus's size and distinctive features could have led to comparisons with other legendary animals, including unicorns, in folklore and storytelling.
Overall, the combination of physical characteristics and the cultural context of the time contributed to the perception of these animals as unicorn-like.
```
---
### Weaviate/Release Notes/Index (docs/weaviate/release-notes/index.md)
---
title: Release Notes
description: "Changelog and release notes for Weaviate Database stable releases and client libraries. Covers supported versions, latest patch updates, minor version history, and upgrade guidance."
image: og/docs/more-resources.jpg
# tags: ['release notes']
---
## Version support policy
Weaviate supports the **latest three minor versions** of Weaviate Database with bug fixes and security patches. Older minor versions are not actively maintained.
We recommend always running the **latest stable patch version** of your current minor release, and upgrading to newer minor versions regularly to stay within the supported range.
For instructions on upgrading Weaviate one minor version at a time, see the [Migration and Upgrades guide](/deploy/migration/index.md).
import QuickLinks from "/src/components/QuickLinks";
export const pythonCardsData = [
{
title: "v1.39",
link: "https://github.com/weaviate/weaviate/releases/tag/v1.39.0",
icon: "fa fa-tags",
},
{
title: "v1.38",
link: "https://github.com/weaviate/weaviate/releases/tag/v1.38.0",
icon: "fa fa-tags",
},
{
title: "v1.37",
link: "https://github.com/weaviate/weaviate/releases/tag/v1.37.0",
icon: "fa fa-tags",
},
{
title: "v1.36",
link: "https://github.com/weaviate/weaviate/releases/tag/v1.36.0",
icon: "fa fa-tags",
},
{
title: "v1.35",
link: "https://weaviate.io/blog/weaviate-1-35-release",
icon: "fa fa-tags",
},
];
Additional information
To run an `Aggregate` query, specify the following: - A target collection to search - One or more aggregated properties, such as: - A meta property - An object property - The `groupedBy` property - Select at least one sub-property for each selected property For details, see [Aggregate](/weaviate/api/graphql/aggregate).Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Additional information
Specify the information that you want your query to return. You can return object properties, object IDs, and object metadata.Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The response is like this:Example response
The response is like this:Example response
The response is like this:Example response
The response is like this:Example response
The response is like this:Example response
The response is like this: ``` 'Jeopardy!' 'Double Jeopardy!' ```Example response
The response is like this:For a list of filter operators, see the [API reference page](../api/graphql/filters.md#filter-structure). ## Filter with one condition Add a `filter` to your query, to limit the result set.
Example response
The output is like this:- Use
any_oforall_offor filtering by any, or all of a list of provided filters. - Use
&or|for filtering by pairs of provided filters.
#### Filter with `&` or `|`
These methods take variadic arguments (e.g. `Filters.and(f1, f2, f3, ...)`). To pass an array (e.g. `fs`) as an argument, provide it like so: `Filters.and(...fs)` which will spread the array into its elements.
Example response
The output is like this:Example response
The output is like this:Additional information
To create a nested filter, follow these steps. - Set the outer `operator` equal to `And` or `Or`. - Add `operands`. - Inside an `operand` expression, set `operator` equal to `And` or `Or` to add the nested group. - Add `operands` to the nested group as needed.Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this: ```json { "data": { "Get": { "JeopardyQuestion": [ { "answer": "Frank Lloyd Wright", "hasCategory": [ { "title": "PEOPLE" } ], "question": "In 1939 this famous architect polished off his Johnson Wax Building in Racine, Wisconsin" }, { "answer": "a luffa", "hasCategory": [ { "title": "FOOD" } ], "question": "When it's young & tender, this gourd used in the bathtub can be eaten like a squash" }, { "answer": "a snail", "hasCategory": [ { "title": "SCIENCE & NATURE" } ], "question": "Like an escargot, the abalone is an edible one of these gastropods" } ] } } } ```Example response
The output is like this:Additional information
The `*` wildcard operator matches zero or more characters. The `?` operator matches exactly one character.Currently, the `Like` filter is not able to match wildcard characters (`?` and `*`) as literal characters ([read more](../api/graphql/filters.md#wildcard-literal-matches-with-like)).
Example response
The output is like this:Example response
``` /* Detailed source-code truncated for AI context efficiency. */ ```Example response
``` /* Detailed source-code truncated for AI context efficiency. */ ```Define object `properties` with the `{prop-name}` syntax to interpolate retrieved content in the prompt.
The properties you use in the prompt do not have to be among the properties you retrieve in the query.
Example response
``` Property 'question': Including, in 19th century, one quarter of world's land & people, the sun never set on it Single prompt result: Did you know that in the 19th century, one quarter of the world's land and people were part of an empire where the sun never set? ☀️🌍 #historybuffs #funfact Property 'question': From Menes to the Ptolemys, this country had more kings than any other in ancient history Single prompt result: Which country in ancient history had more kings than any other, from Menes to the Ptolemys? 👑🏛️ #historybuffs #ancientkings ```Example response
``` Properties: {'points': 400, 'answer': 'the British Empire', 'air_date': datetime.datetime(1984, 12, 10, 0, 0, tzinfo=datetime.timezone.utc), 'question': "Including, in 19th century, one quarter of world's land & people, the sun never set on it", 'round': 'Double Jeopardy!'} Single prompt result: Did you know that in the 19th century, the sun never set on the British Empire, which included one quarter of the world's land and people? #triviatuesday #britishempire Debug: full_prompt: "Convert this quiz question: Including, in 19th century, one quarter of world\'s land & people, the sun never set on it and answer: the British Empire into a trivia tweet." Metadata: usage { prompt_tokens: 46 completion_tokens: 43 total_tokens: 89 } Properties: {'points': 400, 'answer': 'Egypt', 'air_date': datetime.datetime(1989, 9, 5, 0, 0, tzinfo=datetime.timezone.utc), 'question': 'From Menes to the Ptolemys, this country had more kings than any other in ancient history', 'round': 'Double Jeopardy!'} Single prompt result: Did you know that Egypt had more kings than any other country in ancient history, from Menes to the Ptolemys? #triviathursday #ancienthistory Debug: full_prompt: "Convert this quiz question: From Menes to the Ptolemys, this country had more kings than any other in ancient history and answer: Egypt into a trivia tweet." Metadata: usage { prompt_tokens: 42 completion_tokens: 36 total_tokens: 78 } ```Example response
``` Grouped task result: All of these animals are mammals. ```Example response
``` Grouped task result: The commonality among these animals is that they are all native to Australia. ```Example response
``` Grouped task result: They are all animals. Metadata: usage { prompt_tokens: 42 completion_tokens: 36 total_tokens: 78 } ```Example response
``` Properties: {'points': 800, 'answer': 'sheep', 'air_date': datetime.datetime(2007, 12, 13, 0, 0, tzinfo=datetime.timezone.utc), 'question': 'Australians call this animal a jumbuck or a monkey', 'round': 'Jeopardy!'} Properties: {'points': 100, 'answer': 'Australia', 'air_date': datetime.datetime(2000, 3, 10, 0, 0, tzinfo=datetime.timezone.utc), 'question': 'An island named for the animal seen here belongs to this country [kangaroo]', 'round': 'Jeopardy!'} Properties: {'points': 300, 'air_date': datetime.datetime(1996, 7, 18, 0, 0, tzinfo=datetime.timezone.utc), 'answer': 'Kangaroo', 'question': 'Found chiefly in Australia, the wallaby is a smaller type of this marsupial', 'round': 'Jeopardy!'} Grouped task result: I'll formulate a Jeopardy!-style question based on the image of the koala: Answer: This Australian marsupial, often mistakenly called a bear, spends most of its time in eucalyptus trees. Question: What is a koala? ```Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Additional information
For a discussion of fusion methods, see [this blog post](https://weaviate.io/blog/hybrid-search-fusion-algorithms) and [this reference page](../api/graphql/search-operators.md#fusion-algorithms).Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Example response
The response is like this: ``` 'Jeopardy!' 'Double Jeopardy!' ```Example response
The output is like this:Example response
The output is like this:Additional information
**Configure image search** To use images as search inputs, configure an image vectorizer integration for your collection. See the model provider integrations page for a [list of available integrations](../model-providers/index.md).If your query image is stored in a file, you can use the client library to search by its filename.
Example response
Example response
## Questions and feedback import DocsFeedback from "/_includes/docs-feedback.mdx";
Complete code
The weighting in detail
Each distance between the query vector and the target vector is multiplied by the specified weight, then the resulting weighted distances are summed for each object to produce a combined distance. The search results are sorted by this combined distance.The weighting in detail
Each distance is normalized against other results for that target vector. Each normalized distance between the query vector and the target vector is multiplied by the specified weight. The resulting weighted distances are summed for each object to produce a combined distance. The search results are sorted by this combined distance. For a more detailed explanation of how scores are normalized, see the blog post on [hybrid relative score fusion](https://weaviate.io/blog/hybrid-search-fusion-algorithms#relative-score-fusion)How to configure Weaviate to use multimedia search?
**Configure multimedia search** To use images, video, or audio as search inputs, configure a multi-modal vectorizer integration that supports these media types for your collection. For example, Google's `multi2vec-google` with the `gemini-embedding-2` model supports image, video, and audio inputs. See the [model provider integrations](../model-providers/index.md) page for available options. **Collection configuration** The collection must be configured with the appropriate media fields. For example:Example response
The response includes retrieved objects with their properties: ```python Product: Vintage Scholar Turtleneck - $55.0 Product: Glide Platforms - $69.0 Product: Sky Shimmer Sneaks - $69.0 ```Example response
The response includes a generated answer plus supporting information: ```text 📝 Final Answer: For vintage clothing under $60, you might like the Vintage Philosopher Midi Dress by Echo & Stitch. It features deep green velvet fabric with antique gold button details, tailored fit, and pleated skirt. For nice shoes under $60, consider the Glide Platforms by Vivid Verse. These are high-shine pink platform sneakers with cushioned soles. 🔭 Searches Executed: - queries=['vintage clothing'], filters=[[price < 60]], collection='ECommerce' - queries=['nice shoes'], filters=[[price < 60]], collection='ECommerce' 📊 Usage Statistics: - LLM Requests: 5 - Input Tokens: 288 - Output Tokens: 17 - Total Time: 7.58s ```Example response
Results are returned page by page: ```text Page 1: Glide Platforms - $90.0 Garden Haven Tote - $58.0 Sky Shimmer Sneaks - $69.0 Page 2: Garden Haven Tote - $58.0 Celestial Step Platform Sneakers - $90.0 Eloquent Satchel - $59.0 ```Example response
The agent uses conversation history for context: ```text User: What's the weather like? Assistant: The average temperature is 15°C with moderate humidity. User: Is that good for outdoor activities? Assistant: Yes, 15°C is comfortable for most outdoor activities. The moderate humidity levels make it pleasant for hiking, cycling, or sports. ```Example output
Responses are streamed as they're generated: ```text Searching... ⏳ Processing results... 🔍 For vintage... clothing... under $60... you might like... the Vintage... Philosopher Midi Dress... by Echo & Stitch... ✓ Complete ```Example behavior
User-defined filters are always applied in addition to agent-generated filters: ```python # Configuration: price < 100 # User query: "red shoes" # Actual query: (semantic search for "red shoes") AND (price < 100) AND (color = "red") ```Example output
Response inspection reveals the agent's execution details: ```text === Query Agent Response === Original Query: vintage style clothing 🔍 Final Answer Found: For vintage-style clothing under $60, I recommend the Vintage Scholar Turtleneck priced at $55. It features soft, stretchable fabric with timeless pleated details, perfect for a Dark Academia-inspired look. However, no shoes under $60 were found based on available information. 🔍 Searches Executed: - query: 'vintage style clothing' - filters: price < 60 - collection: 'ECommerce' - query: 'nice shoes' - filters: price < 60 - collection: 'ECommerce' ⚠️ Answer is Partial - Missing Information: - No recommendations were provided for nice shoes under $60 ```Additional information
**Configure reranking** To rerank search results, enable a reranker [model integration](../model-providers/index.md) for your collection. A collection can have multiple rerankers. If multiple `reranker` modules are enabled, specify the module you want to use in the `moduleConfig` section of your schema.Example response
The response should look like this:Example response
The response should look like this:Example response
The output is like this:This example uses a base64 representation of an image.
Additional information
Sample test vector
vector := []float32{0.326901312, 0.172652353, 0.574298978, -0.877372618, 0.208563102, 0.534870921, -0.905765693, -0.240794293, 0.2483627, 0.071935073, -0.470612466, 0.899590301, 0.821722525, 0.771190126, -0.729547086, -0.891606557, 0.304722712, -0.299226525, 0.400798778, -0.438221959, 0.84784485, 0.229913025, 0.072704543, 0.754321192, -0.019145501, -0.894141594, -0.994515521, -0.593096071, -0.42883483, 0.24194537, 0.620309746, 0.632115028, 0.588728611, 0.097637792, 0.778057433, 0.218009849, -0.967106101, 0.53489523, -0.41595204, 0.242416186, -0.947618483, -0.521548494, 0.22066765, 0.656955091, -0.937464798, 0.513341425, 0.578846678, 0.249978376, -0.085722009, -0.03557413, 0.943261393, 0.085512458, -0.125636201, 0.554060472, 0.485368427, -0.645984772, 0.756222985, -0.099291789, -0.590909311, 0.233526122, 0.085346719, -0.879696717, -0.5351979, -0.959582549, 0.160636781, -0.505745761, 0.597447967, 0.637738272, -0.7560195, -0.203242247, -0.14202656, 0.0531654, -0.256164061, -0.788468035, 0.687289393, -0.361320829, -0.454431255, -0.056878361, -0.24120844, -0.559818319, -0.260802008, -0.391211829, 0.941519464, 0.427640945, -0.747279873, 0.156631127, 0.283531662, -0.567472453, -0.056855298, 0.376830341, 0.24340912, 0.203539024, -0.472871161, 0.148073935, -0.205732037, -0.113967997, 0.744806131, -0.716108348, -0.121028453, -0.260367162, 0.799248419, 0.693572742, -0.791924921, -0.23802225, 0.61424365, -0.227275991, 0.288018577, 0.43869821, -0.054773369, 0.235872433, 0.150168526, -0.148419033, -0.42652761, 0.708727207, 0.084139137, -0.72887396, -0.218030612, 0.107339953, -0.518407575, 0.835435492, 0.035034357, -0.941809022, 0.787348994, 0.563871276, 0.766441516, -0.027821565, 0.245867777, 0.667148957, 0.738303557, -0.891110299, -0.275965165, -0.768567633, -0.475590831, 0.814911332, -0.297372689, 0.278844884, 0.95130689, 0.637530377, 0.618917313, 0.175740276, -0.249863627, -0.293828547, 0.320150997, -0.197713784, -0.633765065, -0.810942827, 0.591293734, 0.388968601, 0.523304585, -0.171063703, 0.602972529, -0.450091234, 0.345062519, -0.716491932, 0.435084962, -0.991825804, 0.689999161, -0.137097366, -0.537270475, -0.14424947, -0.62181862, 0.44289108, 0.072616733, 0.114381466, -0.972054206, 0.597329412, 0.562940173, 0.549476569, -0.706469709, 0.978081921, 0.180978079, 0.162027999, 0.788607827, -0.267257907, 0.985984986, -0.563312619, -0.640888755, 0.462486684, 0.369103705, 0.650806096, -0.167334677, 0.607351556, 0.822088516, 0.796317805, -0.503272355, -0.251183198, -0.171193987, 0.022293507, 0.428948271, 0.130966005, -0.736595944, 0.304682365, 0.663292867, -0.198997943, 0.035542683, 0.118594925, -0.509118134, 0.169740121, 0.375104805, -0.379886464, -0.498633816, -0.704396843, 0.030748626, 0.944446866, 0.888355185, -0.652586251, -0.906279254, 0.926259459, -0.214344492, 0.322871291, -0.027617198, 0.20895568, 0.035279297, -0.969237773, 0.403299676, 0.428694059, 0.829344779, 0.691959507, 0.383265745, -0.782718812, 0.775060865, -0.779937498, 0.584385461, -0.459012881, 0.662861143, 0.678415842, -0.127245162, -0.634464935, 0.646265039, -0.192781253, 0.950300755, 0.211855294, -0.503585688, 0.836612346, 0.787168113, 0.865806113, 0.38960291, 0.8664508, -0.572625523, 0.56761092, -0.735380506, -0.095070433, -0.783564692, -0.208375599, 0.739675191, 0.073271624, 0.359469611, 0.227572188, 0.03146414, 0.22938932, -0.447168816, 0.997660781, 0.215311392, -0.431177845, 0.016089255, 0.502448595, -0.705274029, -0.289382977, -0.577193696, 0.966175471, -0.510154942, -0.95823724, 0.24204605, 0.365546465, -0.297344885, 0.236294365, 0.446028631, 0.117976098, 0.094099994, 0.260277337, -0.461409164, -0.375480325, -0.614179681, -0.392757615, 0.100161621, -0.814176208, -0.347271514, 0.592469245, -0.988247355, -0.158397473, 0.921216369, -0.962889718, -0.932866744, 0.414358528, 0.12841629, -0.676515076, 0.940077931, -0.434330301, -0.2041959, 0.139998128, -0.937367769, -0.65941309, -0.716202446, -0.707964147, -0.389402878, 0.758786102, 0.543653384, -0.151055143, 0.406115293, -0.667719031, -0.811399948, 0.221955265, -0.493543772, 0.342954834, 0.327300923, -0.19955993, 0.752914123, -0.170643372, -0.14423466, 0.034084297, -0.855779749, 0.741368546, 0.240861775, -0.341099861, -0.6478463, 0.548267419, 0.409670736, 0.995208265, 0.807107939, -0.585172449, 0.163887551, 0.97695251, 0.575339181, -0.569841278, 0.675494554, -0.471893576, -0.030140821, -0.05243822, 0.050174597, -0.412903213, -0.683965383, 0.334143696, 0.421115564, 0.175047935, 0.530304957, 0.304087579, -0.792279648, 0.685567038, -0.803590175, -0.742988649, 0.559471864, -0.720445164, -0.299579897, 0.856260016, -0.181088629, -0.397816074, 0.767682872, 0.738067303, 0.359374803, -0.385285243, -0.038967135, -0.147880482, 0.83122139, -0.446691037, -0.789851962, -0.110046918, -0.468262552, -0.756854501, -0.445852765, 0.978448405, -0.726514778, 0.667864341, 0.74283952, 0.484586568, 0.51334425, 0.819917424, -0.838528257, 0.436940199, -0.448078512, -0.337453429, -0.172542255, 0.17131926, 0.511645199, 0.684561713, 0.486342731, 0.873551862, -0.731099225, -0.753154103, -0.236784718, -0.65032768, -0.239905204, -0.803154248, -0.640516296, 0.855964698, -0.416501359, 0.630052995}Example response
The output is like this:Additional information
- The distance value depends on many factors, including the vectorization model you use. Experiment with your data to find a value that works for you. - [`certainty`](../config-refs/distances.md#distance-vs-certainty) is only available with `cosine` distance. - To find the least similar objects, use the negative cosine distance with `nearVector` search.Example response
The output is like this:Example response
The output is like this:Example response
The output is like this:Candidate window, worked example, and deep pages
A diversified query works with two limits: - the query's top-level `limit` is the **candidate window** that gets diversified, and - the diversity `limit` is the **page size**, or how many objects come back. Each page is taken from the slice `[offset, offset + limit)` of the relevance-ranked results, so `offset` must advance by the query `limit`:The sample data
The JSON file is based on this data. The vector embeddings are generated with the OpenAI API [`text-embedding-ada-002` model](https://platform.openai.com/docs/guides/embeddings/what-are-embeddings). {" "}Your vectors can come from any source, including your own vectorizer model or another model provider such as Cohere or Hugging Face.
| Category | Question | Answer | Vector | | :------- | :---------------------------------------------------------------------------------------------------------------- | :---------------------- | :------------------------------------------------- | | SCIENCE | This organ removes excess glucose from the blood & stores it as glycogen | Liver | [ -0.006632288, -0.0042016874, ..., -0.020163147 ] | | ANIMALS | It's the only living mammal in the order Proboseidea | Elephant | [ -0.0166891, -0.00092290324, ..., -0.032253385 ] | | ANIMALS | The gavial looks very much like a crocodile except for this bodily feature | the nose or snout | [ -0.015592773, 0.019883318, ..., 0.0033349802 ] | | ANIMALS | Weighing around a ton, the eland is the largest species of this animal in Africa | Antelope | [ 0.014535263, -0.016103541, ..., -0.025882969 ] | | ANIMALS | Heaviest of all poisonous snakes is this North American rattlesnake | the diamondback rattler | [ -0.0030859283, 0.015239313, ..., -0.021798335 ] | | SCIENCE | 2000 news: the Gunnison sage grouse isn't just another northern sage grouse, but a new one of this classification | species | [ -0.0090561025, 0.011155112, ..., -0.023036297 ] | | SCIENCE | A metal that is "ductile" can be pulled into this while cold & under pressure | wire | [ -0.02735741, 0.01199829, ..., 0.010396339 ] | | SCIENCE | In 1953 Watson & Crick built a model of the molecular structure of this, the gene-carrying substance | DNA | [ -0.014227471, 0.020493254, ..., -0.0027445166 ] | | SCIENCE | Changes in the tropospheric layer of this are what gives us weather | the atmosphere | [ 0.009625228, 0.027518686, ..., -0.0068922946 ] | | SCIENCE | In 70-degree air, a plane traveling at about 1,130 feet per second breaks it | Sound barrier | [ -0.0013459147, 0.0018580769, ..., -0.033439033 ] |
```bash pip install -U weaviate-client ```
Query response
The response is like this: import BiologyQuestionsJson from "/_includes/code/quickstart/response.biology.questions.mdx";How to list enabled modules
You can check which modules are enabled by viewing the `meta` information for your Weaviate instance, as shown below:How to enable modules
For configurable deployments, you can specify enabled modules. For example, in a Docker deployment, you can do so by listing them on the `ENABLE_MODULES` environment variable, as shown below: ```yaml services: weaviate: environment: ENABLE_MODULES: "text2vec-cohere,text2vec-huggingface,text2vec-openai,text2vec-google,generative-cohere,generative-openai,generative-google" ``` Check the specific documentation for your deployment method ([Docker](/deploy/installation-guides/docker-installation.md), [Kubernetes](/deploy/installation-guides/k8s-installation.md), [Embedded Weaviate](/deploy/installation-guides/embedded.md)) for more information on how to configure it.How to configure the language model
Model parameters are exposed through the generative model provider configuration. You can set them when you create the collection, alongside the generative integration itself. For example, the `generative-cohere` integration can be configured as follows: ```python from weaviate.classes.config import Configure client.collections.create( "DemoCollection", generative_config=Configure.Generative.cohere( # # These parameters are optional # model="command-a-03-2025", # temperature=0.7, # max_tokens=500, # k=5, # stop_sequences=["\n\n"], ) # Additional parameters not shown ) ``` And the `generative-openai` integration can be configured as follows: ```python from weaviate.classes.config import Configure client.collections.create( "DemoCollection", generative_config=Configure.Generative.openai( # # These parameters are optional # model="gpt-5-mini", # temperature=0.7, # max_tokens=500, # frequency_penalty=0, # presence_penalty=0, # top_p=0.7, ) # Additional parameters not shown ) ``` Each parameter is optional. If you do not set a parameter, Weaviate applies the server-defined default. For the available models, the default model and the full parameter list, see the model provider pages for [Cohere](../model-providers/cohere/generative.md) and [OpenAI](../model-providers/openai/generative.md). See the [documentation](../model-providers/index.md) for various model provider integrations.## Other guides and tutorials export const otherGuidesCardsData = [ { title: "How-to manuals", description: "Instructions on how to use most Weaviate features.", link: "/weaviate/guides", icon: "fas fa-compass", }, { title: "Tutorials", description: "Step-by-step guides and practical examples.", link: "/weaviate/tutorials", icon: "fas fa-chalkboard-teacher", }, { title: "Recipes", description: "Jupyter Notebooks that showcase various use cases and functionalities.", link: "/weaviate/recipes", icon: "fas fa-scroll", }, ];
--- ### Weaviate/Starter Guides/Which Weaviate (docs/weaviate/starter-guides/which-weaviate.md) --- title: Weaviate configurations sidebar_position: 1 image: og/docs/tutorials.jpg # tags: ['getting started'] --- Weaviate can be configured and deployed in many different ways. Important configuration decisions include: - The [deployment setup](/deploy/index.mdx) - The [model integration](../model-providers/index.md) to enable This page helps you to find the right combination for your project. ## Deploy Weaviate Weaviate can be deployed in the following ways: - [Embedded Weaviate](/deploy/installation-guides/embedded.md) - [Docker-Compose](/deploy/installation-guides/docker-installation.md) - [Weaviate Cloud (WCD)](/deploy/installation-guides/weaviate-cloud.md) - [DigitalOcean Managed Weaviate](/deploy/installation-guides/digitalocean.md) - [Self-managed Kubernetes](/deploy/installation-guides/k8s-installation.md) - [Hybrid SaaS](https://weaviate.io/pricing) ## Vectorization options When adding data objects to Weaviate, you have two choices: - Specify the object vector directly. - Use a Weaviate vectorizer module to generate the object vector. If you are using a vectorizer module, your choices depend on your input medium/modality, as well as whether you would prefer a local or API-based vectorizer. Generally speaking, an API-based vectorizer is more convenient to use, but it incurs additional costs. A local vectorizer can cost less, but may require specialized hardware (such as a GPU) to run at comparable speeds. For text, [this open-source benchmark](https://huggingface.co/blog/mteb) provides a good overview of the performance of different vectorizers. Remember, domain-specific and real-world performance may vary. ## Use cases Here are some recommendations for different use cases. ### Quick evaluation If you are evaluating Weaviate, we recommend using one of these instance types to get started quickly: - [Weaviate Cloud (WCD)](/cloud) free cluster - [Embedded Weaviate](/deploy/installation-guides/embedded) Use an inference-API based text vectorizer with your instance, for example, `text2vec-cohere`, `text2vec-huggingface`, `text2vec-openai`, or `text2vec-google`. The [Quickstart guide](/weaviate/quickstart) uses a WCD free cluster and an API based vectorizer to run the examples. ### Development For development, we recommend using - [Weaviate Cloud (WCD)](/go/console?utm_content=tutorial) or [Docker Compose](/deploy/installation-guides/docker-installation.md). - A vectorization strategy that matches your production vectorization strategy. #### Docker-Compose vs. Weaviate Cloud (WCD) Of the two, Docker-Compose is more flexible as it exposes all configuration options, and can be used in a local development environment. Additionally, it can use local vectorizer modules such as `text2vec-transformers` or `multi2vec-clip` for example. On the other hand, WCD instances are easier to spin up, and takes away the need to manage the deployment yourself. Note that Embedded Weaviate is currently not recommended for serious development use as it is at an experimental phase. #### Vectorization strategy For development, we recommend using a vectorizer module that at least approximates your needs. As a first point, you must choose: - Whether to vectorize data yourself and import it into Weaviate, or - To use a Weaviate vectorizer module. Then, we recommend choosing a vectorizer module that is as close as possible to your production needs. For example, if search quality is of paramount importance, we suggest using your preferred vectorizer module in development as well. Keep in mind two other factors, which are cost, and their footprint. - Vectorization, such as with an API-based vectorizer, can be expensive. This is especially true if you are dealing with very large datasets. - Vector lengths can vary by a factor of ~5, which will impact both your storage and memory requirements. This can ultimately impact cost down the line. ### Production For production deployments, consider one of these hosting models: - [Weaviate Cloud (WCD)](/cloud) - [DigitalOcean Managed Weaviate](/deploy/installation-guides/digitalocean.md) - [Self-managed Kubernetes](/deploy/installation-guides/k8s-installation.md) - [Hybrid SaaS](/cloud) All of these options are scalable. Kubernetes and Hybrid SaaS offer the most configuration flexibility. A WCD-based solution is the easiest way to deploy Weaviate in terms of setup and maintenance. A self-managed Kubernetes deployment combines flexibility and scalability. DigitalOcean Managed Weaviate is a fully managed option operated by DigitalOcean, a good fit for teams already on DigitalOcean or who want managed hosting outside Weaviate Cloud. If you need additional configuration control, but you don't want to manage your Weaviate deployment, Hybrid SaaS offers a best-of-both-worlds solution. ## By Vectorizer & Reranker Weaviate makes various vectorizer & reranker modules available for different media types, also called modalities. Some model types such as Ollama or Transformers models are locally hosted, while others such as Cohere or OpenAI are API-based. This affects their availability in different Weaviate setups. We recommend reviewing from the available [model integrations](../model-providers/index.md) and their availability in different Weaviate setups. Then, choose the one that best fits your needs. ## Questions and feedback import DocsFeedback from '/\_includes/docs-feedback.mdx';
**Instead of simply raising the limit, consider rethinking your architecture**. If you really need to change the limit, use the [`MAXIMUM_ALLOWED_COLLECTIONS_COUNT`](/deploy/configuration/env-vars/index.md) environment variable. ::: This guide offers an overview of the available architectural choices of using **multi-tenancy** or defining a **dedicated collection** for each subset of data. Consider a scenario where a developer is creating a SaaS platform for product recommendations that allows end users (merchants) to recommend products to their shoppers. In this scenario, each merchant will upload and work only with their own data. One option is for the developer to create a dedicated collection for each merchant's dataset. However, as the number of merchants grows, so does the number of collections, potentially leading to performance bottlenecks and increased operational complexity. This leads us to an important architectural question: Should you use **"Multi-tenancy"** or **"One collection per dataset"**? ## Choosing the right architecture import MultiTenanyVsMultipleCollections from '/docs/weaviate/starter-guides/img/weaviate-multi-tenancy-vs-multiple-collections.png';
When designing a vector database collection definition (data schema) in Weaviate, you must decide between **multi-tenancy** (storing data for multiple tenants in a single collection) or creating **separate collections for each dataset** (the "One collection per dataset" strategy). Each approach has its own advantages and trade-offs, especially in terms of performance, scalability, and management. This guide aims to clarify these concepts and highlight the implications of each approach, focusing on the benefits and drawbacks: - **["One collection per dataset" architecture](#one-collection-per-dataset-architecture)** - **[Multi-tenancy architecture](#multi-tenancy-architecture)** ### "One collection per dataset" architecture In this approach, **each dataset is assigned a dedicated collection** to ensure the separation of data between them. Before the implementation of multi-tenancy in Weaviate, this was the best approach for managing multiple datasets. import MultipleCollectionsExample from '/docs/weaviate/starter-guides/img/weaviate-multiple-collections-example.png';
When a book store registers on our platform, we create the collection{' '}
BookStoreProducts. This allows the store
to customize the collection and add properties that are specific to
their e-commerce platform, like author, title, genre, etc.
Creating a new collection per dataset (ShoeStoreProducts,
GameStoreProducts
, etc.) might seem like a simple and effective way to maintain data
isolation. However, as the platform scales, this approach quickly encounters
significant challenges.
#### Advantages - **Customizability**: Collection definition changes or optimizations can be tailored to individual collections without affecting others. - **Data isolation**: Datasets are completely separated through the use of dedicated collections. #### Challenges - **Resource overhead:** Each collection requires its own definition, indexes, and storage, leading to increased memory and disk usage. Managing managing millions or even thousands of collections becomes nearly impossible. - **Operational complexity:** Collection definition changes must be applied individually to each collection. Every collection must be updated separately and this takes a lot of time and computational effort. :::tip If you are creating more than `20` collections, take a moment to consider if multi-tenancy might be utilized. ::: ### Multi-tenancy architecture Multi-tenancy refers to the practice of dividing a single collection to multiple datasets (tenants). Each tenant’s data is logically isolated through the use of tenant names. Multi-tenancy is especially useful when you want to store data for multiple customers or when you want to store similarly structured data for multiple projects. import MultiTenancyExample from '/docs/weaviate/starter-guides/img/weaviate-multi-tenancy-example.png';
Each tenant is identified by their name, ensuring that their products
remain logically separated within the same collection. When a "book store"
registers on our platform, we can create a new tenant called{' '}
BookStore in the collection Products.
Queries can also be filtered based on the name to retrieve only the relevant
data.
#### Advantages Use multi-tenancy when you need to support a large number of tenants and prioritize resource efficiency and scalability. - **Easier collection definition management:** Definition updates apply universally to all tenants. For example, adding a new property to all products is now much easier. - **Index scalability**: Indexes can be optimized for a single collection rather than fragmented across multiple collections. Each tenant has a dedicated, high-performance vector index, which results in faster query speeds. Instead of searching a shared index space, each tenant responds as if it were the only user on the cluster. - **Data isolation**: Each tenant’s data is completely segregated. This also means that data deletion is much easier and faster. #### Challenges - **Access control complexity**: [Fine-grained access control](/deploy/configuration/authorization.md) must be implemented to ensure data isolation between tenants. - **Uniform collection definition**: All tenants must share the same collection schema and configuration. :::tip Reduce storage costs You can change the [state of a tenant](../managing-resources/tenant-states.mdx) into `inactive` (stored locally on disk) or `offloaded` (stored on cloud storage) in order to save resources.
Find out more about managing multi-tenancy in [How to: Multi-tenancy operations](../../manage-collections/multi-tenancy.mdx). ::: ## Conclusion While the choice between multi-tenancy and dedicated collections depends on your specific use case, the substantial **performance benefits of multi-tenancy** make it the preferred approach for most scenarios. With multi-tenancy, you gain significant resource efficiency by reducing indexing overhead and streamlining collection definition updates across all tenants. Although dedicated collections can offer enhanced data isolation and flexibility in certain cases, their operational complexity and increased resource demands often outweigh these benefits. Regularly monitoring query performance, index size, and resource utilization is crucial to fine-tune your architecture, ensuring it meets both current and future needs. ## Further resources To find out more about multi-tenancy, visit the following pages: - [How-to: Multi-tenancy operations](../../manage-collections/multi-tenancy.mdx) - [How-to: Manage tenant states](../../manage-collections/tenant-states.mdx) ## Questions and feedback import DocsFeedback from '/_includes/docs-feedback.mdx';
See the returned collection definition
``` /* Detailed source-code truncated for AI context efficiency. */ ```Index Type | Vector
Compression | Tenant State | Storage | Performance | Cost | |----------|-------------------|--------------------|---------------|------------|-----------------------|----------| | 🟥 Hot | HNSW | PQ, SQ, BQ | Active | Memory | Fastest | High | | 🟨 Warm | Flat | BQ | Active | SSD | Slower | Moderate | | 🟦 Cold | Any | Any | Inactive | Cloud | Resource not available | Low | ### 🟥 Hot - Describes memory usage - Fastest and most expensive - Primarily driven by [`HNSW`](./indexing.mdx#hnsw-indexes) vector indexes - Always available (active) for use - Costs increase rapidly with scale ### 🟨 Warm - Describes data stored on disk (SSD) - Slower than [hot](#-hot) tier but less expensive - Driven by [flat](./indexing.mdx#flat-indexes) vector index, object data, and [inverted indexes](./indexing.mdx#inverted-indexes) - Always available (active) for use - Costs increase more slowly than hot tier as data grows ### 🟦 Cold import OffloadingLimitation from '/_includes/offloading-limitation.mdx';
enabled?"} objCount{"Does the collection have
more than 100k objects?"} stayUnder{"Is the collection likely to stay
at under 100k objects?"} multiTenant -->|No| objCount multiTenant -->|Yes| useDynamic["Use Dynamic"] objCount -->|Yes| useHNSW["Use HNSW"] objCount -->|No| stayUnder stayUnder -->|Yes| useFlat["Use Flat"] stayUnder -->|"No/Unsure"| useDynamic["Use Dynamic"] end %% Style nodes style multiTenant fill:#ffffff,stroke:#B9C8DF,color:#130C49 style objCount fill:#ffffff,stroke:#B9C8DF,color:#130C49 style stayUnder fill:#ffffff,stroke:#B9C8DF,color:#130C49 %% Terminal nodes style useHNSW fill:#ffffff,stroke:#61BD73,color:#130C49 style useFlat fill:#ffffff,stroke:#61BD73,color:#130C49 style useDynamic fill:#ffffff,stroke:#61BD73,color:#130C49 %% Style subgraph style vectorIndex fill:#ffffff,stroke:#130C49,stroke-width:2px,color:#130C49 ``` If you are unsure which index type to use, the dynamic index type is a good starting point, as it automatically transitions from a flat to an HNSW index based on the number of objects. - [Starter guide: indexes](./indexing.mdx) - [How-to: Set the vector index type](../../manage-collections/vector-config.mdx#set-vector-index-type) ### Vector compression Vector compression techniques reduce the size of vectors by quantizing them into a smaller representation. This can have the impact of reducing memory usage, or improving performance by reducing the amount of data that needs to be read from disk. The trade-off is that the resulting search quality may be lower. Weaviate supports the following vector compression methods: | Compression Method | Index Type | Requires Training | Description | |---------------------------|------------|-------------------|-------------| | Product Quantization (PQ) | HNSW | Yes | Each vector becomes an array of integer-based centroids ([read more](../../concepts/vector-quantization.md#product-quantization)) | | Binary Quantization (BQ) | HNSW, Flat | No | Each vector dimension becomes a bit ([read more](../../concepts/vector-quantization.md#binary-quantization)) | | Scalar Quantization (SQ) | HNSW | Yes | Each vector dimension becomes an integer ([read more](../../concepts/vector-quantization.md#scalar-quantization)) | | Rotational Quantization (RQ) | All index types | No | Each vector is rotated then quantized to an integer ([read more](../../concepts/vector-quantization.md#rotational-quantization)) | As a starting point, use the following guidelines for selecting a compression method: ```mermaid flowchart LR %% Define nodes and connections subgraph compression ["Compression Strategy"] direction LR startIndexType{"What is your
vector index type?"} sample{"Do you have a
representative sample
of your final dataset?"} tunable{"Do you want
tunable
compression?"} bqcompat{"Is your vectorizer
model compatible
with BQ?"} startIndexType -->|HNSW| sample startIndexType -->|Flat| bqcompat sample -->|Yes| tunable tunable -->|Yes| usePQ["Use PQ"] tunable -->|No| useSQ["Use SQ"] sample -->|No| useBQ["Use BQ"] bqcompat -->|Yes| useBQ["Use BQ"] bqcompat -->|No| noCompress["Do not use
compression"] end %% Style nodes style startIndexType fill:#ffffff,stroke:#B9C8DF,color:#130C49 style sample fill:#ffffff,stroke:#B9C8DF,color:#130C49 style tunable fill:#ffffff,stroke:#B9C8DF,color:#130C49 style bqcompat fill:#ffffff,stroke:#B9C8DF,color:#130C49 %% Terminal nodes style usePQ fill:#ffffff,stroke:#61BD73,color:#130C49 style useSQ fill:#ffffff,stroke:#61BD73,color:#130C49 style useBQ fill:#ffffff,stroke:#61BD73,color:#130C49 style noCompress fill:#ffffff,stroke:#61BD73,color:#130C49 %% Style subgraph style compression fill:#ffffff,stroke:#7AD6EB,stroke-width:2px,color:#130C49 ``` If you are unsure which index type to use, scalar quantization is a good starting point, provided that you have a representative sample of your likely final dataset. - [Starter guide: Vector compression](./compression.mdx) - [How-to: Configure vector compression](../../configuration/compression/index.md) ### Tenant states Multi-tenant collections enable you to efficiently manage isolated subsets of data. Each tenant share the same schema and configuration. Weaviate supports the following tenant states: | Tenant state | CRUD & Queries | Vector Index | Inverted Index | Object Data | Time to Activate |Description | |------------------|----------------|--------------|----------------|-------------|------------------|------------| | Active (default) | **Yes** | Hot/Warm | Warm | Warm | None |Tenant is available for use | | Inactive | **No** | Warm | Warm | Warm | Fast |Tenant is locally stored but not available for use | | Offloaded | **No** | Cold | Cold | Cold | Slow |Tenant is stored in cloud storage and not available for use | *Hot* tenants can be deactivated to *warm* storage to reduce memory usage, and any tenant can be offloaded to *cold* storage to reduce memory and disk usage. Conversely, any tenant can be reactivated when needed. ```mermaid flowchart LR %% Define nodes and connections subgraph tenantData ["Tenant Data Availability"] direction LR needNow{"Does the tenant data
need to be
available now?"} howQuick{"When it is needed,
how quickly does it
need to be available?"} needNow -->|Yes| active["Active"] needNow -->|No| howQuick howQuick -->|Quickly| inactive["Inactive"] howQuick -->|"Latency is acceptable"| offloaded["Offloaded"] end %% Style nodes style needNow fill:#ffffff,stroke:#B9C8DF,color:#130C49 style howQuick fill:#ffffff,stroke:#B9C8DF,color:#130C49 %% Terminal nodes style active fill:#ffffff,stroke:#61BD73,color:#130C49 style inactive fill:#ffffff,stroke:#61BD73,color:#130C49 style offloaded fill:#ffffff,stroke:#61BD73,color:#130C49 %% Style subgraph style tenantData fill:#ffffff,stroke:#61BD73,stroke-width:2px,color:#130C49 ``` Consider a strategy of deactivating tenants that are not frequently accessed, and offloading tenants that are rarely accessed. - [Starter guide: tenant states](./tenant-states.mdx) - [How-to: Configure tenant offloading](/deploy/configuration/tenant-offloading.md) - [How-to: Manage tenant states](../../manage-collections/tenant-states.mdx) ## Tips ### Best Practices - Start with the dynamic [index type](#vector-index-types) for new collections. This is particularly useful for multi-tenant collections, as it allows each tenant to use the most appropriate index type. - Use [vector compression](#vector-compression) techniques to optimize storage and query performance, especially for large collections or tenants. - Conduct thorough testing when changing index types or compression methods to ensure performance meets your requirements. ### Common Pitfalls - Overprovisioning hot storage: Keeping all data in hot storage can lead to unnecessary costs. Regularly assess what data truly needs the fastest access. - Neglecting to plan for growth: Not anticipating data growth can lead to performance issues. Always design your resource management strategy with scalability in mind. - Improper tenant management: In multi-tenant scenarios, forgetting to [offload inactive tenants](#tenant-states) can lead to resource waste. Implement automated processes to manage tenant states based on usage patterns. - Mismatch between quantization techniques, model and data: When using compression technique, ensure that the quantization technique is compatible with the model (e.g. BQ) and that the data is sufficient and representative for training (e.g. PQ, SQ). ## Related pages - [Starter guide: Compression](./compression.mdx) - [Starter guide: Indexing](./indexing.mdx) - [Starter guide: Tenant states](./tenant-states.mdx) - [Concepts: Vector Index](../../concepts/indexing/vector-index.md) - [Concepts: Vector Quantization](../../concepts/vector-quantization.md) - [Concepts: Multi-Tenancy](../../concepts/data.md#multi-tenancy) - [How-to: Set the vector index type](../../manage-collections/vector-config.mdx#set-vector-index-type) - [How-to: Configure vector compression](../../configuration/compression/index.md) - [How-to: Perform multi-tenancy operations](../../manage-collections/multi-tenancy.mdx) - [How-to: Manage tenant states](../../manage-collections/tenant-states.mdx) ## Questions and feedback import DocsFeedback from '/_includes/docs-feedback.mdx';
For example, data may not be immediately available after reactivating an offloaded tenant. Similarly, data may not be immediately unavailable after offloading a tenant. This is because the [tenant states are eventually consistent](../../concepts/replication-architecture/consistency.md#tenant-states-and-data-objects), and the change must be propagated to all nodes in the cluster. ::: ### Tenant states and resource usage *Hot* tenants can be deactivated to *warm* storage to reduce memory usage, and any tenant can be offloaded to *cold* storage to reduce memory and disk usage. Conversely, any tenant can be reactivated when needed. Therefore, consider a strategy of deactivating tenants that are not frequently accessed, and offloading tenants that are rarely accessed. For example, imagine an e-commerce platform with separate tenants for each vendor. During a holiday sale, the tenant for a popular electronics vendor might be kept active for quick access, while tenants for seasonal vendors (e.g., Christmas decorations in July) could be offloaded to cold storage to save resources. Understanding how tenant states interact with different index types is crucial for developing an effective resource management strategy. Let's explore this relationship in more detail. ### Tenant states and index types Tenant states management strategies are tied to index types. This is because the index type determines the resources used by a tenant. For example, a tenant with an HNSW index type uses *hot* resources, while a tenant with a flat index type uses *warm* resources. If a multi-tenant collection is configured with a dynamic index type, some tenants may be stored in *warm* storage (flat index) and others in *hot* storage (HNSW index). As a result, effectively managing tenants with HNSW indexes may have the most significant impact on resource usage. We suggest following the following guidelines for selecting a tenant state: ```mermaid flowchart LR %% Define nodes and connections subgraph tenantData ["Tenant Data Availability"] direction LR needNow{"Does the tenant data
need to be
available now?"} howQuick{"When it is needed,
how quickly does it
need to be available?"} needNow -->|Yes| active["Active"] needNow -->|No| howQuick howQuick -->|Quickly| inactive["Inactive"] howQuick -->|"Latency is acceptable"| offloaded["Offloaded"] end %% Style nodes style needNow fill:#ffffff,stroke:#B9C8DF,color:#130C49 style howQuick fill:#ffffff,stroke:#B9C8DF,color:#130C49 %% Terminal nodes style active fill:#ffffff,stroke:#61BD73,color:#130C49 style inactive fill:#ffffff,stroke:#61BD73,color:#130C49 style offloaded fill:#ffffff,stroke:#61BD73,color:#130C49 %% Style subgraph style tenantData fill:#ffffff,stroke:#61BD73,stroke-width:2px,color:#130C49 ``` If you are unsure which tenants can be made inactive or offloaded, consider a strategy of deactivating tenants that have not been accessed for a certain period of time. And offloading tenants that have not been accessed for a longer period of time. ## Hands-on Here are some resources to implement hands-on tenant offloading in Weaviate. ### Configure tenant offloading In order to use tenant offloading, you need to configure an offload module.
(See [How-to: Configure tenant offloading](/deploy/configuration/tenant-offloading.md)) ### Update a tenant state A tenant state can be switched between `active`, `inactive`, and `offloaded` at any time.
(See [How-to: Manage tenant states](../../manage-collections/tenant-states.mdx) for instructions and code examples.) ### Auto-activate tenants You can configure a collection to automatically activate (inactive and offloaded) tenants when they are accessed.
This can provide a balance between resource usage and performance.
(See [How-to: Auto-activate tenants](../../manage-collections/tenant-states.mdx#automatically-activate-tenants) for instructions and code examples.) ## Questions and feedback import DocsFeedback from '/_includes/docs-feedback.mdx';
--- ### Weaviate/Tutorials/Modules (docs/weaviate/tutorials/modules.md) --- title: Modules - an introduction description: Learn about Weaviate modules and enhance your data solutions with specialized features. sidebar_position: 90 image: og/docs/tutorials.jpg # tags: ['modules'] --- import UpdateInProgressNote from '/_includes/update-in-progress.mdx';
The following visualization shows how late interaction works in a ColBERT model, in comparison to a single-vector model. Figure: Late interaction vs single-vector comparison
More about late interaction
In a single-vector approach, two embeddings have the same dimensionality (e.g. 768). So, their similarity is calculated directly, e.g. by calculating their dot product, or cosine distance. In this case, the only interaction occurs when the two vectors are compared.Another approach is a "early interaction" search, as seen in some "cross-encoder" models. In this approach, the query and the object are used throughout the embedding generation and comparison process. While this can lead to more accurate results, the challenge is that embeddings cannot be pre-calculated, before the query is known. So, this approach is often used for "reranker" models where the dataset is small.
Late interaction is a middle ground between these two approaches, using multi-vector embeddings.
Each multi-vector embedding is composed of multiple vectors, where a vector represents a portion of the object, such as a token. For example, one object's embedding may have a shape of (30, 64), meaning it has 30 vectors, each with 64 dimensions. But another object's embedding may have a shape of (20, 64), meaning it has 20 vectors, each with 64 dimensions.
Late interaction takes advantage of this structure by finding the best match for each query token among all tokens in the target text (using MaxSim operation). For example, when searching for 'data science', each token-level vector is compared with the most relevant part of a document, rather than trying to match the vector for the entire phrase at once. The final similarity score combines these individual best matches. This token-level matching helps capture nuanced relationships and word order, making it especially effective for longer texts.
A late interaction search: 1. Compares each query vector against each object vector 1. Combines these token-level comparisons to produce a final similarity score
This approach often leads to better search results, as it can capture more nuanced relationships between objects.
Obtain the embedding manually
This allows you to use any pre-existing embeddings you may have, while benefiting from the convenience of a model integration for other objects. ::: ### 2.1. Connect to Weaviate First, connect to your Weaviate instance using your preferred client library. In this example, we assume you are connecting to a local Weaviate instance. For other types of instances, replace the connection details as needed ([connection examples](docs/weaviate/connections/index.mdx)).
Obtain the embedding manually
Obtain the embedding manually
library"] --> A2["Connect to
Weaviate"] A2 --> B1["Define collection
(with an inference API)"] B1 --> B2["Import objects"] B2 --> C1["Semantic search
(nearText)"] C1 --> C2["RAG
(Generate)"] %% Group nodes in subgraphs with brand colors subgraph sg1 ["1\. Setup"] A1 A2 end subgraph sg2 ["2\. Populate"] B1 B2 end subgraph sg3 ["3\. Query"] C1 C2 end %% Style nodes with white background and darker borders style A1 fill:#ffffff,stroke:#B9C8DF,color:#130C49 style A2 fill:#ffffff,stroke:#B9C8DF,color:#130C49 style B1 fill:#ffffff,stroke:#B9C8DF,color:#130C49 style B2 fill:#ffffff,stroke:#B9C8DF,color:#130C49 style C1 fill:#ffffff,stroke:#B9C8DF,color:#130C49 style C2 fill:#ffffff,stroke:#B9C8DF,color:#130C49 %% Style subgraphs with brand colors style sg1 fill:#ffffff,stroke:#61BD73,stroke-width:2px,color:#130C49 style sg2 fill:#ffffff,stroke:#130C49,stroke-width:2px,color:#130C49 style sg3 fill:#ffffff,stroke:#7AD6EB,stroke-width:2px,color:#130C49 ``` --- ### Prerequisites In order to perform Retrieval Augmented Generation (RAG) in the last step, you will need an [OpenAI](https://platform.openai.com/) account and an OpenAI API key. If you have another preferred [model provider](/weaviate/model-providers), you can use that instead of OpenAI.
## Step 1: Set up Weaviate ### 1.1 Install a client library We recommend using a [client library](../client-libraries/index.mdx) to work with Weaviate. Follow the instructions below to install one of the official client libraries, available in [Python](../client-libraries/python/index.mdx), [JavaScript/TypeScript](../client-libraries/typescript/index.mdx), [Go](../client-libraries/go.md), and [Java](../client-libraries/java/index.mdx). import CodeClientInstall from "/_includes/code/quickstart/clients.install.mdx";
How to create a Weaviate Cloud free cluster
Go to the [Weaviate Cloud console](https://console.weaviate.cloud) and create a free cluster.:::note - Cluster provisioning typically takes 1-3 minutes. - When the cluster is ready, Weaviate Cloud displays a checkmark (`✔️`) next to the cluster name. - Note that Weaviate Cloud may add a random suffix to cluster names to ensure uniqueness. ::: import LatestWeaviateVersion from "/_includes/latest-weaviate-version.mdx";
:::info REST vs gRPC endpoints Weaviate supports both REST and gRPC protocols. For Weaviate Cloud deployments, you only need to provide the REST endpoint URL - the client will automatically configure gRPC. ::: Once you have the **REST Endpoint URL** and the **admin API key**, you can connect to your cluster, and work with Weaviate. The example below shows how to connect to Weaviate and perform a basic operation, like checking the cluster status. import ConnectIsReady from "/_includes/code/quickstart/quickstart.is_ready.mdx";
## Step 2: Populate the database Now, we can populate our database by first defining a collection and then adding data. ### 2.1 Define a collection :::info What is a collection? A collection is a set of objects that share the same data structure, like a table in relational databases or a collection in NoSQL databases. A collection also includes additional configurations that define how the data objects are stored and indexed. ::: The following example creates a _collection_ called `Question` with: - The [Weaviate Embeddings](/weaviate/model-providers/weaviate/embeddings.md) service for creating vectors during ingestion & queries. import CreateCollection from "/_includes/code/quickstart/quickstart.create_collection.mdx";
Do you prefer a different setup?
## Step 3: Queries Weaviate provides a wide range of query tools to help you find the right data. We will try a few searches here. ### 3.1 Semantic search {#semantic-search} Semantic search finds results based on meaning. This is called `nearText` in Weaviate. The following example searches for 2 objects whose meaning is most similar to that of `biology`. import QueryNearText from "/_includes/code/quickstart/quickstart.query.neartext.mdx";
Example response
```json { "answer": "DNA", "question": "In 1953 Watson & Crick built a model of the molecular structure of this, the gene-carrying substance", "category": "SCIENCE" } { "answer": "species", "question": "2000 news: the Gunnison sage grouse isn't just another northern sage grouse, but a new one of this classification", "category": "SCIENCE" } ```'biology'"] subgraph sg1 ["Vector Search"] direction LR VS1["Convert query
to vector"] --> VS2["Find similar
vectors"] VS2 --> VS3["Return top
matches"] end subgraph sg2 ["Results"] R1["Most similar
documents"] end Query --> VS1 VS3 --> R1 %% Style nodes with white background and darker borders style Query fill:#ffffff,stroke:#B9C8DF,color:#130C49 style VS1 fill:#ffffff,stroke:#B9C8DF,color:#130C49 style VS2 fill:#ffffff,stroke:#B9C8DF,color:#130C49 style VS3 fill:#ffffff,stroke:#B9C8DF,color:#130C49 style R1 fill:#ffffff,stroke:#B9C8DF,color:#130C49 %% Style subgraphs with brand colors style sg1 fill:#ffffff,stroke:#61BD73,stroke-width:2px,color:#130C49 style sg2 fill:#ffffff,stroke:#130C49,stroke-width:2px,color:#130C49 ``` :::info Where did the vectors come from? Weaviate used the **Weaviate Embeddings** service to generate a vector embedding for each object during import. During the query, Weaviate similarly converted the query (`biology`) into a vector. As we mentioned above, this is optional. See [Starter Guide: Bring Your Own Vectors](/weaviate/starter-guides/custom-vectors.mdx) if you would prefer to provide your own vectors. ::: :::tip More search types available Weaviate is capable of many types of searches. See, for example, our how-to guides on [similarity searches](../search/similarity.md), [keyword searches](../search/bm25.md), [hybrid searches](../search/hybrid.md), and [filtered searches](../search/filters.md). ::: ### 3.2 Retrieval augmented generation Retrieval augmented generation (RAG), also called generative search, combines the power of generative AI models such as large language models (LLMs) with the up-to-date truthfulness of a database. RAG works by prompting a large language model (LLM) with a combination of a _user query_ and _data retrieved from a database_. This diagram shows the RAG workflow in Weaviate. ``` /* Detailed source-code truncated for AI context efficiency. */ ``` The following example combines the same search (for `biology`) with a prompt to generate a tweet. import QueryRAG from "/_includes/code/quickstart/quickstart.query.rag.mdx";
## Recap In this quickstart guide, you: - Created a free cluster on Weaviate Cloud. - Defined a collection and added data. - Performed queries, including: - Semantic search, and - Retrieval augmented generation. Where to go next is up to you. We include some suggested steps and resources below.
## Questions and feedback import DocsFeedback from "/_includes/docs-feedback.mdx";
RBAC specific environment variables
- `AUTHORIZATION_ENABLE_RBAC`: Enable RBAC to be used. - `AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED`: Enable/disable anonymous users from accessing your Weaviate instance. - `AUTHENTICATION_DB_USERS_ENABLED`: Enable/disable runtime user management. - `AUTHENTICATION_APIKEY_ENABLED`: Enable API key-based authentication. - `AUTHENTICATION_APIKEY_USERS`: The API-key based identities that correspond to the `AUTHENTICATION_APIKEY_ENABLED` variable. - `AUTHENTICATION_APIKEY_ALLOWED_KEYS`: The allowed API keys, they correspond to a specific user identity. - `AUTHORIZATION_RBAC_ROOT_USERS`: Define your root/admin user(s). More environment variables can be found [here](/deploy/configuration/env-vars/index.md).Example output
Example output
Example output
Example output
Example output
### Timestamps - 0:00: Introduction - 0:15: Overview - 0:30: Instantiate Weaviate client & load data - 1:18: Import data with Weaviate-obtained vectors - 2:17: Import data with user-specified vectors - 3:14: Combined option - import with user-specified vectors and specify a vectorizer - 4:20: Wrap-up --- ### Weaviate/Tutorials/Vectorizer Migration (docs/weaviate/tutorials/vectorizer-migration.mdx) --- title: Switching vectorizers description: Learn two approaches to migrate from one vectorizer to another in Weaviate without service interruption image: og/docs/tutorials.jpg # tags: ['migration', 'vectorizers', 'embeddings', 'aliases'] --- import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; import FilteredTextBlock from "@site/src/components/Documentation/FilteredTextBlock"; import PyCode from "!!raw-loader!/docs/weaviate/tutorials/_includes/vectorizer-migration.py"; # Switching vectorizers in Weaviate This tutorial demonstrates two methods for migrating a Weaviate collection to a new vectorizer (embedding model) with minimal disruption to ongoing services. These techniques are helpful for scenarios such as model upgrades, provider changes, or performance optimization. ## Prerequisites Before starting this tutorial, ensure you have: - A [Weaviate Cloud](/go/console?utm_content=tutorial) instance (version `v1.32` or newer) - Python 3.8+ installed - Required Python packages installed: ```bash pip install weaviate-client datasets ``` - Environment variables set for your Weaviate Cloud credentials: ```bash export WEAVIATE_URL="your-weaviate-cloud-url" export WEAVIATE_API_KEY="your-api-key" ``` - Basic familiarity with Weaviate collections and vector search :::tip Get started with Weaviate Cloud Sign up for a free Weaviate Cloud cluster at [console.weaviate.cloud](/go/console?utm_content=tutorial) ::: ## Introduction In a production environment, you might need to change your embedding model for several reasons. You may want to adopt a newer model for **performance improvements** like better search accuracy, or switch models due to the **deprecation** of your current model. There are three basic steps when it comes to switching embedding models: 1. **Baseline performance analysis** Select a representative subset of the data. Using the existing embedding model, calculate a baseline for query/search accuracy. This metric will serve as the benchmark for comparison. 2. **New model evaluation** Generate new vector embeddings for the identical data sample using the updated model. Re-calculate the query/search accuracy using these new embeddings. 3. **Decision & deployment** Compare the accuracy results from the new model against the established baseline. If the new embeddings demonstrate a clear improvement in performance, proceed with deploying the updated model system-wide. This tutorial demonstrates two approaches for switching vectorizers in your application: - [**Method A: Collection aliases**](#method-a-collection-aliases-migration) This approach uses aliases to instantly switch between separate collections. It's perfect for a complete model replacement, minimizing risk and providing an immediate rollback option. - [**Method B: Adding new vectors**](#method-b-add-new-vector) This method allows multiple vectors per data object within a single collection. It's ideal for testing new models alongside existing ones. :::tip For most production use cases, **we recommend using collection aliases** for a seamless and reversible migration. ::: ## Step 0: Create a demo collection (optional) If you want to follow this tutorial locally and execute the code snippets, you can check out the collapsible element below for steps on how to create a collection and import a demo dataset with precomputed vector embeddings.
Step 0: Setup a collection and populate it with demo data
#### Step 0.1: Connect to Weaviate Cloud First, establish a connection to your Weaviate Cloud instance:Choose a different embedding model
Choose a different embedding model
See how to delete data from previous tutorials (or previous runs of this tutorial).
import CautionSchemaDeleteClass from '/_includes/schema-delete-class.mdx'
(All API-based model provider integrations are enabled by default.)", "type": "checkbox-group", "options": [ { "name": "text2vec-transformers", "displayName": "text2vec-transformers", "description": "Text embeddings (transformers library)." }, { "name": "text2vec-ollama", "displayName": "text2vec-ollama", "description": "Ollama embeddings" }, { "name": "text2vec-model2vec", "displayName": "text2vec-model2vec", "description": "Static Model2vec embeddings" }, { "name": "multi2vec-clip", "displayName": "multi2vec-clip", "description": "Multimodal CLIP embeddings (transformers library)" }, { "name": "reranker-transformers", "displayName": "reranker-transformers", "description": "Rerank search results using a local transformers model." }, { "name": "generative-ollama", "displayName": "generative-ollama", "description": "Generative AI models (ollama)" } ] }, { "name": "transformers_model", "displayName": "Transformers Model", "description": "Select the specific model for the text2vec-transformers module.", "type": "select-multiline", "conditions": { "and": ["local_modules~~text2vec-transformers"] }, "options": [ { "name": "google-gemma-3-300m-embedding-1.13.2", "displayName": "Google Gemma 3 300m model", "description": "A good modern model for most use cases." }, { "name": "eurobert-eurobert-2.1b", "displayName": "EuroBERT model (European languages)", "description": "A good modern model for most use cases." }, { "name": "snowflake-snowflake-arctic-embed-m", "displayName": "Snowflake Arctic Embed M model (English)", "description": "" }, { "name": "sentence-transformers-paraphrase-multilingual-MiniLM-L12-v2", "displayName": "paraphrase-multilingual-MiniLM-L12-v2", "description": "Recommended for multilingual use cases." }, { "name": "baai-bge-base-en-v1.5", "displayName": "BAAI/bge-base-en-v1.5", "description": "" } ] }, { "name": "model2vec_model", "displayName": "Model2Vec Model", "description": "Select the specific model for the text2vec-model2vec module.", "type": "select-multiline", "conditions": { "and": ["local_modules~~text2vec-model2vec"] }, "options": [ { "name": "minishlab-potion-base-32M", "displayName": "minishlab-potion-base-32M", "description": "" }, { "name": "minishlab-potion-multilingual-128M", "displayName": "minishlab-potion-multilingual-128M", "description": "" } ] }, { "name": "reranker_transformers_model", "displayName": "Reranker Model", "description": "Select the model for the reranker-transformers module.", "type": "select-multiline", "conditions": { "and": ["local_modules~~reranker-transformers"] }, "options": [ { "name": "cross-encoder-ms-marco-MiniLM-L-6-v2", "displayName": "ms-marco-MiniLM-L-6-v2", "description": "Trained on MS Marco Passage Ranking task." }, { "name": "cross-encoder-ms-marco-TinyBERT-L-2-v2", "displayName": "ms-marco-TinyBERT-L-2-v2", "description": "A smaller, faster model for passage ranking." } ] }, { "name": "t2v_transformers_cuda", "displayName": "CUDA for Transformers", "description": "Enable CUDA for the text2vec-transformers module.", "type": "checkbox-group", "conditions": { "and": ["local_modules~~text2vec-transformers"] }, "options": [ { "name": "enabled", "displayName": "Enable CUDA", "description": "Requires an NVIDIA GPU on the host machine." } ] }, { "name": "reranker_transformers_cuda", "displayName": "CUDA for Reranker", "description": "Enable CUDA for the reranker-transformers module.", "type": "checkbox-group", "conditions": { "and": ["local_modules~~reranker-transformers"] }, "options": [ { "name": "enabled", "displayName": "Enable CUDA", "description": "Requires an NVIDIA GPU on the host machine." } ] }, { "name": "multi2vec_clip_model", "displayName": "CLIP Model", "description": "Select the model for the multi2vec-clip module.", "type": "select-multiline", "conditions": { "and": ["local_modules~~multi2vec-clip"] }, "options": [ { "name": "google-siglip2-so400m-patch16-512", "displayName": "google/siglip2-so400m-patch16-512", "description": "SigLIP 2 model with 512x512 input size (Multilingual, 1152d)" }, { "name": "google-siglip2-so400m-patch16-384", "displayName": "google/siglip2-so400m-patch16-384", "description": "SigLIP 2 model with 384x384 input size (Multilingual, 1152d)" }, { "name": "sentence-transformers-clip-ViT-B-32", "displayName": "sentence-transformers-clip-ViT-B-32", "description": "Texts must be in English. (English, 768d)" }, { "name": "sentence-transformers-clip-ViT-B-32-multilingual-v1", "displayName": "sentence-transformers-clip-ViT-B-32-multilingual-v1", "description": "Supports a wide variety of languages for text. (Multilingual, 768d)" }, { "name": "openai-clip-vit-base-patch16", "displayName": "openai-clip-vit-base-patch16", "description": "Uses a ViT-B/16 Transformer architecture." }, { "name": "ViT-B-16-laion2b_s34b_b88k", "displayName": "ViT-B-16-laion2b_s34b_b88k", "description": "ViT-B/16 Transformer trained with LAION-2B dataset using OpenCLIP." }, { "name": "ViT-B-32-quickgelu-laion400m_e32", "displayName": "ViT-B-32-quickgelu-laion400m_e32", "description": "ViT-B/32 Transformer trained with LAION-400M dataset using OpenCLIP." }, { "name": "xlm-roberta-base-ViT-B-32-laion5b_s13b_b90k", "displayName": "xlm-roberta-base-ViT-B-32-laion5b_s13b_b90k", "description": "ViT-B/32 xlm roberta base model trained with LAION-5B dataset using OpenCLIP." } ] }, { "name": "multi2vec_clip_cuda", "displayName": "CUDA for CLIP", "description": "Enable CUDA for the multi2vec-clip module.", "type": "checkbox-group", "conditions": { "and": ["local_modules~~multi2vec-clip"] }, "options": [ { "name": "enabled", "displayName": "Enable CUDA", "description": "Requires an NVIDIA GPU on the host machine." } ] } ] } --- ### Static/Prompts/Index.Json (static/prompts/index.json) { "quickstart_prompt": { "description": "Build a working movie search app with natural language queries, vector search, and RAG in 5 steps.", "detailedDescription": "The prompt will guide you to create a movie search demo application that uses natural language queries, vector and hybrid search to browse a movie collection.", "features": [ "Create a Movie collection with text2vec-weaviate vectorizer", "Import 5 sample movies (The Matrix, Inception, The Godfather, Spirited Away, The Dark Knight)", "Use the query agent to retrieve information with natural language queries (only Python & TypeScript)", "Implement vector, hybrid, and keyword search modes", "Add Retrieval Augmented Generation (RAG) with Anthropic for AI-powered movie explanations" ], "languages": { "python": { "file": "quickstart-python.md", "title": "Build a Weaviate Web App with FastAPI", "framework": "FastAPI + Uvicorn", "tags": [ "Create Collection", "Import Data", "Query Agent", "Hybrid Search", "RAG" ], "packages": [ "weaviate-client>=4.19.0", "weaviate-agents>=1.2.0", "fastapi", "uvicorn", "python-dotenv" ] }, "typescript": { "file": "quickstart-typescript.md", "title": "Build a Weaviate Web App with Next.js", "framework": "Next.js (App Router) + Tailwind CSS", "tags": [ "Create Collection", "Import Data", "Query Agent", "Hybrid Search", "RAG" ], "packages": ["weaviate-client@^3.11.0", "weaviate-agents@^1.1.0"] }, "go": { "file": "quickstart-go.md", "title": "Build a Weaviate Web App with Gin", "framework": "Gin", "tags": ["Create Collection", "Import Data", "Hybrid Search", "RAG"], "packages": [ "github.com/weaviate/weaviate-go-client/v5@v5.6.0", "github.com/gin-gonic/gin", "github.com/joho/godotenv" ] }, "java": { "file": "quickstart-java.md", "title": "Build a Weaviate Web App with Spring Boot", "framework": "Spring Boot", "tags": ["Create Collection", "Import Data", "Hybrid Search", "RAG"], "packages": ["io.weaviate:client:6.0.1"] }, "csharp": { "file": "quickstart-csharp.md", "title": "Build a Weaviate Web App with ASP.NET Core", "framework": "ASP.NET Core MVC", "tags": ["Create Collection", "Import Data", "Hybrid Search", "RAG"], "packages": ["Weaviate.Client >= 1.0.0"] } } } } --- ### Static/Prompts/Quickstart Csharp (static/prompts/quickstart-csharp.md) # Build a Weaviate Web App with ASP.NET Core Build a web application using ASP.NET Core and Weaviate with 4 sequential sections: ## Sections 1. **Create Collection** - Display collection config and create Movie collection 2. **Import Data** - Show and import 5 sample movie objects 3. **Hybrid Search** - Search with toggle for vector/hybrid/keyword modes 4. **RAG** - Generative search with single prompt and grouped task options ## Prerequisites 1. Create a free Weaviate Cloud cluster at https://console.weaviate.cloud 2. Get your **WEAVIATE_URL** and **WEAVIATE_API_KEY** 3. Get an **ANTHROPIC_API_KEY** from https://console.anthropic.com ## Setup ```bash dotnet new mvc -n WeaviateDemo cd WeaviateDemo dotnet add package Weaviate.Client --version 1.0.0 ``` Required packages: - `Weaviate.Client` (version 1.0.0 or higher) - Weaviate C# client for database operations - ASP.NET Core MVC - Web framework (installed via dotnet new mvc) Add to `appsettings.json`: ```json { "Weaviate": { "Url": "your-weaviate-url", "ApiKey": "your-weaviate-api-key" }, "Anthropic": { "ApiKey": "your-anthropic-api-key" } } ``` ## Implementation Details ### Collection Configuration - Name: "Movie" - Vectorizer: "text2vec-weaviate" - Generative: "generative-anthropic" - Properties: title (text), description (text), genre (text) ### Sample Data (5 movies) 1. The Matrix - "A computer hacker learns about the true nature of reality and his role in the war against its controllers." (Science Fiction) 2. Inception - "A thief who steals corporate secrets through dream-sharing technology is given the task of planting an idea." (Science Fiction) 3. The Godfather - "The aging patriarch of an organized crime dynasty transfers control to his reluctant son." (Crime) 4. Spirited Away - "A young girl becomes trapped in a mysterious world of spirits and must find a way to save her parents." (Animation) 5. The Dark Knight - "Batman faces the Joker, a criminal mastermind who wants to plunge Gotham into anarchy." (Action) ### Search Modes Implement a single slider control (0 to 1) to demonstrate the search continuum: - **Slider at 0**: Pure Keyword search using GraphQL `GetAsync()` with `WithBM25(query)` - **Slider 0.01-0.99**: Hybrid search using GraphQL `GetAsync()` with `WithHybrid(query).WithAlpha(sliderValue)` - **Slider at 1**: Pure Vector search using GraphQL `GetAsync()` with `WithNearText(concepts)` Display the current mode and alpha value based on slider position (e.g., "Hybrid (α=0.5)" or "Pure Vector") Search input placeholder: "Enter your search query (e.g., action movies)" ### RAG Options - Provide two input boxes: one for the search query, another for the generative prompt/task - **Single Prompt**: Use `WithGenerate()` with `SingleResult =` (user-provided prompt, can use {title}, {description}, {genre}) - **Grouped Task**: Use `WithGenerate()` with `GroupedResult =` (user-provided task) Input placeholders: - Search query: "Enter your search query (e.g., superhero movies)" - Single prompt: "Explain the plot of {title} in one sentence" - Grouped task: "Summarize these movies and find common themes" ## Connection Setup ```csharp using Weaviate.Client; var config = new WeaviateConfig { Scheme = "https", Host = configuration["Weaviate:Url"], Headers = new Dictionary
`, content pages have ``s |
| `test_tabbed_code_blocks_all_present` | ALL tab panels have content in HTML (not just active tab) |
| `test_code_blocks_present` | Pages with code have non-empty `` blocks. For the quickstart page, also verifies the exact vectorizer config line for all 5 languages is present in the HTML. |
| `test_details_content_present` | `` elements have body content (not lazy-loaded) |
| `test_images_have_alt_text` | Content images have alt text (excludes decorative SVGs/icons) |
| `test_llms_txt_accessible` | `/llms.txt` returns 200, has substantial content, mentions Weaviate |
| `test_sitemap_accessible` | `/sitemap.xml` returns 200, has 100+ URLs |
### Claude agent tests (Part 2)
Uses Claude Haiku with the `web_fetch` tool:
| Test | What it checks |
|------|---------------|
| `test_claude_can_fetch_code_tabs` | Fetches `/weaviate/quickstart` and extracts the exact vectorizer config line for all 5 languages (Python, TypeScript, Go, Java, C#) |
| `test_claude_can_fetch_collapsible_content` | Fetches `/weaviate/config-refs/collections` and finds `text2vec-contextionary` inside a `` block |
| `test_claude_can_fetch_llms_txt` | Fetches `/llms.txt` and identifies all 3 top-level sections (`agents`, `cloud`, `weaviate`) plus multi-language code examples |
### ChatGPT agent tests (Part 3)
Uses GPT-4.1 Mini with the `web_search_preview` tool:
| Test | What it checks |
|------|---------------|
| `test_chatgpt_can_search_code_tabs` | Finds the quickstart URL, identifies 3+ languages, and checks for vectorizer config lines (requires 3/5) |
| `test_chatgpt_can_search_collapsible_content` | Finds the config-refs URL and `text2vec-contextionary` from the collapsible JSON block |
| `test_chatgpt_can_search_llms_txt` | Finds `/llms.txt` URL, identifies all 3 top-level sections (`agents`, `cloud`, `weaviate`), and multi-language code examples |
## Running the tests
```bash
# HTML structure tests only (no API keys needed)
uv run pytest -m indexability -v
# Agent tests only (requires ANTHROPIC_API_KEY and OPENAI_API_KEY)
uv run pytest -m indexability_agents -v
# All indexability tests
uv run pytest -m "indexability or indexability_agents" -v
```
## CI workflow
The tests run via `.github/workflows/indexability_tests.yml`:
- **Schedule**: Every Sunday at 22:00 UTC
- **Manual**: Via workflow dispatch
- **Branch**: Runs on push to `testing-ci`
- **Runtime**: ~15 minutes maximum
HTML structure tests always run. Agent tests only run if `ANTHROPIC_API_KEY` and `OPENAI_API_KEY` secrets are configured.
## Test pages
The suite tests 13 representative URLs covering all doc sections:
| Page | Features tested |
|------|----------------|
| `/weaviate/quickstart` | tabs, code (with vectorizer line check) |
| `/weaviate/manage-collections/collection-operations` | tabs, code, details |
| `/weaviate/search/similarity` | tabs, code |
| `/weaviate/search/hybrid` | tabs, code |
| `/weaviate/connections/connect-cloud` | tabs, code |
| `/weaviate/config-refs/collections` | details, table |
| `/weaviate/concepts/data-import` | no structural features (200, meta tags, headings, LLM notice only) |
| `/cloud/quickstart` | code |
| `/cloud/manage-clusters/create` | no structural features (200, meta tags, headings, LLM notice only) |
| `/cloud/tools/query-tool` | images |
| `/weaviate/manage-collections/tenant-states` | images |
| `/query-agent/recipes/query-agent-ecommerce-assistant` | code |
| `/weaviate/search` | landing page |
## Quickstart vectorizer lines
The quickstart page has tabbed code for 5 languages. The tests verify these exact lines are present in the HTML and readable by Claude:
| Language | Vectorizer config line |
|----------|----------------------|
| Python | `Configure.Vectors.text2vec_weaviate()` |
| TypeScript | `vectors.text2VecWeaviate()` |
| Go | `Vectorizer: "text2vec-weaviate"` |
| Java | `VectorConfig.text2vecWeaviate()` |
| C# | `v.Text2VecWeaviate()` |
## Dependencies
- `beautifulsoup4` — HTML parsing
- `requests` — HTTP fetching (already in project)
- `anthropic` — Claude API for agent tests
- `openai` — OpenAI API for agent tests
All are listed in the root `pyproject.toml`.
## Adding test pages
To test additional pages, add entries to the `TEST_PAGES` list in `tests/test_docs_indexability.py`:
```python
TEST_PAGES = [
("/path/to/page", {"tabs", "code", "details", "images", "table"}),
# ...
]
```
Available feature tags: `tabs`, `code`, `details`, `images`, `table`. Pages are parametrized — each feature tag enables the corresponding structural test for that page. Tag only what the page actually has: a page tagged `images` with no content image fails rather than passing quietly, which is what keeps the tags from going stale.
### Landing pages
If the page you are adding routes readers onward instead of carrying content of its own — a hub page that is essentially a list of links to its children — also add its path to the `LANDING_PAGES` set in the same file:
```python
LANDING_PAGES = {"/weaviate/search"}
```
`LANDING_PAGES` is the **only** thing that exempts a page from the "content pages have h2 headings" assertion in `test_heading_hierarchy`. An empty feature set does not exempt it. The two mean different things:
- an empty feature set says the page has none of the structural features listed above (no tabs, no code, no details, no table, no images);
- `LANDING_PAGES` says the page owes the reader no h2 headings at all.
A page can be plain prose with an empty feature set and still be a content page that must have h2s, which is why the exemption is tracked separately.
So if you add a landing page with `set()` and leave it out of `LANDING_PAGES`, it fails with `content page has no h2 headings` — a confusing failure, because the feature set already looks like it says "this page has nothing". Add the path to `LANDING_PAGES` instead.
---
### Tests/README LLMS TXT (tests/README-LLMS-TXT.md)
# Testing `llms.txt`
The `llms.txt` file lives in the **`weaviate-io`** repo (`static/llms.txt`, served at
`https://weaviate.io/llms.txt`). It contains Python, TypeScript, Java, and C# code
snippets, recommended versions, and inline links. This directory tests all of that
so the published file cannot drift from working code or current releases.
## Why
`llms.txt` is hand-maintained in a different repo. Untested content rots: APIs
change, releases ship, marketing pages move. Real bugs already caught this way
include re-declared `const`s, swapped function arguments, a vectorizer that
silently returns no results, version recommendations stuck two minor releases
behind, and a 404 on one of the linked LLM-twin pages.
## How it works — two layers
1. **Execution tests** — every snippet is duplicated in this repo as a runnable
script/test and run against a live Weaviate in the normal language CI jobs.
2. **`test_llms_txt_code.py`** — three guard tests that compare the *live*
`llms.txt` against the rest of the world:
- **Snippet coverage** — every code block in `llms.txt` exists verbatim
between `START`/`END` markers in a tested snippet file.
- **Version freshness** — every recommended library version matches the
latest release on the corresponding `weaviate/*` GitHub repo.
- **Link validity** — every URL in `llms.txt` (outside code blocks)
resolves to a 2xx/3xx response.
`llms.txt` is the source of truth for *what users see*; the docs-repo snippets
are the source of truth for *what is verified*; the GitHub Releases API is the
source of truth for *what's current*. The three guard tests force these in sync.
## File layout
```
_includes/code/llms-txt/python/*.py # one file per section
_includes/code/llms-txt/typescript/*.ts # one file per section
_includes/code/java-v6/src/test/java/LlmsTxtTest.java # one @Test per section
_includes/code/csharp/LlmsTxtTest.cs # one [Fact] per section
tests/test_python.py / test_typescript.py / test_java.py / test_csharp.py
# `test_llms_txt*` wires snippets in
tests/test_llms_txt_code.py # snippet coverage + version + link tests
```
Sections covered: local connection, CRUD, queries (near_text/bm25), filtering,
multi-tenancy, named vectors, aggregations, generative search, RBAC, quickstart,
Query Agent.
- **Quickstart** and **Query Agent** are Python/TypeScript only — Query Agent has
no Java/C# SDK; the quickstart runs against Weaviate Cloud.
- Java and C# therefore have 9 snippets each; Python and TypeScript have 11.
## Snippet markers
Each runnable file wraps the exact `llms.txt` block between markers:
```python
# START llms_multi_tenancy
... code identical to the llms.txt block ...
# END llms_multi_tenancy
```
(`//` for TS/Java/C#.) Everything outside the markers — connection setup, seed
data, assertions, cleanup — is test scaffolding and is **not** in `llms.txt`. The
coverage test compares only the marked region, after whitespace normalization.
## The vectorizer rule
`text2vec-weaviate` (Weaviate Embeddings) needs a hosted-service token and cannot
run on the local/CI instance. So:
- **Local-instance snippets** use `text2vec-ollama` (and `generative-ollama`)
pointed at `http://ollama:11434` — keyless, runs in CI.
- **The Cloud quickstart** keeps `text2vec-weaviate`, since it connects to
Weaviate Cloud where Embeddings is available.
The snippet files and the `llms.txt` blocks must match, so both use `text2vec-ollama`
for local examples. Do not "fix" `llms.txt` back to `text2vec-weaviate`.
## Running the tests
Start the local test stack first: `tests/start-weaviate.sh`.
```bash
# Execution tests (per language) — run the snippet code against live Weaviate
uv run pytest tests/test_python.py -k test_llms_txt
uv run pytest tests/test_typescript.py -k test_llms_txt
uv run pytest tests/test_java.py -k test_llms_txt
uv run pytest tests/test_csharp.py -k test_llms_txt
# Three guard tests — fetch https://weaviate.io/llms.txt by default
uv run pytest tests/test_llms_txt_code.py -m llms_txt
# Validate a local (un-deployed) weaviate-io checkout instead of the live file
LLMS_TXT_PATH=/path/to/weaviate-io/static/llms.txt \
uv run pytest tests/test_llms_txt_code.py -m llms_txt
```
`wcd` / `agents` execution snippets (quickstart, Query Agent) need `WEAVIATE_URL`
and `WEAVIATE_API_KEY` for a Weaviate Cloud cluster. The three guard tests need
no Weaviate cluster.
### Environment variables
| Variable | What it does | Used by |
|---|---|---|
| `LLMS_TXT_PATH` | Read `llms.txt` from a local file instead of fetching the live URL | all three guards |
| `GH_API_TOKEN` | GitHub Personal Access Token. Raises GitHub's anonymous rate limit (60/hr → 5000/hr) for the version freshness test | `test_llms_txt_recommended_versions_are_current` |
| `WEAVIATE_URL`, `WEAVIATE_API_KEY` | Cloud cluster the quickstart + Query Agent execution snippets connect to | execution tests only |
## Adding or changing a snippet
1. Edit (or add) the runnable file under `_includes/code/llms-txt/` (Python/TS) or
`LlmsTxtTest.{java,cs}`, keeping the `llms.txt`-facing code between `START`/`END`
markers.
2. Run that language's `test_llms_txt` and confirm it passes — **never** hand-write
a snippet without running it.
3. Copy the verified marked region **verbatim** into `weaviate-io/static/llms.txt`.
The PR-time sync warning (see *CI* below) flags this for you and prints the exact
block to paste.
4. New file? Add its path to the `test_llms_txt` parametrize list in the matching
`tests/test_*.py`.
## What `test_llms_txt_code.py` checks
All three tests are marked `@pytest.mark.llms_txt` and read `llms.txt` from the
live URL by default (or `LLMS_TXT_PATH` if set). Network failure on any of them
results in `pytest.skip(...)` rather than a failure — they can't flake CI.
### 1. `test_llms_txt_snippets_are_covered`
Parses every ```` ```python / ```ts / ```java / ```csharp ```` block out of
`llms.txt`, normalizes whitespace, and requires an identical `START`/`END`
region in the matching snippet file. Failure message lists each uncovered block
so you can see exactly which snippet drifted.
### 2. `test_llms_txt_recommended_versions_are_current`
Parses each `- **Library**: vX.Y.Z+` bullet under *Latest versions* and compares
the captured version to the `tag_name` from
`https://api.github.com/repos/weaviate//releases/latest`. Mapping today:
| `llms.txt` label | GitHub repo |
|---|---|
| `Weaviate Server` | `weaviate/weaviate` |
| `Python client` | `weaviate/weaviate-python-client` |
| `TypeScript client` | `weaviate/typescript-client` |
| `Java client` | `weaviate/java-client` |
| `C# client` | `weaviate/csharp-client` |
| `Agents SDK` | `weaviate/weaviate-agents-python-client` |
`/releases/latest` already filters out pre-releases and drafts, so a single API
call per repo is enough. Only libraries actually listed in `llms.txt` are
checked — adding a new bullet there extends coverage automatically once the
label is added to `LIBRARY_SPECS`. `versions-config.json` is **not** consulted
(it's a manually-maintained build-time fallback that goes stale).
### 3. `test_llms_txt_links_resolve`
Strips ` ``` ... ``` ` code fences, extracts every markdown link (`[t](u)`) and
bare `https?://...` URL, then HEAD-checks them in parallel (12 worker threads,
15-second timeout). Falls back to GET on `405`/`501`. Status categories:
- **ok** — 2xx / 3xx
- **broken** — 404, 5xx, etc. — fails the test
- **skipped** — 401/403/429 (bot-block or rate-limit) and network exceptions —
not a failure
If *every* URL was skipped (no successes anywhere) the whole test skips on the
assumption that the network is down. Uses a browser-style `User-Agent` to keep
Cloudflare-style false positives low.
## Cross-repo deploy ordering
The three guard tests all read the **live** `llms.txt`. That means they only go
green once `weaviate-io` **deploys** changes that match the docs-repo snippet
files / current releases / current URLs. During the window between updating
content and the next `weaviate-io` deploy, the tests correctly report the
drift — that's the design, not a bug.
When wiring these into a CI job, the same window applies: gate normally only
after the matching `weaviate-io` change is live; otherwise mark with
`@pytest.mark.xfail(strict=False)` until the deploy, then remove the xfail.
## CI
Three separate workflows cover this directory:
| Workflow | What it runs |
|---|---|
| `.github/workflows/docs_tests.yml` | Per-language **execution** tests — `test_llms_txt*` in `test_python.py`, `test_typescript.py`, `test_java.py`, `test_csharp.py`. Rides the existing `pyv4` / `ts` / `java` / `csharp` / `agents` markers, so no separate job for these. |
| `.github/workflows/llms_txt_tests.yml` | The three **guard tests** in `test_llms_txt_code.py` — snippet coverage, version freshness, link validity. Single job `test-llms-txt`, runs `uv run pytest tests/test_llms_txt_code.py -m "llms_txt"`. |
| `.github/workflows/llms_txt_snippet_sync.yml` | The **PR-time warning** (`check_llms_txt_drift.py`). Advisory only, see below. |
The guard workflow triggers on:
- **Schedule** — Sundays 23:00 UTC (offset 1h from `indexability_tests.yml`).
- **Push** to `testing-ci` or `llms-txt`.
- **`workflow_dispatch`** for manual runs.
It exports `GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}` so the version-freshness
test gets GitHub's authenticated rate limit (5000/hr instead of 60/hr). No
Weaviate cluster, no Docker, no language toolchains — just Python + network,
so the job is fast (≈ 30-60s end-to-end).
Results post to Slack via the shared `./.github/actions/handle-test-results`
composite under `test-type: 'llms.txt'`, with `continue-on-error: true` on the
pytest step so the notification still fires when a guard fails.
### PR-time snippet sync warning
The guard workflow runs weekly, so a snippet change here can break the published
`llms.txt` days before anyone notices. `tests/check_llms_txt_drift.py` closes that
gap on the docs side. `.github/workflows/llms_txt_snippet_sync.yml` runs it on every
PR touching a file that matches `SNIPPET_GLOBS`, and it answers one question: once
this PR merges, which `llms.txt` code blocks would `test_llms_txt_snippets_are_covered`
no longer find? Those, and only those, are the blocks `weaviate-io/static/llms.txt`
has to update in lockstep.
It reuses this directory's matching logic (`SNIPPET_GLOBS`, the marker and fence
regexes, `_normalize`, `_load_llms_txt`), so it cannot disagree with the weekly job
about what "matches" means. Findings surface as GitHub warning annotations on the
changed snippet lines, plus a job summary carrying the new block to paste into
`llms.txt`.
**It is advisory and never fails the job once it runs.** When a snippet PR is opened, weaviate-io has
not merged or deployed yet, so the live `llms.txt` legitimately cannot match yet; a
blocking check would fire on every honest PR and would just get overridden. It also
stays quiet whenever there is nothing to do: weaviate-io shipped first and `llms.txt`
already carries the new block, only scaffolding outside the markers changed, or a
region was moved or renamed without its code changing.
Run it locally against uncommitted edits:
```bash
uv run python tests/check_llms_txt_drift.py --base HEAD
```
The reverse direction (an `llms.txt` edit in weaviate-io that no longer matches these
snippets) is not automated. There is no check in weaviate-io; this repo's PR-time warning
and the weekly `llms_txt_tests.yml` job are the only automation. An `llms.txt` edit made
directly in weaviate-io that breaks the verbatim match is not caught until the weekly job
runs.
## Per-language gotchas
- **Python** — `text2vec_ollama` / `generative_ollama` take `api_endpoint` and
`model`. Generative uses a longer client `Timeout` (Ollama on CPU is slow).
- **TypeScript** — runs via `npx tsx`. `permissions.collections()` returns an
**array** (spread it: `[...collections(...), ...data(...)]`); `assignRoles` is
`(roleNames, userId)`. Use distinct `const` names per query.
- **Java** (`client6`) — `VectorConfig.text2vecOllama(...)` returns a
`Map.Entry`, not a `VectorConfig`. `Target.text` is `(vectorName, query)`. The
generative provider is passed per query, not on the collection.
- **C#** — verified against the `1.1.0` client. `Query.NearText`/`Generate.NearText`
default-vector calls must use the plain-string overload; the builder-lambda's
`INearTextBuilder` does not convert to `NearTextInput` (compiles, throws at
runtime). Named-vector queries use the lambda + `.TargetVectorsMinimum(...)`.
---
### Tests/Backups/README (tests/backups/README.md)
# WCD test-cluster restore
Tooling to rebuild the Weaviate Cloud (WCD) cluster the docs CI runs against,
from a point-in-time snapshot of every collection's schema and objects (with
their stored vectors and UUIDs).
## What's here
```
restore.py # single entrypoint — runs stages 1–3 (see --stage)
README.md # this file
backup_/ # the snapshot — NOT committed (see "The snapshot")
```
`restore.py` is the only script; the three restore stages are flags on it
(`--stage`). The snapshot directory is **not committed** — it is hundreds of MB
(one objects file alone is ~470 MB), so it is gitignored and obtained
separately (see below).
## The snapshot
A snapshot is a directory named `backup_/` with this layout:
| File pattern | Content |
|--------------------------------------|--------------------------------------------------|
| `backup_metadata.json` | Index: every collection's name, MT flag, tenants |
| `_config.json` | Schema (properties, generative/MT config, …) |
| `_objects.json` | Objects, UUIDs, and stored vectors |
| `__objects.json` | MT objects, one file per tenant |
The current baseline, `backup_20251126_164527/`, was taken on 2025-11-26 and
contains 23 collections (one multi-tenant). Because it is gitignored, it is
**not in a clean checkout** — ask another maintainer for a copy, or restore from
wherever your team stores it, and drop it next to `restore.py`.
`restore.py` locates the snapshot in this order:
1. `$WEAVIATE_BACKUP_DIR`, if set, or
2. the newest `backup_*` directory next to `restore.py`.
Stages 1 and 2 read the snapshot; stage 3 does not (it uses the canonical
dataset package), so a stage-3-only run needs no snapshot.
### Collections owned by the agents tests
`restore.py` **skips** `ECommerce`, `Weather`, and `FinancialContracts` (the
`AGENTS_OWNED_COLLECTIONS` set). They are owned by the Query Agent tests
(`docs/agents/_includes/query_agent.*`), which create them with
`text2vec-weaviate` named vectors and load their data from HuggingFace — but
only `if not collections.exists(...)`. The snapshot's lossy copies (no
vectorizer config) would shadow that and break named-vector queries
(`WEAVIATE_NAMED_VECTOR_ERROR` / `collection_vectors: []`). So a restore rebuilds
**20** collections; the agents tests manage the other three. No non-agents test
depends on the snapshot versions.
## Why three stages
The original backup tool serialized config via `str(...)`, which dropped
structured details: vectorizer config, cross-references, inverted-index flags.
Stage 1 alone gives a cluster with all the *data* back, but `near_text`/`hybrid`
are silently broken because the vectorizer is unset — queries can't be embedded
at runtime. Stage 2 plugs that hole for the 8 collections the docs tests
actually search. Stage 3 reseeds Jeopardy from the canonical package because the
snapshot lost `JeopardyQuestion.hasCategory` too.
```
--stage 1 restore (bulk replay)
├─ recreates schemas from *_config.json
├─ batch-inserts objects with stored vectors + UUIDs
├─ idempotent (skips collections with objects, recreates empty ones)
└─ vectorizer left at "none" → near_vector works, near_text does not
--stage 2 repopulate (drops + recreates 8 specific collections)
├─ adds text2vec-openai (ada-002) so near_text/hybrid work
├─ adds cross-references + inverted-index flags the original tool lost
└─ re-imports preserving stored vectors (no re-embedding cost)
--stage 3 jeopardy reseed (overwrites Jeopardy from canonical pkg)
├─ ignores the snapshot for JeopardyQuestion + JeopardyCategory
└─ uploads weaviate_datasets.JeopardyQuestions10k() with overwrite=True
```
## Running the restore
```bash
export WEAVIATE_URL=""
export WEAVIATE_API_KEY=""
export OPENAI_API_KEY="" # stages 2 + 3 only
uv run python tests/backups/restore.py # all stages (default)
uv run python tests/backups/restore.py --stage 1 # one stage
uv run python tests/backups/restore.py --stage 2,3 # a subset
```
A clean restore on an empty cluster takes a few minutes; stage 1 is the slowest
because of the 10k-object `JeopardyQuestion` batch insert.
- **Stage 1 is idempotent** — re-running it skips collections that already have
objects (and recreates empty/failed ones).
- **Stages 2 and 3 are destructive** — they delete and recreate the collections
they touch.
Stage 1 needs only `WEAVIATE_URL` + `WEAVIATE_API_KEY`. Stages 2 and 3 also need
`OPENAI_API_KEY` because the recreated collections use `text2vec-openai` as the
vectorizer (passed via the `X-OpenAI-Api-Key` header). `restore.py` validates
that the variables required by the selected stages are set before connecting.
## Why ada-002 specifically (don't change this)
The stored vectors in the snapshot are 1536-d `text-embedding-ada-002`. Stage 2
pins the vectorizer to `text-embedding-ada-002` so that **query-time** embedding
lands in the same space as the **stored** vectors. Using v4's current default
(`text-embedding-3-small`) would silently return semantically wrong results —
the embedding spaces don't overlap.
## What stage 2 fixes per collection
Stage 2 only touches the 8 collections the docs tests semantically search
(`COLLECTIONS_TO_FIX` in `restore.py`); everything else stays as stage 1
restored it.
| Collection | Vectorizer | Other repairs |
|----------------------|-------------------------------------------------------|-------------------------------------------------------------------|
| `JeopardyQuestion` | `text2vec-openai` (ada-002), single | `hasCategory` cross-ref → `JeopardyCategory` |
| `Article` | single | `inPublication`, `hasAuthors` cross-refs; `index_timestamps=true` |
| `ArxivPapers` | single | — |
| `Publication` | single | `hasArticles` cross-ref → `Article` |
| `WineReview` | single | `index_null_state=true` (tests filter `IsNull`) |
| `WineReviewMT` | single (MT) | `index_null_state=true` |
| `GitBookChunk` | single | — |
| `WineReviewNV` | named vectors `title`, `title_country`, `review_body` | `index_null_state=true` |
`WineReviewNV` is the only collection with multiple named vectors; everything
else uses the legacy single `"default"` vectorizer.
### A wire-format quirk worth knowing
When a collection uses the legacy single-vectorizer form (not named vectors),
inserts expect an **unnamed** vector. The snapshot stores it as
`{"default": [...]}` (the named-vector shape). The stage-2 `coerce` closure in
`restore.py` unwraps the `default` key before batch insert. Without that,
inserts to those collections fail with a wire-format error.
## Stage 3 — why Jeopardy gets its own path
The snapshot's `JeopardyQuestion` lost the `hasCategory` cross-reference (the
original backup tool didn't serialize references at all). Stage 2 declares the
reference, but the per-object reference data isn't in the snapshot, so reads
would still return empty for `hasCategory`. Stage 3 calls
`weaviate_datasets.JeopardyQuestions10k().upload_dataset(...)`, which ships clean
ada-002 vectors **and** the per-object `hasCategory` links wired up.
Stage 3 overwrites stage 2's Jeopardy work (`overwrite=True`). That's
intentional — stage 2 keeps Jeopardy in its loop because it's the simplest
re-import for the other 8 collections, and stage 3 then supersedes Jeopardy with
the canonical source. The cluster ends at exactly 10,000 Jeopardy objects
(stage 1 imports 10,004 from the snapshot; stage 3 finishes at 10,000).
## Verifying a restore worked
A few quick sanity checks against the restored cluster (run with the same env
vars):
```python
import os, weaviate
from weaviate.classes.init import Auth
c = weaviate.connect_to_weaviate_cloud(
cluster_url=os.environ["WEAVIATE_URL"],
auth_credentials=Auth.api_key(os.environ["WEAVIATE_API_KEY"]),
headers={"X-OpenAI-Api-Key": os.environ["OPENAI_API_KEY"]},
)
try:
for name, expected in [("JeopardyQuestion", 10000), ("Article", 4403),
("ArxivPapers", 2000), ("WineReview", 50)]:
n = c.collections.get(name).aggregate.over_all(total_count=True).total_count
print(f"{name}: {n}{' ✓' if n == expected else f' (expected {expected})'}")
# Query-time vectorization works (proves stage 2 ran):
r = c.collections.get("JeopardyQuestion").query.near_text("famous scientists", limit=1)
print(f"near_text: {r.objects[0].properties.get('question')!r}")
finally:
c.close()
```
Expected counts after a fresh restore (stages 1 → 2 → 3). `ECommerce`,
`Weather`, and `FinancialContracts` are intentionally absent — the agents tests
own them.
| Collection | Count |
|---------------------------------|-------------------|
| `JeopardyQuestion` | 10,000 |
| `Article` | 4,403 |
| `ArxivPapers` | 2,000 |
| `Recipes` | 100 |
| `WineReview` / `WineReviewNV` | 50 / 50 |
| `WineReviewMT` | 50 per tenant × 2 |
| `GitBookChunk` / `JeopardyTiny` | 10 / 10 |
| `JeopardyCategory` | 40 |
| `Movie` | 3 |
## Taking a new snapshot
There is **no snapshot script here** — `restore.py` only *reads* a snapshot. The
current one was produced by a separate tool (iterating
`client.collections.list_all()` and dumping properties + objects + vectors per
collection). If you ever need a newer baseline:
1. Produce the same file layout (`backup_metadata.json` plus per-collection
`_config.json` / `_objects.json`) in a new `backup_/` directory.
2. Be aware of the lossy fields the original tool didn't capture — the schema
features in the `EXTRA_SCHEMA` map in `restore.py` (references,
`index_timestamps`, `index_null_state`). Either fix the backup tool to
serialize them properly, or extend `EXTRA_SCHEMA`.
3. `restore.py` auto-detects the newest `backup_*` directory, so no code change
is needed; or point `$WEAVIATE_BACKUP_DIR` at the new directory explicitly.
## When to restore
- The CI test cluster gets wedged after a failed test run (collections in
inconsistent states, half-deleted data, etc.).
- You're spinning up a fresh WCD cluster for testing and want the same baseline
the docs CI uses.
- You suspect a flaky test is caused by cluster drift, not a real regression.
---
### .Github/PULL REQUEST TEMPLATE (.github/PULL_REQUEST_TEMPLATE.md)
### What's being changed:
### Type of change:
- [ ] **Documentation content** updates (non-breaking change to fix/update documentation )
- [ ] **Bug fix** (non-breaking change to fixes an issue with the site)
- [ ] **Feature** or **enhancements** (non-breaking change to add functionality)
### How has this been tested?
- [ ] **Local build** - the site works as expected when running `yarn start`
---
### .Github/Actions/Handle Test Results/Action.Yml (.github/actions/handle-test-results/action.yml)
name: 'Handle Test Results'
description: 'Calculates test results and sends notifications'
inputs:
test-outcome:
description: 'Outcome of the test step'
required: true
test-type:
description: 'Type of tests run (e.g., Agents, Database-Cloud, Database-Local)'
required: true
languages-tested:
description: 'Languages tested (e.g., Python, TypeScript). Leave empty for non-language-specific test suites — the field will be hidden in the Slack message.'
required: false
scope-label:
description: 'Label for the scope field in the Slack message. Defaults to "Languages Tested". Use e.g. "Checks" for non-language-specific suites (paired with a custom languages-tested value like "Coverage · Versions · Links").'
required: false
default: 'Languages Tested'
slack-bot-token:
description: 'Slack webhook for testing CI channel'
required: false
test-results-xml:
description: 'Space-separated list of JUnit XML result files to parse for test counts (e.g., "pytest-results.xml pytest-results-agents.xml")'
required: false
default: 'pytest-results.xml'
slack-script:
description: 'Path to the Slack notification script (default: _build_scripts/slack-test-results.sh)'
required: false
default: '_build_scripts/slack-test-results.sh'
runs:
using: 'composite'
steps:
- name: Calculate test results
shell: bash
run: |
# Calculate duration
TEST_END_TIME=$(date +%s)
TEST_DURATION=$((TEST_END_TIME - TEST_START_TIME))
# Format duration
if [ $TEST_DURATION -ge 60 ]; then
MINUTES=$((TEST_DURATION / 60))
SECONDS=$((TEST_DURATION % 60))
DURATION_FORMATTED="${MINUTES}m ${SECONDS}s"
else
DURATION_FORMATTED="${TEST_DURATION}s"
fi
echo "TEST_DURATION=$DURATION_FORMATTED" >> $GITHUB_ENV
# Set test status
if [ "${{ inputs.test-outcome }}" = "success" ]; then
echo "TEST_STATUS=success" >> $GITHUB_ENV
else
echo "TEST_STATUS=failure" >> $GITHUB_ENV
fi
# Set test type for notifications
echo "TEST_TYPE=${{ inputs.test-type }}" >> $GITHUB_ENV
echo "LANGUAGES_TESTED=${{ inputs.languages-tested }}" >> $GITHUB_ENV
echo "SCOPE_LABEL=${{ inputs.scope-label }}" >> $GITHUB_ENV
# Parse JUnit XML files for test counts
TOTAL_TESTS=0
TOTAL_FAILURES=0
TOTAL_ERRORS=0
TOTAL_SKIPPED=0
for xml_file in ${{ inputs.test-results-xml }}; do
if [ -f "$xml_file" ]; then
# Extract attributes from the root or element
TESTS=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('$xml_file')
root = tree.getroot()
# Handle both and as root
if root.tag == 'testsuites':
el = root.find('testsuite') or root
else:
el = root
print(el.get('tests', '0'))
" 2>/dev/null || echo "0")
FAILURES=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('$xml_file')
root = tree.getroot()
if root.tag == 'testsuites':
el = root.find('testsuite') or root
else:
el = root
print(el.get('failures', '0'))
" 2>/dev/null || echo "0")
ERRORS=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('$xml_file')
root = tree.getroot()
if root.tag == 'testsuites':
el = root.find('testsuite') or root
else:
el = root
print(el.get('errors', '0'))
" 2>/dev/null || echo "0")
SKIPPED=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('$xml_file')
root = tree.getroot()
if root.tag == 'testsuites':
el = root.find('testsuite') or root
else:
el = root
print(el.get('skipped', '0'))
" 2>/dev/null || echo "0")
TOTAL_TESTS=$((TOTAL_TESTS + TESTS))
TOTAL_FAILURES=$((TOTAL_FAILURES + FAILURES))
TOTAL_ERRORS=$((TOTAL_ERRORS + ERRORS))
TOTAL_SKIPPED=$((TOTAL_SKIPPED + SKIPPED))
fi
done
TOTAL_PASSED=$((TOTAL_TESTS - TOTAL_FAILURES - TOTAL_ERRORS - TOTAL_SKIPPED))
if [ $TOTAL_PASSED -lt 0 ]; then TOTAL_PASSED=0; fi
echo "TEST_TOTAL=$TOTAL_TESTS" >> $GITHUB_ENV
echo "TEST_PASSED=$TOTAL_PASSED" >> $GITHUB_ENV
echo "TEST_FAILED=$((TOTAL_FAILURES + TOTAL_ERRORS))" >> $GITHUB_ENV
echo "TEST_SKIPPED=$TOTAL_SKIPPED" >> $GITHUB_ENV
- name: Get commit author
shell: bash
run: |
source _build_scripts/slack-find-author.sh
- name: Send Slack notification (testing CI)
if: inputs.slack-bot-token != ''
shell: bash
env:
SLACK_BOT: ${{ inputs.slack-bot-token }}
run: |
source ${{ inputs.slack-script }}
---
### .Github/Actions/Setup Test Env/Action.Yml (.github/actions/setup-test-env/action.yml)
name: 'Setup Test Environment'
description: 'Sets up Python and Node.js environments with caching'
inputs:
python-version:
description: 'Python version to use'
required: false
default: '3.10'
node-version:
description: 'Node.js version to use'
required: false
default: "22"
runs:
using: 'composite'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install Python dependencies
shell: bash
run: |
echo "📦 Setting up Python environment..."
uv sync
echo "✅ Python environment ready"
uv run pytest --version
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'yarn'
cache-dependency-path: 'yarn.lock'
- name: Install Node.js dependencies
shell: bash
run: |
echo "📦 Installing Node.js dependencies..."
yarn install --frozen-lockfile
echo "✅ Node.js environment ready"
---
### .Github/ISSUE TEMPLATE/Config.Yml (.github/ISSUE_TEMPLATE/config.yml)
blank_issues_enabled: true
contact_links:
- name: Weaviate Community Forum
url: https://forum.weaviate.io
about: "Community-powered knowledge repository: search, ask questions, give feedback"
- name: Community Support
url: mailto:support@weaviate.io
about: Contact us directly for private support inquiries
---
### .Github/ISSUE TEMPLATE/Create Issue.Yml (.github/ISSUE_TEMPLATE/create_issue.yml)
name: Create a new issue
description: Create a new issue to suggest improvements in the docs or the website
body:
- type: markdown
attributes:
value: |
* For questions, check the [Community Forum](https://forum.weaviate.io).
* Before you file an issue read the [Contributing guide](https://docs.weaviate.io/contributor-guide).
* Check to make sure someone hasn't already opened a similar [issue](https://github.com/weaviate/docs/issues).
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: This project has a Code of Conduct that all participants are expected to understand and follow.
options:
- label: I have read and agree to the Weaviate's [Contributor Guide](https://docs.weaviate.io/contributor-guide) and [Code of Conduct](https://weaviate.io/service/code-of-conduct)
required: true
- type: textarea
attributes:
label: What part of document/web-page on weaviate.io is affected?
description: |
- Give as much detail as you can to help us understand the change you want to see.
- Why should the docs be changed?
- What is the expected outcome?
validations:
required: true
- type: textarea
attributes:
label: Additional comments?
description: Any additional information, configuration, or data that might be necessary to reproduce the issue.
validations:
required: false
---
### .Github/ISSUE TEMPLATE/Doc Feedback.Yml (.github/ISSUE_TEMPLATE/doc_feedback.yml)
name: Documentation Feedback
description: Provide docs feedback
title: "[Documentation Feedback]: "
labels:
- user-feedback
body:
- type: markdown
attributes:
value: |
Thank you for your feedback. We highly appreciate it and are constantly striving to create better, more useful documentation.
- type: input
id: page-url
attributes:
label: Page URL
description: The URL of the page you provide feedback for.
placeholder: ex. https://docs.weaviate.io/weaviate/
validations:
required: true
- type: textarea
id: feedback
attributes:
label: User feedback
description: What is your feedback? Any tips you would want us to consider?
validations:
required: true
---
### .Github/ISSUE TEMPLATE/Report Bug.Yml (.github/ISSUE_TEMPLATE/report_bug.yml)
name: Report a bug
description: A clear and concise description of what the bug is.
body:
- type: markdown
attributes:
value: |
* For questions, check the [Community Forum](https://forum.weaviate.io).
* Before you file an issue read the [Contributing guide](https://github.com/weaviate/docs/blob/main/CONTRIBUTING.md).
* Check to make sure someone hasn't already opened a similar [issue](https://github.com/weaviate/docs/issues).
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: This project has a Code of Conduct that all participants are expected to understand and follow.
options:
- label: I have read and agree to the Weaviate's [Code of Conduct](https://github.com/weaviate/docs/blob/main/CODE_OF_CONDUCT.md)
required: true
- type: textarea
attributes:
label: Steps to reproduce the bug
description: |
- Give as much detail as you can to help us understand the bug you want to report.
- Go to '...'
- Click on '...'
- Scroll down to '...'
- See the error
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Screenshots
description: If applicable, add screenshots or screen captures to help explain your problem.
validations:
required: false
- type: textarea
attributes:
label: Additional context?
description: Any additional information, configuration, or data that might be necessary to reproduce the issue.
validations:
required: false
---
### .Github/Workflows/Branch.Yaml (.github/workflows/branch.yaml)
name: Build and deploy
on:
push:
branches:
- '**'
jobs:
Build-and-deploy:
name: Build and Deploy
runs-on: ubuntu-latest
env:
GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_BOT: ${{ secrets.SLACK_BOT }}
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
GOOGLE_CONTAINER_ID: ${{ secrets.GOOGLE_CONTAINER_ID }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'yarn'
- name: Install dependencies
run: |
rm -vrf node_modules/.cache/webpack
yarn install
- name: Update versions from GitHub
env:
GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
node _build_scripts/update-config-versions.js
- name: Build Docusarus project
run: |
yarn build
- name: Deploy draft to Netlify
run: |
source _build_scripts/publish-draft-to-netlify.sh
- name: Send Slack Message for branch build
if: ${{ github.ref_name != 'main' }}
run: |
source _build_scripts/slack-find-author.sh
source _build_scripts/slack-netlify-message.sh
- name: Deploy to Production Netlify
if: ${{ github.ref_name == 'main' }}
run: |
source _build_scripts/publish-prod-to-netlify.sh
source _build_scripts/slack-find-author.sh
source _build_scripts/slack-release-message.sh
---
### .Github/Workflows/Docs Functionalities Tests.Yml (.github/workflows/docs_functionalities_tests.yml)
name: Docs Functionalities Tests
permissions:
contents: read
on:
schedule:
# Run every Sunday at 23:00 UTC (offset from indexability's 22:00 slot)
- cron: "0 23 * * 0"
workflow_dispatch:
inputs:
base_url:
description: "Base URL to test against (e.g. a Netlify deploy preview)"
required: false
default: "https://docs.weaviate.io"
env:
PYTHON_VERSION: "3.11"
jobs:
test-functionalities:
name: Test Functionalities
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Playwright browser
run: uv run playwright install --with-deps chromium
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run functionalities tests
id: functionalities-tests
continue-on-error: true
env:
DOCS_BASE_URL: ${{ github.event.inputs.base_url || 'https://docs.weaviate.io' }}
# Tell tests/conftest.py to skip its Weaviate docker-compose startup —
# this Playwright-only suite doesn't need a local Weaviate instance.
DOCS_GITHUB_ENV: "true"
run: |
echo "Running Copy-page functionalities tests against ${DOCS_BASE_URL} ..."
uv run pytest -m "functionalities" -v --tb=short --junitxml=pytest-results.xml
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.functionalities-tests.outcome == 'success' && 'success' || 'failure' }}
test-type: 'Functionalities'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
test-results-xml: 'pytest-results.xml'
---
### .Github/Workflows/Docs Tests.Yml (.github/workflows/docs_tests.yml)
name: Docs Code Tests
permissions:
contents: read
on:
schedule:
# Run every Sunday at midnight CET
- cron: "0 23 * * 6"
push:
# Only run when pushed to the testing-ci branch
branches:
- testing-ci
workflow_dispatch:
inputs:
python_client_branch:
description: 'Python client branch (leave empty to use pinned version from pyproject.toml)'
required: false
default: ''
typescript_client_branch:
description: 'TypeScript client branch (leave empty to use the workflow default below)'
required: false
default: ''
java_client_branch:
description: 'Java client branch (leave empty to use the workflow default below)'
required: false
default: ''
csharp_client_branch:
description: 'C# client branch (leave empty to use the workflow default below)'
required: false
default: ''
env:
# Centralize versions for easier maintenance
PYTHON_VERSION: "3.10"
WEAVIATE_VERSION: "1.35.0"
OLLAMA_VERSION: "0.9.6"
CLIP_MODEL_TAG: "sentence-transformers-clip-ViT-B-32"
KEYCLOAK_VERSION: "24.0.3"
jobs:
test-agents:
name: Test Agents
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
# Weaviate connection settings
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
# API Keys
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
# WCD credentials
WCD_USERNAME: ${{ secrets.WCD_USERNAME }}
WCD_PASSWORD: ${{ secrets.WCD_PASSWORD }}
run: |
echo "🧪 Running agents tests..."
uv run pytest -m "agents" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'Agents'
languages-tested: 'Python, TypeScript'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
test-python:
name: Test Python
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet &
sudo rm -rf /opt/ghc &
sudo rm -rf /usr/local/share/boost &
sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
sudo rm -rf /usr/local/lib/android &
sudo rm -rf /usr/local/share/powershell &
sudo rm -rf /usr/local/share/chromium &
wait
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Override Python client branch
if: ${{ inputs.python_client_branch != '' }}
run: |
echo "📦 Installing weaviate-client from branch: ${{ inputs.python_client_branch }}"
uv pip install --reinstall "git+https://github.com/weaviate/weaviate-python-client.git@${{ inputs.python_client_branch }}"
echo "✅ Branch override installed"
- name: Start services
run: |
echo "🚀 Starting Weaviate and Ollama services..."
mkdir -p ~/.ollama
./tests/start-weaviate.sh
sleep 5
echo "✅ Services started:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
- name: Ensure Ollama models are available
run: |
echo "🤖 Checking Ollama models..."
for model in snowflake-arctic-embed nomic-embed-text llama3.2; do
if ! docker exec tests-ollama-1 ollama list 2>/dev/null | grep -q "$model"; then
echo "⬇️ Pulling model: $model"
docker exec tests-ollama-1 ollama pull "$model"
fi
done
echo "✅ All models ready"
- name: Wait for all services to be ready
run: |
echo "⏳ Waiting for services to be ready..."
declare -A services=(
["8099"]="Weaviate Main"
["8580"]="Weaviate RBAC"
["8080"]="Weaviate Instance 1"
["8090"]="Weaviate Instance 2"
["8280"]="Weaviate Instance 3"
["8180"]="Weaviate Instance 4"
["8181"]="Weaviate Instance 5"
["8182"]="Weaviate Instance 6"
)
for port in "${!services[@]}"; do
service_name="${services[$port]}"
echo -n "Checking $service_name (port $port)... "
if timeout 60 bash -c "until curl -sf http://localhost:$port/v1/.well-known/ready &>/dev/null; do sleep 2; done"; then
echo "✅ Ready"
else
echo "⚠️ Not responding (may be optional)"
fi
done
echo "✅ Service check completed"
- name: Configure Keycloak
run: |
echo "🔧 Configuring Keycloak..."
# OIDC tokens are issued with iss=http://keycloak:8081/... (the
# docker-internal hostname, set via KC_HOSTNAME). Clients running
# on the runner host need to resolve that name too — the C# and
# Java clients fetch /.well-known/openid-configuration which
# points at the keycloak: hostname.
echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts
sleep 5
uv run python _includes/code/python/keycloak_helper_script.py
echo "🔑 Obtaining OIDC bearer token..."
TOKEN_JSON=$(curl -sf -X POST \
"http://localhost:8081/realms/weaviate-test/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=weaviate" \
-d "client_secret=weaviate-client-secret-123" \
-d "username=test-admin" \
-d "password=password123" \
-d "scope=openid")
ACCESS_TOKEN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
EXPIRES_IN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('expires_in', 60))")
echo "::add-mask::$ACCESS_TOKEN"
echo "WEAVIATE_OIDC_ACCESS_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV
echo "WEAVIATE_OIDC_EXPIRES_IN=$EXPIRES_IN" >> $GITHUB_ENV
echo "✅ Keycloak configuration completed"
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run Python tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
WCD_USERNAME: ${{ secrets.WCD_USERNAME }}
WCD_PASSWORD: ${{ secrets.WCD_PASSWORD }}
run: |
echo "🧪 Running Python tests..."
uv run pytest -m "(pyv4 or pyv3) and not agents" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'Python'
languages-tested: 'Python'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
- name: Stop services
if: always()
run: |
echo "🛑 Stopping services..."
./tests/stop-weaviate.sh 2>/dev/null || echo "⚠️ Some services may have already stopped"
- name: Cleanup Docker resources
if: always()
run: |
for compose_file in tests/docker-compose*.yml; do
if [[ -f "$compose_file" ]]; then
docker compose -f "$compose_file" down -v --remove-orphans 2>/dev/null || true
fi
done
docker system prune -f --volumes 2>/dev/null || true
test-typescript:
name: Test TypeScript
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet &
sudo rm -rf /opt/ghc &
sudo rm -rf /usr/local/share/boost &
sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
sudo rm -rf /usr/local/lib/android &
wait
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Override TypeScript client branch
run: |
TS_BRANCH="${{ inputs.typescript_client_branch || 'v3.13.1' }}"
echo "📦 Building weaviate-client from branch: $TS_BRANCH"
git clone --depth 1 -b "$TS_BRANCH" https://github.com/weaviate/typescript-client.git /tmp/ts-client
cd /tmp/ts-client
npm install
npm run build
# `yarn add file:` won't replace node_modules/weaviate-client when a
# peer dep (weaviate-agents) pins the same registry version, so the
# registry tarball wins. Use `yarn link` to force a symlink instead.
yarn link
cd "$GITHUB_WORKSPACE"
yarn link weaviate-client
echo "✅ Branch override linked"
- name: Start services
run: |
echo "🚀 Starting Weaviate and Ollama services..."
mkdir -p ~/.ollama
./tests/start-weaviate.sh
sleep 5
echo "✅ Services started:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
- name: Ensure Ollama models are available
run: |
echo "🤖 Checking Ollama models..."
for model in snowflake-arctic-embed nomic-embed-text llama3.2; do
if ! docker exec tests-ollama-1 ollama list 2>/dev/null | grep -q "$model"; then
echo "⬇️ Pulling model: $model"
docker exec tests-ollama-1 ollama pull "$model"
fi
done
echo "✅ All models ready"
- name: Wait for all services to be ready
run: |
echo "⏳ Waiting for services to be ready..."
declare -A services=(
["8099"]="Weaviate Main"
["8580"]="Weaviate RBAC"
["8080"]="Weaviate Instance 1"
["8090"]="Weaviate Instance 2"
["8280"]="Weaviate Instance 3"
["8180"]="Weaviate Instance 4"
["8181"]="Weaviate Instance 5"
["8182"]="Weaviate Instance 6"
)
for port in "${!services[@]}"; do
service_name="${services[$port]}"
echo -n "Checking $service_name (port $port)... "
if timeout 60 bash -c "until curl -sf http://localhost:$port/v1/.well-known/ready &>/dev/null; do sleep 2; done"; then
echo "✅ Ready"
else
echo "⚠️ Not responding (may be optional)"
fi
done
echo "✅ Service check completed"
- name: Configure Keycloak
run: |
echo "🔧 Configuring Keycloak..."
# OIDC tokens are issued with iss=http://keycloak:8081/... (the
# docker-internal hostname, set via KC_HOSTNAME). Clients running
# on the runner host need to resolve that name too — the C# and
# Java clients fetch /.well-known/openid-configuration which
# points at the keycloak: hostname.
echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts
sleep 5
uv run python _includes/code/python/keycloak_helper_script.py
echo "🔑 Obtaining OIDC bearer token..."
TOKEN_JSON=$(curl -sf -X POST \
"http://localhost:8081/realms/weaviate-test/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=weaviate" \
-d "client_secret=weaviate-client-secret-123" \
-d "username=test-admin" \
-d "password=password123" \
-d "scope=openid")
ACCESS_TOKEN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
EXPIRES_IN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('expires_in', 60))")
echo "::add-mask::$ACCESS_TOKEN"
echo "WEAVIATE_OIDC_ACCESS_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV
echo "WEAVIATE_OIDC_EXPIRES_IN=$EXPIRES_IN" >> $GITHUB_ENV
echo "✅ Keycloak configuration completed"
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run TypeScript tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
WCD_USERNAME: ${{ secrets.WCD_USERNAME }}
WCD_PASSWORD: ${{ secrets.WCD_PASSWORD }}
run: |
echo "🧪 Running TypeScript tests..."
uv run pytest -m "ts and not agents" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'TypeScript'
languages-tested: 'TypeScript'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
- name: Stop services
if: always()
run: |
echo "🛑 Stopping services..."
./tests/stop-weaviate.sh 2>/dev/null || echo "⚠️ Some services may have already stopped"
- name: Cleanup Docker resources
if: always()
run: |
for compose_file in tests/docker-compose*.yml; do
if [[ -f "$compose_file" ]]; then
docker compose -f "$compose_file" down -v --remove-orphans 2>/dev/null || true
fi
done
docker system prune -f --volumes 2>/dev/null || true
test-java:
name: Test Java
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet &
sudo rm -rf /opt/ghc &
sudo rm -rf /usr/local/share/boost &
sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
sudo rm -rf /usr/local/lib/android &
wait
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: 'maven'
- name: Clone and build Java client SNAPSHOT
run: |
JAVA_BRANCH="${{ inputs.java_client_branch || '6.3.0' }}"
echo "📦 Building Java client SNAPSHOT from branch: $JAVA_BRANCH"
git clone --depth 1 -b "$JAVA_BRANCH" https://github.com/weaviate/java-client.git /tmp/java-client
cd /tmp/java-client
mvn install -DskipTests -Dmaven.javadoc.skip=true -q
echo "✅ Java client SNAPSHOT installed"
- name: Start services
run: |
echo "🚀 Starting Weaviate and Ollama services..."
mkdir -p ~/.ollama
./tests/start-weaviate.sh
sleep 5
echo "✅ Services started:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
- name: Ensure Ollama models are available
run: |
echo "🤖 Checking Ollama models..."
for model in snowflake-arctic-embed nomic-embed-text llama3.2; do
if ! docker exec tests-ollama-1 ollama list 2>/dev/null | grep -q "$model"; then
echo "⬇️ Pulling model: $model"
docker exec tests-ollama-1 ollama pull "$model"
fi
done
echo "✅ All models ready"
- name: Wait for all services to be ready
run: |
echo "⏳ Waiting for services to be ready..."
declare -A services=(
["8099"]="Weaviate Main"
["8580"]="Weaviate RBAC"
["8080"]="Weaviate Instance 1"
["8090"]="Weaviate Instance 2"
["8280"]="Weaviate Instance 3"
["8180"]="Weaviate Instance 4"
["8181"]="Weaviate Instance 5"
["8182"]="Weaviate Instance 6"
)
for port in "${!services[@]}"; do
service_name="${services[$port]}"
echo -n "Checking $service_name (port $port)... "
if timeout 60 bash -c "until curl -sf http://localhost:$port/v1/.well-known/ready &>/dev/null; do sleep 2; done"; then
echo "✅ Ready"
else
echo "⚠️ Not responding (may be optional)"
fi
done
echo "✅ Service check completed"
- name: Configure Keycloak
run: |
echo "🔧 Configuring Keycloak..."
# OIDC tokens are issued with iss=http://keycloak:8081/... (the
# docker-internal hostname, set via KC_HOSTNAME). Clients running
# on the runner host need to resolve that name too — the C# and
# Java clients fetch /.well-known/openid-configuration which
# points at the keycloak: hostname.
echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts
sleep 5
uv run python _includes/code/python/keycloak_helper_script.py
echo "🔑 Obtaining OIDC bearer token..."
TOKEN_JSON=$(curl -sf -X POST \
"http://localhost:8081/realms/weaviate-test/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=weaviate" \
-d "client_secret=weaviate-client-secret-123" \
-d "username=test-admin" \
-d "password=password123" \
-d "scope=openid")
ACCESS_TOKEN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
EXPIRES_IN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('expires_in', 60))")
echo "::add-mask::$ACCESS_TOKEN"
echo "WEAVIATE_OIDC_ACCESS_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV
echo "WEAVIATE_OIDC_EXPIRES_IN=$EXPIRES_IN" >> $GITHUB_ENV
echo "✅ Keycloak configuration completed"
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run Java tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
echo "🧪 Running Java tests..."
uv run pytest -m "java" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'Java'
languages-tested: 'Java'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
- name: Stop services
if: always()
run: |
echo "🛑 Stopping services..."
./tests/stop-weaviate.sh 2>/dev/null || echo "⚠️ Some services may have already stopped"
- name: Cleanup Docker resources
if: always()
run: |
for compose_file in tests/docker-compose*.yml; do
if [[ -f "$compose_file" ]]; then
docker compose -f "$compose_file" down -v --remove-orphans 2>/dev/null || true
fi
done
docker system prune -f --volumes 2>/dev/null || true
test-csharp:
name: Test C#
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /opt/ghc &
sudo rm -rf /usr/local/share/boost &
sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
sudo rm -rf /usr/local/lib/android &
wait
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up .NET 9.0
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Clone C# client
run: |
CSHARP_BRANCH="${{ inputs.csharp_client_branch || '1.1.1' }}"
echo "📦 Cloning C# client from branch: $CSHARP_BRANCH"
git clone --depth 1 -b "$CSHARP_BRANCH" https://github.com/weaviate/csharp-client.git "${{ github.workspace }}/../csharp-client"
echo "✅ C# client cloned to $(realpath "${{ github.workspace }}/../csharp-client")"
- name: Start services
run: |
echo "🚀 Starting Weaviate and Ollama services..."
mkdir -p ~/.ollama
./tests/start-weaviate.sh
sleep 5
echo "✅ Services started:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
- name: Ensure Ollama models are available
run: |
echo "🤖 Checking Ollama models..."
for model in snowflake-arctic-embed nomic-embed-text llama3.2; do
if ! docker exec tests-ollama-1 ollama list 2>/dev/null | grep -q "$model"; then
echo "⬇️ Pulling model: $model"
docker exec tests-ollama-1 ollama pull "$model"
fi
done
echo "✅ All models ready"
- name: Wait for all services to be ready
run: |
echo "⏳ Waiting for services to be ready..."
declare -A services=(
["8099"]="Weaviate Main"
["8580"]="Weaviate RBAC"
["8080"]="Weaviate Instance 1"
["8090"]="Weaviate Instance 2"
["8280"]="Weaviate Instance 3"
["8180"]="Weaviate Instance 4"
["8181"]="Weaviate Instance 5"
["8182"]="Weaviate Instance 6"
)
for port in "${!services[@]}"; do
service_name="${services[$port]}"
echo -n "Checking $service_name (port $port)... "
if timeout 60 bash -c "until curl -sf http://localhost:$port/v1/.well-known/ready &>/dev/null; do sleep 2; done"; then
echo "✅ Ready"
else
echo "⚠️ Not responding (may be optional)"
fi
done
echo "✅ Service check completed"
- name: Configure Keycloak
run: |
echo "🔧 Configuring Keycloak..."
# OIDC tokens are issued with iss=http://keycloak:8081/... (the
# docker-internal hostname, set via KC_HOSTNAME). Clients running
# on the runner host need to resolve that name too — the C# and
# Java clients fetch /.well-known/openid-configuration which
# points at the keycloak: hostname.
echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts
sleep 5
uv run python _includes/code/python/keycloak_helper_script.py
echo "🔑 Obtaining OIDC bearer token..."
TOKEN_JSON=$(curl -sf -X POST \
"http://localhost:8081/realms/weaviate-test/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=weaviate" \
-d "client_secret=weaviate-client-secret-123" \
-d "username=test-admin" \
-d "password=password123" \
-d "scope=openid")
ACCESS_TOKEN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
EXPIRES_IN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('expires_in', 60))")
echo "::add-mask::$ACCESS_TOKEN"
echo "WEAVIATE_OIDC_ACCESS_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV
echo "WEAVIATE_OIDC_EXPIRES_IN=$EXPIRES_IN" >> $GITHUB_ENV
echo "✅ Keycloak configuration completed"
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run C# tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
echo "🧪 Running C# tests..."
uv run pytest -m "csharp" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'CSharp'
languages-tested: 'C#'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
- name: Stop services
if: always()
run: |
echo "🛑 Stopping services..."
./tests/stop-weaviate.sh 2>/dev/null || echo "⚠️ Some services may have already stopped"
- name: Cleanup Docker resources
if: always()
run: |
for compose_file in tests/docker-compose*.yml; do
if [[ -f "$compose_file" ]]; then
docker compose -f "$compose_file" down -v --remove-orphans 2>/dev/null || true
fi
done
docker system prune -f --volumes 2>/dev/null || true
---
### .Github/Workflows/Indexability Tests.Yml (.github/workflows/indexability_tests.yml)
name: Docs Indexability Tests
permissions:
contents: read
on:
schedule:
# Run every Sunday at 22:00 UTC
- cron: "0 22 * * 0"
push:
branches:
- testing-ci
- llm-indexability
workflow_dispatch:
env:
PYTHON_VERSION: "3.10"
jobs:
test-indexability:
name: Test Indexability
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run HTML structure tests
id: html-tests
continue-on-error: true
run: |
echo "Running HTML indexability tests..."
uv run pytest -m "indexability" -v --tb=short --junitxml=pytest-results.xml
- name: Run agent tests
id: agent-tests
continue-on-error: true
if: env.ANTHROPIC_API_KEY != '' && env.OPENAI_API_KEY != ''
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
echo "Running agent indexability tests..."
uv run pytest -m "indexability_agents" -v --tb=short --junitxml=pytest-results-agents.xml
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ (steps.html-tests.outcome == 'success' && (steps.agent-tests.outcome == 'success' || steps.agent-tests.outcome == 'skipped')) && 'success' || 'failure' }}
test-type: 'Indexability'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
test-results-xml: 'pytest-results.xml pytest-results-agents.xml'
slack-script: '_build_scripts/slack-indexability-results.sh'
---
### .Github/Workflows/Llms Txt Snippet Sync.Yml (.github/workflows/llms_txt_snippet_sync.yml)
name: llms.txt Snippet Sync
# llms.txt is hand-maintained in the weaviate-io repo, so a snippet change here can
# strand a block in the published file. Only the weekly llms_txt_tests.yml job notices,
# which means the break surfaces days later. This gives the author the signal at PR time.
#
# It is advisory by design and never fails: when a snippet PR is opened, weaviate-io has
# not merged or deployed yet, so the live llms.txt legitimately cannot match. A blocking
# check would fire on every honest PR and would just be overridden.
permissions:
contents: read
on:
pull_request:
paths:
# Kept in step with SNIPPET_GLOBS in tests/test_llms_txt_code.py. Java and C#
# snippets live with their language suites, not under _includes/code/llms-txt/.
- "_includes/code/llms-txt/**"
- "_includes/code/java-v6/src/test/java/LlmsTxtTest.java"
- "_includes/code/csharp/LlmsTxtTest.cs"
- "tests/test_llms_txt_code.py"
- "tests/check_llms_txt_drift.py"
env:
PYTHON_VERSION: "3.11"
jobs:
check-snippet-sync:
name: Check llms.txt snippet sync
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Fetch the PR base commit
# Shallow single-commit fetch: the check only needs the base tree to read the
# pre-change snippet files. If it fails the check reports a degraded warning
# rather than blocking.
continue-on-error: true
run: git fetch --no-tags --depth=1 origin ${{ github.event.pull_request.base.sha }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install pytest
# The check imports tests/test_llms_txt_code.py to reuse its matching logic, and
# that module imports pytest. Nothing else from the test suite is needed, so this
# stays far cheaper than the full setup-test-env composite.
run: python -m pip install --quiet "pytest>=8.3.5"
- name: Check llms.txt snippet sync
run: python tests/check_llms_txt_drift.py --base "${{ github.event.pull_request.base.sha }}"
---
### .Github/Workflows/Llms Txt Tests.Yml (.github/workflows/llms_txt_tests.yml)
name: llms.txt Tests
permissions:
contents: read
on:
schedule:
# Run every Sunday at 23:00 UTC (slightly offset from the indexability job)
- cron: "0 23 * * 0"
push:
branches:
- testing-ci
- llms-txt
workflow_dispatch:
env:
PYTHON_VERSION: "3.10"
jobs:
test-llms-txt:
name: Test llms.txt
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run llms.txt guard tests
id: run-tests
continue-on-error: true
env:
# Raises GitHub's anonymous rate limit (60/hr → 5000/hr) for the
# version-freshness test, which hits /releases/latest per library.
GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "🧪 Running llms.txt guard tests (snippet coverage, version freshness, link validity)..."
uv run pytest tests/test_llms_txt_code.py -m "llms_txt" -v --tb=short --durations=10 --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'llms.txt'
# Not language-specific — relabel the field and describe what was checked.
scope-label: 'Checks'
languages-tested: 'Snippet coverage · Versions · Links'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
---
### .Github/Workflows/Pull Requests.Yaml (.github/workflows/pull_requests.yaml)
name: PR links validation
on:
pull_request:
jobs:
Validate-links:
name: Validate links
runs-on: ubuntu-latest
env:
GOOGLE_CONTAINER_ID: ${{ secrets.GOOGLE_CONTAINER_ID }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'yarn'
- name: Install dependencies
run: |
rm -vrf node_modules/.cache/webpack
yarn install
- name: Update versions from GitHub
env:
GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
node _build_scripts/update-config-versions.js
- name: Build a dev version
run: yarn build-dev
- name: Validate links from the build.dev folder
run: node _build_scripts/validate-links-pr.js
---
` blocks. For the quickstart page, also verifies the exact vectorizer config line for all 5 languages is present in the HTML. |
| `test_details_content_present` | `` elements have body content (not lazy-loaded) |
| `test_images_have_alt_text` | Content images have alt text (excludes decorative SVGs/icons) |
| `test_llms_txt_accessible` | `/llms.txt` returns 200, has substantial content, mentions Weaviate |
| `test_sitemap_accessible` | `/sitemap.xml` returns 200, has 100+ URLs |
### Claude agent tests (Part 2)
Uses Claude Haiku with the `web_fetch` tool:
| Test | What it checks |
|------|---------------|
| `test_claude_can_fetch_code_tabs` | Fetches `/weaviate/quickstart` and extracts the exact vectorizer config line for all 5 languages (Python, TypeScript, Go, Java, C#) |
| `test_claude_can_fetch_collapsible_content` | Fetches `/weaviate/config-refs/collections` and finds `text2vec-contextionary` inside a `` block |
| `test_claude_can_fetch_llms_txt` | Fetches `/llms.txt` and identifies all 3 top-level sections (`agents`, `cloud`, `weaviate`) plus multi-language code examples |
### ChatGPT agent tests (Part 3)
Uses GPT-4.1 Mini with the `web_search_preview` tool:
| Test | What it checks |
|------|---------------|
| `test_chatgpt_can_search_code_tabs` | Finds the quickstart URL, identifies 3+ languages, and checks for vectorizer config lines (requires 3/5) |
| `test_chatgpt_can_search_collapsible_content` | Finds the config-refs URL and `text2vec-contextionary` from the collapsible JSON block |
| `test_chatgpt_can_search_llms_txt` | Finds `/llms.txt` URL, identifies all 3 top-level sections (`agents`, `cloud`, `weaviate`), and multi-language code examples |
## Running the tests
```bash
# HTML structure tests only (no API keys needed)
uv run pytest -m indexability -v
# Agent tests only (requires ANTHROPIC_API_KEY and OPENAI_API_KEY)
uv run pytest -m indexability_agents -v
# All indexability tests
uv run pytest -m "indexability or indexability_agents" -v
```
## CI workflow
The tests run via `.github/workflows/indexability_tests.yml`:
- **Schedule**: Every Sunday at 22:00 UTC
- **Manual**: Via workflow dispatch
- **Branch**: Runs on push to `testing-ci`
- **Runtime**: ~15 minutes maximum
HTML structure tests always run. Agent tests only run if `ANTHROPIC_API_KEY` and `OPENAI_API_KEY` secrets are configured.
## Test pages
The suite tests 13 representative URLs covering all doc sections:
| Page | Features tested |
|------|----------------|
| `/weaviate/quickstart` | tabs, code (with vectorizer line check) |
| `/weaviate/manage-collections/collection-operations` | tabs, code, details |
| `/weaviate/search/similarity` | tabs, code |
| `/weaviate/search/hybrid` | tabs, code |
| `/weaviate/connections/connect-cloud` | tabs, code |
| `/weaviate/config-refs/collections` | details, table |
| `/weaviate/concepts/data-import` | no structural features (200, meta tags, headings, LLM notice only) |
| `/cloud/quickstart` | code |
| `/cloud/manage-clusters/create` | no structural features (200, meta tags, headings, LLM notice only) |
| `/cloud/tools/query-tool` | images |
| `/weaviate/manage-collections/tenant-states` | images |
| `/query-agent/recipes/query-agent-ecommerce-assistant` | code |
| `/weaviate/search` | landing page |
## Quickstart vectorizer lines
The quickstart page has tabbed code for 5 languages. The tests verify these exact lines are present in the HTML and readable by Claude:
| Language | Vectorizer config line |
|----------|----------------------|
| Python | `Configure.Vectors.text2vec_weaviate()` |
| TypeScript | `vectors.text2VecWeaviate()` |
| Go | `Vectorizer: "text2vec-weaviate"` |
| Java | `VectorConfig.text2vecWeaviate()` |
| C# | `v.Text2VecWeaviate()` |
## Dependencies
- `beautifulsoup4` — HTML parsing
- `requests` — HTTP fetching (already in project)
- `anthropic` — Claude API for agent tests
- `openai` — OpenAI API for agent tests
All are listed in the root `pyproject.toml`.
## Adding test pages
To test additional pages, add entries to the `TEST_PAGES` list in `tests/test_docs_indexability.py`:
```python
TEST_PAGES = [
("/path/to/page", {"tabs", "code", "details", "images", "table"}),
# ...
]
```
Available feature tags: `tabs`, `code`, `details`, `images`, `table`. Pages are parametrized — each feature tag enables the corresponding structural test for that page. Tag only what the page actually has: a page tagged `images` with no content image fails rather than passing quietly, which is what keeps the tags from going stale.
### Landing pages
If the page you are adding routes readers onward instead of carrying content of its own — a hub page that is essentially a list of links to its children — also add its path to the `LANDING_PAGES` set in the same file:
```python
LANDING_PAGES = {"/weaviate/search"}
```
`LANDING_PAGES` is the **only** thing that exempts a page from the "content pages have h2 headings" assertion in `test_heading_hierarchy`. An empty feature set does not exempt it. The two mean different things:
- an empty feature set says the page has none of the structural features listed above (no tabs, no code, no details, no table, no images);
- `LANDING_PAGES` says the page owes the reader no h2 headings at all.
A page can be plain prose with an empty feature set and still be a content page that must have h2s, which is why the exemption is tracked separately.
So if you add a landing page with `set()` and leave it out of `LANDING_PAGES`, it fails with `content page has no h2 headings` — a confusing failure, because the feature set already looks like it says "this page has nothing". Add the path to `LANDING_PAGES` instead.
---
### Tests/README LLMS TXT (tests/README-LLMS-TXT.md)
# Testing `llms.txt`
The `llms.txt` file lives in the **`weaviate-io`** repo (`static/llms.txt`, served at
`https://weaviate.io/llms.txt`). It contains Python, TypeScript, Java, and C# code
snippets, recommended versions, and inline links. This directory tests all of that
so the published file cannot drift from working code or current releases.
## Why
`llms.txt` is hand-maintained in a different repo. Untested content rots: APIs
change, releases ship, marketing pages move. Real bugs already caught this way
include re-declared `const`s, swapped function arguments, a vectorizer that
silently returns no results, version recommendations stuck two minor releases
behind, and a 404 on one of the linked LLM-twin pages.
## How it works — two layers
1. **Execution tests** — every snippet is duplicated in this repo as a runnable
script/test and run against a live Weaviate in the normal language CI jobs.
2. **`test_llms_txt_code.py`** — three guard tests that compare the *live*
`llms.txt` against the rest of the world:
- **Snippet coverage** — every code block in `llms.txt` exists verbatim
between `START`/`END` markers in a tested snippet file.
- **Version freshness** — every recommended library version matches the
latest release on the corresponding `weaviate/*` GitHub repo.
- **Link validity** — every URL in `llms.txt` (outside code blocks)
resolves to a 2xx/3xx response.
`llms.txt` is the source of truth for *what users see*; the docs-repo snippets
are the source of truth for *what is verified*; the GitHub Releases API is the
source of truth for *what's current*. The three guard tests force these in sync.
## File layout
```
_includes/code/llms-txt/python/*.py # one file per section
_includes/code/llms-txt/typescript/*.ts # one file per section
_includes/code/java-v6/src/test/java/LlmsTxtTest.java # one @Test per section
_includes/code/csharp/LlmsTxtTest.cs # one [Fact] per section
tests/test_python.py / test_typescript.py / test_java.py / test_csharp.py
# `test_llms_txt*` wires snippets in
tests/test_llms_txt_code.py # snippet coverage + version + link tests
```
Sections covered: local connection, CRUD, queries (near_text/bm25), filtering,
multi-tenancy, named vectors, aggregations, generative search, RBAC, quickstart,
Query Agent.
- **Quickstart** and **Query Agent** are Python/TypeScript only — Query Agent has
no Java/C# SDK; the quickstart runs against Weaviate Cloud.
- Java and C# therefore have 9 snippets each; Python and TypeScript have 11.
## Snippet markers
Each runnable file wraps the exact `llms.txt` block between markers:
```python
# START llms_multi_tenancy
... code identical to the llms.txt block ...
# END llms_multi_tenancy
```
(`//` for TS/Java/C#.) Everything outside the markers — connection setup, seed
data, assertions, cleanup — is test scaffolding and is **not** in `llms.txt`. The
coverage test compares only the marked region, after whitespace normalization.
## The vectorizer rule
`text2vec-weaviate` (Weaviate Embeddings) needs a hosted-service token and cannot
run on the local/CI instance. So:
- **Local-instance snippets** use `text2vec-ollama` (and `generative-ollama`)
pointed at `http://ollama:11434` — keyless, runs in CI.
- **The Cloud quickstart** keeps `text2vec-weaviate`, since it connects to
Weaviate Cloud where Embeddings is available.
The snippet files and the `llms.txt` blocks must match, so both use `text2vec-ollama`
for local examples. Do not "fix" `llms.txt` back to `text2vec-weaviate`.
## Running the tests
Start the local test stack first: `tests/start-weaviate.sh`.
```bash
# Execution tests (per language) — run the snippet code against live Weaviate
uv run pytest tests/test_python.py -k test_llms_txt
uv run pytest tests/test_typescript.py -k test_llms_txt
uv run pytest tests/test_java.py -k test_llms_txt
uv run pytest tests/test_csharp.py -k test_llms_txt
# Three guard tests — fetch https://weaviate.io/llms.txt by default
uv run pytest tests/test_llms_txt_code.py -m llms_txt
# Validate a local (un-deployed) weaviate-io checkout instead of the live file
LLMS_TXT_PATH=/path/to/weaviate-io/static/llms.txt \
uv run pytest tests/test_llms_txt_code.py -m llms_txt
```
`wcd` / `agents` execution snippets (quickstart, Query Agent) need `WEAVIATE_URL`
and `WEAVIATE_API_KEY` for a Weaviate Cloud cluster. The three guard tests need
no Weaviate cluster.
### Environment variables
| Variable | What it does | Used by |
|---|---|---|
| `LLMS_TXT_PATH` | Read `llms.txt` from a local file instead of fetching the live URL | all three guards |
| `GH_API_TOKEN` | GitHub Personal Access Token. Raises GitHub's anonymous rate limit (60/hr → 5000/hr) for the version freshness test | `test_llms_txt_recommended_versions_are_current` |
| `WEAVIATE_URL`, `WEAVIATE_API_KEY` | Cloud cluster the quickstart + Query Agent execution snippets connect to | execution tests only |
## Adding or changing a snippet
1. Edit (or add) the runnable file under `_includes/code/llms-txt/` (Python/TS) or
`LlmsTxtTest.{java,cs}`, keeping the `llms.txt`-facing code between `START`/`END`
markers.
2. Run that language's `test_llms_txt` and confirm it passes — **never** hand-write
a snippet without running it.
3. Copy the verified marked region **verbatim** into `weaviate-io/static/llms.txt`.
The PR-time sync warning (see *CI* below) flags this for you and prints the exact
block to paste.
4. New file? Add its path to the `test_llms_txt` parametrize list in the matching
`tests/test_*.py`.
## What `test_llms_txt_code.py` checks
All three tests are marked `@pytest.mark.llms_txt` and read `llms.txt` from the
live URL by default (or `LLMS_TXT_PATH` if set). Network failure on any of them
results in `pytest.skip(...)` rather than a failure — they can't flake CI.
### 1. `test_llms_txt_snippets_are_covered`
Parses every ```` ```python / ```ts / ```java / ```csharp ```` block out of
`llms.txt`, normalizes whitespace, and requires an identical `START`/`END`
region in the matching snippet file. Failure message lists each uncovered block
so you can see exactly which snippet drifted.
### 2. `test_llms_txt_recommended_versions_are_current`
Parses each `- **Library**: vX.Y.Z+` bullet under *Latest versions* and compares
the captured version to the `tag_name` from
`https://api.github.com/repos/weaviate//releases/latest`. Mapping today:
| `llms.txt` label | GitHub repo |
|---|---|
| `Weaviate Server` | `weaviate/weaviate` |
| `Python client` | `weaviate/weaviate-python-client` |
| `TypeScript client` | `weaviate/typescript-client` |
| `Java client` | `weaviate/java-client` |
| `C# client` | `weaviate/csharp-client` |
| `Agents SDK` | `weaviate/weaviate-agents-python-client` |
`/releases/latest` already filters out pre-releases and drafts, so a single API
call per repo is enough. Only libraries actually listed in `llms.txt` are
checked — adding a new bullet there extends coverage automatically once the
label is added to `LIBRARY_SPECS`. `versions-config.json` is **not** consulted
(it's a manually-maintained build-time fallback that goes stale).
### 3. `test_llms_txt_links_resolve`
Strips ` ``` ... ``` ` code fences, extracts every markdown link (`[t](u)`) and
bare `https?://...` URL, then HEAD-checks them in parallel (12 worker threads,
15-second timeout). Falls back to GET on `405`/`501`. Status categories:
- **ok** — 2xx / 3xx
- **broken** — 404, 5xx, etc. — fails the test
- **skipped** — 401/403/429 (bot-block or rate-limit) and network exceptions —
not a failure
If *every* URL was skipped (no successes anywhere) the whole test skips on the
assumption that the network is down. Uses a browser-style `User-Agent` to keep
Cloudflare-style false positives low.
## Cross-repo deploy ordering
The three guard tests all read the **live** `llms.txt`. That means they only go
green once `weaviate-io` **deploys** changes that match the docs-repo snippet
files / current releases / current URLs. During the window between updating
content and the next `weaviate-io` deploy, the tests correctly report the
drift — that's the design, not a bug.
When wiring these into a CI job, the same window applies: gate normally only
after the matching `weaviate-io` change is live; otherwise mark with
`@pytest.mark.xfail(strict=False)` until the deploy, then remove the xfail.
## CI
Three separate workflows cover this directory:
| Workflow | What it runs |
|---|---|
| `.github/workflows/docs_tests.yml` | Per-language **execution** tests — `test_llms_txt*` in `test_python.py`, `test_typescript.py`, `test_java.py`, `test_csharp.py`. Rides the existing `pyv4` / `ts` / `java` / `csharp` / `agents` markers, so no separate job for these. |
| `.github/workflows/llms_txt_tests.yml` | The three **guard tests** in `test_llms_txt_code.py` — snippet coverage, version freshness, link validity. Single job `test-llms-txt`, runs `uv run pytest tests/test_llms_txt_code.py -m "llms_txt"`. |
| `.github/workflows/llms_txt_snippet_sync.yml` | The **PR-time warning** (`check_llms_txt_drift.py`). Advisory only, see below. |
The guard workflow triggers on:
- **Schedule** — Sundays 23:00 UTC (offset 1h from `indexability_tests.yml`).
- **Push** to `testing-ci` or `llms-txt`.
- **`workflow_dispatch`** for manual runs.
It exports `GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}` so the version-freshness
test gets GitHub's authenticated rate limit (5000/hr instead of 60/hr). No
Weaviate cluster, no Docker, no language toolchains — just Python + network,
so the job is fast (≈ 30-60s end-to-end).
Results post to Slack via the shared `./.github/actions/handle-test-results`
composite under `test-type: 'llms.txt'`, with `continue-on-error: true` on the
pytest step so the notification still fires when a guard fails.
### PR-time snippet sync warning
The guard workflow runs weekly, so a snippet change here can break the published
`llms.txt` days before anyone notices. `tests/check_llms_txt_drift.py` closes that
gap on the docs side. `.github/workflows/llms_txt_snippet_sync.yml` runs it on every
PR touching a file that matches `SNIPPET_GLOBS`, and it answers one question: once
this PR merges, which `llms.txt` code blocks would `test_llms_txt_snippets_are_covered`
no longer find? Those, and only those, are the blocks `weaviate-io/static/llms.txt`
has to update in lockstep.
It reuses this directory's matching logic (`SNIPPET_GLOBS`, the marker and fence
regexes, `_normalize`, `_load_llms_txt`), so it cannot disagree with the weekly job
about what "matches" means. Findings surface as GitHub warning annotations on the
changed snippet lines, plus a job summary carrying the new block to paste into
`llms.txt`.
**It is advisory and never fails the job once it runs.** When a snippet PR is opened, weaviate-io has
not merged or deployed yet, so the live `llms.txt` legitimately cannot match yet; a
blocking check would fire on every honest PR and would just get overridden. It also
stays quiet whenever there is nothing to do: weaviate-io shipped first and `llms.txt`
already carries the new block, only scaffolding outside the markers changed, or a
region was moved or renamed without its code changing.
Run it locally against uncommitted edits:
```bash
uv run python tests/check_llms_txt_drift.py --base HEAD
```
The reverse direction (an `llms.txt` edit in weaviate-io that no longer matches these
snippets) is not automated. There is no check in weaviate-io; this repo's PR-time warning
and the weekly `llms_txt_tests.yml` job are the only automation. An `llms.txt` edit made
directly in weaviate-io that breaks the verbatim match is not caught until the weekly job
runs.
## Per-language gotchas
- **Python** — `text2vec_ollama` / `generative_ollama` take `api_endpoint` and
`model`. Generative uses a longer client `Timeout` (Ollama on CPU is slow).
- **TypeScript** — runs via `npx tsx`. `permissions.collections()` returns an
**array** (spread it: `[...collections(...), ...data(...)]`); `assignRoles` is
`(roleNames, userId)`. Use distinct `const` names per query.
- **Java** (`client6`) — `VectorConfig.text2vecOllama(...)` returns a
`Map.Entry`, not a `VectorConfig`. `Target.text` is `(vectorName, query)`. The
generative provider is passed per query, not on the collection.
- **C#** — verified against the `1.1.0` client. `Query.NearText`/`Generate.NearText`
default-vector calls must use the plain-string overload; the builder-lambda's
`INearTextBuilder` does not convert to `NearTextInput` (compiles, throws at
runtime). Named-vector queries use the lambda + `.TargetVectorsMinimum(...)`.
---
### Tests/Backups/README (tests/backups/README.md)
# WCD test-cluster restore
Tooling to rebuild the Weaviate Cloud (WCD) cluster the docs CI runs against,
from a point-in-time snapshot of every collection's schema and objects (with
their stored vectors and UUIDs).
## What's here
```
restore.py # single entrypoint — runs stages 1–3 (see --stage)
README.md # this file
backup_/ # the snapshot — NOT committed (see "The snapshot")
```
`restore.py` is the only script; the three restore stages are flags on it
(`--stage`). The snapshot directory is **not committed** — it is hundreds of MB
(one objects file alone is ~470 MB), so it is gitignored and obtained
separately (see below).
## The snapshot
A snapshot is a directory named `backup_/` with this layout:
| File pattern | Content |
|--------------------------------------|--------------------------------------------------|
| `backup_metadata.json` | Index: every collection's name, MT flag, tenants |
| `_config.json` | Schema (properties, generative/MT config, …) |
| `_objects.json` | Objects, UUIDs, and stored vectors |
| `__objects.json` | MT objects, one file per tenant |
The current baseline, `backup_20251126_164527/`, was taken on 2025-11-26 and
contains 23 collections (one multi-tenant). Because it is gitignored, it is
**not in a clean checkout** — ask another maintainer for a copy, or restore from
wherever your team stores it, and drop it next to `restore.py`.
`restore.py` locates the snapshot in this order:
1. `$WEAVIATE_BACKUP_DIR`, if set, or
2. the newest `backup_*` directory next to `restore.py`.
Stages 1 and 2 read the snapshot; stage 3 does not (it uses the canonical
dataset package), so a stage-3-only run needs no snapshot.
### Collections owned by the agents tests
`restore.py` **skips** `ECommerce`, `Weather`, and `FinancialContracts` (the
`AGENTS_OWNED_COLLECTIONS` set). They are owned by the Query Agent tests
(`docs/agents/_includes/query_agent.*`), which create them with
`text2vec-weaviate` named vectors and load their data from HuggingFace — but
only `if not collections.exists(...)`. The snapshot's lossy copies (no
vectorizer config) would shadow that and break named-vector queries
(`WEAVIATE_NAMED_VECTOR_ERROR` / `collection_vectors: []`). So a restore rebuilds
**20** collections; the agents tests manage the other three. No non-agents test
depends on the snapshot versions.
## Why three stages
The original backup tool serialized config via `str(...)`, which dropped
structured details: vectorizer config, cross-references, inverted-index flags.
Stage 1 alone gives a cluster with all the *data* back, but `near_text`/`hybrid`
are silently broken because the vectorizer is unset — queries can't be embedded
at runtime. Stage 2 plugs that hole for the 8 collections the docs tests
actually search. Stage 3 reseeds Jeopardy from the canonical package because the
snapshot lost `JeopardyQuestion.hasCategory` too.
```
--stage 1 restore (bulk replay)
├─ recreates schemas from *_config.json
├─ batch-inserts objects with stored vectors + UUIDs
├─ idempotent (skips collections with objects, recreates empty ones)
└─ vectorizer left at "none" → near_vector works, near_text does not
--stage 2 repopulate (drops + recreates 8 specific collections)
├─ adds text2vec-openai (ada-002) so near_text/hybrid work
├─ adds cross-references + inverted-index flags the original tool lost
└─ re-imports preserving stored vectors (no re-embedding cost)
--stage 3 jeopardy reseed (overwrites Jeopardy from canonical pkg)
├─ ignores the snapshot for JeopardyQuestion + JeopardyCategory
└─ uploads weaviate_datasets.JeopardyQuestions10k() with overwrite=True
```
## Running the restore
```bash
export WEAVIATE_URL=""
export WEAVIATE_API_KEY=""
export OPENAI_API_KEY="" # stages 2 + 3 only
uv run python tests/backups/restore.py # all stages (default)
uv run python tests/backups/restore.py --stage 1 # one stage
uv run python tests/backups/restore.py --stage 2,3 # a subset
```
A clean restore on an empty cluster takes a few minutes; stage 1 is the slowest
because of the 10k-object `JeopardyQuestion` batch insert.
- **Stage 1 is idempotent** — re-running it skips collections that already have
objects (and recreates empty/failed ones).
- **Stages 2 and 3 are destructive** — they delete and recreate the collections
they touch.
Stage 1 needs only `WEAVIATE_URL` + `WEAVIATE_API_KEY`. Stages 2 and 3 also need
`OPENAI_API_KEY` because the recreated collections use `text2vec-openai` as the
vectorizer (passed via the `X-OpenAI-Api-Key` header). `restore.py` validates
that the variables required by the selected stages are set before connecting.
## Why ada-002 specifically (don't change this)
The stored vectors in the snapshot are 1536-d `text-embedding-ada-002`. Stage 2
pins the vectorizer to `text-embedding-ada-002` so that **query-time** embedding
lands in the same space as the **stored** vectors. Using v4's current default
(`text-embedding-3-small`) would silently return semantically wrong results —
the embedding spaces don't overlap.
## What stage 2 fixes per collection
Stage 2 only touches the 8 collections the docs tests semantically search
(`COLLECTIONS_TO_FIX` in `restore.py`); everything else stays as stage 1
restored it.
| Collection | Vectorizer | Other repairs |
|----------------------|-------------------------------------------------------|-------------------------------------------------------------------|
| `JeopardyQuestion` | `text2vec-openai` (ada-002), single | `hasCategory` cross-ref → `JeopardyCategory` |
| `Article` | single | `inPublication`, `hasAuthors` cross-refs; `index_timestamps=true` |
| `ArxivPapers` | single | — |
| `Publication` | single | `hasArticles` cross-ref → `Article` |
| `WineReview` | single | `index_null_state=true` (tests filter `IsNull`) |
| `WineReviewMT` | single (MT) | `index_null_state=true` |
| `GitBookChunk` | single | — |
| `WineReviewNV` | named vectors `title`, `title_country`, `review_body` | `index_null_state=true` |
`WineReviewNV` is the only collection with multiple named vectors; everything
else uses the legacy single `"default"` vectorizer.
### A wire-format quirk worth knowing
When a collection uses the legacy single-vectorizer form (not named vectors),
inserts expect an **unnamed** vector. The snapshot stores it as
`{"default": [...]}` (the named-vector shape). The stage-2 `coerce` closure in
`restore.py` unwraps the `default` key before batch insert. Without that,
inserts to those collections fail with a wire-format error.
## Stage 3 — why Jeopardy gets its own path
The snapshot's `JeopardyQuestion` lost the `hasCategory` cross-reference (the
original backup tool didn't serialize references at all). Stage 2 declares the
reference, but the per-object reference data isn't in the snapshot, so reads
would still return empty for `hasCategory`. Stage 3 calls
`weaviate_datasets.JeopardyQuestions10k().upload_dataset(...)`, which ships clean
ada-002 vectors **and** the per-object `hasCategory` links wired up.
Stage 3 overwrites stage 2's Jeopardy work (`overwrite=True`). That's
intentional — stage 2 keeps Jeopardy in its loop because it's the simplest
re-import for the other 8 collections, and stage 3 then supersedes Jeopardy with
the canonical source. The cluster ends at exactly 10,000 Jeopardy objects
(stage 1 imports 10,004 from the snapshot; stage 3 finishes at 10,000).
## Verifying a restore worked
A few quick sanity checks against the restored cluster (run with the same env
vars):
```python
import os, weaviate
from weaviate.classes.init import Auth
c = weaviate.connect_to_weaviate_cloud(
cluster_url=os.environ["WEAVIATE_URL"],
auth_credentials=Auth.api_key(os.environ["WEAVIATE_API_KEY"]),
headers={"X-OpenAI-Api-Key": os.environ["OPENAI_API_KEY"]},
)
try:
for name, expected in [("JeopardyQuestion", 10000), ("Article", 4403),
("ArxivPapers", 2000), ("WineReview", 50)]:
n = c.collections.get(name).aggregate.over_all(total_count=True).total_count
print(f"{name}: {n}{' ✓' if n == expected else f' (expected {expected})'}")
# Query-time vectorization works (proves stage 2 ran):
r = c.collections.get("JeopardyQuestion").query.near_text("famous scientists", limit=1)
print(f"near_text: {r.objects[0].properties.get('question')!r}")
finally:
c.close()
```
Expected counts after a fresh restore (stages 1 → 2 → 3). `ECommerce`,
`Weather`, and `FinancialContracts` are intentionally absent — the agents tests
own them.
| Collection | Count |
|---------------------------------|-------------------|
| `JeopardyQuestion` | 10,000 |
| `Article` | 4,403 |
| `ArxivPapers` | 2,000 |
| `Recipes` | 100 |
| `WineReview` / `WineReviewNV` | 50 / 50 |
| `WineReviewMT` | 50 per tenant × 2 |
| `GitBookChunk` / `JeopardyTiny` | 10 / 10 |
| `JeopardyCategory` | 40 |
| `Movie` | 3 |
## Taking a new snapshot
There is **no snapshot script here** — `restore.py` only *reads* a snapshot. The
current one was produced by a separate tool (iterating
`client.collections.list_all()` and dumping properties + objects + vectors per
collection). If you ever need a newer baseline:
1. Produce the same file layout (`backup_metadata.json` plus per-collection
`_config.json` / `_objects.json`) in a new `backup_/` directory.
2. Be aware of the lossy fields the original tool didn't capture — the schema
features in the `EXTRA_SCHEMA` map in `restore.py` (references,
`index_timestamps`, `index_null_state`). Either fix the backup tool to
serialize them properly, or extend `EXTRA_SCHEMA`.
3. `restore.py` auto-detects the newest `backup_*` directory, so no code change
is needed; or point `$WEAVIATE_BACKUP_DIR` at the new directory explicitly.
## When to restore
- The CI test cluster gets wedged after a failed test run (collections in
inconsistent states, half-deleted data, etc.).
- You're spinning up a fresh WCD cluster for testing and want the same baseline
the docs CI uses.
- You suspect a flaky test is caused by cluster drift, not a real regression.
---
### .Github/PULL REQUEST TEMPLATE (.github/PULL_REQUEST_TEMPLATE.md)
### What's being changed:
### Type of change:
- [ ] **Documentation content** updates (non-breaking change to fix/update documentation )
- [ ] **Bug fix** (non-breaking change to fixes an issue with the site)
- [ ] **Feature** or **enhancements** (non-breaking change to add functionality)
### How has this been tested?
- [ ] **Local build** - the site works as expected when running `yarn start`
---
### .Github/Actions/Handle Test Results/Action.Yml (.github/actions/handle-test-results/action.yml)
name: 'Handle Test Results'
description: 'Calculates test results and sends notifications'
inputs:
test-outcome:
description: 'Outcome of the test step'
required: true
test-type:
description: 'Type of tests run (e.g., Agents, Database-Cloud, Database-Local)'
required: true
languages-tested:
description: 'Languages tested (e.g., Python, TypeScript). Leave empty for non-language-specific test suites — the field will be hidden in the Slack message.'
required: false
scope-label:
description: 'Label for the scope field in the Slack message. Defaults to "Languages Tested". Use e.g. "Checks" for non-language-specific suites (paired with a custom languages-tested value like "Coverage · Versions · Links").'
required: false
default: 'Languages Tested'
slack-bot-token:
description: 'Slack webhook for testing CI channel'
required: false
test-results-xml:
description: 'Space-separated list of JUnit XML result files to parse for test counts (e.g., "pytest-results.xml pytest-results-agents.xml")'
required: false
default: 'pytest-results.xml'
slack-script:
description: 'Path to the Slack notification script (default: _build_scripts/slack-test-results.sh)'
required: false
default: '_build_scripts/slack-test-results.sh'
runs:
using: 'composite'
steps:
- name: Calculate test results
shell: bash
run: |
# Calculate duration
TEST_END_TIME=$(date +%s)
TEST_DURATION=$((TEST_END_TIME - TEST_START_TIME))
# Format duration
if [ $TEST_DURATION -ge 60 ]; then
MINUTES=$((TEST_DURATION / 60))
SECONDS=$((TEST_DURATION % 60))
DURATION_FORMATTED="${MINUTES}m ${SECONDS}s"
else
DURATION_FORMATTED="${TEST_DURATION}s"
fi
echo "TEST_DURATION=$DURATION_FORMATTED" >> $GITHUB_ENV
# Set test status
if [ "${{ inputs.test-outcome }}" = "success" ]; then
echo "TEST_STATUS=success" >> $GITHUB_ENV
else
echo "TEST_STATUS=failure" >> $GITHUB_ENV
fi
# Set test type for notifications
echo "TEST_TYPE=${{ inputs.test-type }}" >> $GITHUB_ENV
echo "LANGUAGES_TESTED=${{ inputs.languages-tested }}" >> $GITHUB_ENV
echo "SCOPE_LABEL=${{ inputs.scope-label }}" >> $GITHUB_ENV
# Parse JUnit XML files for test counts
TOTAL_TESTS=0
TOTAL_FAILURES=0
TOTAL_ERRORS=0
TOTAL_SKIPPED=0
for xml_file in ${{ inputs.test-results-xml }}; do
if [ -f "$xml_file" ]; then
# Extract attributes from the root or element
TESTS=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('$xml_file')
root = tree.getroot()
# Handle both and as root
if root.tag == 'testsuites':
el = root.find('testsuite') or root
else:
el = root
print(el.get('tests', '0'))
" 2>/dev/null || echo "0")
FAILURES=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('$xml_file')
root = tree.getroot()
if root.tag == 'testsuites':
el = root.find('testsuite') or root
else:
el = root
print(el.get('failures', '0'))
" 2>/dev/null || echo "0")
ERRORS=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('$xml_file')
root = tree.getroot()
if root.tag == 'testsuites':
el = root.find('testsuite') or root
else:
el = root
print(el.get('errors', '0'))
" 2>/dev/null || echo "0")
SKIPPED=$(python3 -c "
import xml.etree.ElementTree as ET
tree = ET.parse('$xml_file')
root = tree.getroot()
if root.tag == 'testsuites':
el = root.find('testsuite') or root
else:
el = root
print(el.get('skipped', '0'))
" 2>/dev/null || echo "0")
TOTAL_TESTS=$((TOTAL_TESTS + TESTS))
TOTAL_FAILURES=$((TOTAL_FAILURES + FAILURES))
TOTAL_ERRORS=$((TOTAL_ERRORS + ERRORS))
TOTAL_SKIPPED=$((TOTAL_SKIPPED + SKIPPED))
fi
done
TOTAL_PASSED=$((TOTAL_TESTS - TOTAL_FAILURES - TOTAL_ERRORS - TOTAL_SKIPPED))
if [ $TOTAL_PASSED -lt 0 ]; then TOTAL_PASSED=0; fi
echo "TEST_TOTAL=$TOTAL_TESTS" >> $GITHUB_ENV
echo "TEST_PASSED=$TOTAL_PASSED" >> $GITHUB_ENV
echo "TEST_FAILED=$((TOTAL_FAILURES + TOTAL_ERRORS))" >> $GITHUB_ENV
echo "TEST_SKIPPED=$TOTAL_SKIPPED" >> $GITHUB_ENV
- name: Get commit author
shell: bash
run: |
source _build_scripts/slack-find-author.sh
- name: Send Slack notification (testing CI)
if: inputs.slack-bot-token != ''
shell: bash
env:
SLACK_BOT: ${{ inputs.slack-bot-token }}
run: |
source ${{ inputs.slack-script }}
---
### .Github/Actions/Setup Test Env/Action.Yml (.github/actions/setup-test-env/action.yml)
name: 'Setup Test Environment'
description: 'Sets up Python and Node.js environments with caching'
inputs:
python-version:
description: 'Python version to use'
required: false
default: '3.10'
node-version:
description: 'Node.js version to use'
required: false
default: "22"
runs:
using: 'composite'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 2
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ inputs.python-version }}
- name: Install uv
uses: astral-sh/setup-uv@v5
- name: Install Python dependencies
shell: bash
run: |
echo "📦 Setting up Python environment..."
uv sync
echo "✅ Python environment ready"
uv run pytest --version
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'yarn'
cache-dependency-path: 'yarn.lock'
- name: Install Node.js dependencies
shell: bash
run: |
echo "📦 Installing Node.js dependencies..."
yarn install --frozen-lockfile
echo "✅ Node.js environment ready"
---
### .Github/ISSUE TEMPLATE/Config.Yml (.github/ISSUE_TEMPLATE/config.yml)
blank_issues_enabled: true
contact_links:
- name: Weaviate Community Forum
url: https://forum.weaviate.io
about: "Community-powered knowledge repository: search, ask questions, give feedback"
- name: Community Support
url: mailto:support@weaviate.io
about: Contact us directly for private support inquiries
---
### .Github/ISSUE TEMPLATE/Create Issue.Yml (.github/ISSUE_TEMPLATE/create_issue.yml)
name: Create a new issue
description: Create a new issue to suggest improvements in the docs or the website
body:
- type: markdown
attributes:
value: |
* For questions, check the [Community Forum](https://forum.weaviate.io).
* Before you file an issue read the [Contributing guide](https://docs.weaviate.io/contributor-guide).
* Check to make sure someone hasn't already opened a similar [issue](https://github.com/weaviate/docs/issues).
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: This project has a Code of Conduct that all participants are expected to understand and follow.
options:
- label: I have read and agree to the Weaviate's [Contributor Guide](https://docs.weaviate.io/contributor-guide) and [Code of Conduct](https://weaviate.io/service/code-of-conduct)
required: true
- type: textarea
attributes:
label: What part of document/web-page on weaviate.io is affected?
description: |
- Give as much detail as you can to help us understand the change you want to see.
- Why should the docs be changed?
- What is the expected outcome?
validations:
required: true
- type: textarea
attributes:
label: Additional comments?
description: Any additional information, configuration, or data that might be necessary to reproduce the issue.
validations:
required: false
---
### .Github/ISSUE TEMPLATE/Doc Feedback.Yml (.github/ISSUE_TEMPLATE/doc_feedback.yml)
name: Documentation Feedback
description: Provide docs feedback
title: "[Documentation Feedback]: "
labels:
- user-feedback
body:
- type: markdown
attributes:
value: |
Thank you for your feedback. We highly appreciate it and are constantly striving to create better, more useful documentation.
- type: input
id: page-url
attributes:
label: Page URL
description: The URL of the page you provide feedback for.
placeholder: ex. https://docs.weaviate.io/weaviate/
validations:
required: true
- type: textarea
id: feedback
attributes:
label: User feedback
description: What is your feedback? Any tips you would want us to consider?
validations:
required: true
---
### .Github/ISSUE TEMPLATE/Report Bug.Yml (.github/ISSUE_TEMPLATE/report_bug.yml)
name: Report a bug
description: A clear and concise description of what the bug is.
body:
- type: markdown
attributes:
value: |
* For questions, check the [Community Forum](https://forum.weaviate.io).
* Before you file an issue read the [Contributing guide](https://github.com/weaviate/docs/blob/main/CONTRIBUTING.md).
* Check to make sure someone hasn't already opened a similar [issue](https://github.com/weaviate/docs/issues).
- type: checkboxes
id: terms
attributes:
label: Code of Conduct
description: This project has a Code of Conduct that all participants are expected to understand and follow.
options:
- label: I have read and agree to the Weaviate's [Code of Conduct](https://github.com/weaviate/docs/blob/main/CODE_OF_CONDUCT.md)
required: true
- type: textarea
attributes:
label: Steps to reproduce the bug
description: |
- Give as much detail as you can to help us understand the bug you want to report.
- Go to '...'
- Click on '...'
- Scroll down to '...'
- See the error
validations:
required: true
- type: textarea
attributes:
label: Expected behavior
description: A clear and concise description of what you expected to happen.
validations:
required: true
- type: textarea
attributes:
label: Screenshots
description: If applicable, add screenshots or screen captures to help explain your problem.
validations:
required: false
- type: textarea
attributes:
label: Additional context?
description: Any additional information, configuration, or data that might be necessary to reproduce the issue.
validations:
required: false
---
### .Github/Workflows/Branch.Yaml (.github/workflows/branch.yaml)
name: Build and deploy
on:
push:
branches:
- '**'
jobs:
Build-and-deploy:
name: Build and Deploy
runs-on: ubuntu-latest
env:
GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SLACK_BOT: ${{ secrets.SLACK_BOT }}
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
GOOGLE_CONTAINER_ID: ${{ secrets.GOOGLE_CONTAINER_ID }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'yarn'
- name: Install dependencies
run: |
rm -vrf node_modules/.cache/webpack
yarn install
- name: Update versions from GitHub
env:
GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
node _build_scripts/update-config-versions.js
- name: Build Docusarus project
run: |
yarn build
- name: Deploy draft to Netlify
run: |
source _build_scripts/publish-draft-to-netlify.sh
- name: Send Slack Message for branch build
if: ${{ github.ref_name != 'main' }}
run: |
source _build_scripts/slack-find-author.sh
source _build_scripts/slack-netlify-message.sh
- name: Deploy to Production Netlify
if: ${{ github.ref_name == 'main' }}
run: |
source _build_scripts/publish-prod-to-netlify.sh
source _build_scripts/slack-find-author.sh
source _build_scripts/slack-release-message.sh
---
### .Github/Workflows/Docs Functionalities Tests.Yml (.github/workflows/docs_functionalities_tests.yml)
name: Docs Functionalities Tests
permissions:
contents: read
on:
schedule:
# Run every Sunday at 23:00 UTC (offset from indexability's 22:00 slot)
- cron: "0 23 * * 0"
workflow_dispatch:
inputs:
base_url:
description: "Base URL to test against (e.g. a Netlify deploy preview)"
required: false
default: "https://docs.weaviate.io"
env:
PYTHON_VERSION: "3.11"
jobs:
test-functionalities:
name: Test Functionalities
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Playwright browser
run: uv run playwright install --with-deps chromium
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run functionalities tests
id: functionalities-tests
continue-on-error: true
env:
DOCS_BASE_URL: ${{ github.event.inputs.base_url || 'https://docs.weaviate.io' }}
# Tell tests/conftest.py to skip its Weaviate docker-compose startup —
# this Playwright-only suite doesn't need a local Weaviate instance.
DOCS_GITHUB_ENV: "true"
run: |
echo "Running Copy-page functionalities tests against ${DOCS_BASE_URL} ..."
uv run pytest -m "functionalities" -v --tb=short --junitxml=pytest-results.xml
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.functionalities-tests.outcome == 'success' && 'success' || 'failure' }}
test-type: 'Functionalities'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
test-results-xml: 'pytest-results.xml'
---
### .Github/Workflows/Docs Tests.Yml (.github/workflows/docs_tests.yml)
name: Docs Code Tests
permissions:
contents: read
on:
schedule:
# Run every Sunday at midnight CET
- cron: "0 23 * * 6"
push:
# Only run when pushed to the testing-ci branch
branches:
- testing-ci
workflow_dispatch:
inputs:
python_client_branch:
description: 'Python client branch (leave empty to use pinned version from pyproject.toml)'
required: false
default: ''
typescript_client_branch:
description: 'TypeScript client branch (leave empty to use the workflow default below)'
required: false
default: ''
java_client_branch:
description: 'Java client branch (leave empty to use the workflow default below)'
required: false
default: ''
csharp_client_branch:
description: 'C# client branch (leave empty to use the workflow default below)'
required: false
default: ''
env:
# Centralize versions for easier maintenance
PYTHON_VERSION: "3.10"
WEAVIATE_VERSION: "1.35.0"
OLLAMA_VERSION: "0.9.6"
CLIP_MODEL_TAG: "sentence-transformers-clip-ViT-B-32"
KEYCLOAK_VERSION: "24.0.3"
jobs:
test-agents:
name: Test Agents
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
# Weaviate connection settings
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
# API Keys
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
# WCD credentials
WCD_USERNAME: ${{ secrets.WCD_USERNAME }}
WCD_PASSWORD: ${{ secrets.WCD_PASSWORD }}
run: |
echo "🧪 Running agents tests..."
uv run pytest -m "agents" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'Agents'
languages-tested: 'Python, TypeScript'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
test-python:
name: Test Python
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet &
sudo rm -rf /opt/ghc &
sudo rm -rf /usr/local/share/boost &
sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
sudo rm -rf /usr/local/lib/android &
sudo rm -rf /usr/local/share/powershell &
sudo rm -rf /usr/local/share/chromium &
wait
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Override Python client branch
if: ${{ inputs.python_client_branch != '' }}
run: |
echo "📦 Installing weaviate-client from branch: ${{ inputs.python_client_branch }}"
uv pip install --reinstall "git+https://github.com/weaviate/weaviate-python-client.git@${{ inputs.python_client_branch }}"
echo "✅ Branch override installed"
- name: Start services
run: |
echo "🚀 Starting Weaviate and Ollama services..."
mkdir -p ~/.ollama
./tests/start-weaviate.sh
sleep 5
echo "✅ Services started:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
- name: Ensure Ollama models are available
run: |
echo "🤖 Checking Ollama models..."
for model in snowflake-arctic-embed nomic-embed-text llama3.2; do
if ! docker exec tests-ollama-1 ollama list 2>/dev/null | grep -q "$model"; then
echo "⬇️ Pulling model: $model"
docker exec tests-ollama-1 ollama pull "$model"
fi
done
echo "✅ All models ready"
- name: Wait for all services to be ready
run: |
echo "⏳ Waiting for services to be ready..."
declare -A services=(
["8099"]="Weaviate Main"
["8580"]="Weaviate RBAC"
["8080"]="Weaviate Instance 1"
["8090"]="Weaviate Instance 2"
["8280"]="Weaviate Instance 3"
["8180"]="Weaviate Instance 4"
["8181"]="Weaviate Instance 5"
["8182"]="Weaviate Instance 6"
)
for port in "${!services[@]}"; do
service_name="${services[$port]}"
echo -n "Checking $service_name (port $port)... "
if timeout 60 bash -c "until curl -sf http://localhost:$port/v1/.well-known/ready &>/dev/null; do sleep 2; done"; then
echo "✅ Ready"
else
echo "⚠️ Not responding (may be optional)"
fi
done
echo "✅ Service check completed"
- name: Configure Keycloak
run: |
echo "🔧 Configuring Keycloak..."
# OIDC tokens are issued with iss=http://keycloak:8081/... (the
# docker-internal hostname, set via KC_HOSTNAME). Clients running
# on the runner host need to resolve that name too — the C# and
# Java clients fetch /.well-known/openid-configuration which
# points at the keycloak: hostname.
echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts
sleep 5
uv run python _includes/code/python/keycloak_helper_script.py
echo "🔑 Obtaining OIDC bearer token..."
TOKEN_JSON=$(curl -sf -X POST \
"http://localhost:8081/realms/weaviate-test/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=weaviate" \
-d "client_secret=weaviate-client-secret-123" \
-d "username=test-admin" \
-d "password=password123" \
-d "scope=openid")
ACCESS_TOKEN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
EXPIRES_IN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('expires_in', 60))")
echo "::add-mask::$ACCESS_TOKEN"
echo "WEAVIATE_OIDC_ACCESS_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV
echo "WEAVIATE_OIDC_EXPIRES_IN=$EXPIRES_IN" >> $GITHUB_ENV
echo "✅ Keycloak configuration completed"
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run Python tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
WCD_USERNAME: ${{ secrets.WCD_USERNAME }}
WCD_PASSWORD: ${{ secrets.WCD_PASSWORD }}
run: |
echo "🧪 Running Python tests..."
uv run pytest -m "(pyv4 or pyv3) and not agents" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'Python'
languages-tested: 'Python'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
- name: Stop services
if: always()
run: |
echo "🛑 Stopping services..."
./tests/stop-weaviate.sh 2>/dev/null || echo "⚠️ Some services may have already stopped"
- name: Cleanup Docker resources
if: always()
run: |
for compose_file in tests/docker-compose*.yml; do
if [[ -f "$compose_file" ]]; then
docker compose -f "$compose_file" down -v --remove-orphans 2>/dev/null || true
fi
done
docker system prune -f --volumes 2>/dev/null || true
test-typescript:
name: Test TypeScript
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet &
sudo rm -rf /opt/ghc &
sudo rm -rf /usr/local/share/boost &
sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
sudo rm -rf /usr/local/lib/android &
wait
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Override TypeScript client branch
run: |
TS_BRANCH="${{ inputs.typescript_client_branch || 'v3.13.1' }}"
echo "📦 Building weaviate-client from branch: $TS_BRANCH"
git clone --depth 1 -b "$TS_BRANCH" https://github.com/weaviate/typescript-client.git /tmp/ts-client
cd /tmp/ts-client
npm install
npm run build
# `yarn add file:` won't replace node_modules/weaviate-client when a
# peer dep (weaviate-agents) pins the same registry version, so the
# registry tarball wins. Use `yarn link` to force a symlink instead.
yarn link
cd "$GITHUB_WORKSPACE"
yarn link weaviate-client
echo "✅ Branch override linked"
- name: Start services
run: |
echo "🚀 Starting Weaviate and Ollama services..."
mkdir -p ~/.ollama
./tests/start-weaviate.sh
sleep 5
echo "✅ Services started:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
- name: Ensure Ollama models are available
run: |
echo "🤖 Checking Ollama models..."
for model in snowflake-arctic-embed nomic-embed-text llama3.2; do
if ! docker exec tests-ollama-1 ollama list 2>/dev/null | grep -q "$model"; then
echo "⬇️ Pulling model: $model"
docker exec tests-ollama-1 ollama pull "$model"
fi
done
echo "✅ All models ready"
- name: Wait for all services to be ready
run: |
echo "⏳ Waiting for services to be ready..."
declare -A services=(
["8099"]="Weaviate Main"
["8580"]="Weaviate RBAC"
["8080"]="Weaviate Instance 1"
["8090"]="Weaviate Instance 2"
["8280"]="Weaviate Instance 3"
["8180"]="Weaviate Instance 4"
["8181"]="Weaviate Instance 5"
["8182"]="Weaviate Instance 6"
)
for port in "${!services[@]}"; do
service_name="${services[$port]}"
echo -n "Checking $service_name (port $port)... "
if timeout 60 bash -c "until curl -sf http://localhost:$port/v1/.well-known/ready &>/dev/null; do sleep 2; done"; then
echo "✅ Ready"
else
echo "⚠️ Not responding (may be optional)"
fi
done
echo "✅ Service check completed"
- name: Configure Keycloak
run: |
echo "🔧 Configuring Keycloak..."
# OIDC tokens are issued with iss=http://keycloak:8081/... (the
# docker-internal hostname, set via KC_HOSTNAME). Clients running
# on the runner host need to resolve that name too — the C# and
# Java clients fetch /.well-known/openid-configuration which
# points at the keycloak: hostname.
echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts
sleep 5
uv run python _includes/code/python/keycloak_helper_script.py
echo "🔑 Obtaining OIDC bearer token..."
TOKEN_JSON=$(curl -sf -X POST \
"http://localhost:8081/realms/weaviate-test/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=weaviate" \
-d "client_secret=weaviate-client-secret-123" \
-d "username=test-admin" \
-d "password=password123" \
-d "scope=openid")
ACCESS_TOKEN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
EXPIRES_IN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('expires_in', 60))")
echo "::add-mask::$ACCESS_TOKEN"
echo "WEAVIATE_OIDC_ACCESS_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV
echo "WEAVIATE_OIDC_EXPIRES_IN=$EXPIRES_IN" >> $GITHUB_ENV
echo "✅ Keycloak configuration completed"
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run TypeScript tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
WCD_USERNAME: ${{ secrets.WCD_USERNAME }}
WCD_PASSWORD: ${{ secrets.WCD_PASSWORD }}
run: |
echo "🧪 Running TypeScript tests..."
uv run pytest -m "ts and not agents" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'TypeScript'
languages-tested: 'TypeScript'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
- name: Stop services
if: always()
run: |
echo "🛑 Stopping services..."
./tests/stop-weaviate.sh 2>/dev/null || echo "⚠️ Some services may have already stopped"
- name: Cleanup Docker resources
if: always()
run: |
for compose_file in tests/docker-compose*.yml; do
if [[ -f "$compose_file" ]]; then
docker compose -f "$compose_file" down -v --remove-orphans 2>/dev/null || true
fi
done
docker system prune -f --volumes 2>/dev/null || true
test-java:
name: Test Java
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet &
sudo rm -rf /opt/ghc &
sudo rm -rf /usr/local/share/boost &
sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
sudo rm -rf /usr/local/lib/android &
wait
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: 'maven'
- name: Clone and build Java client SNAPSHOT
run: |
JAVA_BRANCH="${{ inputs.java_client_branch || '6.3.0' }}"
echo "📦 Building Java client SNAPSHOT from branch: $JAVA_BRANCH"
git clone --depth 1 -b "$JAVA_BRANCH" https://github.com/weaviate/java-client.git /tmp/java-client
cd /tmp/java-client
mvn install -DskipTests -Dmaven.javadoc.skip=true -q
echo "✅ Java client SNAPSHOT installed"
- name: Start services
run: |
echo "🚀 Starting Weaviate and Ollama services..."
mkdir -p ~/.ollama
./tests/start-weaviate.sh
sleep 5
echo "✅ Services started:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
- name: Ensure Ollama models are available
run: |
echo "🤖 Checking Ollama models..."
for model in snowflake-arctic-embed nomic-embed-text llama3.2; do
if ! docker exec tests-ollama-1 ollama list 2>/dev/null | grep -q "$model"; then
echo "⬇️ Pulling model: $model"
docker exec tests-ollama-1 ollama pull "$model"
fi
done
echo "✅ All models ready"
- name: Wait for all services to be ready
run: |
echo "⏳ Waiting for services to be ready..."
declare -A services=(
["8099"]="Weaviate Main"
["8580"]="Weaviate RBAC"
["8080"]="Weaviate Instance 1"
["8090"]="Weaviate Instance 2"
["8280"]="Weaviate Instance 3"
["8180"]="Weaviate Instance 4"
["8181"]="Weaviate Instance 5"
["8182"]="Weaviate Instance 6"
)
for port in "${!services[@]}"; do
service_name="${services[$port]}"
echo -n "Checking $service_name (port $port)... "
if timeout 60 bash -c "until curl -sf http://localhost:$port/v1/.well-known/ready &>/dev/null; do sleep 2; done"; then
echo "✅ Ready"
else
echo "⚠️ Not responding (may be optional)"
fi
done
echo "✅ Service check completed"
- name: Configure Keycloak
run: |
echo "🔧 Configuring Keycloak..."
# OIDC tokens are issued with iss=http://keycloak:8081/... (the
# docker-internal hostname, set via KC_HOSTNAME). Clients running
# on the runner host need to resolve that name too — the C# and
# Java clients fetch /.well-known/openid-configuration which
# points at the keycloak: hostname.
echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts
sleep 5
uv run python _includes/code/python/keycloak_helper_script.py
echo "🔑 Obtaining OIDC bearer token..."
TOKEN_JSON=$(curl -sf -X POST \
"http://localhost:8081/realms/weaviate-test/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=weaviate" \
-d "client_secret=weaviate-client-secret-123" \
-d "username=test-admin" \
-d "password=password123" \
-d "scope=openid")
ACCESS_TOKEN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
EXPIRES_IN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('expires_in', 60))")
echo "::add-mask::$ACCESS_TOKEN"
echo "WEAVIATE_OIDC_ACCESS_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV
echo "WEAVIATE_OIDC_EXPIRES_IN=$EXPIRES_IN" >> $GITHUB_ENV
echo "✅ Keycloak configuration completed"
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run Java tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
echo "🧪 Running Java tests..."
uv run pytest -m "java" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'Java'
languages-tested: 'Java'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
- name: Stop services
if: always()
run: |
echo "🛑 Stopping services..."
./tests/stop-weaviate.sh 2>/dev/null || echo "⚠️ Some services may have already stopped"
- name: Cleanup Docker resources
if: always()
run: |
for compose_file in tests/docker-compose*.yml; do
if [[ -f "$compose_file" ]]; then
docker compose -f "$compose_file" down -v --remove-orphans 2>/dev/null || true
fi
done
docker system prune -f --volumes 2>/dev/null || true
test-csharp:
name: Test C#
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Free disk space
run: |
sudo rm -rf /opt/ghc &
sudo rm -rf /usr/local/share/boost &
sudo rm -rf "$AGENT_TOOLSDIRECTORY" &
sudo rm -rf /usr/local/lib/android &
wait
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Set up .NET 9.0
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0.x'
- name: Clone C# client
run: |
CSHARP_BRANCH="${{ inputs.csharp_client_branch || '1.1.1' }}"
echo "📦 Cloning C# client from branch: $CSHARP_BRANCH"
git clone --depth 1 -b "$CSHARP_BRANCH" https://github.com/weaviate/csharp-client.git "${{ github.workspace }}/../csharp-client"
echo "✅ C# client cloned to $(realpath "${{ github.workspace }}/../csharp-client")"
- name: Start services
run: |
echo "🚀 Starting Weaviate and Ollama services..."
mkdir -p ~/.ollama
./tests/start-weaviate.sh
sleep 5
echo "✅ Services started:"
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
- name: Ensure Ollama models are available
run: |
echo "🤖 Checking Ollama models..."
for model in snowflake-arctic-embed nomic-embed-text llama3.2; do
if ! docker exec tests-ollama-1 ollama list 2>/dev/null | grep -q "$model"; then
echo "⬇️ Pulling model: $model"
docker exec tests-ollama-1 ollama pull "$model"
fi
done
echo "✅ All models ready"
- name: Wait for all services to be ready
run: |
echo "⏳ Waiting for services to be ready..."
declare -A services=(
["8099"]="Weaviate Main"
["8580"]="Weaviate RBAC"
["8080"]="Weaviate Instance 1"
["8090"]="Weaviate Instance 2"
["8280"]="Weaviate Instance 3"
["8180"]="Weaviate Instance 4"
["8181"]="Weaviate Instance 5"
["8182"]="Weaviate Instance 6"
)
for port in "${!services[@]}"; do
service_name="${services[$port]}"
echo -n "Checking $service_name (port $port)... "
if timeout 60 bash -c "until curl -sf http://localhost:$port/v1/.well-known/ready &>/dev/null; do sleep 2; done"; then
echo "✅ Ready"
else
echo "⚠️ Not responding (may be optional)"
fi
done
echo "✅ Service check completed"
- name: Configure Keycloak
run: |
echo "🔧 Configuring Keycloak..."
# OIDC tokens are issued with iss=http://keycloak:8081/... (the
# docker-internal hostname, set via KC_HOSTNAME). Clients running
# on the runner host need to resolve that name too — the C# and
# Java clients fetch /.well-known/openid-configuration which
# points at the keycloak: hostname.
echo "127.0.0.1 keycloak" | sudo tee -a /etc/hosts
sleep 5
uv run python _includes/code/python/keycloak_helper_script.py
echo "🔑 Obtaining OIDC bearer token..."
TOKEN_JSON=$(curl -sf -X POST \
"http://localhost:8081/realms/weaviate-test/protocol/openid-connect/token" \
-d "grant_type=password" \
-d "client_id=weaviate" \
-d "client_secret=weaviate-client-secret-123" \
-d "username=test-admin" \
-d "password=password123" \
-d "scope=openid")
ACCESS_TOKEN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin)['access_token'])")
EXPIRES_IN=$(echo "$TOKEN_JSON" | python3 -c "import json,sys; print(json.load(sys.stdin).get('expires_in', 60))")
echo "::add-mask::$ACCESS_TOKEN"
echo "WEAVIATE_OIDC_ACCESS_TOKEN=$ACCESS_TOKEN" >> $GITHUB_ENV
echo "WEAVIATE_OIDC_EXPIRES_IN=$EXPIRES_IN" >> $GITHUB_ENV
echo "✅ Keycloak configuration completed"
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run C# tests
id: run-tests
continue-on-error: true
env:
DOCS_GITHUB_ENV: "true"
WEAVIATE_URL: ${{ secrets.WEAVIATE_URL }}
WEAVIATE_HOST: ${{ secrets.WEAVIATE_HOST }}
WEAVIATE_HOSTNAME: ${{ secrets.WEAVIATE_HOSTNAME }}
WEAVIATE_HTTP_HOST: ${{ secrets.WEAVIATE_HTTP_HOST }}
WEAVIATE_GRPC_HOST: ${{ secrets.WEAVIATE_GRPC_HOST }}
WEAVIATE_API_KEY: ${{ secrets.WEAVIATE_API_KEY }}
WEAVIATE_LOCAL_API_KEY: ${{ secrets.WEAVIATE_LOCAL_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
COHERE_API_KEY: ${{ secrets.COHERE_API_KEY }}
HUGGINGFACE_API_KEY: ${{ secrets.HUGGINGFACE_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
echo "🧪 Running C# tests..."
uv run pytest -m "csharp" -v --tb=short --durations=10 -s --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'CSharp'
languages-tested: 'C#'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
- name: Stop services
if: always()
run: |
echo "🛑 Stopping services..."
./tests/stop-weaviate.sh 2>/dev/null || echo "⚠️ Some services may have already stopped"
- name: Cleanup Docker resources
if: always()
run: |
for compose_file in tests/docker-compose*.yml; do
if [[ -f "$compose_file" ]]; then
docker compose -f "$compose_file" down -v --remove-orphans 2>/dev/null || true
fi
done
docker system prune -f --volumes 2>/dev/null || true
---
### .Github/Workflows/Indexability Tests.Yml (.github/workflows/indexability_tests.yml)
name: Docs Indexability Tests
permissions:
contents: read
on:
schedule:
# Run every Sunday at 22:00 UTC
- cron: "0 22 * * 0"
push:
branches:
- testing-ci
- llm-indexability
workflow_dispatch:
env:
PYTHON_VERSION: "3.10"
jobs:
test-indexability:
name: Test Indexability
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run HTML structure tests
id: html-tests
continue-on-error: true
run: |
echo "Running HTML indexability tests..."
uv run pytest -m "indexability" -v --tb=short --junitxml=pytest-results.xml
- name: Run agent tests
id: agent-tests
continue-on-error: true
if: env.ANTHROPIC_API_KEY != '' && env.OPENAI_API_KEY != ''
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
run: |
echo "Running agent indexability tests..."
uv run pytest -m "indexability_agents" -v --tb=short --junitxml=pytest-results-agents.xml
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ (steps.html-tests.outcome == 'success' && (steps.agent-tests.outcome == 'success' || steps.agent-tests.outcome == 'skipped')) && 'success' || 'failure' }}
test-type: 'Indexability'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
test-results-xml: 'pytest-results.xml pytest-results-agents.xml'
slack-script: '_build_scripts/slack-indexability-results.sh'
---
### .Github/Workflows/Llms Txt Snippet Sync.Yml (.github/workflows/llms_txt_snippet_sync.yml)
name: llms.txt Snippet Sync
# llms.txt is hand-maintained in the weaviate-io repo, so a snippet change here can
# strand a block in the published file. Only the weekly llms_txt_tests.yml job notices,
# which means the break surfaces days later. This gives the author the signal at PR time.
#
# It is advisory by design and never fails: when a snippet PR is opened, weaviate-io has
# not merged or deployed yet, so the live llms.txt legitimately cannot match. A blocking
# check would fire on every honest PR and would just be overridden.
permissions:
contents: read
on:
pull_request:
paths:
# Kept in step with SNIPPET_GLOBS in tests/test_llms_txt_code.py. Java and C#
# snippets live with their language suites, not under _includes/code/llms-txt/.
- "_includes/code/llms-txt/**"
- "_includes/code/java-v6/src/test/java/LlmsTxtTest.java"
- "_includes/code/csharp/LlmsTxtTest.cs"
- "tests/test_llms_txt_code.py"
- "tests/check_llms_txt_drift.py"
env:
PYTHON_VERSION: "3.11"
jobs:
check-snippet-sync:
name: Check llms.txt snippet sync
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Fetch the PR base commit
# Shallow single-commit fetch: the check only needs the base tree to read the
# pre-change snippet files. If it fails the check reports a degraded warning
# rather than blocking.
continue-on-error: true
run: git fetch --no-tags --depth=1 origin ${{ github.event.pull_request.base.sha }}
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install pytest
# The check imports tests/test_llms_txt_code.py to reuse its matching logic, and
# that module imports pytest. Nothing else from the test suite is needed, so this
# stays far cheaper than the full setup-test-env composite.
run: python -m pip install --quiet "pytest>=8.3.5"
- name: Check llms.txt snippet sync
run: python tests/check_llms_txt_drift.py --base "${{ github.event.pull_request.base.sha }}"
---
### .Github/Workflows/Llms Txt Tests.Yml (.github/workflows/llms_txt_tests.yml)
name: llms.txt Tests
permissions:
contents: read
on:
schedule:
# Run every Sunday at 23:00 UTC (slightly offset from the indexability job)
- cron: "0 23 * * 0"
push:
branches:
- testing-ci
- llms-txt
workflow_dispatch:
env:
PYTHON_VERSION: "3.10"
jobs:
test-llms-txt:
name: Test llms.txt
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up test environment
uses: ./.github/actions/setup-test-env
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Record test start time
run: echo "TEST_START_TIME=$(date +%s)" >> $GITHUB_ENV
- name: Run llms.txt guard tests
id: run-tests
continue-on-error: true
env:
# Raises GitHub's anonymous rate limit (60/hr → 5000/hr) for the
# version-freshness test, which hits /releases/latest per library.
GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "🧪 Running llms.txt guard tests (snippet coverage, version freshness, link validity)..."
uv run pytest tests/test_llms_txt_code.py -m "llms_txt" -v --tb=short --durations=10 --junitxml=pytest-results.xml
echo "✅ Tests completed"
- name: Handle test results
if: always()
uses: ./.github/actions/handle-test-results
with:
test-outcome: ${{ steps.run-tests.outcome }}
test-type: 'llms.txt'
# Not language-specific — relabel the field and describe what was checked.
scope-label: 'Checks'
languages-tested: 'Snippet coverage · Versions · Links'
slack-bot-token: ${{ secrets.TESTING_CI_SLACK_BOT }}
---
### .Github/Workflows/Pull Requests.Yaml (.github/workflows/pull_requests.yaml)
name: PR links validation
on:
pull_request:
jobs:
Validate-links:
name: Validate links
runs-on: ubuntu-latest
env:
GOOGLE_CONTAINER_ID: ${{ secrets.GOOGLE_CONTAINER_ID }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: 'yarn'
- name: Install dependencies
run: |
rm -vrf node_modules/.cache/webpack
yarn install
- name: Update versions from GitHub
env:
GH_API_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
node _build_scripts/update-config-versions.js
- name: Build a dev version
run: yarn build-dev
- name: Validate links from the build.dev folder
run: node _build_scripts/validate-links-pr.js
---
