Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 87 additions & 1 deletion llama_runner/config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,22 @@
import json
import logging


CONFIG_DIR = os.path.expanduser("~/.llama-runner")
CONFIG_FILE = os.path.join(CONFIG_DIR, "config.json")
LOG_FILE = os.path.join(CONFIG_DIR, "error.log")


# Ensure the log directory exists
if not os.path.exists(CONFIG_DIR):
os.makedirs(CONFIG_DIR, exist_ok=True)


# Set up logging
logging.basicConfig(filename=LOG_FILE, level=logging.ERROR,
format='%(asctime)s - %(levelname)s - %(message)s')


def ensure_config_exists():
"""
Ensures that the configuration directory and file exist.
Expand All @@ -27,6 +31,7 @@ def ensure_config_exists():
logging.error(f"Error creating config directory: {e}")
return False


if not os.path.exists(CONFIG_FILE):
try:
default_config = {
Expand All @@ -48,6 +53,7 @@ def ensure_config_exists():
return False
return True


def load_config():
"""
Loads the configuration from the JSON file.
Expand All @@ -56,28 +62,31 @@ def load_config():
if not ensure_config_exists():
return {}


try:
with open(CONFIG_FILE, "r") as f:
config = json.load(f)

# Ensure default_runtime and concurrentRunners exist
if "default_runtime" not in config:
config["default_runtime"] = "llama-server"
if "concurrentRunners" not in config:
config["concurrentRunners"] = 1


# Ensure proxies section and its sub-keys exist with defaults
proxies_config = config.get("proxies", {})
if not isinstance(proxies_config, dict): # Handle case where 'proxies' might exist but not as a dict
proxies_config = {}


ollama_proxy_config = proxies_config.get("ollama", {})
if not isinstance(ollama_proxy_config, dict):
ollama_proxy_config = {}
if "enabled" not in ollama_proxy_config:
ollama_proxy_config["enabled"] = True
proxies_config["ollama"] = ollama_proxy_config


lmstudio_proxy_config = proxies_config.get("lmstudio", {})
if not isinstance(lmstudio_proxy_config, dict):
lmstudio_proxy_config = {}
Expand All @@ -89,6 +98,7 @@ def load_config():

config["proxies"] = proxies_config


# Ensure logging section and its sub-keys exist with defaults
logging_config = config.get("logging", {})
if not isinstance(logging_config, dict): # Handle case where 'logging' might exist but not as a dict
Expand Down Expand Up @@ -134,13 +144,88 @@ def load_config():
print("Warning: Config: 'llama-runtimes' key exists but is not a dictionary. Ignoring.")
# If 'llama-runtimes' is not in config or is None, it's handled gracefully (no changes made to it)



raw_audio = config.get("audio")
if isinstance(raw_audio, dict):
# Process runtimes
raw_runtimes = raw_audio.get("runtimes")
processed_runtimes = {}
if isinstance(raw_runtimes, dict):
for name, details in raw_runtimes.items():
if isinstance(details, dict):
runtime_path = details.get("runtime")
if isinstance(runtime_path, str) and runtime_path.strip():
processed_runtimes[name] = {
"runtime": runtime_path.strip()
}
else:
logging.warning(f"Config: Audio runtime '{name}' has invalid or empty 'runtime' path. Skipping.")
print(f"Warning: Config: Audio runtime '{name}' has invalid or empty 'runtime' path. Skipping.")
else:
logging.warning(f"Config: Audio runtime '{name}' details should be a dictionary. Skipping.")
print(f"Warning: Config: Audio runtime '{name}' details should be a dictionary. Skipping.")
elif raw_runtimes is not None:
logging.warning("Config: 'audio.runtimes' exists but is not a dictionary. Ignoring.")
print("Warning: Config: 'audio.runtimes' exists but is not a dictionary. Ignoring.")


