Prajith04 commited on
Commit
0df7243
·
verified ·
1 Parent(s): d99ee04

Upload 6 files

Browse files
Files changed (6) hide show
  1. Dockerfile +27 -0
  2. main.py +103 -0
  3. requirements.txt +8 -0
  4. static/styles.css +38 -0
  5. templates/chat.html +35 -0
  6. vectordb_utils.py +31 -0
Dockerfile ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Base image with Python
2
+ FROM python:3.10-slim
3
+
4
+ # Install system dependencies
5
+ RUN apt-get update && apt-get install -y \
6
+ git \
7
+ curl \
8
+ && rm -rf /var/lib/apt/lists/*
9
+
10
+ # Set work directory
11
+ WORKDIR /app
12
+
13
+ # Copy application files
14
+ COPY . /app
15
+
16
+ # Install Python dependencies
17
+ RUN pip install --no-cache-dir --upgrade pip
18
+ RUN pip install --no-cache-dir -r requirements.txt
19
+
20
+ # Download SentenceTransformer model (caches on build)
21
+ RUN python -c "from sentence_transformers import SentenceTransformer; SentenceTransformer('all-MiniLM-L6-v2')"
22
+
23
+ # Expose the app on port 7860 for Hugging Face Spaces
24
+ ENV PORT 7860
25
+
26
+ # Start FastAPI with uvicorn
27
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
main.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # main.py
2
+
3
+ from fastapi import FastAPI, Request, Form
4
+ from fastapi.responses import HTMLResponse
5
+ from fastapi.staticfiles import StaticFiles
6
+ from fastapi.templating import Jinja2Templates
7
+ from dotenv import load_dotenv
8
+
9
+ # Load environment variables
10
+ load_dotenv()
11
+ from gliner import GLiNER
12
+ from groq import Groq
13
+ from vectordb_utils import search_vectordb, init_qdrant_collection
14
+ GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
15
+ app = FastAPI()
16
+ templates = Jinja2Templates(directory="templates")
17
+ app.mount("/static", StaticFiles(directory="static"), name="static")
18
+
19
+ # Load models
20
+ gliner_model = GLiNER.from_pretrained("urchade/gliner_medium-v2.1")
21
+ groq_client = Groq(api_key=GROQ_API_KEY)
22
+
23
+ init_qdrant_collection()
24
+
25
+ def extract_entities(text):
26
+ labels = ["PRODUCT", "ISSUE", "PROBLEM", "SERVICE"]
27
+ return gliner_model.predict_entities(text, labels)
28
+
29
+ def validate_answer(user_query, retrieved_answer):
30
+ prompt = f"""
31
+ You are a validator assistant.
32
+
33
+ Given the user query and the answer retrieved from a knowledge base, decide if the answer is relevant and correctly addresses the query.
34
+
35
+ Respond ONLY with:
36
+ - "YES" if the answer is appropriate.
37
+ - "NO" if the answer is unrelated, inaccurate, or insufficient.
38
+
39
+ User Query:
40
+ {user_query}
41
+
42
+ Retrieved Answer:
43
+ {retrieved_answer}
44
+
45
+ Is the answer appropriate?
46
+ """
47
+ completion = groq_client.chat.completions.create(
48
+ model="meta-llama/llama-4-scout-17b-16e-instruct",
49
+ messages=[{"role": "user", "content": prompt}],
50
+ temperature=0, max_completion_tokens=10, top_p=1
51
+ )
52
+ return completion.choices[0].message.content.strip()
53
+
54
+ def generate_response(user_query, validated_answer):
55
+ prompt = f"""
56
+ You are a customer support agent.
57
+
58
+ Using the following validated support answer, respond helpfully and politely to the user's query.
59
+ warning: there should not be [],<> tags in the response
60
+
61
+ User Query:
62
+ {user_query}
63
+
64
+ Support Answer:
65
+ {validated_answer}
66
+
67
+ Compose your response:
68
+ """
69
+ completion = groq_client.chat.completions.create(
70
+ model="meta-llama/llama-4-scout-17b-16e-instruct",
71
+ messages=[{"role": "user", "content": prompt}],
72
+ temperature=0.8, max_completion_tokens=1000, top_p=1
73
+ )
74
+ return completion.choices[0].message.content.strip()
75
+
76
+ @app.get("/", response_class=HTMLResponse)
77
+ async def home(request: Request):
78
+ return templates.TemplateResponse("chat.html", {"request": request, "chat_history": []})
79
+
80
+ @app.post("/chat", response_class=HTMLResponse)
81
+ async def chat(request: Request, message: str = Form(...)):
82
+ entities = extract_entities(message)
83
+ entity_info = [(e['text'], e['label']) for e in entities]
84
+
85
+ results = search_vectordb(message)
86
+ if not results:
87
+ bot_reply = "Sorry, I couldn't find anything helpful."
88
+ else:
89
+ answer = results[0].payload["response"]
90
+ if validate_answer(message, answer) == "YES":
91
+ bot_reply = generate_response(message, answer)
92
+ else:
93
+ bot_reply = "Sorry, I couldn't find a suitable answer. Please contact support."
94
+
95
+ chat_history = [
96
+ {"sender": "User", "message": message},
97
+ {"sender": "Bot", "message": bot_reply}
98
+ ]
99
+ return templates.TemplateResponse("chat.html", {
100
+ "request": request,
101
+ "chat_history": chat_history,
102
+ "entities": entity_info
103
+ })
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ jinja2
4
+ gliner
5
+ groq
6
+ qdrant-client
7
+ sentence-transformers
8
+ dotenv
static/styles.css ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: Arial, sans-serif;
3
+ background-color: #f5f7fa;
4
+ padding: 20px;
5
+ }
6
+ .chat-container {
7
+ max-width: 600px;
8
+ margin: auto;
9
+ background: white;
10
+ padding: 20px;
11
+ border-radius: 8px;
12
+ }
13
+ .chat-box {
14
+ border: 1px solid #ccc;
15
+ padding: 10px;
16
+ max-height: 300px;
17
+ overflow-y: auto;
18
+ margin-bottom: 10px;
19
+ }
20
+ .user {
21
+ text-align: right;
22
+ color: #0066cc;
23
+ }
24
+ .bot {
25
+ text-align: left;
26
+ color: #333;
27
+ }
28
+ form {
29
+ display: flex;
30
+ gap: 10px;
31
+ }
32
+ input[type="text"] {
33
+ flex-grow: 1;
34
+ padding: 8px;
35
+ }
36
+ .entities {
37
+ margin-top: 20px;
38
+ }
templates/chat.html ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Support Chatbot</title>
5
+ <link rel="stylesheet" href="/static/styles.css">
6
+ </head>
7
+ <body>
8
+ <div class="chat-container">
9
+ <h2>🧠 Customer Support Chatbot</h2>
10
+ <div class="chat-box">
11
+ {% for entry in chat_history %}
12
+ <div class="{{ 'user' if entry.sender == 'User' else 'bot' }}">
13
+ <strong>{{ entry.sender }}:</strong> {{ entry.message }}
14
+ </div>
15
+ {% endfor %}
16
+ </div>
17
+
18
+ <form method="post" action="/chat">
19
+ <input type="text" name="message" placeholder="Type your question..." required>
20
+ <button type="submit">Send</button>
21
+ </form>
22
+
23
+ {% if entities %}
24
+ <div class="entities">
25
+ <h4>🔍 Detected Entities:</h4>
26
+ <ul>
27
+ {% for text, label in entities %}
28
+ <li>{{ label }}: <em>{{ text }}</em></li>
29
+ {% endfor %}
30
+ </ul>
31
+ </div>
32
+ {% endif %}
33
+ </div>
34
+ </body>
35
+ </html>
vectordb_utils.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # vectordb_utils.py
2
+
3
+ from qdrant_client import QdrantClient
4
+ from qdrant_client.models import VectorParams, Distance, PointStruct
5
+ from sentence_transformers import SentenceTransformer
6
+ import uuid
7
+
8
+ encoder = SentenceTransformer("all-MiniLM-L6-v2")
9
+ qdrant = QdrantClient(":memory:")
10
+ collection_name = "customer_support_docsv1"
11
+
12
+ def init_qdrant_collection():
13
+ qdrant.recreate_collection(
14
+ collection_name=collection_name,
15
+ vectors_config=VectorParams(size=384, distance=Distance.COSINE)
16
+ )
17
+
18
+ def add_to_vectordb(query, response):
19
+ vector = encoder.encode(query).tolist()
20
+ qdrant.upload_points(
21
+ collection_name=collection_name,
22
+ points=[PointStruct(
23
+ id=str(uuid.uuid4()),
24
+ vector=vector,
25
+ payload={"query": query, "response": response}
26
+ )]
27
+ )
28
+
29
+ def search_vectordb(query, limit=3):
30
+ vector = encoder.encode(query).tolist()
31
+ return qdrant.search(collection_name=collection_name, query_vector=vector, limit=limit)