From 9174185bdc7ba0c8cb7471e59b475d111c06ab5f Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Thu, 20 Nov 2025 09:51:14 +0800 Subject: [PATCH 1/6] feat: tos_backend --- veadk/configs/database_configs.py | 44 +++++ .../backends/tos_vector_backend.py | 177 ++++++++++++++++++ 2 files changed, 221 insertions(+) create mode 100644 veadk/knowledgebase/backends/tos_vector_backend.py diff --git a/veadk/configs/database_configs.py b/veadk/configs/database_configs.py index 6de68036..f6ab714f 100644 --- a/veadk/configs/database_configs.py +++ b/veadk/configs/database_configs.py @@ -130,3 +130,47 @@ class NormalTOSConfig(BaseSettings): region: str = "cn-beijing" bucket: str + + +class TOSVectorConfig(BaseSettings): + model_config = SettingsConfigDict(env_prefix="DATABASE_TOS_VECTOR_") + + endpoint: str = "tosvectors-cn-boe.volces.com" + + region: str = "cn-beijing" + + security_token: str | None = None + + max_retry_count: int = 3 + + max_connections: int = 1024 + + connection_time: int = 10 + + enable_verify_ssl: bool = True + + dns_cache_time: int = 15 + + proxy_host: str | None = None + + proxy_port: int | None = None + + proxy_username: str | None = None + + proxy_password: str | None = None + + high_latency_log_threshold: int = 100 + + socket_timeout: int = 30 + + credentials_provider: object | None = None + + except100_continue_threshold: int = 65536 + + user_agent_product_name: str | None = None + + user_agent_soft_name: str | None = None + + user_agent_soft_version: str | None = None + + user_agent_customized_key_values: dict[str, str] | None = None diff --git a/veadk/knowledgebase/backends/tos_vector_backend.py b/veadk/knowledgebase/backends/tos_vector_backend.py new file mode 100644 index 00000000..30e29a70 --- /dev/null +++ b/veadk/knowledgebase/backends/tos_vector_backend.py @@ -0,0 +1,177 @@ +# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import json +import os + +from llama_index.core import ( + Document, + SimpleDirectoryReader, +) +from llama_index.core.schema import BaseNode +from llama_index.embeddings.openai_like import OpenAILikeEmbedding +from pydantic import Field +from tos.models2 import Vector, VectorData +from typing_extensions import Any, override + +import veadk.config # noqa E401 +from veadk.configs.database_configs import TOSVectorConfig +from veadk.configs.model_configs import EmbeddingModelConfig, NormalEmbeddingModelConfig +from veadk.knowledgebase.backends.base_backend import BaseKnowledgebaseBackend +from veadk.knowledgebase.backends.utils import get_llama_index_splitter + +try: + from tos.vector_client import VectorClient + from tos import DataType, DistanceMetricType +except ImportError: + raise ImportError( + "Please install VeADK extensions\npip install veadk-python[extensions]" + ) + + +class TosVectorKnowledgeBackend(BaseKnowledgebaseBackend): + """TOS-based backend for knowledgebase.""" + + volcengine_access_key: str | None = Field( + default_factory=lambda: os.getenv("VOLCENGINE_ACCESS_KEY") + ) + volcengine_secret_key: str | None = Field( + default_factory=lambda: os.getenv("VOLCENGINE_SECRET_KEY") + ) + tos_vector_bucket_name: str | None = Field( + default_factory=lambda: os.getenv("DATABASE_TOS_VECTOR_BUCKET") + ) + tos_vector_account_id: str | None = Field( + default_factory=lambda: os.getenv("DATABASE_TOS_VECTOR_ACCOUNT_ID") + ) + tos_vector_config: TOSVectorConfig = Field(default_factory=TOSVectorConfig) + embedding_config: EmbeddingModelConfig | NormalEmbeddingModelConfig = Field( + default_factory=EmbeddingModelConfig + ) + + def model_post_init(self, __context: Any) -> None: + self.precheck_index_naming() + self._tos_client = VectorClient( + ak=self.volcengine_access_key, + sk=self.volcengine_secret_key, + **self.tos_vector_config.model_dump(), + ) + # create_bucket and index if not exist + self._create_index() + + self._embed_model = OpenAILikeEmbedding( + model_name=self.embedding_config.name, + api_key=self.embedding_config.api_key, + api_base=self.embedding_config.api_base, + ) + + def _bucket_exists(self) -> bool: + bucket_list_resp = self._tos_client.list_vector_buckets() + bucket_list = [ + bucket.vector_bucket_name for bucket in bucket_list_resp.vector_buckets + ] + if self.tos_vector_bucket_name in bucket_list: + return True + else: + return False + + def _index_exists(self) -> bool: + index_list_resp = self._tos_client.list_indexes( + vector_bucket_name=self.tos_vector_bucket_name, + account_id=self.tos_vector_account_id, + ) + index_list = [index.index_name for index in index_list_resp.indexes] + if self.index in index_list: + return True + else: + return False + + def _create_index(self): + if not self._bucket_exists(): + self._tos_client.create_vector_bucket( + vector_bucket_name=self.tos_vector_bucket_name, + ) + if not self._index_exists(): + self._tos_client.create_index( + vector_bucket_name=self.tos_vector_bucket_name, + account_id=self.tos_vector_account_id, + index_name=self.index, + data_type=DataType.DataTypeFloat32, + dimension=self.embedding_config.dim, + distance_metric=DistanceMetricType.DistanceMetricCosine, + ) + + def precheck_index_naming(self) -> None: + pass + + def _process_and_store_documents(self, documents: list[Document]) -> bool: + nodes = self._split_documents(documents) + vectors = [] + for node in nodes: + embedding = self._embed_model.get_text_embedding(node.text) + vectors.append( + Vector( + key=node.node_id, + data=VectorData(float32=embedding), + metadata={"text": node.text, "metadata": json.dumps(node.metadata)}, + ) + ) + result = self._tos_client.put_vectors( + vector_bucket_name=self.tos_vector_bucket_name, + account_id=self.tos_vector_account_id, + index_name=self.index, + vectors=vectors, + ) + return result.status_code == 200 + + @override + def add_from_directory(self, directory: str, *args, **kwargs) -> bool: + documents = SimpleDirectoryReader(input_dir=directory).load_data() + return self._process_and_store_documents(documents) + + @override + def add_from_files(self, files: list[str], *args, **kwargs) -> bool: + documents = SimpleDirectoryReader(input_files=files).load_data() + return self._process_and_store_documents(documents) + + @override + def add_from_text(self, text: str | list[str], *args, **kwargs) -> bool: + if isinstance(text, str): + documents = [Document(text=text)] + else: + documents = [Document(text=t) for t in text] + + return self._process_and_store_documents(documents) + + @override + def search(self, query: str, top_k: int = 5) -> list[str]: + query_vector = self._embed_model.get_text_embedding(query) + + search_result = self._tos_client.query_vectors( + vector_bucket_name=self.tos_vector_bucket_name, + account_id=self.tos_vector_account_id, + index_name=self.index, + query_vector=VectorData(float32=query_vector), + top_k=top_k, + ) + + return [vector.metadata["text"] for vector in search_result.vectors] + + def _split_documents(self, documents: list[Document]) -> list[BaseNode]: + """Split document into chunks""" + nodes = [] + for document in documents: + splitter = get_llama_index_splitter(document.metadata.get("file_path", "")) + _nodes = splitter.get_nodes_from_documents([document]) + nodes.extend(_nodes) + return nodes From d2653b9593107f343fb7ca2bc7be21445c1462ac Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Thu, 20 Nov 2025 15:45:58 +0800 Subject: [PATCH 2/6] feat: tos for knowledgebase --- .../backends/tos_vector_backend.py | 64 +++++++++++-------- veadk/knowledgebase/knowledgebase.py | 9 ++- 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/veadk/knowledgebase/backends/tos_vector_backend.py b/veadk/knowledgebase/backends/tos_vector_backend.py index 30e29a70..b1f97ced 100644 --- a/veadk/knowledgebase/backends/tos_vector_backend.py +++ b/veadk/knowledgebase/backends/tos_vector_backend.py @@ -21,7 +21,6 @@ from llama_index.core.schema import BaseNode from llama_index.embeddings.openai_like import OpenAILikeEmbedding from pydantic import Field -from tos.models2 import Vector, VectorData from typing_extensions import Any, override import veadk.config # noqa E401 @@ -33,6 +32,8 @@ try: from tos.vector_client import VectorClient from tos import DataType, DistanceMetricType + from tos.exceptions import TosServerError + from tos.models2 import Vector, VectorData except ImportError: raise ImportError( "Please install VeADK extensions\npip install veadk-python[extensions]" @@ -76,25 +77,40 @@ def model_post_init(self, __context: Any) -> None: ) def _bucket_exists(self) -> bool: - bucket_list_resp = self._tos_client.list_vector_buckets() - bucket_list = [ - bucket.vector_bucket_name for bucket in bucket_list_resp.vector_buckets - ] - if self.tos_vector_bucket_name in bucket_list: - return True - else: - return False + try: + bucket_exist = self._tos_client.get_vector_bucket( + vector_bucket_name=self.tos_vector_bucket_name, + account_id=self.tos_vector_account_id, + ) + return bucket_exist.status_code == 200 + except TosServerError as e: + if e.status_code == 404: + return False + else: + raise e def _index_exists(self) -> bool: - index_list_resp = self._tos_client.list_indexes( - vector_bucket_name=self.tos_vector_bucket_name, - account_id=self.tos_vector_account_id, - ) - index_list = [index.index_name for index in index_list_resp.indexes] - if self.index in index_list: - return True - else: - return False + try: + index_exist = self._tos_client.get_index( + vector_bucket_name=self.tos_vector_bucket_name, + account_id=self.tos_vector_account_id, + index_name=self.index, + ) + return index_exist.status_code == 200 + except TosServerError as e: + if e.status_code == 404: + return False + else: + raise e + + def _split_documents(self, documents: list[Document]) -> list[BaseNode]: + """Split document into chunks""" + nodes = [] + for document in documents: + splitter = get_llama_index_splitter(document.metadata.get("file_path", "")) + _nodes = splitter.get_nodes_from_documents([document]) + nodes.extend(_nodes) + return nodes def _create_index(self): if not self._bucket_exists(): @@ -118,6 +134,8 @@ def _process_and_store_documents(self, documents: list[Document]) -> bool: nodes = self._split_documents(documents) vectors = [] for node in nodes: + if not node.text: + continue embedding = self._embed_model.get_text_embedding(node.text) vectors.append( Vector( @@ -163,15 +181,7 @@ def search(self, query: str, top_k: int = 5) -> list[str]: index_name=self.index, query_vector=VectorData(float32=query_vector), top_k=top_k, + return_metadata=True, ) return [vector.metadata["text"] for vector in search_result.vectors] - - def _split_documents(self, documents: list[Document]) -> list[BaseNode]: - """Split document into chunks""" - nodes = [] - for document in documents: - splitter = get_llama_index_splitter(document.metadata.get("file_path", "")) - _nodes = splitter.get_nodes_from_documents([document]) - nodes.extend(_nodes) - return nodes diff --git a/veadk/knowledgebase/knowledgebase.py b/veadk/knowledgebase/knowledgebase.py index 298a1a90..ba105ca8 100644 --- a/veadk/knowledgebase/knowledgebase.py +++ b/veadk/knowledgebase/knowledgebase.py @@ -51,6 +51,12 @@ def _get_backend_cls(backend: str) -> type[BaseKnowledgebaseBackend]: ) return RedisKnowledgeBackend + case "tos_vector": + from veadk.knowledgebase.backends.tos_vector_backend import ( + TosVectorKnowledgeBackend, + ) + + return TosVectorKnowledgeBackend raise ValueError(f"Unsupported knowledgebase backend: {backend}") @@ -165,7 +171,8 @@ class KnowledgeBase(BaseModel): description: str = "This knowledgebase stores some user-related information." backend: Union[ - Literal["local", "opensearch", "viking", "redis"], BaseKnowledgebaseBackend + Literal["local", "opensearch", "viking", "redis", "tos_vector"], + BaseKnowledgebaseBackend, ] = "local" backend_config: dict = Field(default_factory=dict) From 452a59a090e5bed6d8a79b4629b13b9d46fc65db Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Wed, 26 Nov 2025 19:35:40 +0800 Subject: [PATCH 3/6] feat: add tos base --- .../backends/tos_vector_backend.py | 40 +++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/veadk/knowledgebase/backends/tos_vector_backend.py b/veadk/knowledgebase/backends/tos_vector_backend.py index b1f97ced..ce7c9a12 100644 --- a/veadk/knowledgebase/backends/tos_vector_backend.py +++ b/veadk/knowledgebase/backends/tos_vector_backend.py @@ -24,8 +24,10 @@ from typing_extensions import Any, override import veadk.config # noqa E401 +from veadk.auth.veauth.utils import get_credential_from_vefaas_iam from veadk.configs.database_configs import TOSVectorConfig from veadk.configs.model_configs import EmbeddingModelConfig, NormalEmbeddingModelConfig +from veadk.integrations.ve_tos.ve_tos import VeTOS from veadk.knowledgebase.backends.base_backend import BaseKnowledgebaseBackend from veadk.knowledgebase.backends.utils import get_llama_index_splitter @@ -56,13 +58,16 @@ class TosVectorKnowledgeBackend(BaseKnowledgebaseBackend): default_factory=lambda: os.getenv("DATABASE_TOS_VECTOR_ACCOUNT_ID") ) tos_vector_config: TOSVectorConfig = Field(default_factory=TOSVectorConfig) + + session_token: str = "" + embedding_config: EmbeddingModelConfig | NormalEmbeddingModelConfig = Field( default_factory=EmbeddingModelConfig ) def model_post_init(self, __context: Any) -> None: self.precheck_index_naming() - self._tos_client = VectorClient( + self._tos_vector_client = VectorClient( ak=self.volcengine_access_key, sk=self.volcengine_secret_key, **self.tos_vector_config.model_dump(), @@ -70,6 +75,8 @@ def model_post_init(self, __context: Any) -> None: # create_bucket and index if not exist self._create_index() + self._tos_client = self._get_tos_client() + self._embed_model = OpenAILikeEmbedding( model_name=self.embedding_config.name, api_key=self.embedding_config.api_key, @@ -78,7 +85,7 @@ def model_post_init(self, __context: Any) -> None: def _bucket_exists(self) -> bool: try: - bucket_exist = self._tos_client.get_vector_bucket( + bucket_exist = self._tos_vector_client.get_vector_bucket( vector_bucket_name=self.tos_vector_bucket_name, account_id=self.tos_vector_account_id, ) @@ -91,7 +98,7 @@ def _bucket_exists(self) -> bool: def _index_exists(self) -> bool: try: - index_exist = self._tos_client.get_index( + index_exist = self._tos_vector_client.get_index( vector_bucket_name=self.tos_vector_bucket_name, account_id=self.tos_vector_account_id, index_name=self.index, @@ -114,11 +121,11 @@ def _split_documents(self, documents: list[Document]) -> list[BaseNode]: def _create_index(self): if not self._bucket_exists(): - self._tos_client.create_vector_bucket( + self._tos_vector_client.create_vector_bucket( vector_bucket_name=self.tos_vector_bucket_name, ) if not self._index_exists(): - self._tos_client.create_index( + self._tos_vector_client.create_index( vector_bucket_name=self.tos_vector_bucket_name, account_id=self.tos_vector_account_id, index_name=self.index, @@ -127,6 +134,25 @@ def _create_index(self): distance_metric=DistanceMetricType.DistanceMetricCosine, ) + def _get_tos_client(self) -> VeTOS: + volcengine_access_key = self.volcengine_access_key + volcengine_secret_key = self.volcengine_secret_key + session_token = self.session_token + + if not (volcengine_access_key and volcengine_secret_key): + cred = get_credential_from_vefaas_iam() + volcengine_access_key = cred.access_key_id + volcengine_secret_key = cred.secret_access_key + session_token = cred.session_token + + return VeTOS( + ak=volcengine_access_key, + sk=volcengine_secret_key, + session_token=session_token, + region=self.tos_vector_config.region, + bucket_name=self.tos_vector_bucket_name, + ) + def precheck_index_naming(self) -> None: pass @@ -144,7 +170,7 @@ def _process_and_store_documents(self, documents: list[Document]) -> bool: metadata={"text": node.text, "metadata": json.dumps(node.metadata)}, ) ) - result = self._tos_client.put_vectors( + result = self._tos_vector_client.put_vectors( vector_bucket_name=self.tos_vector_bucket_name, account_id=self.tos_vector_account_id, index_name=self.index, @@ -175,7 +201,7 @@ def add_from_text(self, text: str | list[str], *args, **kwargs) -> bool: def search(self, query: str, top_k: int = 5) -> list[str]: query_vector = self._embed_model.get_text_embedding(query) - search_result = self._tos_client.query_vectors( + search_result = self._tos_vector_client.query_vectors( vector_bucket_name=self.tos_vector_bucket_name, account_id=self.tos_vector_account_id, index_name=self.index, From 4de3e2f407db53400f07a279271984eed2ec157f Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Wed, 3 Dec 2025 10:20:21 +0800 Subject: [PATCH 4/6] fix: region --- veadk/configs/database_configs.py | 2 +- .../backends/tos_vector_backend.py | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/veadk/configs/database_configs.py b/veadk/configs/database_configs.py index f6ab714f..29fea0cc 100644 --- a/veadk/configs/database_configs.py +++ b/veadk/configs/database_configs.py @@ -135,7 +135,7 @@ class NormalTOSConfig(BaseSettings): class TOSVectorConfig(BaseSettings): model_config = SettingsConfigDict(env_prefix="DATABASE_TOS_VECTOR_") - endpoint: str = "tosvectors-cn-boe.volces.com" + endpoint: str = "tosvectors-cn-beijing.volces.com" region: str = "cn-beijing" diff --git a/veadk/knowledgebase/backends/tos_vector_backend.py b/veadk/knowledgebase/backends/tos_vector_backend.py index ce7c9a12..a14e9b14 100644 --- a/veadk/knowledgebase/backends/tos_vector_backend.py +++ b/veadk/knowledgebase/backends/tos_vector_backend.py @@ -30,7 +30,9 @@ from veadk.integrations.ve_tos.ve_tos import VeTOS from veadk.knowledgebase.backends.base_backend import BaseKnowledgebaseBackend from veadk.knowledgebase.backends.utils import get_llama_index_splitter +from veadk.utils.logger import get_logger +logger = get_logger() try: from tos.vector_client import VectorClient from tos import DataType, DistanceMetricType @@ -120,10 +122,10 @@ def _split_documents(self, documents: list[Document]) -> list[BaseNode]: return nodes def _create_index(self): - if not self._bucket_exists(): - self._tos_vector_client.create_vector_bucket( - vector_bucket_name=self.tos_vector_bucket_name, - ) + self._tos_vector_client.create_vector_bucket( + vector_bucket_name=self.tos_vector_bucket_name, + ) + if not self._index_exists(): self._tos_vector_client.create_index( vector_bucket_name=self.tos_vector_bucket_name, @@ -180,11 +182,19 @@ def _process_and_store_documents(self, documents: list[Document]) -> bool: @override def add_from_directory(self, directory: str, *args, **kwargs) -> bool: + # fixme + logger.warning( + "add_from_directory is not yet fully developed and may have issues such as missing images." + ) documents = SimpleDirectoryReader(input_dir=directory).load_data() return self._process_and_store_documents(documents) @override def add_from_files(self, files: list[str], *args, **kwargs) -> bool: + # fixme + logger.warning( + "add_from_files is not yet fully developed and may have issues such as missing images." + ) documents = SimpleDirectoryReader(input_files=files).load_data() return self._process_and_store_documents(documents) From 6b71edcf6addd694699fc2d4d8fefd81267201b9 Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Mon, 15 Dec 2025 15:30:49 +0800 Subject: [PATCH 5/6] fix: tos logger and config --- config.yaml.full | 5 +++ .../backends/tos_vector_backend.py | 40 ++----------------- 2 files changed, 8 insertions(+), 37 deletions(-) diff --git a/config.yaml.full b/config.yaml.full index 1e723bdd..92d95a65 100644 --- a/config.yaml.full +++ b/config.yaml.full @@ -142,6 +142,11 @@ database: mem0: base_url: # default "https://api.mem0.ai/v1", using full https url including port api_key: #api_key + tos_vector: + endpoint: tosvectors-cn-beijing.volces.com # default Volcengine TOS Vector endpoint + region: cn-beijing # default Volcengine TOS Vector region + bucket: + account_id: diff --git a/veadk/knowledgebase/backends/tos_vector_backend.py b/veadk/knowledgebase/backends/tos_vector_backend.py index a14e9b14..a7d37bd8 100644 --- a/veadk/knowledgebase/backends/tos_vector_backend.py +++ b/veadk/knowledgebase/backends/tos_vector_backend.py @@ -24,15 +24,14 @@ from typing_extensions import Any, override import veadk.config # noqa E401 -from veadk.auth.veauth.utils import get_credential_from_vefaas_iam from veadk.configs.database_configs import TOSVectorConfig from veadk.configs.model_configs import EmbeddingModelConfig, NormalEmbeddingModelConfig -from veadk.integrations.ve_tos.ve_tos import VeTOS + from veadk.knowledgebase.backends.base_backend import BaseKnowledgebaseBackend from veadk.knowledgebase.backends.utils import get_llama_index_splitter from veadk.utils.logger import get_logger -logger = get_logger() +logger = get_logger(__name__) try: from tos.vector_client import VectorClient from tos import DataType, DistanceMetricType @@ -77,27 +76,12 @@ def model_post_init(self, __context: Any) -> None: # create_bucket and index if not exist self._create_index() - self._tos_client = self._get_tos_client() - self._embed_model = OpenAILikeEmbedding( model_name=self.embedding_config.name, api_key=self.embedding_config.api_key, api_base=self.embedding_config.api_base, ) - def _bucket_exists(self) -> bool: - try: - bucket_exist = self._tos_vector_client.get_vector_bucket( - vector_bucket_name=self.tos_vector_bucket_name, - account_id=self.tos_vector_account_id, - ) - return bucket_exist.status_code == 200 - except TosServerError as e: - if e.status_code == 404: - return False - else: - raise e - def _index_exists(self) -> bool: try: index_exist = self._tos_vector_client.get_index( @@ -122,6 +106,7 @@ def _split_documents(self, documents: list[Document]) -> list[BaseNode]: return nodes def _create_index(self): + # no need to check if bucket exists, create_bucket will create it if not exist self._tos_vector_client.create_vector_bucket( vector_bucket_name=self.tos_vector_bucket_name, ) @@ -136,25 +121,6 @@ def _create_index(self): distance_metric=DistanceMetricType.DistanceMetricCosine, ) - def _get_tos_client(self) -> VeTOS: - volcengine_access_key = self.volcengine_access_key - volcengine_secret_key = self.volcengine_secret_key - session_token = self.session_token - - if not (volcengine_access_key and volcengine_secret_key): - cred = get_credential_from_vefaas_iam() - volcengine_access_key = cred.access_key_id - volcengine_secret_key = cred.secret_access_key - session_token = cred.session_token - - return VeTOS( - ak=volcengine_access_key, - sk=volcengine_secret_key, - session_token=session_token, - region=self.tos_vector_config.region, - bucket_name=self.tos_vector_bucket_name, - ) - def precheck_index_naming(self) -> None: pass From e63c049cd759bb6b1e56339837af2348b68c5c9d Mon Sep 17 00:00:00 2001 From: "hanzhi.421" Date: Mon, 15 Dec 2025 15:31:49 +0800 Subject: [PATCH 6/6] fix: one line --- veadk/knowledgebase/backends/tos_vector_backend.py | 1 + 1 file changed, 1 insertion(+) diff --git a/veadk/knowledgebase/backends/tos_vector_backend.py b/veadk/knowledgebase/backends/tos_vector_backend.py index a7d37bd8..779187c7 100644 --- a/veadk/knowledgebase/backends/tos_vector_backend.py +++ b/veadk/knowledgebase/backends/tos_vector_backend.py @@ -11,6 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + import json import os