Surajgjadhav's picture
Added Solace Space Code
cb95f1c verified
Raw
History Blame Contribute Delete
4.88 kB
"""Gradio event handlers and chat history normalization."""
from __future__ import annotations
from typing import Any, Dict, Generator, List, Optional, Sequence, Tuple
from .config import LOGGER
from .model import contains_malformed_output, stream_model_reply
from .prompts import CRISIS_MESSAGE, LOADING_MESSAGE, contains_crisis_language
def extract_message_text(content: Any) -> str:
"""Read text from plain and Gradio-normalized Chatbot message content."""
if isinstance(content, str):
return content
if isinstance(content, list):
text_parts: List[str] = []
for item in content:
if isinstance(item, dict) and isinstance(item.get("text"), str):
text_parts.append(item["text"])
elif hasattr(item, "text") and isinstance(item.text, str):
text_parts.append(item.text)
return "\n".join(text_parts)
if isinstance(content, dict) and isinstance(content.get("text"), str):
return content["text"]
if hasattr(content, "text") and isinstance(content.text, str):
return content.text
return ""
def normalize_history(history: Optional[Sequence[Any]]) -> List[Dict[str, str]]:
"""Accept modern messages or legacy tuple history and return chat messages."""
messages: List[Dict[str, str]] = []
for item in history or []:
if isinstance(item, dict):
role = item.get("role")
content = extract_message_text(item.get("content"))
if role in {"user", "assistant"} and content.strip():
if role == "assistant" and contains_malformed_output(content):
continue
messages.append({"role": role, "content": content})
continue
if hasattr(item, "role") and hasattr(item, "content"):
role = getattr(item, "role")
content = extract_message_text(getattr(item, "content"))
if role in {"user", "assistant"} and content.strip():
if role == "assistant" and contains_malformed_output(content):
continue
messages.append({"role": role, "content": content})
continue
if isinstance(item, (list, tuple)) and len(item) == 2:
user_text, assistant_text = item
if isinstance(user_text, str) and user_text.strip():
messages.append({"role": "user", "content": user_text})
if isinstance(assistant_text, str) and assistant_text.strip():
messages.append({"role": "assistant", "content": assistant_text})
return messages
def add_user_message(
message: str,
history: Optional[Sequence[Any]],
) -> Tuple[List[Dict[str, str]], str]:
clean_message = (message or "").strip()
messages = normalize_history(history)
if not clean_message:
return messages, ""
messages.append({"role": "user", "content": clean_message})
return messages, ""
def add_quick_prompt(
prompt: str,
history: Optional[Sequence[Any]],
) -> Tuple[List[Dict[str, str]], str]:
return add_user_message(prompt, history)
def generate_assistant_reply(
history: Optional[Sequence[Any]],
) -> Generator[List[Dict[str, str]], None, None]:
messages = normalize_history(history)
if not messages or messages[-1]["role"] != "user":
yield messages
return
latest_user_message = messages[-1]["content"]
if contains_crisis_language(latest_user_message):
messages.append({"role": "assistant", "content": CRISIS_MESSAGE})
yield messages
return
messages.append({"role": "assistant", "content": LOADING_MESSAGE})
yield messages
try:
for token in stream_model_reply(messages[:-1]):
if messages[-1]["content"] == LOADING_MESSAGE:
messages[-1]["content"] = ""
messages[-1]["content"] += token
yield messages
except (FileNotFoundError, RuntimeError) as exc:
LOGGER.warning("SolaceLLM is not ready: %s", exc)
messages[-1]["content"] = (
"I couldn't reach SolaceLLM locally yet. "
f"{exc}\n\nCheck that `llama-cpp-python` is installed and either "
"`SOLACE_MODEL_PATH` points to a local GGUF file or `SOLACE_MODEL_REPO` "
"points to a Hugging Face GGUF repo."
)
yield messages
except Exception as exc: # pragma: no cover - depends on local runtime setup.
LOGGER.exception("Unable to generate a SolaceLLM response")
messages[-1]["content"] = (
"I couldn't reach SolaceLLM locally yet. "
f"{exc}\n\nCheck that `llama-cpp-python` is installed and either "
"`SOLACE_MODEL_PATH` points to a local GGUF file or `SOLACE_MODEL_REPO` "
"points to a Hugging Face GGUF repo."
)
yield messages