Skip to content

Commit b35b7c0

Browse files
author
Zhe Yu
committed
feat(cli): Refactor update command to use DB adapter layer
1 parent af1454d commit b35b7c0

3 files changed

Lines changed: 56 additions & 82 deletions

File tree

Lines changed: 46 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,93 +1,65 @@
11
import asyncio
22
import logging
33
import os
4-
import sys
54
from asyncio import Lock
65

76
import tqdm
8-
from chromadb.api.types import IncludeEnum
9-
from chromadb.errors import InvalidCollectionException
107

118
from vectorcode.cli_utils import Config
12-
from vectorcode.common import ClientManager, get_collection, verify_ef
13-
from vectorcode.subcommands.vectorise import VectoriseStats, chunked_add, show_stats
9+
from vectorcode.database import get_database_connector
10+
from vectorcode.database.types import ResultType
11+
from vectorcode.database.utils import hash_file
12+
from vectorcode.subcommands.vectorise import (
13+
VectoriseStats,
14+
show_stats,
15+
vectorise_worker,
16+
)
17+
from vectorcode.subcommands.vectorise.filter import FilterManager
1418

1519
logger = logging.getLogger(name=__name__)
1620

1721

1822
async def update(configs: Config) -> int:
19-
async with ClientManager().get_client(configs) as client:
20-
try:
21-
collection = await get_collection(client, configs, False)
22-
except IndexError as e:
23-
print(
24-
f"{e.__class__.__name__}: Failed to get/create the collection. Please check your config."
25-
)
26-
return 1
27-
except (ValueError, InvalidCollectionException) as e:
28-
print(
29-
f"{e.__class__.__name__}: There's no existing collection for {configs.project_root}",
30-
file=sys.stderr,
31-
)
32-
return 1
33-
if collection is None: # pragma: nocover
34-
logger.error(
35-
f"Failed to find a collection at {configs.project_root} from {configs.db_url}"
36-
)
37-
return 1
38-
if not verify_ef(collection, configs): # pragma: nocover
39-
return 1
23+
assert configs.project_root is not None
24+
database = get_database_connector(configs)
4025

41-
metas = (await collection.get(include=[IncludeEnum.metadatas]))["metadatas"]
42-
if metas is None or len(metas) == 0: # pragma: nocover
43-
logger.debug("Empty collection.")
44-
return 0
26+
filters = FilterManager()
4527

46-
files_gen = (str(meta.get("path", "")) for meta in metas)
47-
files = set()
48-
orphanes = set()
49-
for file in files_gen:
50-
if os.path.isfile(file):
51-
files.add(file)
52-
else:
53-
orphanes.add(file)
28+
collection_files = (
29+
await database.list_collection_content(what=ResultType.document)
30+
).files
5431

55-
stats = VectoriseStats(removed=len(orphanes))
56-
collection_lock = Lock()
57-
stats_lock = Lock()
58-
max_batch_size = await client.get_max_batch_size()
59-
semaphore = asyncio.Semaphore(os.cpu_count() or 1)
32+
existing_hashes = set(i.sha256 for i in collection_files)
6033

61-
with tqdm.tqdm(
62-
total=len(files), desc="Vectorising files...", disable=configs.pipe
63-
) as bar:
64-
logger.info(f"Updating embeddings for {len(files)} file(s).")
65-
try:
66-
tasks = [
67-
asyncio.create_task(
68-
chunked_add(
69-
str(file),
70-
collection,
71-
collection_lock,
72-
stats,
73-
stats_lock,
74-
configs,
75-
max_batch_size,
76-
semaphore,
77-
)
78-
)
79-
for file in files
80-
]
81-
for task in asyncio.as_completed(tasks):
82-
await task
83-
bar.update(1)
84-
except asyncio.CancelledError: # pragma: nocover
85-
print("Abort.", file=sys.stderr)
86-
return 1
34+
files = (i.path for i in collection_files)
35+
if not configs.force:
36+
filters.add_filter(lambda x: hash_file(x) not in existing_hashes)
37+
else: # pragma: nocover
38+
logger.info("Ignoring exclude specs.")
39+
40+
files = list(filters(files))
41+
stats = VectoriseStats()
42+
stats_lock = Lock()
43+
semaphore = asyncio.Semaphore(os.cpu_count() or 1)
44+
45+
with tqdm.tqdm(
46+
total=len(files), desc="Vectorising files...", disable=configs.pipe
47+
) as bar:
48+
try:
49+
tasks = [
50+
asyncio.create_task(
51+
vectorise_worker(database, file, semaphore, stats, stats_lock)
52+
)
53+
for file in files
54+
]
55+
for task in asyncio.as_completed(tasks):
56+
await task
57+
bar.update(1)
58+
except asyncio.CancelledError:
59+
logger.warning("Abort.")
60+
return 1
8761

88-
if len(orphanes):
89-
logger.info(f"Removing {len(orphanes)} orphaned files from database.")
90-
await collection.delete(where={"path": {"$in": list(orphanes)}})
62+
await database.check_orphanes()
9163

92-
show_stats(configs, stats)
93-
return 0
64+
show_stats(configs=configs, stats=stats)
65+
return 0

src/vectorcode/subcommands/vectorise/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import hashlib
44
import logging
55
import os
6-
import sys
76
import uuid
87
from asyncio import Lock, Semaphore
98
from typing import Iterable, Optional
@@ -306,7 +305,7 @@ async def vectorise(configs: Config) -> int:
306305
await task
307306
bar.update(1)
308307
except asyncio.CancelledError:
309-
print("Abort.", file=sys.stderr)
308+
logger.warning("Abort.")
310309
return 1
311310

312311
await database.check_orphanes()

src/vectorcode/subcommands/vectorise/filter.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,14 @@ def __call__(self, files: Iterable[str]) -> Iterable[str]:
3636
f"Applying the following filters: {list(i.__name__ for i in self._filters)} to the following files ({len(files)}): {files}"
3737
)
3838

39-
for f in self._filters:
40-
files = filter(f, files)
41-
42-
if self._has_debugging(): # pragma: nocover
43-
files = tuple(files)
44-
logger.debug(f"{f.__name__} remaining items ({len(files)}): {files}")
39+
if self._filters:
40+
for f in self._filters:
41+
files = filter(f, files)
42+
43+
if self._has_debugging(): # pragma: nocover
44+
files = tuple(files)
45+
logger.debug(
46+
f"{f.__name__} remaining items ({len(files)}): {files}"
47+
)
4548

4649
return files

0 commit comments

Comments
 (0)