Skip to content

Commit f73a661

Browse files
authored
revert(cli): Revert "Verify chromadb connection by checking openapi title." (#246)
* Revert "fix(cli): Verify chromadb connection by checking openapi title" This reverts commit bb52bcd. * feat(cli): Improve error message for chromadb connection issues
1 parent 1820ace commit f73a661

File tree

3 files changed

+70
-33
lines changed

3 files changed

+70
-33
lines changed

src/vectorcode/common.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
import asyncio
22
import contextlib
33
import hashlib
4-
import json
54
import logging
65
import os
76
import socket
87
import subprocess
98
import sys
10-
import traceback
119
from asyncio.subprocess import Process
1210
from dataclasses import dataclass
1311
from typing import Any, AsyncGenerator, Optional
@@ -48,19 +46,16 @@ async def get_collections(
4846

4947

5048
async def try_server(base_url: str):
51-
openapi_url = f"{base_url}/openapi.json"
52-
try:
53-
async with httpx.AsyncClient() as client:
54-
response = await client.get(url=openapi_url)
55-
logger.debug(f"Fetching openapi.json from {openapi_url}: {response=}")
56-
if response.status_code != 200:
57-
return False
58-
openapi_json = json.loads(response.content.decode())
59-
if openapi_json:
60-
return openapi_json.get("info", {}).get("title", "").lower() == "chroma"
61-
except Exception as e:
62-
logger.info(f"Failed to connect to chromadb at {base_url}")
63-
logger.debug(traceback.format_exception(e))
49+
for ver in ("v1", "v2"): # v1 for legacy, v2 for latest chromadb.
50+
heartbeat_url = f"{base_url}/api/{ver}/heartbeat"
51+
try:
52+
async with httpx.AsyncClient() as client:
53+
response = await client.get(url=heartbeat_url)
54+
logger.debug(f"Heartbeat {heartbeat_url} returned {response=}")
55+
if response.status_code == 200:
56+
return True
57+
except (httpx.ConnectError, httpx.ConnectTimeout):
58+
pass
6459
return False
6560

6661

src/vectorcode/main.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import sys
55
import traceback
66

7+
import httpx
8+
79
from vectorcode import __version__
810
from vectorcode.cli_utils import (
911
CliAction,
@@ -100,8 +102,12 @@ async def async_main():
100102
from vectorcode.subcommands import files
101103

102104
return_val = await files(final_configs)
103-
except Exception:
105+
except Exception as e:
104106
return_val = 1
107+
if isinstance(e, httpx.RemoteProtocolError): # pragma: nocover
108+
e.add_note(
109+
f"Please verify that {final_configs.db_url} is a working chromadb server."
110+
)
105111
logger.error(traceback.format_exc())
106112
finally:
107113
await ClientManager().kill_servers()

tests/test_common.py

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,59 @@ def test_get_embedding_function_init_exception():
9797
)
9898

9999

100+
@pytest.mark.asyncio
101+
async def test_try_server_versions():
102+
# Test successful v1 response
103+
with patch("httpx.AsyncClient") as mock_client:
104+
mock_response = MagicMock()
105+
mock_response.status_code = 200
106+
mock_client.return_value.__aenter__.return_value.get.return_value = (
107+
mock_response
108+
)
109+
assert await try_server("http://localhost:8300") is True
110+
mock_client.return_value.__aenter__.return_value.get.assert_called_once_with(
111+
url="http://localhost:8300/api/v1/heartbeat"
112+
)
113+
114+
# Test fallback to v2 when v1 fails
115+
with patch("httpx.AsyncClient") as mock_client:
116+
mock_response_v1 = MagicMock()
117+
mock_response_v1.status_code = 404
118+
mock_response_v2 = MagicMock()
119+
mock_response_v2.status_code = 200
120+
mock_client.return_value.__aenter__.return_value.get.side_effect = [
121+
mock_response_v1,
122+
mock_response_v2,
123+
]
124+
assert await try_server("http://localhost:8300") is True
125+
assert mock_client.return_value.__aenter__.return_value.get.call_count == 2
126+
127+
# Test both versions fail
128+
with patch("httpx.AsyncClient") as mock_client:
129+
mock_response_v1 = MagicMock()
130+
mock_response_v1.status_code = 404
131+
mock_response_v2 = MagicMock()
132+
mock_response_v2.status_code = 500
133+
mock_client.return_value.__aenter__.return_value.get.side_effect = [
134+
mock_response_v1,
135+
mock_response_v2,
136+
]
137+
assert await try_server("http://localhost:8300") is False
138+
139+
# Test connection error cases
140+
with patch("httpx.AsyncClient") as mock_client:
141+
mock_client.return_value.__aenter__.return_value.get.side_effect = (
142+
httpx.ConnectError("Cannot connect")
143+
)
144+
assert await try_server("http://localhost:8300") is False
145+
146+
with patch("httpx.AsyncClient") as mock_client:
147+
mock_client.return_value.__aenter__.return_value.get.side_effect = (
148+
httpx.ConnectTimeout("Connection timeout")
149+
)
150+
assert await try_server("http://localhost:8300") is False
151+
152+
100153
def test_verify_ef():
101154
# Mocking AsyncCollection and Config
102155
mock_collection = MagicMock()
@@ -137,18 +190,10 @@ async def test_try_server_mocked(mock_socket):
137190
with patch("httpx.AsyncClient") as mock_client:
138191
mock_response = MagicMock()
139192
mock_response.status_code = 200
140-
mock_response.content = b'{"info":{"title": "Chroma"}}'
141193
mock_client.return_value.__aenter__.return_value.get.return_value = (
142194
mock_response
143195
)
144196
assert await try_server("http://localhost:8000") is True
145-
with patch("httpx.AsyncClient") as mock_client:
146-
mock_response = MagicMock()
147-
mock_response.status_code = 404
148-
mock_client.return_value.__aenter__.return_value.get.return_value = (
149-
mock_response
150-
)
151-
assert await try_server("http://localhost:8000") is False
152197

153198
# Mocking httpx.AsyncClient to raise a ConnectError
154199
with patch("httpx.AsyncClient") as mock_client:
@@ -157,15 +202,6 @@ async def test_try_server_mocked(mock_socket):
157202
)
158203
assert await try_server("http://localhost:8000") is False
159204

160-
with patch("httpx.AsyncClient") as mock_client:
161-
mock_response = MagicMock()
162-
mock_response.status_code = 200
163-
mock_response.content = b'{"info":{"title": "Dummy"}}'
164-
mock_client.return_value.__aenter__.return_value.get.return_value = (
165-
mock_response
166-
)
167-
assert await try_server("http://localhost:8000") is False
168-
169205
# Mocking httpx.AsyncClient to raise a ConnectTimeout
170206
with patch("httpx.AsyncClient") as mock_client:
171207
mock_client.return_value.__aenter__.return_value.get.side_effect = (

0 commit comments

Comments
 (0)