Spaces:
Sleeping
Sleeping
import os | |
import gradio as gr | |
from docx import Document | |
from langchain_openai import ChatOpenAI | |
from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate, AIMessagePromptTemplate | |
from langchain_core.output_parsers import StrOutputParser | |
from langchain_core.runnables import RunnablePassthrough | |
from langchain_community.document_loaders import TextLoader | |
# Configuration | |
SECRET_KEY = "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" # Replace with your actual API key | |
RUNBOOK_DIR = "./runbooks" | |
# Initialize LLMs | |
llm = ChatOpenAI(model="gpt-4o", temperature=0.4, api_key=SECRET_KEY, streaming=True) | |
selector_llm = ChatOpenAI(model="gpt-4o", temperature=0, api_key=SECRET_KEY) | |
llm_recc = ChatOpenAI(api_key=SECRET_KEY, model="gpt-4o") | |
output_parser = StrOutputParser() | |
previous_selected_runbook = "" | |
# Load runbooks | |
def load_runbooks(): | |
runbooks = {} | |
for file in os.listdir(RUNBOOK_DIR): | |
path = os.path.join(RUNBOOK_DIR, file) | |
try: | |
if file.endswith(".txt"): | |
# Load text files using TextLoader | |
loader = TextLoader(path) | |
docs = loader.load() | |
content = "\n".join([doc.page_content for doc in docs]) | |
elif file.endswith(".docx"): | |
# Load .docx files using python-docx | |
doc = Document(path) | |
content = "\n".join([para.text for para in doc.paragraphs]) | |
else: | |
# Skip unsupported file types | |
continue | |
# Add the file's content to the runbooks dictionary | |
runbooks[file] = content | |
except Exception as e: | |
print(f"Error loading file {file}: {e}") | |
return runbooks | |
RUNBOOKS = load_runbooks() | |
RUNBOOK_NAMES = list(RUNBOOKS.keys()) | |
# Prompt templates with roles | |
system_prompt = SystemMessagePromptTemplate.from_template( | |
"You are an IT support assistant. Respond using only the immediate next step based strictly on the runbook content. Never provide multiple actions. Escalate only when the user explicitly asks." | |
) | |
user_prompt = HumanMessagePromptTemplate.from_template( | |
"Runbook Names:\n{runbook_names}\nRunbook Content:\n{runbook_contents}\nConversation History:\n{conversation_history}\nUser: {user_message}" | |
) | |
assistant_prompt = AIMessagePromptTemplate.from_template("Assistant:") | |
selector_prompt = ChatPromptTemplate.from_template(""" | |
Choose the best runbook from: | |
{runbook_names} | |
User: {user_message} | |
Selected: | |
""") | |
recc_template = ChatPromptTemplate.from_template(""" | |
You are a support agent assistant analyzing user cases. The test case shows what the user has talked with AI assistant so far. | |
Now the user wants to talk to a human. Based on the test case and runbook below, | |
suggest up to 3 recommendations which the human agent can ask the user to continue the conversation from the step where the user is stuck. For each recommendation: | |
1. Reference specific steps from the runbook, the steps should be exactly present in the runbook | |
2. Add confidence score (70-100% if directly supported by runbook, 50-69% if inferred) | |
3. Prioritize most critical actions first | |
4. Strictly do not output anything which is not present in the runbook. | |
Test Case: {test_case} | |
Case Description: {description} | |
Runbook Content: {runbook} | |
Generate upto 3 recommendations strictly in this format: | |
1. [Action] (Confidence: X%) - [Reasoning] | |
2. [Action] (Confidence: X%) - [Reasoning] | |
""") | |
# File readers | |
def read_test_case(file_path): | |
try: | |
with open(file_path, "r") as f: | |
return f.read() | |
except FileNotFoundError: | |
raise FileNotFoundError(f"Test case file not found at {file_path}") | |
def read_runbook(file_path): | |
try: | |
return Document(file_path) | |
except FileNotFoundError: | |
raise FileNotFoundError(f"Runbook file not found at {file_path}") | |
def get_recommendations(test_case, runbook_path): | |
runbook = read_runbook(runbook_path) | |
description = os.path.basename(runbook_path) | |
return | |
def respond(message, history): | |
global previous_selected_runbook | |
escalation_buffer = "" | |
buffer = "" | |
escalation_triggered = False | |
# Select runbook | |
if previous_selected_runbook: | |
selected_runbook = previous_selected_runbook | |
else: | |
selected = selector_llm.invoke(selector_prompt.format( | |
runbook_names="\n".join(RUNBOOK_NAMES), | |
user_message=message | |
)).content.strip() | |
selected_runbook = next((rb for rb in RUNBOOKS if rb in selected), "") | |
previous_selected_runbook = selected_runbook | |
runbook_content = "\n".join([f"--- {k} ---\n{v}" for k, v in RUNBOOKS.items()]) | |
conversation_history = "\n".join([f"{turn[0]}: {turn[1]}" for turn in history]) | |
if "human" in message and not escalation_triggered: | |
escalation_triggered = True | |
conversation_text = conversation_history + f"\nUser: {message}" | |
buffer = "Escalating to human agent..." | |
for token in llm_recc.stream(recc_template.format( | |
test_case=conversation_text, | |
description=os.path.basename(selected_runbook), | |
runbook=RUNBOOKS[selected_runbook])): | |
escalation_buffer += token.content | |
yield (buffer, escalation_buffer, selected_runbook) | |
return | |
full_prompt = ChatPromptTemplate.from_messages([ | |
system_prompt, | |
user_prompt, | |
assistant_prompt | |
]) | |
for token in llm.stream(full_prompt.format( | |
runbook_names="\n".join(RUNBOOK_NAMES), | |
runbook_contents=runbook_content, | |
conversation_history=conversation_history, | |
user_message=message | |
)): | |
buffer += token.content | |
yield (buffer, escalation_buffer, selected_runbook) | |
# UI Setup | |
def clear_conversation(): | |
return [], "", "", "No runbook selected" | |
with gr.Blocks() as demo: | |
gr.Markdown("# IT Support Assistant") | |
with gr.Row(): | |
with gr.Column(scale=1): | |
gr.Markdown("### Available Runbooks") | |
gr.Markdown("\n".join([f"- **{name}**" for name in RUNBOOK_NAMES])) | |
selected_runbook_display = gr.Markdown("No runbook selected") | |
with gr.Row(): | |
with gr.Column(scale=3): | |
chat = gr.ChatInterface( | |
respond, | |
additional_outputs=[ | |
gr.Textbox(label="Escalation Recommendations", lines=5, value=""), | |
selected_runbook_display | |
], | |
examples=["Increase Mail Size", "Outlook calendar not responding"], | |
cache_examples=False | |
) | |
with gr.Row(): | |
clear_button = gr.Button("Clear Conversation") | |
clear_button.click( | |
clear_conversation, | |
outputs=[ | |
chat.chatbot, | |
chat.additional_outputs[0], | |
chat.textbox, | |
selected_runbook_display | |
] | |
) | |
demo.queue().launch() | |