# Process models
raw_models = raw_audio.get("models")
processed_models = {}
if isinstance(raw_models, dict):
for model_name, model_info in raw_models.items():
if isinstance(model_info, dict):
model_path = model_info.get("model_path")
runtime = model_info.get("runtime")
parameters = model_info.get("parameters", {})
if isinstance(model_path, str) and model_path.strip():
if isinstance(parameters, dict):
processed_models[model_name] = {
"model_path": model_path.strip(),
"runtime": runtime,
"parameters": parameters
}
else:
logging.warning(f"Config: Parameters for model '{model_name}' should be a dictionary. Using empty dict instead.")
print(f"Warning: Config: Parameters for model '{model_name}' should be a dictionary. Using empty dict instead.")
processed_models[model_name] = {
"model_path": model_path.strip(),
"runtime": runtime,
"parameters": {}
}
else:
logging.warning(f"Config: Model '{model_name}' has invalid or empty 'model_path'. Skipping.")
print(f"Warning: Config: Model '{model_name}' has invalid or empty 'model_path'. Skipping.")
else:
logging.warning(f"Config: Model entry '{model_name}' is not a dictionary. Skipping.")
print(f"Warning: Config: Model entry '{model_name}' is not a dictionary. Skipping.")
elif raw_models is not None:
logging.warning("Config: 'audio.models' exists but is not a dictionary. Ignoring.")
print("Warning: Config: 'audio.models' exists but is not a dictionary. Ignoring.")


# Update the audio section in config
config["audio"] = {
"runtimes": processed_runtimes,
"models": processed_models
}


elif raw_audio is not None:
logging.warning("Config: 'audio' key exists but is not a dictionary. Ignoring.")
print("Warning: Config: 'audio' key exists but is not a dictionary. Ignoring.")



print(f"Loaded config (processed): {config}") # Print loaded config
return config
except (OSError, json.JSONDecodeError) as e:
print(f"Error loading config file: {e}")
logging.error(f"Error loading config file: {e}")
return {}


def calculate_system_fingerprint(config: dict) -> str:
"""Calculates a 16-character hash of the configuration parameters."""
import hashlib
Expand All @@ -149,6 +234,7 @@ def calculate_system_fingerprint(config: dict) -> str:
hash_object = hashlib.md5(config_str.encode())
return hash_object.hexdigest()[:16]


if __name__ == '__main__':
# Example usage:
config = load_config()
Expand Down
7 changes: 6 additions & 1 deletion llama_runner/headless_service_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ def _initialize_services(self):
# Initialize LlamaRunnerManager
self.llama_runner_manager = LlamaRunnerManager(
models=self.models_specific_config, # This is app_config['models']
llama_runtimes=self.app_config.get('llama-runtimes', {}), # Ensure correct key
llama_runtimes=self.app_config.get('llama-runtimes', {}), # Ensure correct key,
audio_config=self.app_config.get('audio', {}),
default_runtime=self.app_config.get('default_runtime', 'llama-server'), # Ensure correct key and default
model_status_widgets={} # No UI widgets in headless mode
)
Expand All @@ -48,6 +49,7 @@ def _initialize_services(self):

# Get proxy and logging settings from the unified config
proxies_config = self.app_config.get('proxies', {})
audio_config = self.app_config.get('audio', {})
ollama_proxy_settings = proxies_config.get('ollama', {})
lmstudio_proxy_settings = proxies_config.get('lmstudio', {})
logging_settings = self.app_config.get('logging', {})
Expand All @@ -63,7 +65,9 @@ def _initialize_services(self):
self.ollama_proxy = OllamaProxyThread(
all_models_config=self.models_specific_config,
runtimes_config=self.app_config.get('llama-runtimes', {}),
audio_config=audio_config,
is_model_running_callback=self.llama_runner_manager.is_llama_runner_running,
is_model_whisper_running=self.llama_runner_manager.is_whisper_runner_running,
get_runner_port_callback=self.llama_runner_manager.get_runner_port,
request_runner_start_callback=self.llama_runner_manager.request_runner_start,
prompt_logging_enabled=prompt_logging_enabled,
Expand All @@ -80,6 +84,7 @@ def _initialize_services(self):
self.lmstudio_proxy = LMStudioProxyThread( # Using the aliased FastAPIProxyThread
all_models_config=self.models_specific_config,
runtimes_config=self.app_config.get('llama-runtimes', {}),
audio_config=audio_config,
is_model_running_callback=self.llama_runner_manager.is_llama_runner_running,
get_runner_port_callback=self.llama_runner_manager.get_runner_port,
request_runner_start_callback=self.llama_runner_manager.request_runner_start,
Expand Down
1 change: 0 additions & 1 deletion llama_runner/llama_cpp_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ async def start(self):
command.append(f"--{arg_name}")
command.append(str(value))

print(f"Starting llama.cpp server with command: {' '.join(command)}")
logging.info(f"Starting llama.cpp server with command: {' '.join(command)}")

# Clear the output buffer before starting a new process
Expand Down
Loading