Skip to content

Commit cd8715d

Browse files
cheesecake100201sarthakdeshpandefranciscojavierarceo
authored
chore: Added openai compatible vector io endpoints for chromadb (llamastack#2489)
# What does this PR do? This PR implements the openai compatible endpoints for chromadb Closes llamastack#2462 ## Test Plan Ran ollama llama stack server and ran the command `pytest -sv --stack-config=http://localhost:8321 tests/integration/vector_io/test_openai_vector_stores.py --embedding-model all-MiniLM-L6-v2` 8 failed, 27 passed, 8 skipped, 1 xfailed The failed ones are regarding files api --------- Signed-off-by: Francisco Javier Arceo <[email protected]> Co-authored-by: sarthakdeshpande <[email protected]> Co-authored-by: Francisco Javier Arceo <[email protected]> Co-authored-by: Francisco Arceo <[email protected]>
1 parent fd2aab8 commit cd8715d

File tree

18 files changed

+670
-142
lines changed

18 files changed

+670
-142
lines changed

docs/source/providers/vector_io/inline_chromadb.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,15 @@ See [Chroma's documentation](https://docs.trychroma.com/docs/overview/introducti
4242
| Field | Type | Required | Default | Description |
4343
|-------|------|----------|---------|-------------|
4444
| `db_path` | `<class 'str'>` | No | PydanticUndefined | |
45+
| `kvstore` | `utils.kvstore.config.RedisKVStoreConfig \| utils.kvstore.config.SqliteKVStoreConfig \| utils.kvstore.config.PostgresKVStoreConfig \| utils.kvstore.config.MongoDBKVStoreConfig` | No | sqlite | Config for KV store backend |
4546

4647
## Sample Configuration
4748

4849
```yaml
4950
db_path: ${env.CHROMADB_PATH}
51+
kvstore:
52+
type: sqlite
53+
db_path: ${env.SQLITE_STORE_DIR:=~/.llama/dummy}/chroma_inline_registry.db
5054

5155
```
5256

docs/source/providers/vector_io/remote_chromadb.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,15 @@ See [Chroma's documentation](https://docs.trychroma.com/docs/overview/introducti
4141
| Field | Type | Required | Default | Description |
4242
|-------|------|----------|---------|-------------|
4343
| `url` | `str \| None` | No | PydanticUndefined | |
44+
| `kvstore` | `utils.kvstore.config.RedisKVStoreConfig \| utils.kvstore.config.SqliteKVStoreConfig \| utils.kvstore.config.PostgresKVStoreConfig \| utils.kvstore.config.MongoDBKVStoreConfig` | No | sqlite | Config for KV store backend |
4445

4546
## Sample Configuration
4647

4748
```yaml
4849
url: ${env.CHROMADB_URL}
50+
kvstore:
51+
type: sqlite
52+
db_path: ${env.SQLITE_STORE_DIR:=~/.llama/dummy}/chroma_remote_registry.db
4953

5054
```
5155

llama_stack/providers/inline/vector_io/chroma/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@ async def get_provider_impl(config: ChromaVectorIOConfig, deps: dict[Api, Any]):
1616
ChromaVectorIOAdapter,
1717
)
1818

19-
impl = ChromaVectorIOAdapter(config, deps[Api.inference])
19+
impl = ChromaVectorIOAdapter(config, deps[Api.inference], deps.get(Api.files))
2020
await impl.initialize()
2121
return impl

llama_stack/providers/inline/vector_io/chroma/config.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,25 @@
66

77
from typing import Any
88

9-
from pydantic import BaseModel
9+
from pydantic import BaseModel, Field
1010

11+
from llama_stack.providers.utils.kvstore.config import KVStoreConfig, SqliteKVStoreConfig
12+
from llama_stack.schema_utils import json_schema_type
1113

14+
15+
@json_schema_type
1216
class ChromaVectorIOConfig(BaseModel):
1317
db_path: str
18+
kvstore: KVStoreConfig = Field(description="Config for KV store backend")
1419

1520
@classmethod
16-
def sample_run_config(cls, db_path: str = "${env.CHROMADB_PATH}", **kwargs: Any) -> dict[str, Any]:
17-
return {"db_path": db_path}
21+
def sample_run_config(
22+
cls, __distro_dir__: str, db_path: str = "${env.CHROMADB_PATH}", **kwargs: Any
23+
) -> dict[str, Any]:
24+
return {
25+
"db_path": db_path,
26+
"kvstore": SqliteKVStoreConfig.sample_run_config(
27+
__distro_dir__=__distro_dir__,
28+
db_name="chroma_inline_registry.db",
29+
),
30+
}

llama_stack/providers/remote/vector_io/chroma/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@
1212
async def get_adapter_impl(config: ChromaVectorIOConfig, deps: dict[Api, ProviderSpec]):
1313
from .chroma import ChromaVectorIOAdapter
1414

15-
impl = ChromaVectorIOAdapter(config, deps[Api.inference])
15+
impl = ChromaVectorIOAdapter(config, deps[Api.inference], deps.get(Api.files))
1616
await impl.initialize()
1717
return impl

llama_stack/providers/remote/vector_io/chroma/chroma.py

Lines changed: 18 additions & 120 deletions
Original file line numberDiff line numberDiff line change
@@ -12,25 +12,19 @@
1212
import chromadb
1313
from numpy.typing import NDArray
1414

15+
from llama_stack.apis.files import Files
1516
from llama_stack.apis.inference import InterleavedContent
1617
from llama_stack.apis.vector_dbs import VectorDB
1718
from llama_stack.apis.vector_io import (
1819
Chunk,
1920
QueryChunksResponse,
20-
SearchRankingOptions,
2121
VectorIO,
22-
VectorStoreChunkingStrategy,
23-
VectorStoreDeleteResponse,
24-
VectorStoreFileContentsResponse,
25-
VectorStoreFileObject,
26-
VectorStoreFileStatus,
27-
VectorStoreListFilesResponse,
28-
VectorStoreListResponse,
29-
VectorStoreObject,
30-
VectorStoreSearchResponsePage,
3122
)
3223
from llama_stack.providers.datatypes import Api, VectorDBsProtocolPrivate
3324
from llama_stack.providers.inline.vector_io.chroma import ChromaVectorIOConfig as InlineChromaVectorIOConfig
25+
from llama_stack.providers.utils.kvstore import kvstore_impl
26+
from llama_stack.providers.utils.kvstore.api import KVStore
27+
from llama_stack.providers.utils.memory.openai_vector_store_mixin import OpenAIVectorStoreMixin
3428
from llama_stack.providers.utils.memory.vector_store import (
3529
EmbeddingIndex,
3630
VectorDBWithIndex,
@@ -42,6 +36,13 @@
4236

4337
ChromaClientType = chromadb.api.AsyncClientAPI | chromadb.api.ClientAPI
4438

39+
VERSION = "v3"
40+
VECTOR_DBS_PREFIX = f"vector_dbs:chroma:{VERSION}::"
41+
VECTOR_INDEX_PREFIX = f"vector_index:chroma:{VERSION}::"
42+
OPENAI_VECTOR_STORES_PREFIX = f"openai_vector_stores:chroma:{VERSION}::"
43+
OPENAI_VECTOR_STORES_FILES_PREFIX = f"openai_vector_stores_files:chroma:{VERSION}::"
44+
OPENAI_VECTOR_STORES_FILES_CONTENTS_PREFIX = f"openai_vector_stores_files_contents:chroma:{VERSION}::"
45+
4546

4647
# this is a helper to allow us to use async and non-async chroma clients interchangeably
4748
async def maybe_await(result):
@@ -51,9 +52,10 @@ async def maybe_await(result):
5152

5253

5354
class ChromaIndex(EmbeddingIndex):
54-
def __init__(self, client: ChromaClientType, collection):
55+
def __init__(self, client: ChromaClientType, collection, kvstore: KVStore | None = None):
5556
self.client = client
5657
self.collection = collection
58+
self.kvstore = kvstore
5759

5860
async def add_chunks(self, chunks: list[Chunk], embeddings: NDArray):
5961
assert len(chunks) == len(embeddings), (
@@ -122,24 +124,23 @@ async def query_hybrid(
122124
raise NotImplementedError("Hybrid search is not supported in Chroma")
123125

124126

125-
class ChromaVectorIOAdapter(VectorIO, VectorDBsProtocolPrivate):
127+
class ChromaVectorIOAdapter(OpenAIVectorStoreMixin, VectorIO, VectorDBsProtocolPrivate):
126128
def __init__(
127129
self,
128130
config: RemoteChromaVectorIOConfig | InlineChromaVectorIOConfig,
129131
inference_api: Api.inference,
132+
files_api: Files | None,
130133
) -> None:
131134
log.info(f"Initializing ChromaVectorIOAdapter with url: {config}")
132135
self.config = config
133136
self.inference_api = inference_api
134-
135137
self.client = None
136138
self.cache = {}
139+
self.kvstore: KVStore | None = None
137140

138141
async def initialize(self) -> None:
142+
self.kvstore = await kvstore_impl(self.config.kvstore)
139143
if isinstance(self.config, RemoteChromaVectorIOConfig):
140-
if not self.config.url:
141-
raise ValueError("URL is a required parameter for the remote Chroma provider's config")
142-
143144
log.info(f"Connecting to Chroma server at: {self.config.url}")
144145
url = self.config.url.rstrip("/")
145146
parsed = urlparse(url)
@@ -151,6 +152,7 @@ async def initialize(self) -> None:
151152
else:
152153
log.info(f"Connecting to Chroma local db at: {self.config.db_path}")
153154
self.client = chromadb.PersistentClient(path=self.config.db_path)
155+
self.openai_vector_stores = await self._load_openai_vector_stores()
154156

155157
async def shutdown(self) -> None:
156158
pass
@@ -206,107 +208,3 @@ async def _get_and_cache_vector_db_index(self, vector_db_id: str) -> VectorDBWit
206208
index = VectorDBWithIndex(vector_db, ChromaIndex(self.client, collection), self.inference_api)
207209
self.cache[vector_db_id] = index
208210
return index
209-
210-
async def openai_create_vector_store(
211-
self,
212-
name: str,
213-
file_ids: list[str] | None = None,
214-
expires_after: dict[str, Any] | None = None,
215-
chunking_strategy: dict[str, Any] | None = None,
216-
metadata: dict[str, Any] | None = None,
217-
embedding_model: str | None = None,
218-
embedding_dimension: int | None = 384,
219-
provider_id: str | None = None,
220-
) -> VectorStoreObject:
221-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
222-
223-
async def openai_list_vector_stores(
224-
self,
225-
limit: int | None = 20,
226-
order: str | None = "desc",
227-
after: str | None = None,
228-
before: str | None = None,
229-
) -> VectorStoreListResponse:
230-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
231-
232-
async def openai_retrieve_vector_store(
233-
self,
234-
vector_store_id: str,
235-
) -> VectorStoreObject:
236-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
237-
238-
async def openai_update_vector_store(
239-
self,
240-
vector_store_id: str,
241-
name: str | None = None,
242-
expires_after: dict[str, Any] | None = None,
243-
metadata: dict[str, Any] | None = None,
244-
) -> VectorStoreObject:
245-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
246-
247-
async def openai_delete_vector_store(
248-
self,
249-
vector_store_id: str,
250-
) -> VectorStoreDeleteResponse:
251-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
252-
253-
async def openai_search_vector_store(
254-
self,
255-
vector_store_id: str,
256-
query: str | list[str],
257-
filters: dict[str, Any] | None = None,
258-
max_num_results: int | None = 10,
259-
ranking_options: SearchRankingOptions | None = None,
260-
rewrite_query: bool | None = False,
261-
search_mode: str | None = "vector",
262-
) -> VectorStoreSearchResponsePage:
263-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
264-
265-
async def openai_attach_file_to_vector_store(
266-
self,
267-
vector_store_id: str,
268-
file_id: str,
269-
attributes: dict[str, Any] | None = None,
270-
chunking_strategy: VectorStoreChunkingStrategy | None = None,
271-
) -> VectorStoreFileObject:
272-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
273-
274-
async def openai_list_files_in_vector_store(
275-
self,
276-
vector_store_id: str,
277-
limit: int | None = 20,
278-
order: str | None = "desc",
279-
after: str | None = None,
280-
before: str | None = None,
281-
filter: VectorStoreFileStatus | None = None,
282-
) -> VectorStoreListFilesResponse:
283-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
284-
285-
async def openai_retrieve_vector_store_file(
286-
self,
287-
vector_store_id: str,
288-
file_id: str,
289-
) -> VectorStoreFileObject:
290-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
291-
292-
async def openai_retrieve_vector_store_file_contents(
293-
self,
294-
vector_store_id: str,
295-
file_id: str,
296-
) -> VectorStoreFileContentsResponse:
297-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
298-
299-
async def openai_update_vector_store_file(
300-
self,
301-
vector_store_id: str,
302-
file_id: str,
303-
attributes: dict[str, Any] | None = None,
304-
) -> VectorStoreFileObject:
305-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")
306-
307-
async def openai_delete_vector_store_file(
308-
self,
309-
vector_store_id: str,
310-
file_id: str,
311-
) -> VectorStoreFileObject:
312-
raise NotImplementedError("OpenAI Vector Stores API is not supported in Chroma")

llama_stack/providers/remote/vector_io/chroma/config.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,23 @@
66

77
from typing import Any
88

9-
from pydantic import BaseModel
9+
from pydantic import BaseModel, Field
1010

11+
from llama_stack.providers.utils.kvstore.config import KVStoreConfig, SqliteKVStoreConfig
12+
from llama_stack.schema_utils import json_schema_type
1113

14+
15+
@json_schema_type
1216
class ChromaVectorIOConfig(BaseModel):
1317
url: str | None
18+
kvstore: KVStoreConfig = Field(description="Config for KV store backend")
1419

1520
@classmethod
16-
def sample_run_config(cls, url: str = "${env.CHROMADB_URL}", **kwargs: Any) -> dict[str, Any]:
17-
return {"url": url}
21+
def sample_run_config(cls, __distro_dir__: str, url: str = "${env.CHROMADB_URL}", **kwargs: Any) -> dict[str, Any]:
22+
return {
23+
"url": url,
24+
"kvstore": SqliteKVStoreConfig.sample_run_config(
25+
__distro_dir__=__distro_dir__,
26+
db_name="chroma_remote_registry.db",
27+
),
28+
}

llama_stack/templates/ci-tests/run.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ providers:
133133
provider_type: remote::chromadb
134134
config:
135135
url: ${env.CHROMADB_URL:=}
136+
kvstore:
137+
type: sqlite
138+
db_path: ${env.SQLITE_STORE_DIR:=~/.llama/distributions/starter/}/chroma_remote_registry.db
136139
- provider_id: ${env.ENABLE_PGVECTOR:=__disabled__}
137140
provider_type: remote::pgvector
138141
config:

llama_stack/templates/open-benchmark/open_benchmark.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ def get_distribution_template() -> DistributionTemplate:
122122
Provider(
123123
provider_id="${env.ENABLE_CHROMADB:+chromadb}",
124124
provider_type="remote::chromadb",
125-
config=ChromaVectorIOConfig.sample_run_config(url="${env.CHROMADB_URL:=}"),
125+
config=ChromaVectorIOConfig.sample_run_config(
126+
f"~/.llama/distributions/{name}", url="${env.CHROMADB_URL:=}"
127+
),
126128
),
127129
Provider(
128130
provider_id="${env.ENABLE_PGVECTOR:+pgvector}",

llama_stack/templates/open-benchmark/run.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ providers:
4646
provider_type: remote::chromadb
4747
config:
4848
url: ${env.CHROMADB_URL:=}
49+
kvstore:
50+
type: sqlite
51+
db_path: ${env.SQLITE_STORE_DIR:=~/.llama/distributions/open-benchmark}/chroma_remote_registry.db
4952
- provider_id: ${env.ENABLE_PGVECTOR:+pgvector}
5053
provider_type: remote::pgvector
5154
config:

0 commit comments

Comments
 (0)