LlamaIndex Vector Stores Integration: Tencent Cloud COS Vectors
English | 中文
llama-index-vector-stores-cos integrates Tencent Cloud COS Vectors with LlamaIndex, letting you use COS Vectors as the backend of a VectorStoreIndex for vector insertion, retrieval, and metadata filtering — with zero ops overhead.
- PyPI:
llama-index-vector-stores-cos - Python:
>=3.10, <3.14 - License: MIT
Features
- 🚀 Plug & play: swap in as your
VectorStoreIndexbackend in a single line; works with any LlamaIndexQueryEngine/Retriever. - 🧩 Standard interface: implements
BasePydanticVectorStorewith fulladd/query/get_nodes/delete/delete_nodes/clear, plus asynca*counterparts. - 🔎 Rich filtering: supports the standard LlamaIndex
MetadataFilters—EQ/NE/GT/GTE/LT/LTE/IN/NIN/IS_EMPTYcombined withAND/OR. - 🛡 Built-in throttling: token-bucket limits writes to 5
put_vectorscalls per second to stay safely below service limits. - ☁️ Fully managed: both the vector bucket and the index are hosted on Tencent Cloud COS Vectors — no self-hosted vector database required.
Installation
Install the main package:
pip install llama-index-vector-stores-cos
Install LlamaIndex ecosystem components as needed (examples below use OpenAI):
# Embedding & LLM (install as needed)
pip install llama-index-embeddings-openai llama-index-llms-openai
# Or local Ollama
# pip install llama-index-embeddings-ollama
# Or HuggingFace
# pip install llama-index-embeddings-huggingface
Enable COS Vectors
Full guide: Tencent Cloud Vector Bucket Docs (or 腾讯云向量存储桶 in Chinese).
Quick steps:
-
Log in to the Tencent Cloud Console and enable Cloud Object Storage (COS).
-
Enable COS Vectors and create a vector bucket in your target region (e.g.
ap-guangzhou). -
Go to CAM and create (or reuse) an API key pair (
SecretId+SecretKey). -
Grab the vector bucket's dedicated access domain, in the form of:
vectors.<region>.coslake.com # e.g. vectors.ap-guangzhou.coslake.com
⚠️
Domainmust be the COS Vectors dedicated domain (vectors.<region>.coslake.com), not the regular COS domaincos.<region>.myqcloud.com. Using the wrong one leads to access failures.
Recommended: inject credentials via environment variables:
export COS_SECRET_ID=your_secret_id
export COS_SECRET_KEY=your_secret_key
export OPENAI_API_KEY=your_openai_api_key
Embedding model
This document uses OpenAI text-embedding-3-small (1536 dim) as the minimal example. You can swap in OllamaEmbedding, HuggingFaceEmbedding, or any LlamaIndex BaseEmbedding implementation — just make sure the dimension you pass when creating the index matches the embedding model's output dimension.
from llama_index.core import Settings
from llama_index.embeddings.openai import OpenAIEmbedding
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small") # 1536 dim
Quick start
1. Create a new index and get a VectorStore
create_index_from_bucket creates an index in the specified vector bucket and returns a ready-to-use COSVectorStore.
import os
from qcloud_cos import CosConfig
from llama_index.vector_stores.cos import COSVectorStore
config = CosConfig(
Region="ap-guangzhou",
SecretId=os.environ["COS_SECRET_ID"],
SecretKey=os.environ["COS_SECRET_KEY"],
Domain="vectors.ap-guangzhou.coslake.com",
)
vector_store = COSVectorStore.create_index_from_bucket(
bucket_name="example-bucket-1250000000",
index_name="example-index",
dimension=1536, # must match the embedding output
distance_metric="cosine", # or "euclidean"
config=config,
)
2. Reuse an existing index
If the index already exists, just construct a COSVectorStore directly:
from qcloud_cos import CosConfig, CosVectorsClient
from llama_index.vector_stores.cos import COSVectorStore
client = CosVectorsClient(CosConfig(
Region="ap-guangzhou",
SecretId=os.environ["COS_SECRET_ID"],
SecretKey=os.environ["COS_SECRET_KEY"],
Domain="vectors.ap-guangzhou.coslake.com",
))
vector_store = COSVectorStore(
bucket_name="example-bucket-1250000000",
index_name="example-index",
client=client, # or pass config=CosConfig(...) instead
)
At least one of
client/configis required; if both are provided,clientwins.
Usage examples
Insert vectors
From text (TextNode)
from llama_index.core.schema import TextNode
from llama_index.core import Settings
nodes = [
TextNode(text="The Oriental Pearl Tower sits by the Huangpu River in Shanghai.", metadata={"city": "shanghai"}),
TextNode(text="The Forbidden City was the imperial palace of the Ming and Qing dynasties.", metadata={"city": "beijing"}),
]
for n in nodes:
n.embedding = Settings.embed_model.get_text_embedding(n.get_content())
vector_store.add(nodes)
From documents (SimpleDirectoryReader)
from llama_index.core import VectorStoreIndex, StorageContext, SimpleDirectoryReader
documents = SimpleDirectoryReader("./data").load_data()
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
Retrieve vectors
Plain similarity search
retriever = index.as_retriever(similarity_top_k=3)
nodes = retriever.retrieve("Where is the Oriental Pearl Tower?")
for n in nodes:
print(n.score, n.text[:80])
Or use the query engine directly:
query_engine = index.as_query_engine(similarity_top_k=3)
print(query_engine.query("Where is the Oriental Pearl Tower?"))
Metadata-filtered search
from llama_index.core.vector_stores import (
MetadataFilter,
MetadataFilters,
FilterOperator,
FilterCondition,
)
filters = MetadataFilters(
filters=[
MetadataFilter(key="city", value="shanghai", operator=FilterOperator.EQ),
MetadataFilter(key="tags", value=["landmark", "tourism"], operator=FilterOperator.IN),
],
condition=FilterCondition.AND, # OR is also supported; NOT is NOT supported
)
retriever = index.as_retriever(similarity_top_k=5, filters=filters)
nodes = retriever.retrieve("What landmarks are in Shanghai?")
Supported operators: EQ / NE / GT / GTE / LT / LTE / IN / NIN / IS_EMPTY. Conditions: AND / OR. See Parameters.
Node management
# Fetch nodes by ID
nodes = vector_store.get_nodes(node_ids=["node-id-1", "node-id-2"])
# Delete nodes by ID
vector_store.delete_nodes(node_ids=["node-id-1"])
# Delete all nodes belonging to a source doc (matched by metadata ref_doc_id)
vector_store.delete(ref_doc_id="doc-id-xxx")
# ⚠️ Destroy the whole index (dangerous — this is NOT a "truncate")
vector_store.clear()
delete_nodesdoes not support thefiltersargument;clear()callsdelete_indexdirectly.
End-to-end LlamaIndex example
A minimal runnable pipeline: load documents → build index → ask questions.
import os
from qcloud_cos import CosConfig
from llama_index.core import (
Settings,
SimpleDirectoryReader,
StorageContext,
VectorStoreIndex,
)
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI
from llama_index.vector_stores.cos import COSVectorStore
# 1. Configure embedding & LLM
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small") # 1536 dim
Settings.llm = OpenAI(model="gpt-4o-mini")
# 2. Connect to COS Vectors
config = CosConfig(
Region="ap-guangzhou",
SecretId=os.environ["COS_SECRET_ID"],
SecretKey=os.environ["COS_SECRET_KEY"],
Domain="vectors.ap-guangzhou.coslake.com",
)
# 3. Create a new index (use COSVectorStore(...) directly if the index already exists)
vector_store = COSVectorStore.create_index_from_bucket(
bucket_name="example-bucket-1250000000",
index_name="llama-index-demo",
dimension=1536,
distance_metric="cosine",
config=config,
)
# 4. Read local docs → insert vectors
documents = SimpleDirectoryReader("./data").load_data()
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex.from_documents(documents, storage_context=storage_context)
# 5. Query
query_engine = index.as_query_engine(similarity_top_k=3)
print(query_engine.query("What is this document mainly about?"))
Parameters
COSVectorStore(...) constructor
| Param | Type | Default | Description |
|---|---|---|---|
bucket_name |
str |
required | COS vector bucket name. |
index_name |
str |
required | Index name. |
data_type |
str |
"float32" |
Vector data type. |
insert_batch_size |
int |
500 |
Max items per write batch; upper bound is 500, otherwise ValueError. |
text_field |
Optional[str] |
None |
If set, restore TextNode.text from this metadata key; otherwise use the default serializer. |
distance_metric |
str |
"cosine" |
Distance metric; supports "cosine" and "euclidean". |
client |
Optional[CosVectorsClient] |
None |
Pre-built client; one of client/config is required. |
config |
Optional[CosConfig] |
None |
Used to build the client internally; one of client/config is required. |
COSVectorStore.create_index_from_bucket(...) extra params
| Param | Type | Default | Description |
|---|---|---|---|
dimension |
int |
required | Vector dimension; must match the embedding output. |
distance_metric |
str |
"cosine" |
Distance metric for the new index; supports "cosine" and "euclidean". |
data_type |
str |
"float32" |
Vector data type of the new index. |
insert_batch_size |
int |
500 |
Forwarded to the returned COSVectorStore; upper bound is 500. |
non_filterable_metadata_keys |
Optional[List] |
None |
Metadata keys that cannot be used as filters; _node_content and _node_type are always appended automatically. |
client / config |
— | — | Same as the constructor; one of them is required. |
Supported filter operators
| Operator | Supported | Notes |
|---|---|---|
EQ |
✅ | equals |
NE |
✅ | not equal |
GT/GTE |
✅ | greater (or eq) |
LT/LTE |
✅ | less (or eq) |
IN |
✅ | value in list |
NIN |
✅ | value not in |
IS_EMPTY |
✅ | field missing |
TEXT_MATCH / CONTAINS / others |
❌ | not supported |
| Condition | Supported |
|---|---|
AND |
✅ |
OR |
✅ |
NOT |
❌ |
Limits & notes
addwrites nodes in batches ofinsert_batch_size, and the client throttles batches to 5put_vectorscalls per second; large writes will auto-sleep.insert_batch_sizeis capped at 500.queryonly supportsVectorStoreQueryMode.DEFAULT; passingHYBRID/SPARSEraisesNotImplementedError.get_nodesrequiresnode_ids;filtersis currently ignored.delete_nodesdoes not supportfilters;node_idsis required.clear()callsdelete_indexand destroys the whole index — use with care.- Every core method has an async counterpart:
async_add,aquery,aget_nodes,adelete,adelete_nodes,aclear.
FAQ
Q1: ValueError: Either 'client' or 'config' must be provided.
You passed neither client nor config when constructing COSVectorStore (or calling create_index_from_bucket). Provide at least one.
Q2: ValueError: insert_batch_size must be <= 500
COS Vectors caps batched writes at 500. Lower insert_batch_size.
Q3: ValueError: NOT condition is not supported for COS Vectors filters
FilterCondition.NOT is not supported. Rewrite with NE / NIN instead.
Q4: NotImplementedError: Only DEFAULT query mode is supported for COSVectorStore
Make sure you're using the default query mode; do not pass VectorStoreQueryMode.HYBRID / SPARSE etc.
Q5: Access fails and returns an XML-formatted error message
In most cases, Domain is missing, or it was set to the regular COS domain (for example, cos.<region>.myqcloud.com). Use the COS Vectors dedicated domain vectors.<region>.coslake.com instead.
Q6: What is text_field and when do I need it?
By default, COSVectorStore serializes the full node into metadata (key _node_content) and rebuilds the TextNode on read. If you'd rather restore text from a custom metadata field (e.g. content), set text_field="content"; that field must exist in the metadata you write.
Q7: Dimension mismatch error
The dimension you pass to create_index_from_bucket must exactly match your embedding model's output dimension (OpenAI text-embedding-3-small = 1536, bge-m3 = 1024, etc.).
Q8: What does clear() actually do?
It calls delete_index and destroys the whole index, not just its data. After calling it you'll need to recreate the index via create_index_from_bucket.
License
Released under the MIT License.
Metadata
Release files for llama-index-vector-stores-cos 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| llama_index_vector_stores_cos-0.1.1.tar.gz | 11.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| llama_index_vector_stores_cos-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.1 kB
Release files / llama_index_vector_stores_cos-0.1.1.tar.gz
| Download URL | llama_index_vector_stores_cos-0.1.1.tar.gz |
|---|---|
| Size | 11.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0ae5b13c50ad28978532980432cfacb08ce606071d78c80e00731082e99cdc4c
|
|
BLAKE2b-256 checksum How to use checksums |
93bc912134757a5a7a45c9315f1b29d938726bcc23708f307d6ffdabb697cd23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.13
|
Release files / llama_index_vector_stores_cos-0.1.1-py3-none-any.whl
| Download URL | llama_index_vector_stores_cos-0.1.1-py3-none-any.whl |
|---|---|
| Size | 11.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
00d729ad61c7febf7ea41e790782793000f81b587f2291f46730846c9b8d4213
|
|
BLAKE2b-256 checksum How to use checksums |
7c90dde8daebfc753868ffbf5feb8b8efebd6b4b9d00cee4c1d76e15b0211d6e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.11.13
|