File size: 6,526 Bytes
8174974
29a8952
8174974
efa60b3
 
66128ac
efa60b3
8174974
b3f2d3d
8174974
49f0804
 
 
 
 
66128ac
b3f2d3d
efa60b3
8174974
b3f2d3d
8174974
0e52653
66128ac
0e52653
66128ac
 
 
 
b35a08e
 
0e52653
 
b3f2d3d
29a8952
 
b3f2d3d
8174974
49f0804
0ca9296
49f0804
8174974
 
 
0ca9296
 
 
 
 
8174974
 
 
 
 
0ca9296
 
 
66128ac
8174974
 
 
 
b35a08e
49f0804
b35a08e
49f0804
b35a08e
 
 
66128ac
8174974
29a8952
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8174974
ef8cc0f
8174974
e16b3ce
29a8952
8174974
 
 
e16b3ce
8174974
 
 
d40453d
e16b3ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29a8952
e16b3ce
 
 
b3f2d3d
29a8952
 
 
e16b3ce
29a8952
e16b3ce
 
 
 
29a8952
e6fc2f8
29a8952
 
e6fc2f8
 
29a8952
e6fc2f8
29a8952
 
 
 
 
 
 
ef8cc0f
29a8952
 
8174974
ef8cc0f
29a8952
b3f2d3d
e16b3ce
d40453d
 
29a8952
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# Smart Customer Support Assistant (Enhanced UI Version)
# Note: Core analysis logic remains unchanged, now with text generation and customer selection

import streamlit as st
from transformers import pipeline
import re

# ------------------------------
# Load models (now includes 3rd: text generation)
# ------------------------------
emotion_classifier = pipeline(
    "text-classification",
    model="j-hartmann/emotion-english-distilroberta-base",
    return_all_scores=True
)
intent_classifier = pipeline("zero-shot-classification", model="facebook/bart-large-mnli")
text_generator = pipeline("text2text-generation", model="declare-lab/flan-alpaca-base")

# ------------------------------
# Candidate tasks / prompts
# ------------------------------
candidate_tasks = [
    "change mobile plan",
    "top up balance",
    "report service outage",
    "ask for billing support",
    "reactivate service",
    "cancel subscription",
    "check account status",
    "upgrade device"
]

def generate_response(intent):
    prompt = f"Generate a polite and helpful customer service response for the request '{intent}'. Include a greeting, summary of current status like plan or balance using anonymized placeholders (e.g. Plan X, Β₯X), a suitable recommendation, and end with a question offering assistance."
    output = text_generator(prompt, max_new_tokens=100, do_sample=True)[0]['generated_text']
    return output

urgent_emotions = {"anger", "frustration", "anxiety", "urgency", "afraid", "annoyed"}
moderate_emotions = {"confused", "sad", "tired", "concerned", "sadness"}

# ------------------------------
# Emotion processing
# ------------------------------
def refine_emotion_label(text, model_emotion):
    text_lower = text.lower()
    urgent_keywords = ["fix", "now", "immediately", "urgent", "can't", "need", "asap"]
    exclamations = text.count("!")
    upper_words = sum(1 for word in text.split() if word.isupper())
    signal_score = sum([
        any(word in text_lower for word in urgent_keywords),
        exclamations >= 2,
        upper_words >= 1
    ])
    if model_emotion.lower() in {"joy", "neutral", "sadness"} and signal_score >= 2:
        return "urgency"
    return model_emotion

def get_emotion_label(emotion_result, text):
    sorted_emotions = sorted(emotion_result[0], key=lambda x: x['score'], reverse=True)
    return refine_emotion_label(text, sorted_emotions[0]['label'])

def get_emotion_score(emotion):
    if emotion.lower() in urgent_emotions:
        return 1.0
    elif emotion.lower() in moderate_emotions:
        return 0.6
    else:
        return 0.2

# ------------------------------
# UI: Sidebar for customer selection
# ------------------------------
st.set_page_config(page_title="Smart Customer Support Assistant", layout="wide")
st.sidebar.title("πŸ“ Customer Selector")
if "customers" not in st.session_state:
    st.session_state.customers = {"Customer A": [], "Customer B": [], "Customer C": []}
customer_names = list(st.session_state.customers.keys())
selected_customer = st.sidebar.selectbox("Choose a customer:", customer_names)

# Load or init selected customer's session
if "chat_sessions" not in st.session_state:
    st.session_state.chat_sessions = {}
if selected_customer not in st.session_state.chat_sessions:
    st.session_state.chat_sessions[selected_customer] = {
        "chat": [],
        "system_result": None,
        "agent_reply": "",
        "support_required": ""
    }
session = st.session_state.chat_sessions[selected_customer]

# ------------------------------
# Main Interface
# ------------------------------
st.title("Smart Customer Support Assistant (for Agents Only)")

st.markdown("### Conversation")
for msg in session["chat"]:
    with st.chat_message(msg['role']):
        st.markdown(msg['content'])

col1, col2 = st.columns([6,1])
with col1:
    user_input = st.text_input("Enter customer message:", key="user_input")
with col2:
    analyze_clicked = st.button("Analyze", use_container_width=True)

if analyze_clicked and user_input.strip():
    emotion_result = emotion_classifier(user_input)
    emotion_label = get_emotion_label(emotion_result, user_input)
    emotion_score = get_emotion_score(emotion_label)

    intent_result = intent_classifier(user_input, candidate_tasks)
    top_intents = [label for label, score in zip(intent_result['labels'], intent_result['scores']) if score > 0.15][:3]

    content_score = 0.0
    if any(x in user_input.lower() for x in ["out of service", "can't", "urgent", "immediately"]):
        content_score += 0.4
    if any(label in ["top up balance", "reactivate service"] for label in top_intents):
        content_score += 0.4

    final_score = 0.5 * emotion_score + 0.5 * content_score
    session["chat"].append({"role": "user", "content": user_input})

    if final_score < 0.5 and top_intents:
        intent = top_intents[0]
        response = generate_response(intent)
        session["chat"].append({"role": "assistant", "content": response})
        session["system_result"] = None
        session["support_required"] = "🟒 Automated response handled this request."
    else:
        session["system_result"] = {
            "emotion": emotion_label,
            "tone": "Urgent" if emotion_score > 0.8 else "Concerned" if emotion_score > 0.5 else "Calm",
            "intents": top_intents
        }
        session["support_required"] = "πŸ”΄ Human support required."

if session["support_required"]:
    st.markdown(f"### {session['support_required']}")

st.subheader("Agent Response Console")
session["agent_reply"] = st.text_area("Compose your reply:", value=session["agent_reply"])
if st.button("Send Reply"):
    if session["agent_reply"].strip():
        session["chat"].append({"role": "assistant", "content": session["agent_reply"]})
        session["agent_reply"] = ""
        session["system_result"] = None
        session["support_required"] = ""

if session["system_result"] is not None:
    st.markdown("#### Customer Status")
    st.markdown(f"- **Emotion:** {session['system_result']['emotion'].capitalize()}")
    st.markdown(f"- **Tone:** {session['system_result']['tone']}")

    st.markdown("#### Detected Customer Needs")
    for intent in session['system_result']['intents']:
        suggestion = generate_response(intent)
        st.markdown(f"**β€’ {intent.capitalize()}**")
        st.code(suggestion)
        if st.button("Copy to agent reply box", key=f"btn_{selected_customer}_{intent}"):
            session["agent_reply"] = suggestion