"""
LLM-assistant configuration API.
"""
from danoan.llm_assistant.common import exception, model
from danoan.llm_assistant.common.logging_config import setup_logging
import logging
import os
from functools import lru_cache
from pathlib import Path
import toml
from typing import Optional
setup_logging()
logger = logging.getLogger(__name__)
########################################
# Configuration files
########################################
LLM_ASSISTANT_ENV_VARIABLE = "LLM_ASSISTANT_CONFIGURATION_FOLDER"
LLM_ASSISTANT_CONFIGURATION_FILENAME = "llm-assistant-config.toml"
@lru_cache
def _get_first_configuration_filepath_within_file_hierarchy(
base_dir: Path,
) -> Optional[Path]:
"""
Traverses the parents of the working directory until
the configuration file is found.
"""
visited = set()
folders_to_visit = [base_dir]
while len(folders_to_visit) > 0:
cur_folder = folders_to_visit.pop()
if cur_folder in visited:
break
logger.debug(f"Visiting {cur_folder}")
visited.add(cur_folder)
folders_to_visit.append(cur_folder.parent)
for p in cur_folder.iterdir():
if not p.is_dir():
if p.name == LLM_ASSISTANT_CONFIGURATION_FILENAME:
return p
continue
return None
[docs]
def get_configuration_folder() -> Path:
"""
Return directory where configuration file is stored.
First checks if a configuration file exists in the file hierarchy.
If that is the case, return the directory where the configuration file
is located.
If the procedure above does not find a configuration file, return the
value stored in the environment variable LLM_ASSISTANT_ENV_VARIABLE.
If the environment variable is not defined, raise an error.
Raises:
EnvironmentVariableNotDefinedError: If the LLM_ASSISTANT_ENV_VARIABLE
is not defined and a configuration file
is not found in the file hierarchy
"""
logger.debug("Start hierarchical search of configuation file")
config_filepath = _get_first_configuration_filepath_within_file_hierarchy(
Path(os.getcwd())
)
if config_filepath:
return config_filepath.parent
logger.debug(
"Hierarchical search failed. Check environment variable {LLM_ASSISTANT_ENV_VARIABLE}"
)
if LLM_ASSISTANT_ENV_VARIABLE in os.environ:
return Path(os.environ[LLM_ASSISTANT_ENV_VARIABLE]).expanduser()
raise exception.EnvironmentVariableNotDefinedError()
[docs]
def get_environment_variable_value() -> Path:
f"""
Return the value stored by {LLM_ASSISTANT_ENV_VARIABLE}.
Raises:
EnvironmentVariableNotDefinedError: If the LLM_ASSISTANT_ENV_VARIABLE
is not defined and a configuration file
is not found in the file hierarchy
"""
if LLM_ASSISTANT_ENV_VARIABLE in os.environ:
return Path(os.environ[LLM_ASSISTANT_ENV_VARIABLE]).expanduser()
raise exception.EnvironmentVariableNotDefinedError()
[docs]
def get_configuration_filepath() -> Path:
"""
Return path to llm-assistant configuration file.
"""
return get_configuration_folder() / LLM_ASSISTANT_CONFIGURATION_FILENAME
[docs]
def get_configuration() -> model.LLMAssistantConfiguration:
"""
Return configuration object.
"""
config_filepath = get_configuration_filepath()
if not config_filepath.exists():
raise exception.ConfigurationFileDoesNotExistError()
with open(config_filepath, "r") as f:
return model.LLMAssistantConfiguration.from_dict(**toml.load(f))
[docs]
def get_prompt_configuration(prompt_name: str) -> model.PromptConfiguration:
"""
Get prompt configuration object.
It searches the prompt configuration file within the directory specified
by runner.prompt_collection_folder setting.
Raises:
FileNotFoundError: if prompt configuration file is not found.
"""
config = get_configuration()
if config.prompt:
prompt_config_filepath = get_absolute_configuration_path(
config.prompt.prompt_collection_folder / prompt_name / "config.toml"
)
else:
raise FileNotFoundError(
2, "File not found", "prompt collection folder is not specified"
)
if not prompt_config_filepath.exists():
raise FileNotFoundError(2, "File not found", prompt_config_filepath)
with open(prompt_config_filepath) as f:
return model.PromptConfiguration(**toml.load(f))
[docs]
def get_absolute_configuration_path(path: Path):
"""
Get absolute path of a configuration parameter.
Paths in the configuration file are given relative to the location of
the configuration file. This function resolves to its absolute path.
"""
if path.is_absolute():
return path
else:
return get_configuration_folder() / path