Spaces:
Running
Running
# Chat.py | |
# Chat functions for interacting with the LLMs as chatbots | |
import base64 | |
# Imports | |
import json | |
import logging | |
import os | |
import re | |
import tempfile | |
import time | |
from datetime import datetime | |
from pathlib import Path | |
# | |
# External Imports | |
# | |
# Local Imports | |
from App_Function_Libraries.DB.DB_Manager import get_conversation_name, save_chat_history_to_database | |
from App_Function_Libraries.LLM_API_Calls import chat_with_openai, chat_with_anthropic, chat_with_cohere, \ | |
chat_with_groq, chat_with_openrouter, chat_with_deepseek, chat_with_mistral, chat_with_huggingface | |
from App_Function_Libraries.LLM_API_Calls_Local import chat_with_aphrodite, chat_with_local_llm, chat_with_ollama, \ | |
chat_with_kobold, chat_with_llama, chat_with_oobabooga, chat_with_tabbyapi, chat_with_vllm, chat_with_custom_openai | |
from App_Function_Libraries.DB.SQLite_DB import load_media_content | |
from App_Function_Libraries.Utils.Utils import generate_unique_filename, load_and_log_configs | |
from App_Function_Libraries.Metrics.metrics_logger import log_counter, log_histogram | |
# | |
#################################################################################################### | |
# | |
# Functions: | |
def chat_api_call(api_endpoint, api_key, input_data, prompt, temp, system_message=None): | |
log_counter("chat_api_call_attempt", labels={"api_endpoint": api_endpoint}) | |
start_time = time.time() | |
if not api_key: | |
api_key = None | |
model = None | |
try: | |
logging.info(f"Debug - Chat API Call - API Endpoint: {api_endpoint}") | |
logging.info(f"Debug - Chat API Call - API Key: {api_key}") | |
logging.info(f"Debug - Chat chat_api_call - API Endpoint: {api_endpoint}") | |
if api_endpoint.lower() == 'openai': | |
response = chat_with_openai(api_key, input_data, prompt, temp, system_message) | |
elif api_endpoint.lower() == 'anthropic': | |
# Retrieve the model from config | |
loaded_config_data = load_and_log_configs() | |
model = loaded_config_data['models']['anthropic'] if loaded_config_data else None | |
response = chat_with_anthropic( | |
api_key=api_key, | |
input_data=input_data, | |
model=model, | |
custom_prompt_arg=prompt, | |
system_prompt=system_message | |
) | |
elif api_endpoint.lower() == "cohere": | |
response = chat_with_cohere( | |
api_key, | |
input_data, | |
model=model, | |
custom_prompt_arg=prompt, | |
system_prompt=system_message, | |
temp=temp | |
) | |
elif api_endpoint.lower() == "groq": | |
response = chat_with_groq(api_key, input_data, prompt, temp, system_message) | |
elif api_endpoint.lower() == "openrouter": | |
response = chat_with_openrouter(api_key, input_data, prompt, temp, system_message) | |
elif api_endpoint.lower() == "deepseek": | |
response = chat_with_deepseek(api_key, input_data, prompt, temp, system_message) | |
elif api_endpoint.lower() == "mistral": | |
response = chat_with_mistral(api_key, input_data, prompt, temp, system_message) | |
elif api_endpoint.lower() == "llama.cpp": | |
response = chat_with_llama(input_data, prompt, temp, None, api_key, system_message) | |
elif api_endpoint.lower() == "kobold": | |
response = chat_with_kobold(input_data, api_key, prompt, temp, system_message) | |
elif api_endpoint.lower() == "ooba": | |
response = chat_with_oobabooga(input_data, api_key, prompt, temp, system_message) | |
elif api_endpoint.lower() == "tabbyapi": | |
response = chat_with_tabbyapi(input_data, prompt, temp, system_message) | |
elif api_endpoint.lower() == "vllm": | |
response = chat_with_vllm(input_data, prompt, system_message) | |
elif api_endpoint.lower() == "local-llm": | |
response = chat_with_local_llm(input_data, prompt, temp, system_message) | |
elif api_endpoint.lower() == "huggingface": | |
response = chat_with_huggingface(api_key, input_data, prompt, temp) # , system_message) | |
elif api_endpoint.lower() == "ollama": | |
response = chat_with_ollama(input_data, prompt, None, api_key, temp, system_message) | |
elif api_endpoint.lower() == "aphrodite": | |
response = chat_with_aphrodite(input_data, prompt, temp, system_message) | |
elif api_endpoint.lower() == "custom-openai-api": | |
response = chat_with_custom_openai(api_key, input_data, prompt, temp, system_message) | |
else: | |
raise ValueError(f"Unsupported API endpoint: {api_endpoint}") | |
call_duration = time.time() - start_time | |
log_histogram("chat_api_call_duration", call_duration, labels={"api_endpoint": api_endpoint}) | |
log_counter("chat_api_call_success", labels={"api_endpoint": api_endpoint}) | |
return response | |
except Exception as e: | |
log_counter("chat_api_call_error", labels={"api_endpoint": api_endpoint, "error": str(e)}) | |
logging.error(f"Error in chat function: {str(e)}") | |
return f"An error occurred: {str(e)}" | |
def chat(message, history, media_content, selected_parts, api_endpoint, api_key, prompt, temperature, | |
system_message=None): | |
log_counter("chat_attempt", labels={"api_endpoint": api_endpoint}) | |
start_time = time.time() | |
try: | |
logging.info(f"Debug - Chat Function - Message: {message}") | |
logging.info(f"Debug - Chat Function - Media Content: {media_content}") | |
logging.info(f"Debug - Chat Function - Selected Parts: {selected_parts}") | |
logging.info(f"Debug - Chat Function - API Endpoint: {api_endpoint}") | |
# logging.info(f"Debug - Chat Function - Prompt: {prompt}") | |
# Ensure selected_parts is a list | |
if not isinstance(selected_parts, (list, tuple)): | |
selected_parts = [selected_parts] if selected_parts else [] | |
# logging.debug(f"Debug - Chat Function - Selected Parts (after check): {selected_parts}") | |
# Combine the selected parts of the media content | |
combined_content = "\n\n".join( | |
[f"{part.capitalize()}: {media_content.get(part, '')}" for part in selected_parts if part in media_content]) | |
# Print first 500 chars | |
# logging.debug(f"Debug - Chat Function - Combined Content: {combined_content[:500]}...") | |
# Prepare the input for the API | |
input_data = f"{combined_content}\n\n" if combined_content else "" | |
for old_message, old_response in history: | |
input_data += f"{old_message}\nAssistant: {old_response}\n\n" | |
input_data += f"{message}\n" | |
if system_message: | |
print(f"System message: {system_message}") | |
logging.debug(f"Debug - Chat Function - System Message: {system_message}") | |
temperature = float(temperature) if temperature else 0.7 | |
temp = temperature | |
logging.debug(f"Debug - Chat Function - Temperature: {temperature}") | |
logging.debug(f"Debug - Chat Function - API Key: {api_key[:10]}") | |
logging.debug(f"Debug - Chat Function - Prompt: {prompt}") | |
# Use the existing API request code based on the selected endpoint | |
response = chat_api_call(api_endpoint, api_key, input_data, prompt, temp, system_message) | |
chat_duration = time.time() - start_time | |
log_histogram("chat_duration", chat_duration, labels={"api_endpoint": api_endpoint}) | |
log_counter("chat_success", labels={"api_endpoint": api_endpoint}) | |
return response | |
except Exception as e: | |
log_counter("chat_error", labels={"api_endpoint": api_endpoint, "error": str(e)}) | |
logging.error(f"Error in chat function: {str(e)}") | |
return f"An error occurred: {str(e)}" | |
def save_chat_history_to_db_wrapper(chatbot, conversation_id, media_content, media_name=None): | |
log_counter("save_chat_history_to_db_attempt") | |
start_time = time.time() | |
logging.info(f"Attempting to save chat history. Media content type: {type(media_content)}") | |
try: | |
# Extract the media_id and media_name from the media_content | |
media_id = None | |
if isinstance(media_content, dict): | |
media_id = None | |
logging.debug(f"Media content keys: {media_content.keys()}") | |
if 'content' in media_content: | |
try: | |
content = media_content['content'] | |
if isinstance(content, str): | |
content_json = json.loads(content) | |
elif isinstance(content, dict): | |
content_json = content | |
else: | |
raise ValueError(f"Unexpected content type: {type(content)}") | |
# Use the webpage_url as the media_id | |
media_id = content_json.get('webpage_url') | |
# Use the title as the media_name | |
media_name = content_json.get('title') | |
logging.info(f"Extracted media_id: {media_id}, media_name: {media_name}") | |
except json.JSONDecodeError: | |
logging.error("Failed to decode JSON from media_content['content']") | |
except Exception as e: | |
logging.error(f"Error processing media_content: {str(e)}") | |
else: | |
logging.warning("'content' key not found in media_content") | |
else: | |
logging.warning(f"media_content is not a dictionary. Type: {type(media_content)}") | |
if media_id is None: | |
# If we couldn't find a media_id, we'll use a placeholder | |
media_id = "unknown_media" | |
logging.warning(f"Unable to extract media_id from media_content. Using placeholder: {media_id}") | |
if media_name is None: | |
media_name = "Unnamed Media" | |
logging.warning(f"Unable to extract media_name from media_content. Using placeholder: {media_name}") | |
# Generate a unique conversation name using media_id and current timestamp | |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
conversation_name = f"{media_name}_{timestamp}" | |
new_conversation_id = save_chat_history_to_database(chatbot, conversation_id, media_id, media_name, | |
conversation_name) | |
save_duration = time.time() - start_time | |
log_histogram("save_chat_history_to_db_duration", save_duration) | |
log_counter("save_chat_history_to_db_success") | |
return new_conversation_id, f"Chat history saved successfully as {conversation_name}!" | |
except Exception as e: | |
log_counter("save_chat_history_to_db_error", labels={"error": str(e)}) | |
error_message = f"Failed to save chat history: {str(e)}" | |
logging.error(error_message, exc_info=True) | |
return conversation_id, error_message | |
def save_chat_history(history, conversation_id, media_content): | |
log_counter("save_chat_history_attempt") | |
start_time = time.time() | |
try: | |
content, conversation_name = generate_chat_history_content(history, conversation_id, media_content) | |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
safe_conversation_name = re.sub(r'[^a-zA-Z0-9_-]', '_', conversation_name) | |
base_filename = f"{safe_conversation_name}_{timestamp}.json" | |
# Create a temporary file | |
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as temp_file: | |
temp_file.write(content) | |
temp_file_path = temp_file.name | |
# Generate a unique filename | |
unique_filename = generate_unique_filename(os.path.dirname(temp_file_path), base_filename) | |
final_path = os.path.join(os.path.dirname(temp_file_path), unique_filename) | |
# Rename the temporary file to the unique filename | |
os.rename(temp_file_path, final_path) | |
save_duration = time.time() - start_time | |
log_histogram("save_chat_history_duration", save_duration) | |
log_counter("save_chat_history_success") | |
return final_path | |
except Exception as e: | |
log_counter("save_chat_history_error", labels={"error": str(e)}) | |
logging.error(f"Error saving chat history: {str(e)}") | |
return None | |
def generate_chat_history_content(history, conversation_id, media_content): | |
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") | |
conversation_name = get_conversation_name(conversation_id) | |
if not conversation_name: | |
media_name = extract_media_name(media_content) | |
if media_name: | |
conversation_name = f"{media_name}-chat" | |
else: | |
conversation_name = f"chat-{timestamp}" # Fallback name | |
chat_data = { | |
"conversation_id": conversation_id, | |
"conversation_name": conversation_name, | |
"timestamp": timestamp, | |
"history": [ | |
{ | |
"role": "user" if i % 2 == 0 else "bot", | |
"content": msg[0] if isinstance(msg, tuple) else msg | |
} | |
for i, msg in enumerate(history) | |
] | |
} | |
return json.dumps(chat_data, indent=2), conversation_name | |
def extract_media_name(media_content): | |
if isinstance(media_content, dict): | |
content = media_content.get('content', {}) | |
if isinstance(content, str): | |
try: | |
content = json.loads(content) | |
except json.JSONDecodeError: | |
logging.warning("Failed to parse media_content JSON string") | |
return None | |
# Try to extract title from the content | |
if isinstance(content, dict): | |
return content.get('title') or content.get('name') | |
logging.warning(f"Unexpected media_content format: {type(media_content)}") | |
return None | |
def update_chat_content(selected_item, use_content, use_summary, use_prompt, item_mapping): | |
log_counter("update_chat_content_attempt") | |
start_time = time.time() | |
logging.debug(f"Debug - Update Chat Content - Selected Item: {selected_item}\n") | |
logging.debug(f"Debug - Update Chat Content - Use Content: {use_content}\n\n\n\n") | |
logging.debug(f"Debug - Update Chat Content - Use Summary: {use_summary}\n\n") | |
logging.debug(f"Debug - Update Chat Content - Use Prompt: {use_prompt}\n\n") | |
logging.debug(f"Debug - Update Chat Content - Item Mapping: {item_mapping}\n\n") | |
if selected_item and selected_item in item_mapping: | |
media_id = item_mapping[selected_item] | |
content = load_media_content(media_id) | |
selected_parts = [] | |
if use_content and "content" in content: | |
selected_parts.append("content") | |
if use_summary and "summary" in content: | |
selected_parts.append("summary") | |
if use_prompt and "prompt" in content: | |
selected_parts.append("prompt") | |
# Modified debug print | |
if isinstance(content, dict): | |
print(f"Debug - Update Chat Content - Content keys: {list(content.keys())}") | |
for key, value in content.items(): | |
print(f"Debug - Update Chat Content - {key} (first 500 char): {str(value)[:500]}\n\n\n\n") | |
else: | |
print(f"Debug - Update Chat Content - Content(first 500 char): {str(content)[:500]}\n\n\n\n") | |
print(f"Debug - Update Chat Content - Selected Parts: {selected_parts}") | |
update_duration = time.time() - start_time | |
log_histogram("update_chat_content_duration", update_duration) | |
log_counter("update_chat_content_success") | |
return content, selected_parts | |
else: | |
log_counter("update_chat_content_error", labels={"error": str("No item selected or item not in mapping")}) | |
print(f"Debug - Update Chat Content - No item selected or item not in mapping") | |
return {}, [] | |
# | |
# End of Chat functions | |
####################################################################################################################### | |
####################################################################################################################### | |
# | |
# Character Card Functions | |
CHARACTERS_FILE = Path('.', 'Helper_Scripts', 'Character_Cards', 'Characters.json') | |
def save_character(character_data): | |
log_counter("save_character_attempt") | |
start_time = time.time() | |
characters_file = os.path.join(os.path.dirname(__file__), '..', 'Helper_Scripts', 'Character_Cards', 'Characters.json') | |
characters_dir = os.path.dirname(characters_file) | |
try: | |
if os.path.exists(characters_file): | |
with open(characters_file, 'r') as f: | |
characters = json.load(f) | |
else: | |
characters = {} | |
char_name = character_data['name'] | |
# Save the image separately if it exists | |
if 'image' in character_data: | |
img_data = base64.b64decode(character_data['image']) | |
img_filename = f"{char_name.replace(' ', '_')}.png" | |
img_path = os.path.join(characters_dir, img_filename) | |
with open(img_path, 'wb') as f: | |
f.write(img_data) | |
character_data['image_path'] = os.path.abspath(img_path) | |
del character_data['image'] # Remove the base64 image data from the JSON | |
characters[char_name] = character_data | |
with open(characters_file, 'w') as f: | |
json.dump(characters, f, indent=2) | |
save_duration = time.time() - start_time | |
log_histogram("save_character_duration", save_duration) | |
log_counter("save_character_success") | |
logging.info(f"Character '{char_name}' saved successfully.") | |
except Exception as e: | |
log_counter("save_character_error", labels={"error": str(e)}) | |
logging.error(f"Error saving character: {str(e)}") | |
def load_characters(): | |
log_counter("load_characters_attempt") | |
start_time = time.time() | |
try: | |
characters_file = os.path.join(os.path.dirname(__file__), '..', 'Helper_Scripts', 'Character_Cards', 'Characters.json') | |
if os.path.exists(characters_file): | |
with open(characters_file, 'r') as f: | |
characters = json.load(f) | |
logging.debug(f"Loaded {len(characters)} characters from {characters_file}") | |
load_duration = time.time() - start_time | |
log_histogram("load_characters_duration", load_duration) | |
log_counter("load_characters_success", labels={"character_count": len(characters)}) | |
return characters | |
else: | |
logging.warning(f"Characters file not found: {characters_file}") | |
return {} | |
except Exception as e: | |
log_counter("load_characters_error", labels={"error": str(e)}) | |
return {} | |
def get_character_names(): | |
log_counter("get_character_names_attempt") | |
start_time = time.time() | |
try: | |
characters = load_characters() | |
names = list(characters.keys()) | |
get_names_duration = time.time() - start_time | |
log_histogram("get_character_names_duration", get_names_duration) | |
log_counter("get_character_names_success", labels={"name_count": len(names)}) | |
return names | |
except Exception as e: | |
log_counter("get_character_names_error", labels={"error": str(e)}) | |
logging.error(f"Error getting character names: {str(e)}") | |
return [] | |
# | |
# End of Chat.py | |
########################################################################################################################## | |