Skip to content

Commit

Permalink
Merge pull request #389 from rmusser01/dev
Browse files Browse the repository at this point in the history
Potential fix for #388
  • Loading branch information
rmusser01 authored Oct 22, 2024
2 parents 30d1cb4 + 64004a4 commit e9755af
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 769 deletions.
Binary file modified .gitignore
Binary file not shown.
2 changes: 1 addition & 1 deletion App_Function_Libraries/Gradio_UI/Audio_ingestion_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def update_prompts(preset_name):
inputs=preset_prompt,
outputs=[custom_prompt_input, system_prompt_input]
)

global_api_endpoints
api_name_input = gr.Dropdown(
choices=[None, "Local-LLM", "OpenAI", "Anthropic", "Cohere", "Groq", "DeepSeek", "Mistral", "OpenRouter",
"Llama.cpp", "Kobold", "Ooba", "Tabbyapi", "VLLM","ollama", "HuggingFace", "Custom-OpenAI-API"],
Expand Down
31 changes: 23 additions & 8 deletions App_Function_Libraries/Gradio_UI/Llamafile_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@
#
# Functions:

BASE_DIR = os.path.dirname(os.path.abspath(__file__))
MODELS_DIR = os.path.join(BASE_DIR, "Models")

def create_chat_with_llamafile_tab():
# Function to update model path based on selection
def on_local_model_change(selected_model: str, search_directory: str) -> str:
Expand All @@ -35,8 +38,13 @@ def update_dropdowns(search_directory: str) -> Tuple[dict, str]:
logging.debug(f"Directory does not exist: {search_directory}") # Debug print for non-existing directory
return gr.update(choices=[], value=None), "Directory does not exist."

logging.debug(f"Directory exists: {search_directory}, scanning for files...") # Confirm directory exists
model_files = get_gguf_llamafile_files(search_directory)
try:
logging.debug(f"Directory exists: {search_directory}, scanning for files...") # Confirm directory exists
model_files = get_gguf_llamafile_files(search_directory)
logging.debug("Completed scanning for model files.")
except Exception as e:
logging.error(f"Error scanning directory: {e}")
return gr.update(choices=[], value=None), f"Error scanning directory: {e}"

if not model_files:
logging.debug(f"No model files found in {search_directory}") # Debug print for no files found
Expand Down Expand Up @@ -117,15 +125,22 @@ def download_preset_model(selected_model: str) -> Tuple[str, str]:

# Option 1: Select from Local Filesystem
with gr.Row():
search_directory = gr.Textbox(label="Model Directory",
placeholder="Enter directory path(currently '.\Models')",
value=".\Models",
interactive=True)
search_directory = gr.Textbox(
label="Model Directory",
placeholder="Enter directory path (currently './Models')",
value=MODELS_DIR,
interactive=True
)

# Initial population of local models
initial_dropdown_update, _ = update_dropdowns(".\Models")
initial_dropdown_update, _ = update_dropdowns(MODELS_DIR)
logging.debug(f"Scanning directory: {MODELS_DIR}")
refresh_button = gr.Button("Refresh Models")
local_model_dropdown = gr.Dropdown(label="Select Model from Directory", choices=[])
local_model_dropdown = gr.Dropdown(
label="Select Model from Directory",
choices=initial_dropdown_update["choices"],
value=None
)
# Display selected model path
model_value = gr.Textbox(label="Selected Model File Path", value="", interactive=False)

Expand Down
1 change: 0 additions & 1 deletion App_Function_Libraries/Gradio_UI/Website_scraping_tab.py
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,6 @@ async def scrape_and_summarize_wrapper(
return convert_json_to_markdown(json.dumps({"error": f"Invalid JSON format for custom cookies: {e}"}))

if scrape_method == "Individual URLs":
# FIXME modify scrape_and_summarize_multiple to accept custom_cookies
result = await scrape_and_summarize_multiple(url_input, custom_prompt, api_name, api_key, keywords,
custom_titles, system_prompt, summarize_checkbox, custom_cookies=custom_cookies_list)
elif scrape_method == "Sitemap":
Expand Down
31 changes: 17 additions & 14 deletions App_Function_Libraries/Local_LLM/Local_LLM_Inference_Engine_Lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,13 @@
####################
# Import necessary libraries
#import atexit
import glob
import logging
import os
import re
import signal
import subprocess
import sys
import time
from pathlib import Path
from typing import List, Optional
#
# Import 3rd-pary Libraries
Expand Down Expand Up @@ -157,21 +156,25 @@ def get_gguf_llamafile_files(directory: str) -> List[str]:
"""
logging.debug(f"Scanning directory: {directory}") # Debug print for directory

# Print all files in the directory for debugging
all_files = os.listdir(directory)
logging.debug(f"All files in directory: {all_files}")

pattern_gguf = os.path.join(directory, "*.gguf")
pattern_llamafile = os.path.join(directory, "*.llamafile")
try:
dir_path = Path(directory)
all_files = list(dir_path.iterdir())
logging.debug(f"All files in directory: {[str(f) for f in all_files]}")
except Exception as e:
logging.error(f"Failed to list files in directory {directory}: {e}")
return []

gguf_files = glob.glob(pattern_gguf)
llamafile_files = glob.glob(pattern_llamafile)
try:
gguf_files = list(dir_path.glob("*.gguf"))
llamafile_files = list(dir_path.glob("*.llamafile"))

# Debug: Print the files found
logging.debug(f"Found .gguf files: {gguf_files}")
logging.debug(f"Found .llamafile files: {llamafile_files}")
logging.debug(f"Found .gguf files: {[str(f) for f in gguf_files]}")
logging.debug(f"Found .llamafile files: {[str(f) for f in llamafile_files]}")

return [os.path.basename(f) for f in gguf_files + llamafile_files]
return [f.name for f in gguf_files + llamafile_files]
except Exception as e:
logging.error(f"Error during glob operations in directory {directory}: {e}")
return []


# Initialize process with type annotation
Expand Down
175 changes: 140 additions & 35 deletions App_Function_Libraries/Local_LLM/Local_LLM_ollama.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,92 +5,197 @@
import psutil
import os
import signal

import logging
import threading
import shutil

# Configure Logging
# logging.basicConfig(
# level=logging.DEBUG, # Set to DEBUG to capture all levels of logs
# format='%(asctime)s - %(levelname)s - %(message)s',
# handlers=[
# logging.FileHandler("app.log"),
# logging.StreamHandler()
# ]
# )

def is_ollama_installed():
"""
Checks if the 'ollama' executable is available in the system's PATH.
Returns True if installed, False otherwise.
"""
return shutil.which('ollama') is not None

def get_ollama_models():
"""
Retrieves available Ollama models by executing 'ollama list'.
Returns a list of model names or an empty list if an error occurs.
"""
try:
result = subprocess.run(['ollama', 'list'], capture_output=True, text=True, check=True)
result = subprocess.run(['ollama', 'list'], capture_output=True, text=True, check=True, timeout=10)
models = result.stdout.strip().split('\n')[1:] # Skip header
return [model.split()[0] for model in models]
except subprocess.CalledProcessError:
model_names = [model.split()[0] for model in models if model.strip()]
logging.debug(f"Available Ollama models: {model_names}")
return model_names
except FileNotFoundError:
logging.error("Ollama executable not found. Please ensure Ollama is installed and in your PATH.")
return []
except subprocess.TimeoutExpired:
logging.error("Ollama 'list' command timed out.")
return []
except subprocess.CalledProcessError as e:
logging.error(f"Error executing Ollama 'list': {e}")
return []
except Exception as e:
logging.error(f"Unexpected error in get_ollama_models: {e}")
return []


def pull_ollama_model(model_name):
"""
Pulls the specified Ollama model if Ollama is installed.
"""
if not is_ollama_installed():
logging.error("Ollama is not installed.")
return "Failed to pull model: Ollama is not installed or not in your PATH."

try:
subprocess.run(['ollama', 'pull', model_name], check=True)
subprocess.run(['ollama', 'pull', model_name], check=True, timeout=300) # Adjust timeout as needed
logging.info(f"Successfully pulled model: {model_name}")
return f"Successfully pulled model: {model_name}"
except subprocess.TimeoutExpired:
logging.error(f"Pulling model '{model_name}' timed out.")
return f"Failed to pull model '{model_name}': Operation timed out."
except subprocess.CalledProcessError as e:
return f"Failed to pull model: {e}"

logging.error(f"Failed to pull model '{model_name}': {e}")
return f"Failed to pull model '{model_name}': {e}"
except FileNotFoundError:
logging.error("Ollama executable not found. Please ensure Ollama is installed and in your PATH.")
return "Failed to pull model: Ollama executable not found."
except Exception as e:
logging.error(f"Unexpected error in pull_ollama_model: {e}")
return f"Failed to pull model '{model_name}': {e}"

def serve_ollama_model(model_name, port):
"""
Serves the specified Ollama model on the given port if Ollama is installed.
"""
if not is_ollama_installed():
logging.error("Ollama is not installed.")
return "Error: Ollama is not installed or not in your PATH."

try:
# Check if a server is already running on the specified port
for conn in psutil.net_connections():
if conn.laddr.port == int(port):
return f"Port {port} is already in use. Please choose a different port."
logging.warning(f"Port {port} is already in use.")
return f"Error: Port {port} is already in use. Please choose a different port."

# Start the Ollama server
port = str(port)
os.environ["OLLAMA_HOST"] = port
cmd = f"ollama serve"
process = subprocess.Popen(cmd, shell=True)
return f"Started Ollama server for model {model_name} on port {port}. Process ID: {process.pid}"
cmd = ['ollama', 'serve', model_name, '--port', str(port)]
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
logging.info(f"Started Ollama server for model '{model_name}' on port {port}. PID: {process.pid}")
return f"Started Ollama server for model '{model_name}' on port {port}. Process ID: {process.pid}"
except FileNotFoundError:
logging.error("Ollama executable not found.")
return "Error: Ollama executable not found. Please ensure Ollama is installed and in your PATH."
except Exception as e:
logging.error(f"Error starting Ollama server: {e}")
return f"Error starting Ollama server: {e}"


def stop_ollama_server(pid):
"""
Stops the Ollama server with the specified process ID if Ollama is installed.
"""
if not is_ollama_installed():
logging.error("Ollama is not installed.")
return "Error: Ollama is not installed or not in your PATH."

try:
if platform.system() == "Windows":
os.system(f"taskkill /F /PID {pid}")
return f"Stopped Ollama server with PID {pid}"
elif platform.system() == "Linux":
os.system(f"kill {pid}")
return f"Stopped Ollama server with PID {pid}"
elif platform.system() == "Darwin":
os.system("""osascript -e 'tell app "Ollama" to quit'""")
return f"(Hopefully) Stopped Ollama server using osascript..."
subprocess.run(['taskkill', '/F', '/PID', str(pid)], check=True)
elif platform.system() in ["Linux", "Darwin"]:
os.kill(pid, signal.SIGTERM)
logging.info(f"Stopped Ollama server with PID {pid}")
return f"Stopped Ollama server with PID {pid}"
except ProcessLookupError:
logging.warning(f"No process found with PID {pid}")
return f"No process found with PID {pid}"
except Exception as e:
logging.error(f"Error stopping Ollama server: {e}")
return f"Error stopping Ollama server: {e}"


def create_ollama_tab():
"""
Creates the Ollama Model Serving tab in the Gradio interface with lazy loading.
"""
ollama_installed = is_ollama_installed()

with gr.Tab("Ollama Model Serving"):
if not ollama_installed:
gr.Markdown(
"# Ollama Model Serving\n\n"
"**Ollama is not installed or not found in your PATH. Please install Ollama to use this feature.**"
)
return # Exit early, no need to add further components

gr.Markdown("# Ollama Model Serving")

with gr.Row():
model_list = gr.Dropdown(label="Available Models", choices=get_ollama_models())
# Initialize Dropdowns with placeholders
model_list = gr.Dropdown(
label="Available Models",
choices=["Click 'Refresh Model List' to load models"],
value="Click 'Refresh Model List' to load models"
)
refresh_button = gr.Button("Refresh Model List")

with gr.Row():
new_model_name = gr.Textbox(label="Model to Pull")
new_model_name = gr.Textbox(label="Model to Pull", placeholder="Enter model name")
pull_button = gr.Button("Pull Model")

pull_output = gr.Textbox(label="Pull Status")

with gr.Row():
# FIXME - Update to update config.txt file
serve_model = gr.Dropdown(label="Model to Serve", choices=get_ollama_models())
serve_model = gr.Dropdown(
label="Model to Serve",
choices=["Click 'Refresh Model List' to load models"],
value="Click 'Refresh Model List' to load models"
)
port = gr.Number(label="Port", value=11434, precision=0)
serve_button = gr.Button("Start Server")

serve_output = gr.Textbox(label="Server Status")

with gr.Row():
pid = gr.Number(label="Server Process ID", precision=0)
pid = gr.Number(label="Server Process ID (Enter the PID to stop)", precision=0)
stop_button = gr.Button("Stop Server")

stop_output = gr.Textbox(label="Stop Status")

def update_model_lists():
"""
Retrieves the list of available Ollama models and updates the dropdowns.
"""
models = get_ollama_models()
return gr.update(choices=models), gr.update(choices=models)

refresh_button.click(update_model_lists, outputs=[model_list, serve_model])
pull_button.click(pull_ollama_model, inputs=[new_model_name], outputs=[pull_output])
serve_button.click(serve_ollama_model, inputs=[serve_model, port], outputs=[serve_output])
stop_button.click(stop_ollama_server, inputs=[pid], outputs=[stop_output])
if models:
return gr.update(choices=models, value=models[0]), gr.update(choices=models, value=models[0])
else:
return gr.update(choices=["No models found"], value="No models found"), gr.update(choices=["No models found"], value="No models found")

def async_update_model_lists():
"""
Asynchronously updates the model lists to prevent blocking.
"""
def task():
choices1, choices2 = update_model_lists()
model_list.update(choices=choices1['choices'], value=choices1.get('value'))
serve_model.update(choices=choices2['choices'], value=choices2.get('value'))
threading.Thread(target=task).start()

# Bind the refresh button to the asynchronous update function
refresh_button.click(fn=async_update_model_lists, inputs=[], outputs=[])

# Bind the pull, serve, and stop buttons to their respective functions
pull_button.click(fn=pull_ollama_model, inputs=[new_model_name], outputs=[pull_output])
serve_button.click(fn=serve_ollama_model, inputs=[serve_model, port], outputs=[serve_output])
stop_button.click(fn=stop_ollama_server, inputs=[pid], outputs=[stop_output])
5 changes: 4 additions & 1 deletion App_Function_Libraries/Utils/Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,8 @@ def load_and_log_configs():
logging.debug(f"Loaded Tabby API IP: {tabby_api_IP}")
logging.debug(f"Loaded VLLM API URL: {vllm_api_url}")

# Retrieve default API choices from the configuration file
default_api = config.get('API', 'default_api', fallback='openai')

# Retrieve output paths from the configuration file
output_path = config.get('Paths', 'output_path', fallback='results')
Expand Down Expand Up @@ -340,7 +342,8 @@ def load_and_log_configs():
'embedding_api_key': embedding_api_key,
'chunk_size': chunk_size,
'overlap': overlap
}
},
'default_api': default_api
}

except Exception as e:
Expand Down
File renamed without changes
File renamed without changes.
Loading

0 comments on commit e9755af

Please sign in to comment.