Files
wehub-resource-sync 86db9aae8e
Documentation / build (push) Waiting to run
Documentation / deploy (push) Blocked by required conditions
chore: import upstream snapshot with attribution
2026-07-13 13:34:55 +08:00

1325 lines
46 KiB
Python

# Copyright 2023-2026 llmware
# 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.
"""The configs module implements the configuration logic using classes for llmware.
The implementation includes the central llmware config class LLMWareConfig, and the config classes for all
supported text index databases and vector databases.
"""
import os
import platform
import logging
try:
from colorama import Fore
COLOR_WHITE = Fore.WHITE
COLOR_RESET = Fore.RESET
COLOR_RED = Fore.RED
COLOR_YELLOW = Fore.YELLOW
COLOR_GREEN= Fore.GREEN
COLOR_BLUE = Fore.BLUE
except:
COLOR_WHITE = ""
COLOR_RESET = ""
COLOR_RED = ""
COLOR_YELLOW = ""
COLOR_GREEN = ""
COLOR_BLUE = ""
class CustomFormatter(logging.Formatter):
""" CustomFormatter - Configuration of global logging formatting - WIP. """
format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s (%(filename)s:%(lineno)d)"
FORMATS = {
logging.DEBUG: COLOR_GREEN + format + COLOR_RESET,
logging.INFO: COLOR_WHITE + format + COLOR_RESET,
logging.WARNING: COLOR_YELLOW + format + COLOR_YELLOW,
logging.ERROR: COLOR_RED + format + COLOR_RESET,
logging.CRITICAL: COLOR_RED + format + COLOR_RESET,
25: COLOR_BLUE + format + COLOR_RESET
}
def format(self, record):
log_fmt = self.FORMATS.get(record.levelno)
formatter = logging.Formatter(log_fmt)
return formatter.format(record)
class LLMWareConfig:
"""LLMWare global configuration object - use set/get to update """
if platform.system() == "Windows":
_base_fp = {"home_path": os.environ.get("USERPROFILE"),
"llmware_path_name": "llmware_data" + os.sep}
else:
_base_fp = {"home_path": os.environ.get("HOME"),
"llmware_path_name": "llmware_data" + os.sep}
_fp = {"model_repo_path_name": "model_repo" + os.sep,
"library_path_name": "accounts" + os.sep,
"input_path_name": "input_channel" + os.sep,
"parser_path_name": "parser_history" + os.sep,
"query_path_name": "query_history" + os.sep,
"prompt_path_name": "prompt_history" + os.sep,
"tmp_path_name": "tmp" + os.sep}
# note: two alias for postgres vector db - "postgres" and "pg_vector" are the same
_supported = {"vector_db": ["chromadb", "neo4j", "milvus", "pg_vector", "postgres", "redis", "pinecone", "faiss", "qdrant", "mongo_atlas","lancedb"],
"collection_db": ["mongo", "postgres", "sqlite"],
"table_db": ["postgres", "sqlite"]}
# change 0.4.0: default collection_db set to "sqlite"
_conf = {"collection_db": "sqlite",
"vector_db": "milvus",
"table_db": "sqlite",
"debug_mode": 0,
"llmware_sample_files_bucket": "llmware-sample-docs",
"llmware_public_models_bucket": "llmware-public-models",
"shared_lib_path": os.path.join(os.path.dirname(os.path.realpath(__file__)), "lib"),
"logging_level": logging.WARNING,
"logging_format": COLOR_WHITE + '%(levelname)-4s: %(message)s' + COLOR_RESET,
"logging_level_by_module": {"llmware.embeddings": 20, "llmware.models": 20, "llmware.agents":20,
"llmware.prompts": 20, "llmware.resources": 20,
"llmware.setup": 20, "llmware.parsers": 20},
"agent_writer_mode": "screen",
"agent_log_file": "agent_log.txt",
"model_register": {"module": "llmware.models", "method": "register"},
"model_post_init": {"module": "llmware.models", "method": "post_init"},
"model_validate": {"module": "llmware.models", "method": "validate"},
"model_preview": {"module": "llmware.models", "method": "preview"},
"model_fetch": {"module": "llmware.models", "method": "pull_snapshot_from_hf"},
"model_router": {"module": "llmware.models", "method": "route_optimizer"},
"apply_model_load_router": False,
"apply_default_fetch_override": False
}
@classmethod
def get_config(cls,name):
"""Get config value by key"""
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls,name, value):
"""Set config value by key"""
cls._conf[name] = value
@classmethod
def get_home(cls):
"""Get home directory path"""
return cls._base_fp["home_path"]
@classmethod
def set_home(cls, new_value):
"""Set home directory path"""
cls._base_fp["home_path"] = new_value
@classmethod
def set_llmware_path_name(cls, new_value):
"""Set main path name for llmware data path"""
cls._base_fp["llmware_path_name"] = new_value
@classmethod
def get_fp_name(cls,file_path):
"""Get file path from configs"""
if file_path in cls._fp:
return cls._fp[file_path]
raise ConfigKeyException(file_path)
@classmethod
def set_fp_name(cls,file_path, new_value):
"""Set file path in configs"""
if file_path in cls._fp:
cls._fp.update({file_path, new_value})
@classmethod
def get_llmware_path(cls):
"""Get llmware absolute folder directory path"""
return os.path.join(cls._base_fp["home_path"], cls._base_fp["llmware_path_name"])
@classmethod
def get_library_path(cls):
"""Get library absolute folder directory path"""
return os.path.join(cls._base_fp["home_path"], cls._base_fp["llmware_path_name"], cls._fp["library_path_name"])
@classmethod
def get_model_repo_path(cls):
"""Get model repo absolute folder directory path"""
return os.path.join(cls._base_fp["home_path"],cls._base_fp["llmware_path_name"], cls._fp["model_repo_path_name"])
@classmethod
def get_input_path(cls):
"""Get input absolute folder directory path"""
return os.path.join(cls._base_fp["home_path"], cls._base_fp["llmware_path_name"], cls._fp["input_path_name"])
@classmethod
def get_parser_path(cls):
"""Get parser absolute folder directory path"""
return os.path.join(cls._base_fp["home_path"],cls._base_fp["llmware_path_name"], cls._fp["parser_path_name"])
@classmethod
def get_query_path(cls):
"""Get query absolute folder directory path"""
return os.path.join(cls._base_fp["home_path"],cls._base_fp["llmware_path_name"], cls._fp["query_path_name"])
@classmethod
def get_prompt_path(cls):
"""Get prompt absolute folder directory path"""
return os.path.join(cls._base_fp["home_path"], cls._base_fp["llmware_path_name"],cls._fp["prompt_path_name"])
@classmethod
def get_tmp_path(cls):
"""Get tmp absolute folder directory path"""
return os.path.join(cls._base_fp["home_path"], cls._base_fp["llmware_path_name"],cls._fp["tmp_path_name"])
@classmethod
def get_path(cls, name):
"""Get absolute folder path by name"""
if name+"_name" in cls._fp:
return os.path.join(cls._base_fp["home_path"], cls._base_fp["llmware_path_name"],
cls._fp[name+"_name"])
raise LLMWareException(message=f"LLMWareConfig - get_path - could not find home path - {name}")
@classmethod
def get_active_db(cls):
""" Returns the current selected default database for Library text collections """
return cls._conf["collection_db"]
@classmethod
def set_active_db(cls, new_db):
""" Sets the default database for Library text collections """
if new_db in cls._supported["collection_db"]:
cls._conf["collection_db"] = new_db
else:
raise LLMWareException(message=f"LLMWareConfig - set_active_db - selected "
f"db is not supported - {new_db}")
@classmethod
def get_vector_db(cls):
""" Gets the default vector database selection """
return cls._conf["vector_db"]
@classmethod
def set_vector_db(cls, vector_db_name):
""" Sets the default vector database """
if vector_db_name in cls._supported["vector_db"]:
cls._conf["vector_db"] = vector_db_name
else:
raise LLMWareException(message=f"LLMWareConfig - set_vector_db - "
f"selected db is not supported - {vector_db_name}")
@classmethod
def get_table_db(cls):
""" Gets the default table (SQL) database """
return cls._conf["table_db"]
@classmethod
def set_table_db(cls, table_db_name):
""" Sets the default table (SQL) database """
if table_db_name in cls._supported["table_db"]:
cls._conf["table_db"] = table_db_name
else:
raise LLMWareException(message=f"LLMWareConfig - set_table_db - "
f"selected db is not supported - {table_db_name}")
@classmethod
def get_supported_vector_db(cls):
return cls._supported["vector_db"]
@classmethod
def get_supported_collection_db(cls):
return cls._supported["collection_db"]
@classmethod
def get_supported_table_db(cls):
return cls._supported["table_db"]
@classmethod
def setup_llmware_workspace (cls):
"""Set up llmware main working folder directory"""
# create file structure - configured through use of env variable ["HOME"]
home_path = cls._base_fp["home_path"]
if not os.path.exists(home_path):
raise LLMWareException(message=f"LLMWareConfig - get_path - could not find home path - {home_path}")
llmware_path = cls.get_llmware_path()
if not os.path.exists(llmware_path):
os.mkdir(llmware_path)
library_path = cls.get_library_path()
if not os.path.exists(library_path):
os.mkdir(library_path)
input_path = cls.get_input_path()
if not os.path.exists(input_path):
os.mkdir(input_path)
model_repo_path = cls.get_model_repo_path()
if not os.path.exists(model_repo_path):
os.mkdir(model_repo_path)
parser_path = cls.get_parser_path()
if not os.path.exists(parser_path):
os.mkdir(parser_path)
query_path = cls.get_query_path()
if not os.path.exists(query_path):
os.mkdir(query_path)
prompt_path = cls.get_prompt_path()
if not os.path.exists(prompt_path):
os.mkdir(prompt_path)
tmp_path = cls.get_tmp_path()
if not os.path.exists(tmp_path):
os.mkdir(tmp_path)
# set 'open' read/write directory permissions, e.g., chmod 777
os.chmod(library_path, 0o777)
os.chmod(input_path, 0o777)
os.chmod(model_repo_path, 0o777)
os.chmod(parser_path, 0o777)
os.chmod(query_path, 0o777)
os.chmod(prompt_path, 0o777)
os.chmod(tmp_path, 0o777)
return 0
@classmethod
def create_new_account(cls, account_name):
"""Sets up a new secondary account file structure"""
# Useful in deploying llmware in multi-account and multi-user applications
# note: assumes account user permissions to be implemented in calling application
library_path = cls.get_library_path()
new_account_path = os.path.join(library_path, account_name)
os.mkdir(new_account_path)
return 0
@classmethod
def get_db_uri_string(cls):
""" Retrieves the db_uri_string for the current active default text collection database """
active_db = cls.get_active_db()
uri_string = None
if active_db == "mongo": uri_string = MongoConfig.get_uri_string()
if active_db == "postgres": uri_string = PostgresConfig.get_uri_string()
if active_db == "sqlite": uri_string = SQLiteConfig.get_uri_string()
return uri_string
@classmethod
def get_db_configs(cls):
""" Gets the db configs for the selected default text collection database """
active_db = cls.get_active_db()
configs = {}
if active_db == "mongo": configs = MongoConfig.get_db_configs()
if active_db == "postgres": configs = PostgresConfig.get_db_configs()
if active_db == "sqlite": configs = SQLiteConfig.get_db_configs()
return configs
@classmethod
def get_db_user_name(cls):
""" Get the db user name for the default db """
active_db = cls.get_active_db()
user_name = ""
if active_db == "mongo": user_name = MongoConfig.get_user_name()
if active_db == "postgres": user_name = PostgresConfig.get_user_name()
if active_db == "sqlite": user_name = SQLiteConfig.get_user_name()
return user_name
@classmethod
def get_db_pw(cls):
""" Get the db password for the default db """
active_db = cls.get_active_db()
pw = ""
if active_db == "mongo": pw = MongoConfig.get_db_pw()
if active_db == "postgres": pw = PostgresConfig.get_db_pw()
if active_db == "sqlite": pw = SQLiteConfig.get_db_pw()
return pw
@classmethod
def get_logging_level(cls):
return cls._conf["logging_level"]
@classmethod
def set_logging_level(cls, log_level_str):
log_level_str = log_level_str.upper()
accepted_levels = ["INFO", "DEBUG", "WARNING", "ERROR","CRITICAL"]
if log_level_str not in accepted_levels:
raise ConfigKeyException(f"Exception: proposed logging level does not correspond to a "
f"recognized level, e.g., {accepted_levels}")
if log_level_str == "INFO": new_level = logging.INFO
elif log_level_str == "DEBUG": new_level = logging.DEBUG
elif log_level_str == "WARNING": new_level = logging.WARNING
elif log_level_str == "ERROR": new_level = logging.ERROR
elif log_level_str == "CRITICAL": new_level = logging.CRITICAL
else:
new_level = logging.DEBUG
cls._conf["logging_level"] = new_level
return new_level
@classmethod
def get_logging_format(cls):
return cls._conf["logging_format"]
@classmethod
def set_logging_format(cls, formatting_str):
cls._conf["logging_format"] = formatting_str
return True
@classmethod
def get_logging_level_by_module(cls, module):
if module in cls._conf["logging_level_by_module"]:
return cls._conf["logging_level_by_module"][module]
else:
raise ConfigKeyException(module)
@classmethod
def set_logging_level_by_module(cls, module, level):
if module in cls._conf["logging_level_by_module"]:
cls._conf["logging_level_by_module"][module] = level
else:
cls._conf["logging_level_by_module"].update({module: level})
return cls._conf["logging_level_by_module"]
@classmethod
def get_agent_writer_mode(cls):
return cls._conf["agent_writer_mode"]
@classmethod
def set_agent_writer_mode(cls, mode):
if mode in ["screen", "file", "off"]:
cls._conf["agent_writer_mode"] = mode
else:
raise ConfigKeyException(mode)
return cls._conf["agent_writer_mode"]
@classmethod
def get_agent_log_file(cls):
return cls._conf["agent_log_file"]
@classmethod
def set_agent_log_file(cls, log_file_name):
cls._conf["agent_log_file"] = log_file_name
return cls._conf["agent_log_file"]
class VectorDBRegistry:
""" Registry of supported Vector DBs, and the class module used to interface with the DB. """
vector_db_list = {"milvus": {"module": "llmware.embeddings", "class": "EmbeddingMilvus"},
"chromadb": {"module": "llmware.embeddings", "class": "EmbeddingChromaDB"},
"qdrant": {"module": "llmware.embeddings", "class": "EmbeddingQdrant"},
"postgres": {"module": "llmware.embeddings", "class": "EmbeddingPGVector"},
"pg_vector": {"module": "llmware.embeddings", "class": "EmbeddingPGVector"},
"redis": {"module": "llmware.embeddings", "class": "EmbeddingRedis"},
"neo4j": {"module": "llmware.embeddings", "class": "EmbeddingNeo4j"},
"lancedb": {"module": "llmware.embeddings", "class": "EmbeddingLanceDB"},
"faiss": {"module": "llmware.embeddings", "class": "EmbeddingFAISS"},
"pinecone": {"module": "llmware.embeddings", "class": "EmbeddingPinecone"},
"mongo_atlas": {"module": "llmware.embeddings", "class": "EmbeddingMongoAtlas"}
}
@classmethod
def get_vector_db_list(cls):
""" List current view of implemented supported vector db for embeddings. """
return cls.vector_db_list
@classmethod
def add_vector_db(cls, db_name, vector_db_class, module="llmware.embeddings"):
""" Adds a vector db including the module and class. """
new_entry = {db_name: {"module": module, "class": vector_db_class}}
cls.vector_db_list.update(new_entry)
return True
logging.basicConfig(format=LLMWareConfig().get_logging_format(), level=LLMWareConfig().get_logging_level())
class ONNXConfig:
""" Configuration object for ONNXRuntime and ONNXRuntime Genai - these parameters are consumed by
the ONNXGenerativeModel class in module llmware.models. In most cases, the parameters do not
require attention, but provided for more options to adapt to particular environments and use cases. """
# note: breaking changes in the onnxruntime_genai api starting in version 0.6, which was released in
# February 2025 - if you pip install, you should get version 0.6
# if using an older version of onnxruntime_genai, then set the legacy flag to True
_conf = {"version": "0.6",
"legacy": False}
@classmethod
def get_config(cls, param):
return cls._conf[param]
@classmethod
def set_config(cls, param, value):
cls._conf[param] = value
@classmethod
def get_legacy_flag(cls):
return cls._conf["legacy"]
@classmethod
def set_legacy_flag(cls, boolean_val):
cls._conf["legacy"] = boolean_val
return boolean_val
class OVConfig:
""" Configuration object for OpenVino - these parameters are consumed by the
OVGenerativeModel class in module llmware.models. In most cases, the parameters
do not require attention, but provided for more options for performance tuning with
GPU deployment in particular. """
_conf = {"device": "GPU",
"use_ov_tokenizer": False,
"generation_version": "ov_genai_pip",
"use_gpu_if_available": True,
"cache": True,
"cache_with_model": True,
"cache_custom_path": "",
"apply_performance_hints": True,
"verbose_mode": False,
"get_token_counts": True
}
_supported_hints = ["MODEL_PRIORITY", "GPU_HOST_TASK_PRIORITY",
"GPU_QUEUE_THROTTLE", "GPU_QUEUE_PRIORITY"]
# this is a subset of useful GPU performance hints - will expand options over time
_gpu_hints = {
"MODEL_PRIORITY": "HIGH",
"GPU_HOST_TASK_PRIORITY": "HIGH",
"GPU_QUEUE_THROTTLE": "HIGH",
"GPU_QUEUE_PRIORITY": "HIGH"
}
@classmethod
def get_config(cls, param):
return cls._conf[param]
@classmethod
def set_config(cls, param, value):
cls._conf[param]= value
@classmethod
def get_gpu_hints(cls):
return cls._gpu_hints
@classmethod
def set_gpu_hint(cls, param, value):
# will add safety checks for type - most in form of "HIGH" | "MEDIUM" | "LOW"
# for more information, please see OpenVino documentation
if param in cls._supported_hints:
cls._gpu_hints[param] = value
@classmethod
def optimize_for_gpu(cls):
return cls._conf["use_gpu_if_available"]
@classmethod
def generation_version(cls):
return cls._conf["generation_version"]
class MilvusConfig:
"""Configuration object for Milvus"""
_conf = {"host": os.environ.get("MILVUS_HOST", "localhost"),
"port": os.environ.get("MILVUS_PORT", 19530),
"db_name": os.environ.get("MILVUS_DB", "default"),
"partitions": [],
# new attributes to support embedded milvus lite
"lite": False,
"lite_folder_path": LLMWareConfig().get_library_path(),
"lite_name": "milvus_lite.db",
}
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise "Key not found in configs"
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
class MongoConfig:
"""Configuration object for MongoDB"""
_conf = {"db_uri": os.environ.get("COLLECTION_DB_URI", "mongodb://localhost:27017/"),
"user_name": "",
"pw": "",
"atlas_db_uri": "",
"db_name":""}
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
@classmethod
def get_uri_string(cls):
return cls._conf["db_uri"]
@classmethod
def get_db_configs(cls):
configs = {}
for keys, values in cls._conf.items():
configs.update({keys:values})
return configs
@classmethod
def get_user_name(cls):
return cls._conf["user_name"]
@classmethod
def get_db_pw(cls):
return cls._conf["pw"]
class PostgresConfig:
"""Configuration object for Postgres DB"""
_conf = {"host": os.environ.get("USER_MANAGED_PG_HOST", "localhost"),
"port": os.environ.get("USER_MANAGED_PG_PORT", 5432),
"db_name": os.environ.get("USER_MANAGED_PG_DB_NAME", "postgres"),
"user_name": os.environ.get("USER_MANAGED_PG_USER_NAME", "postgres"),
"pw": os.environ.get("USER_MANAGED_PG_PW", ""),
# to create full copy, set "postgres_schema" to "full"
"pgvector_schema": "vector_only"}
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
@classmethod
def get_uri_string(cls):
port = cls._conf["port"]
host = cls._conf["host"]
db_name = cls._conf["db_name"]
# canonical simple format of postgres uri string
input_collection_db_path = f"postgresql://postgres@{host}:{port}/{db_name}"
# print("update: postgres get_uri_string - ", input_collection_db_path)
return input_collection_db_path
@classmethod
def get_db_configs(cls):
configs = {}
for keys, values in cls._conf.items():
configs.update({keys:values})
return configs
@classmethod
def get_user_name(cls):
return cls._conf["user_name"]
@classmethod
def get_db_pw(cls):
return cls._conf["pw"]
class RedisConfig:
"""Configuration object for Redis"""
_conf = {"host": os.environ.get("USER_MANAGED_REDIS_HOST", "localhost"),
"port": os.environ.get("USER_MANAGED_REDIS_PORT", 6379),
"user_name": "",
"pw": "",
"db_name": ""}
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
class PineconeConfig:
"""Configuration object for Pinecone"""
_conf = {
"pinecone_api_key": os.environ.get("USER_MANAGED_PINECONE_API_KEY"),
"pinecone_cloud": os.environ.get("USER_MANAGED_PINECONE_CLOUD"),
"pinecone_region": os.environ.get("USER_MANAGED_PINECONE_REGION")
}
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
class LanceDBConfig:
_conf = {'uri': '/tmp/lancedb/'}
@classmethod
def get_config(cls,name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
class SQLiteConfig:
"""Configuration object for SQLite"""
_conf = {"host": os.environ.get("USER_MANAGED_SQLITE_HOST", "localhost"),
"port": os.environ.get("USER_MANAGED_SQLITE_PORT", 6333),
"sqlite_db_folder_path": LLMWareConfig().get_library_path(),
"user_name": "",
"pw": "",
"db_name": "sqlite_llmware.db",
# add new parameter for SQLTables
"db_experimental": "sqlite_experimental.db"}
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
@classmethod
def get_uri_string (cls):
"""For SQLite the URI string is the local file with full absolute path"""
db_file = os.path.join(cls._conf["sqlite_db_folder_path"], cls._conf["db_name"])
return db_file
# new method for SQLTables DB
@classmethod
def get_uri_string_experimental_db(cls):
"""For SQLite the URI string is the local file with full absolute path"""
db_file = os.path.join(cls._conf["sqlite_db_folder_path"], cls._conf["db_experimental"])
return db_file
# end method
@classmethod
def get_db_configs(cls):
configs = {}
for keys, values in cls._conf.items():
configs.update({keys:values})
return configs
@classmethod
def get_user_name(cls):
return cls._conf["user_name"]
@classmethod
def get_db_pw(cls):
return cls._conf["pw"]
class QdrantConfig:
"""Configuration object for Qdrant"""
_conf = {
"location": os.environ.get("USER_MANAGED_QDRANT_LOCATION", None),
"host": os.environ.get("USER_MANAGED_QDRANT_HOST", None),
"url": os.environ.get("USER_MANAGED_QDRANT_URL", None),
"path": os.environ.get("USER_MANAGED_QDRANT_PATH", None),
"port": os.environ.get("USER_MANAGED_QDRANT_PORT", 6333),
"grpc_port": os.environ.get("USER_MANAGED_QDRANT_GRPC_PORT", 6334),
"https": os.environ.get("USER_MANAGED_QDRANT_HTTPS", None),
"api_key": os.environ.get("USER_MANAGED_QDRANT_API_KEY", None),
"prefix": os.environ.get("USER_MANAGED_QDRANT_PREFIX", None),
"timeout": os.environ.get("USER_MANAGED_QDRANT_TIMEOUT", None),
"prefer_grpc": os.environ.get("USER_MANAGED_QDRANT_PREFER_GRPC", False),
"force_disable_check_same_thread": os.environ.get(
"USER_MANAGED_QDRANT_FORCE_DISABLE_CHECK_SAME_THREAD", False
),
}
@classmethod
def get_config(cls, name=None):
if not name:
return cls._conf
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
class AWSS3Config:
"""Configuration object for AWS S3"""
_conf = {"access_key": os.environ.get("USER_MANAGED_S3_ACCESS_KEY", ""),
"secret_key": os.environ.get("USER_MANAGED_S#_SECRET_KEY", "")}
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
class LLMWareTableSchema:
# notes:
# 1. bigserial type for Postgres
# 2. "text" and "table" replaced with "text_block" and "table_block" in SQL DB for safety / reserved
_block = {"_id": "bigserial",
"block_ID": "integer",
"doc_ID": "integer",
"content_type": "text",
"file_type": "text",
"master_index": "integer",
"master_index2": "integer",
"coords_x": "integer",
"coords_y": "integer",
"coords_cx": "integer",
"coords_cy": "integer",
"author_or_speaker": "text",
"added_to_collection": "text",
"file_source": "text",
"table_block": "text",
"modified_date": "text",
"created_date": "text",
"creator_tool": "text",
"external_files": "text",
"text_block": "text",
"header_text": "text",
"text_search": "text",
"user_tags": "text",
"special_field1": "text",
"special_field2": "text",
"special_field3": "text",
"graph_status": "text",
"dialog": "text",
"embedding_flags": "jsonb",
"PRIMARY KEY": "(_id)"}
_library_card = {"library_name": "text",
"embedding": "json",
"knowledge_graph": "text",
"unique_doc_id": "integer",
"documents": "integer",
"blocks": "integer",
"images": "integer",
"pages": "integer",
"tables": "integer",
"account_name": "text",
"PRIMARY KEY": "(library_name)"}
# used for basic tests of db connectivity and access
_simple_test = {"library_name": "text",
"hello_number": "integer"}
_status = {"key": "text",
"summary": "text",
"start_time": "text",
"end_time": "text",
"total": "integer",
"current": "integer",
"units": "text",
"PRIMARY KEY": "(key)"}
_parser_record = {"_id": "bigserial",
"job_id": "text",
"parser_type": "text",
"library_name": "text",
"account_name": "text",
"file_name": "text",
"message": "text",
"ocr_flag": "text",
"fail_flag": "text",
"time_stamp": "text",
"PRIMARY KEY": "(_id)"}
_reserved_schema_names = ["block", "library", "status", "parser_record"]
_custom_schema_names = []
_custom_schema = []
@classmethod
def get_block_schema(cls):
return cls._block
@classmethod
def get_library_card_schema(cls):
return cls._library_card
@classmethod
def get_status_schema(cls):
return cls._status
@classmethod
def get_parser_table_schema(cls):
return cls._parser_record
@classmethod
def register_custom_schema(cls, table_name, schema,replace=False):
if table_name not in cls._custom_schema_names:
cls._custom_schema_names.append(table_name)
if table_name not in cls._custom_schema:
cls._custom_schema.append({table_name: schema})
else:
if replace:
cls._custom_schema.append({table_name: schema})
return cls._custom_schema
@classmethod
def get_custom_tables(cls):
return cls._custom_schema_names
@classmethod
def get_custom_schema(cls):
return cls._custom_schema
class Neo4jConfig:
"""Configuration object for Neo4j"""
_conf = {
'uri': os.environ.get('NEO4J_URI', 'neo4j://localhost:7687'),
'user': os.environ.get('NEO4J_USERNAME', 'neo4j'),
'password': os.environ.get('NEO4J_PASSWORD', 'neo4j'),
'database': os.environ.get('NEO4J_DATABASE', 'llmware')
}
@classmethod
def get_db_configs(cls):
configs = {}
for keys, values in cls._conf.items():
configs.update({keys:values})
return configs
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
@classmethod
def get_uri_string(cls):
return cls._conf["uri"]
@classmethod
def get_user_name(cls):
return cls._conf["user"]
@classmethod
def get_db_pw(cls):
return cls._conf["password"]
@classmethod
def get_database_name(cls):
return cls._conf["database"]
class ChromaDBConfig:
"""Configuration object for chroma.
Update - v0.2.12 - default is set to use chroma as a file-based local persistent storage.
This is a change from the previous default which used chroma as an in-memory (ephemeral) store.
Chroma can be used with or without (default) a client/server architecture. If it is used with a client/server
architecture, you have to set the authentication mechanism. The authentication mechanism can be either
username/password or token.
- env variable CHROMA_HOST is None -> not client/server mode (default),
- env variable CHROMA_HOST is set -> client/server mode
If you want to use Chroma without the client/server architecture, the env variable CHROMA_HOST has to be
None (default). In this mode, you can choose between in-memory (also called ephemeral, non-persistent) and
persistent.
Update: starting v0.2.12 - persistent path set as default to LLMWareConfigs().library_path(), which is the
same default path for a local sqlite.db
- to change to in-memory / non-persistent, set this config to None
- e.g., ChromaDBConfig().set_config("persistent_path", None)
If you want to use Chroma in client/server mode, the env variable CHROMA_HOST needs to be set. In addition,
you have to set
- env variable CHROMA_SERVER_AUTH_PROVIDER, and
- env variable CHROMA_SERVER_AUTH_CREDENTIALS_PROVIDER
the value depends on the authentication mechanism you want to use.
For more information, please visit https://docs.trychroma.com/getting-started
"""
_conf = {
# update - v0.2.12 -> collection on ChromaDB corresponds with the LLMWare library name
# 'collection': os.environ.get('CHROMA_COLLECTION', 'llmware'),
#
# update - v0.2.12 -> by default, persistent path set to make chroma persistent.
# If this is None, then an in-memory only chroma instance will be created.
#
'persistent_path': LLMWareConfig().get_library_path(),
#
# Configs below are only relevant when chromadb is run in client/server mode.
#
'host': os.environ.get('CHROMA_HOST', None),
'port': os.environ.get('CHROMA_PORT', 8000),
'ssl': os.environ.get('CHROMA_SSL', False),
'headers': os.environ.get('CHROMA_HEADERS', {}),
# The provider decides whether we use authentication via username and password, or via a token.
# - For the username and password, this has to be set to chromadb.auth.basic.BasicAuthServerProvider
# - For the token, this has to be set to chromadb.auth.token.TokenAuthServerProvider
'auth_provider': os.environ.get('CHROMA_SERVER_AUTH_PROVIDER', None),
# The credential provider supplies the username and password or the token. This setting hence
# depends on the variable just above.
# - For the username and password, this has to be set to chromadb.auth.providers.HtpasswdFileServerAuthCredentialsProvider
# - For the token, this has to be set to chromadb.auth.token.TokenAuthServerProvider
'auth_credentials_provider': os.environ.get('CHROMA_SERVER_AUTH_CREDENTIALS_PROVIDER', None),
# Settings for authentication via username and password.
'user': os.environ.get('CHROMA_USERNAME', 'admin'),
'password': os.environ.get('CHROMA_PASSWORD', 'admin'),
'auth_credentials_file': os.environ.get('CHROMA_SERVER_AUTH_CREDENTIALS_FILE', 'server.htpasswd'),
# Settings for authentication via token.
'auth_credentials': os.environ.get('CHROMA_SERVER_AUTH_CREDENTIALS', None),
'auth_token_transport_header': os.environ.get('CHROMA_SERVER_AUTH_TOKEN_TRANSPORT_HEADER', None),
}
@classmethod
def get_db_configs(cls):
configs = {}
for keys, values in cls._conf.items():
configs.update({keys:values})
return configs
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
@classmethod
def get_uri_string(cls):
return cls._conf["uri"]
@classmethod
def get_user_name(cls):
return cls._conf["user"]
@classmethod
def get_db_pw(cls):
return cls._conf["password"]
@classmethod
def get_auth_provider(cls):
return cls._conf["auth_provider"]
@classmethod
def get_auth_credentials_provider(cls):
return cls._conf["auth_credentials_provider"]
@classmethod
def get_auth_credentials_file(cls):
return cls._conf["auth_credentials_file"]
@classmethod
def get_auth_credentials(cls):
return cls._conf["auth_credentials"]
@classmethod
def get_auth_token_transport_header(cls):
return cls._conf["auth_token_transport_header"]
class OpenAIConfig:
"""Configuration object for OpenAI - primarily for configuring Azure OpenAI credentials.
Primary use is to setup an AzureOpenAI client that will be used in place of the standard
OpenAI client.
Within LLMWare, the OpenAI model classes will check this config before creating a new OpenAI client.
If there is a client already established in _conf["openai_client"], that client will be used in the
inference/embedding process.
For example:
# create your AzureOpenAI client
from openai import AzureOpenAI
client = AzureOpenAI(
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
api_key=os.getenv("AZURE_OPENAI_API_KEY"),
api_version="2024-08-01-preview")
# add that client to the OpenAIConfig:
OpenAIConfig().set_azure_client(client)
"""
_conf = {"openai_client": None,
"api_key": None, # placeholder / not used currently
"api_version": None, # placeholder / not used currently
"use_azure_endpoint": False} # placeholder / not used currently
_azure_model_name_maps = {
}
# useful mapping for Azure OpenAI default model names
# e.g., Azure removes the "."
_azure_strip_dot = True
@classmethod
def get_config(cls, name):
if name in cls._conf:
return cls._conf[name]
raise ConfigKeyException(name)
@classmethod
def set_config(cls, name, value):
cls._conf[name] = value
@classmethod
def set_azure_client(cls, azure_client):
cls._conf["openai_client"] = azure_client
@classmethod
def get_azure_client(cls):
return cls._conf["openai_client"]
@classmethod
def get_azure_model_name(cls, model_name):
""" Azure OpenAI default names remove '.' from OpenAI names. """
if cls._azure_strip_dot:
azure_name = model_name.replace(".","")
else:
azure_name = model_name
return azure_name
@classmethod
def set_azure_strip_dot(cls, setting):
if isinstance(setting,bool):
cls._azure_strip_dot = setting
class LLMWareException(Exception):
""" Base exception class in LLMWare. """
__module__ = 'llmware'
def __init__(self, message="An unspecified error occurred"):
super().__init__(message)
self.message = message
class DependencyNotInstalledException(LLMWareException):
def __init__(self, required_library_dependency):
message = f"'{required_library_dependency}' needs to be installed to use this function."
super().__init__(message)
class ModelNotFoundException(LLMWareException):
def __init__(self, model_name):
message = f"'{model_name}' could not be located"
super().__init__(message)
class ConfigKeyException(LLMWareException):
def __init__(self, config_key):
message = f"'{config_key}' is not a valid configuration key."
super().__init__(message)
class ModuleNotFoundException(LLMWareException):
def __init__(self, module_name):
message = (f"Module '{module_name}' could not be located. Please confirm the file path and extension. "
f"\n--There may be a missing binary, or an unsupported "
f"operating system platform.\n--Binaries are shipped in LLMWare for Mac (Metal), Windows (x86), "
f"and Linux (x86).\n--Please try re-installing and check documentation or raise issue "
f"at main Github repository at: https://www.github.com/llmware-ai/llmware.git.")
super().__init__(message)
class GGUFLibNotLoadedException(LLMWareException):
""" Exception raised when GGUF Lib back-end can not be loaded successfully. Exception tries to be
more helpful in sharing suggestions for potential causes and remedies. """
def __init__(self, module_name, os_platform, use_gpu, _lib_path, custom_path):
# over time, may add more details by os_platform
if custom_path:
message = (f"GGUF lib from custom path - '{_lib_path}' could not be successfully loaded. Please "
f"check that the lib is a llama_cpp back-end binary. Assuming that it is a valid build,"
f"then the most likely cause of the error is that the back-end binary was compiled "
f"with instructions not compatible with the current system.")
else:
if not use_gpu:
message = (f"GGUF lib '{module_name}' could not be successfully loaded from shared library. This is "
f"most likely because the prepackaged binary does not match your OS configuration. "
f"LLMWare ships with 6 pre-built GGUF back-ends for Mac (Metal, Metal-no-acc), Windows (x86, CUDA), and "
f"Linux (x86, CUDA). These binaries depend upon low-level instruction capabilities provided"
f"by the processor and OS, and for Windows and Linux assume that AVX and AVX2 will be "
f"enabled. Useful debugging tips:\n--Ensure llmware 0.2.4+ installed, and if cloned from "
f"repository that all of the libs were fully updated"
f"\n--Linux - Ubuntu 20+ and GLIBC 2.31+ (will likely not run if GLIBC < 2.31); "
f"\n--Check CPU capabilities using `py-cpuinfo' library, which will generate a nice "
f"dictionary view of supported OS capabilities.\n"
f"--Raise an Issue on llmware github and please share the py-cpuinfo report for your system.")
else:
message = (f"GGUF lib '{module_name}' for CUDA could not be loaded successfully, and an attempt to "
f"fall-back to the CPU based library also failed. To debug the CUDA availability, "
f"check the following:\n--`nvcc --version` to get the CUDA Driver version on your system."
f"\n--Win CUDA and Linux CUDA builds today require at least CUDA >= 12.1.\n"
f"--Pytorch is used to identify CUDA information - please check with the commands - "
f"`torch.cuda.is_available()` and `torch.version.cuda` that Pytorch is finding the "
f"right driver on your system.\n--Raise on Issue on llwmare github and share relevant "
f"system details on OS and CUDA drivers installed.")
super().__init__(message)