|
1 | 1 | import asyncio |
2 | 2 | import logging |
3 | 3 | import os |
4 | | -import sys |
5 | 4 | from asyncio import Lock |
6 | 5 |
|
7 | 6 | import tqdm |
8 | | -from chromadb.api.types import IncludeEnum |
9 | | -from chromadb.errors import InvalidCollectionException |
10 | 7 |
|
11 | 8 | 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 |
14 | 18 |
|
15 | 19 | logger = logging.getLogger(name=__name__) |
16 | 20 |
|
17 | 21 |
|
18 | 22 | 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) |
40 | 25 |
|
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() |
45 | 27 |
|
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 |
54 | 31 |
|
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) |
60 | 33 |
|
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 |
87 | 61 |
|
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() |
91 | 63 |
|
92 | | - show_stats(configs, stats) |
93 | | - return 0 |
| 64 | + show_stats(configs=configs, stats=stats) |
| 65 | + return 0 |
0 commit comments