AI Agent commited on
Commit Β·
8aa24f9
1
Parent(s): 130f6cd
Add feedback UI and capture to nitdaa_summary.json
Browse files- app.py +40 -5
- templates/index.html +45 -0
app.py
CHANGED
|
@@ -193,7 +193,7 @@ def log_session(event_type: str, token: str, ip: str):
|
|
| 193 |
|
| 194 |
_known_sessions = {}
|
| 195 |
|
| 196 |
-
def log_query_summary(token: str, ip: str, query: str, chunks_retrieved: int, gen_time: float, success: bool, error: str = ""):
|
| 197 |
try:
|
| 198 |
log_dir = Path(__file__).parent / "app" / "logs"
|
| 199 |
log_dir.mkdir(parents=True, exist_ok=True)
|
|
@@ -205,6 +205,7 @@ def log_query_summary(token: str, ip: str, query: str, chunks_retrieved: int, ge
|
|
| 205 |
|
| 206 |
entry = {
|
| 207 |
"timestamp": ts,
|
|
|
|
| 208 |
"ip_address": ip,
|
| 209 |
"session_token": token,
|
| 210 |
"question": query,
|
|
@@ -857,13 +858,12 @@ def query_start():
|
|
| 857 |
_query_jobs[job_id]["done"] = True
|
| 858 |
|
| 859 |
gen_time = metrics.get("time_seconds", 0)
|
| 860 |
-
log_query_summary(token, remote_addr, q, top_k or 10, gen_time, True)
|
| 861 |
except Exception as e:
|
| 862 |
-
log.exception("Query
|
| 863 |
_query_jobs[job_id]["events"].append({"error": str(e)})
|
| 864 |
-
_query_jobs[job_id]["error"] = str(e)
|
| 865 |
_query_jobs[job_id]["done"] = True
|
| 866 |
-
log_query_summary(token, remote_addr, q, top_k or 10, 0, False, str(e))
|
| 867 |
|
| 868 |
_query_executor.submit(_run)
|
| 869 |
|
|
@@ -901,6 +901,41 @@ def query_stream(job_id):
|
|
| 901 |
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
|
| 902 |
)
|
| 903 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 904 |
|
| 905 |
# ββ Headless API v1 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 906 |
|
|
|
|
| 193 |
|
| 194 |
_known_sessions = {}
|
| 195 |
|
| 196 |
+
def log_query_summary(token: str, ip: str, query: str, chunks_retrieved: int, gen_time: float, success: bool, error: str = "", job_id: str = ""):
|
| 197 |
try:
|
| 198 |
log_dir = Path(__file__).parent / "app" / "logs"
|
| 199 |
log_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
| 205 |
|
| 206 |
entry = {
|
| 207 |
"timestamp": ts,
|
| 208 |
+
"job_id": job_id,
|
| 209 |
"ip_address": ip,
|
| 210 |
"session_token": token,
|
| 211 |
"question": query,
|
|
|
|
| 858 |
_query_jobs[job_id]["done"] = True
|
| 859 |
|
| 860 |
gen_time = metrics.get("time_seconds", 0)
|
| 861 |
+
log_query_summary(token, remote_addr, q, top_k or 10, gen_time, True, "", job_id)
|
| 862 |
except Exception as e:
|
| 863 |
+
log.exception("Query failed")
|
| 864 |
_query_jobs[job_id]["events"].append({"error": str(e)})
|
|
|
|
| 865 |
_query_jobs[job_id]["done"] = True
|
| 866 |
+
log_query_summary(token, remote_addr, q, top_k or 10, 0, False, str(e), job_id)
|
| 867 |
|
| 868 |
_query_executor.submit(_run)
|
| 869 |
|
|
|
|
| 901 |
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
|
| 902 |
)
|
| 903 |
|
| 904 |
+
@app.route("/api/feedback", methods=["POST"])
|
| 905 |
+
@limiter.limit("20 per minute")
|
| 906 |
+
def api_feedback():
|
| 907 |
+
data = request.get_json() or {}
|
| 908 |
+
job_id = data.get("job_id", "")
|
| 909 |
+
rating = data.get("rating", "neutral")
|
| 910 |
+
text = data.get("text", "")
|
| 911 |
+
token = get_session_token()
|
| 912 |
+
|
| 913 |
+
try:
|
| 914 |
+
log_dir = Path(__file__).parent / "app" / "logs"
|
| 915 |
+
log_dir.mkdir(parents=True, exist_ok=True)
|
| 916 |
+
summary_file = log_dir / "nitdaa_summary.json"
|
| 917 |
+
|
| 918 |
+
from datetime import datetime, timezone, timedelta
|
| 919 |
+
ist = timezone(timedelta(hours=5, minutes=30))
|
| 920 |
+
ts = datetime.now(ist).strftime("%Y-%m-%d %H:%M:%S")
|
| 921 |
+
|
| 922 |
+
entry = {
|
| 923 |
+
"timestamp": ts,
|
| 924 |
+
"session_token": token,
|
| 925 |
+
"job_id": job_id,
|
| 926 |
+
"type": "feedback",
|
| 927 |
+
"feedback_rating": rating,
|
| 928 |
+
"feedback_text": text
|
| 929 |
+
}
|
| 930 |
+
with open(summary_file, "a") as f:
|
| 931 |
+
f.write(json.dumps(entry) + "\n")
|
| 932 |
+
|
| 933 |
+
async_sync_log(str(summary_file), "nitdaa_summary.json")
|
| 934 |
+
return jsonify({"ok": True})
|
| 935 |
+
except Exception as e:
|
| 936 |
+
log.error(f"Failed to save feedback: {e}")
|
| 937 |
+
return jsonify({"error": str(e)}), 500
|
| 938 |
+
|
| 939 |
|
| 940 |
# ββ Headless API v1 βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 941 |
|
templates/index.html
CHANGED
|
@@ -507,6 +507,12 @@
|
|
| 507 |
Analysis took ${data.metrics.time_seconds?.toFixed(1) || '?'} seconds.
|
| 508 |
Tokens: ${data.metrics.tokens_in || '?'} in, ${data.metrics.tokens_out || '?'} out.
|
| 509 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 510 |
`;
|
| 511 |
answerContent.innerHTML += metricsHtml;
|
| 512 |
window.scrollTo(0, document.body.scrollHeight);
|
|
@@ -550,6 +556,45 @@
|
|
| 550 |
}
|
| 551 |
});
|
| 552 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 553 |
</script>
|
| 554 |
</body>
|
| 555 |
</html>
|
|
|
|
| 507 |
Analysis took ${data.metrics.time_seconds?.toFixed(1) || '?'} seconds.
|
| 508 |
Tokens: ${data.metrics.tokens_in || '?'} in, ${data.metrics.tokens_out || '?'} out.
|
| 509 |
</div>
|
| 510 |
+
<div id="feedback-container-${jobId}" style="margin-top: 15px; display: flex; align-items: center; gap: 10px; background: rgba(0,0,0,0.03); padding: 10px; border-radius: 8px;">
|
| 511 |
+
<button class="fb-btn fb-up" onclick="submitFeedback('${jobId}', 'positive')" style="background:none; border:none; cursor:pointer; font-size:1.5rem; transition: transform 0.2s;">π</button>
|
| 512 |
+
<button class="fb-btn fb-down" onclick="submitFeedback('${jobId}', 'negative')" style="background:none; border:none; cursor:pointer; font-size:1.5rem; transition: transform 0.2s;">π</button>
|
| 513 |
+
<input type="text" id="fb-text-${jobId}" placeholder="Optional feedback..." style="flex-grow:1; padding:8px; border:1px solid #ddd; border-radius:4px; font-size: 0.9rem;">
|
| 514 |
+
<button onclick="submitFeedback('${jobId}', null)" style="padding:8px 15px; background: var(--primary-color); color: white; border: none; border-radius: 4px; cursor:pointer; font-weight: bold;">Submit</button>
|
| 515 |
+
</div>
|
| 516 |
`;
|
| 517 |
answerContent.innerHTML += metricsHtml;
|
| 518 |
window.scrollTo(0, document.body.scrollHeight);
|
|
|
|
| 556 |
}
|
| 557 |
});
|
| 558 |
});
|
| 559 |
+
|
| 560 |
+
// Feedback Logic
|
| 561 |
+
window.submitFeedback = async function(jobId, explicitRating) {
|
| 562 |
+
const container = document.getElementById(`feedback-container-${jobId}`);
|
| 563 |
+
if (!container) return;
|
| 564 |
+
|
| 565 |
+
let rating = container.getAttribute('data-rating') || 'neutral';
|
| 566 |
+
|
| 567 |
+
if (explicitRating) {
|
| 568 |
+
rating = explicitRating;
|
| 569 |
+
container.setAttribute('data-rating', rating);
|
| 570 |
+
// UI updates for selected icon
|
| 571 |
+
const upBtn = container.querySelector('.fb-up');
|
| 572 |
+
const downBtn = container.querySelector('.fb-down');
|
| 573 |
+
upBtn.style.transform = rating === 'positive' ? 'scale(1.2)' : 'scale(1)';
|
| 574 |
+
upBtn.style.filter = rating === 'positive' ? 'drop-shadow(0 0 5px #4caf50)' : 'none';
|
| 575 |
+
|
| 576 |
+
downBtn.style.transform = rating === 'negative' ? 'scale(1.2)' : 'scale(1)';
|
| 577 |
+
downBtn.style.filter = rating === 'negative' ? 'drop-shadow(0 0 5px #f44336)' : 'none';
|
| 578 |
+
}
|
| 579 |
+
|
| 580 |
+
const textInput = document.getElementById(`fb-text-${jobId}`);
|
| 581 |
+
const text = textInput ? textInput.value : "";
|
| 582 |
+
|
| 583 |
+
try {
|
| 584 |
+
const res = await fetch('/api/feedback', {
|
| 585 |
+
method: 'POST',
|
| 586 |
+
headers: { 'Content-Type': 'application/json', 'X-Session-Token': SESSION_TOKEN },
|
| 587 |
+
body: JSON.stringify({ job_id: jobId, rating: rating, text: text })
|
| 588 |
+
});
|
| 589 |
+
|
| 590 |
+
if (res.ok && !explicitRating) {
|
| 591 |
+
// If explicitRating is null, user clicked the Submit button
|
| 592 |
+
container.innerHTML = '<span style="color: #4caf50; font-weight: bold; margin: 0 auto;">Feedback submitted! Thank you.</span>';
|
| 593 |
+
}
|
| 594 |
+
} catch (e) {
|
| 595 |
+
console.error("Failed to submit feedback", e);
|
| 596 |
+
}
|
| 597 |
+
};
|
| 598 |
</script>
|
| 599 |
</body>
|
| 600 |
</html>
|