chroma-core / chroma

the AI-native open-source embedding database
https://www.trychroma.com/
Apache License 2.0
13.54k stars 1.15k forks source link

[Install issue]: type object 'CreateCollection' has no attribute 'model_validate' during Create Index #2137

Open zhou9110 opened 2 months ago

zhou9110 commented 2 months ago

What happened?

After installed ChromaDB and run chroma run, it throws an error when I try to create a new index using the NodeJS client.

The reason I find out is because the version of pydantic is too old on my machine. Mine was 1.10.12, and upgrading the library to the latest version (2.7.1) solves this issue.

pip install pydantic --upgrade

It seems the version in the requirement.txt is pydantic>=1.9 so it doesn't upgrade the library automatically during install, should the version number gets bumped? Reference: https://github.com/chroma-core/chroma/blob/b34f90ce41a82d54ca4d68e43009309cd8b43f89/requirements.txt#L18

The code that throws the error (model_validate does not exist on 1.10): https://github.com/chroma-core/chroma/blob/b34f90ce41a82d54ca4d68e43009309cd8b43f89/chromadb/server/fastapi/__init__.py#L561

Versions

Chroma v0.5.0, Python 3.9.13, MacOS 14.4.1

Relevant log output

ERROR:    [06-05-2024 22:51:40] type object 'CreateCollection' has no attribute 'model_validate'
Traceback (most recent call last):
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/anyio/streams/memory.py", line 81, in receive
    return self.receive_nowait()
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/anyio/streams/memory.py", line 76, in receive_nowait
    raise WouldBlock
anyio.WouldBlock

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/middleware/base.py", line 159, in call_next
    message = await recv_stream.receive()
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/anyio/streams/memory.py", line 101, in receive
    raise EndOfStream
anyio.EndOfStream

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/chromadb/server/fastapi/__init__.py", line 78, in catch_exceptions_middleware
    return await call_next(request)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/middleware/base.py", line 165, in call_next
    raise app_exc
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/middleware/base.py", line 151, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/middleware/base.py", line 191, in __call__
    response = await self.dispatch_func(request, call_next)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/chromadb/server/fastapi/__init__.py", line 92, in check_http_version_middleware
    return await call_next(request)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/middleware/base.py", line 165, in call_next
    raise app_exc
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/middleware/base.py", line 151, in coro
    await self.app(scope, receive_or_disconnect, send_no_error)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/middleware/exceptions.py", line 65, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/routing.py", line 756, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/routing.py", line 776, in app
    await route.handle(scope, receive, send)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/routing.py", line 297, in handle
    await self.app(scope, receive, send)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/routing.py", line 77, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/_exception_handler.py", line 64, in wrapped_app
    raise exc
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/_exception_handler.py", line 53, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/starlette/routing.py", line 72, in app
    response = await func(request)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/fastapi/routing.py", line 278, in app
    raw_response = await run_endpoint_function(
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/fastapi/routing.py", line 191, in run_endpoint_function
    return await dependant.call(**values)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/chromadb/telemetry/opentelemetry/__init__.py", line 130, in wrapper
    return await f(*args, **kwargs)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/chromadb/server/fastapi/__init__.py", line 630, in create_collection
    await to_thread.run_sync(
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/anyio/to_thread.py", line 28, in run_sync
    return await get_asynclib().run_sync_in_worker_thread(func, *args, cancellable=cancellable,
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/anyio/_backends/_asyncio.py", line 818, in run_sync_in_worker_thread
    return await future
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/anyio/_backends/_asyncio.py", line 754, in run
    result = context.run(func, *args)
  File "/Users/user/opt/anaconda3/lib/python3.9/site-packages/chromadb/server/fastapi/__init__.py", line 606, in process_create_collection
    create = CreateCollection.model_validate(orjson.loads(raw_body))
AttributeError: type object 'CreateCollection' has no attribute 'model_validate'
INFO:     [06-05-2024 22:51:40] ::1:53570 - "POST /api/v1/collections?tenant=default_tenant&database=default_database HTTP/1.1" 500
HammadB commented 2 months ago

This is a good point - @tazarov should we bump the version?

tazarov commented 2 months ago

Pydantic 1.x also has similar function to so maybe we can implement an adapter that is version-aware function to keep backward compatibility.

We've added the orjson serialization in 0.5.0 and so far @zhou9110 is the first to notice which leads me to believe that not so many people are running pydantic <2.x. Maybe bumping the pydantic range >=2.0 and ditching 1.x as part of the 0.5.x versions can be an OK approach.

However if we go 2.x route we may want to also add pydantic-settings package and refactor the config (can be done in a separate PR).

tazarov commented 2 months ago

looking at pydantic-settings package I can see that it basically requires pydantic > 2.7 https://github.com/pydantic/pydantic-settings/blob/6d25cee4bb7a6db592ca0da123c53f3d775cd1e1/pyproject.toml#L43

Using pedantic-settings as a package dependency, we're effectively forcing a pydantic>2.0 upgrade, which in @zhou9110's and other's cases might be a breaking change.

Another interesting aspect is our fastapi dep fastapi>=0.95.2, which translates:

https://github.com/tiangolo/fastapi/blob/c81e136d75f5ac4252df740b35551cf2afb4c7f1/pyproject.toml#L45

The above will cause some dependency resolution issues with older fastapi versions.

In the end, it will be a trade-off and a bit of a jerk move to force users to upgrade their deps which may come from other dependencies.

Bottom line:

pydantic>2.0 with pydantic-settings

support for both versions

@HammadB, any strong opinion on which option to go for?