sharath88 commited on
Commit
3e0ad89
·
1 Parent(s): 4e1019a

Add Mistral context QA demo

Browse files
Files changed (6) hide show
  1. Dockerfile.dockerfile +11 -0
  2. main.py +86 -0
  3. requirements.txt +4 -0
  4. static/app.js +68 -0
  5. static/style.css +108 -0
  6. templates/index.html +43 -0
Dockerfile.dockerfile ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ ENV PORT=7860
11
+ CMD uvicorn main:app --host 0.0.0.0 --port $PORT
main.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import requests
3
+ from fastapi import FastAPI, Request
4
+ from fastapi.responses import HTMLResponse, JSONResponse
5
+ from fastapi.staticfiles import StaticFiles
6
+ from fastapi.templating import Jinja2Templates
7
+ from pydantic import BaseModel
8
+
9
+ HF_API_TOKEN = os.getenv("HF_API_TOKEN")
10
+ MODEL_ID = "mistralai/Mistral-7B-Instruct-v0.3"
11
+ API_URL = f"https://api-inference.huggingface.co/models/{MODEL_ID}"
12
+
13
+ if HF_API_TOKEN is None:
14
+ raise RuntimeError("HF_API_TOKEN environment variable is not set.")
15
+
16
+ HEADERS = {"Authorization": f"Bearer {HF_API_TOKEN}"}
17
+
18
+ app = FastAPI()
19
+
20
+ # static + templates
21
+ app.mount("/static", StaticFiles(directory="static"), name="static")
22
+ templates = Jinja2Templates(directory="templates")
23
+
24
+
25
+ class ChatRequest(BaseModel):
26
+ context: str
27
+ question: str
28
+
29
+
30
+ @app.get("/", response_class=HTMLResponse)
31
+ async def home(request: Request):
32
+ return templates.TemplateResponse("index.html", {"request": request})
33
+
34
+
35
+ @app.post("/chat")
36
+ async def chat_endpoint(payload: ChatRequest):
37
+ context = payload.context.strip()
38
+ question = payload.question.strip()
39
+
40
+ if not context or not question:
41
+ return JSONResponse({"answer": "Please provide both context and a question."})
42
+
43
+ # Build an instruct-style prompt
44
+ prompt = f"""[INST] You are a helpful assistant that answers questions ONLY using the given context.
45
+ If the answer cannot be found in the context, say you don't know and do not hallucinate.
46
+
47
+ Context:
48
+ {context}
49
+
50
+ Question:
51
+ {question}
52
+
53
+ Answer concisely based only on the context.[/INST]"""
54
+
55
+ try:
56
+ resp = requests.post(
57
+ API_URL,
58
+ headers=HEADERS,
59
+ json={
60
+ "inputs": prompt,
61
+ "parameters": {
62
+ "max_new_tokens": 256,
63
+ "temperature": 0.2,
64
+ },
65
+ },
66
+ timeout=60,
67
+ )
68
+ resp.raise_for_status()
69
+ data = resp.json()
70
+
71
+ # data is usually a list with {"generated_text": "..."}
72
+ generated = data[0].get("generated_text", "")
73
+
74
+ # In many TGI backends, generated_text contains prompt + completion.
75
+ # Try to cut off the prompt part if present.
76
+ if generated.startswith(prompt):
77
+ answer = generated[len(prompt) :].strip()
78
+ else:
79
+ answer = generated.strip()
80
+
81
+ return {"answer": answer}
82
+ except Exception as e:
83
+ return JSONResponse(
84
+ {"answer": f"Error calling model: {e}"},
85
+ status_code=500,
86
+ )
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ jinja2
4
+ requests
static/app.js ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ let currentContext = "";
2
+
3
+ const contextText = document.getElementById("contextText");
4
+ const setContextBtn = document.getElementById("setContextBtn");
5
+ const contextStatus = document.getElementById("contextStatus");
6
+
7
+ const chatBox = document.getElementById("chatBox");
8
+ const questionInput = document.getElementById("questionInput");
9
+ const sendBtn = document.getElementById("sendBtn");
10
+
11
+ setContextBtn.addEventListener("click", () => {
12
+ currentContext = contextText.value.trim();
13
+ if (!currentContext) {
14
+ contextStatus.textContent = "Context is empty.";
15
+ contextStatus.className = "status status-warn";
16
+ } else {
17
+ contextStatus.textContent = "Context set. You can start asking questions.";
18
+ contextStatus.className = "status status-ok";
19
+ }
20
+ });
21
+
22
+ function appendMessage(text, who) {
23
+ const div = document.createElement("div");
24
+ div.className = who === "user" ? "msg user-msg" : "msg bot-msg";
25
+ div.textContent = text;
26
+ chatBox.appendChild(div);
27
+ chatBox.scrollTop = chatBox.scrollHeight;
28
+ }
29
+
30
+ async function sendQuestion() {
31
+ const question = questionInput.value.trim();
32
+ if (!question) return;
33
+ if (!currentContext) {
34
+ appendMessage("Please set the context first.", "bot");
35
+ return;
36
+ }
37
+
38
+ appendMessage(question, "user");
39
+ questionInput.value = "";
40
+
41
+ appendMessage("Thinking...", "bot");
42
+ const thinkingBubble = chatBox.lastChild;
43
+
44
+ try {
45
+ const res = await fetch("/chat", {
46
+ method: "POST",
47
+ headers: { "Content-Type": "application/json" },
48
+ body: JSON.stringify({
49
+ context: currentContext,
50
+ question: question
51
+ })
52
+ });
53
+
54
+ const data = await res.json();
55
+ thinkingBubble.textContent = data.answer || "No answer returned.";
56
+ } catch (err) {
57
+ console.error(err);
58
+ thinkingBubble.textContent = "Error contacting the backend.";
59
+ }
60
+ }
61
+
62
+ sendBtn.addEventListener("click", sendQuestion);
63
+ questionInput.addEventListener("keydown", (e) => {
64
+ if (e.key === "Enter") {
65
+ e.preventDefault();
66
+ sendQuestion();
67
+ }
68
+ });
static/style.css ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ body {
2
+ font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
3
+ background: #0b1020;
4
+ color: #f5f5f5;
5
+ margin: 0;
6
+ }
7
+
8
+ .container {
9
+ max-width: 900px;
10
+ margin: 40px auto;
11
+ padding: 24px;
12
+ background: #141b2f;
13
+ border-radius: 12px;
14
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.4);
15
+ }
16
+
17
+ h1 {
18
+ margin-top: 0;
19
+ font-size: 1.9rem;
20
+ }
21
+
22
+ .subtitle {
23
+ color: #c0c6e0;
24
+ }
25
+
26
+ textarea {
27
+ width: 100%;
28
+ background: #0d1324;
29
+ color: #f5f5f5;
30
+ border: 1px solid #2c3554;
31
+ border-radius: 8px;
32
+ padding: 10px;
33
+ resize: vertical;
34
+ }
35
+
36
+ button {
37
+ margin-top: 8px;
38
+ padding: 8px 16px;
39
+ border-radius: 8px;
40
+ border: none;
41
+ background: #4c6fff;
42
+ color: white;
43
+ font-weight: 600;
44
+ cursor: pointer;
45
+ }
46
+
47
+ button:hover {
48
+ background: #3453e6;
49
+ }
50
+
51
+ .status {
52
+ font-size: 0.9rem;
53
+ margin-top: 4px;
54
+ }
55
+
56
+ .status-ok {
57
+ color: #8be48b;
58
+ }
59
+
60
+ .status-warn {
61
+ color: #ffce56;
62
+ }
63
+
64
+ .chat-box {
65
+ margin-top: 10px;
66
+ border-radius: 8px;
67
+ background: #0d1324;
68
+ border: 1px solid #2c3554;
69
+ padding: 10px;
70
+ max-height: 350px;
71
+ overflow-y: auto;
72
+ }
73
+
74
+ .msg {
75
+ margin-bottom: 8px;
76
+ padding: 8px 10px;
77
+ border-radius: 8px;
78
+ font-size: 0.95rem;
79
+ }
80
+
81
+ .user-msg {
82
+ background: #3453e6;
83
+ align-self: flex-end;
84
+ }
85
+
86
+ .bot-msg {
87
+ background: #1f2840;
88
+ }
89
+
90
+ .system-msg {
91
+ font-size: 0.9rem;
92
+ color: #a3abcd;
93
+ }
94
+
95
+ .chat-input-row {
96
+ display: flex;
97
+ gap: 8px;
98
+ margin-top: 10px;
99
+ }
100
+
101
+ .chat-input-row input {
102
+ flex: 1;
103
+ padding: 8px;
104
+ border-radius: 8px;
105
+ border: 1px solid #2c3554;
106
+ background: #0d1324;
107
+ color: #f5f5f5;
108
+ }
templates/index.html ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <title>Mistral Context QA Demo</title>
6
+ <link rel="stylesheet" href="/static/style.css" />
7
+ </head>
8
+ <body>
9
+ <div class="container">
10
+ <h1>Mistral Context QA Demo</h1>
11
+ <p class="subtitle">
12
+ Paste a paragraph in the context box, then ask questions about it in the chat below.
13
+ </p>
14
+
15
+ <h2>Context</h2>
16
+ <textarea
17
+ id="contextText"
18
+ rows="6"
19
+ placeholder="Paste any article, paragraph, or documentation here..."
20
+ ></textarea>
21
+ <button id="setContextBtn">Set / Update Context</button>
22
+ <p id="contextStatus" class="status"></p>
23
+
24
+ <h2>Chat</h2>
25
+ <div id="chatBox" class="chat-box">
26
+ <div class="system-msg">
27
+ Paste some context above, then ask me a question about it!
28
+ </div>
29
+ </div>
30
+
31
+ <div class="chat-input-row">
32
+ <input
33
+ type="text"
34
+ id="questionInput"
35
+ placeholder="Ask a question about the context..."
36
+ />
37
+ <button id="sendBtn">Send</button>
38
+ </div>
39
+ </div>
40
+
41
+ <script src="/static/app.js"></script>
42
+ </body>
43
+ </html>