Spaces:
Running
Running
File size: 5,729 Bytes
8031e9c bd958a4 | 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 | <!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NLP Demo</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: -apple-system, sans-serif; max-width: 720px; margin: 0 auto; padding: 24px; background: #f5f5f5; }
h1 { text-align: center; margin-bottom: 8px; }
.subtitle { text-align: center; color: #666; margin-bottom: 24px; font-size: 14px; }
.tabs { display: flex; gap: 4px; margin-bottom: 16px; }
.tab { flex: 1; padding: 10px; border: none; cursor: pointer; border-radius: 8px 8px 0 0; font-size: 14px; font-weight: 600; }
.tab.active { background: #fff; color: #f97316; }
.tab:not(.active) { background: #e5e5e5; color: #666; }
.panel { background: #fff; border-radius: 0 0 12px 12px; padding: 20px; display: none; }
.panel.active { display: block; }
label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 6px; color: #333; }
textarea, input[type="text"] { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; font-family: inherit; margin-bottom: 12px; }
textarea { resize: vertical; }
button { background: #f97316; color: #fff; border: none; padding: 10px 24px; border-radius: 6px; font-size: 14px; cursor: pointer; font-weight: 600; }
button:hover { background: #ea580c; }
button:disabled { opacity: .5; cursor: not-allowed; }
.result { margin-top: 16px; padding: 16px; border-radius: 8px; background: #f0f9ff; border: 1px solid #bae6fd; font-size: 15px; white-space: pre-wrap; }
.result.error { background: #fef2f2; border-color: #fecaca; color: #dc2626; }
.spinner { display: inline-block; width: 14px; height: 14px; border: 2px solid #ccc; border-top-color: #333; border-radius: 50%; animation: spin .6s linear infinite; margin-right: 6px; vertical-align: middle; }
@keyframes spin { to { transform: rotate(360deg); } }
footer { text-align: center; margin-top: 32px; font-size: 12px; color: #aaa; }
</style>
</head>
<body>
<h1>🤗 NLP Demo</h1>
<p class="subtitle">Hugging Face Inference API 驱动,无需 GPU</p>
<div class="tabs">
<button class="tab active" onclick="switchTab('classify')">📝 情绪分类</button>
<button class="tab" onclick="switchTab('qa')">❓ 问答</button>
</div>
<div id="classify" class="panel active">
<label>输入中文句子</label>
<input id="clsInput" type="text" placeholder="例如:曾经最好的朋友,现在连点赞都不会了。" />
<button id="clsBtn" onclick="doClassify()">开始分类</button>
<div id="clsResult" class="result" style="display:none"></div>
</div>
<div id="qa" class="panel">
<label>上下文</label>
<textarea id="qaCtx" rows="4" placeholder="粘贴一段文本作为上下文..."></textarea>
<label>问题</label>
<input id="qaQ" type="text" placeholder="根据上下文提问..." />
<button id="qaBtn" onclick="doQA()">提 问</button>
<div id="qaResult" class="result" style="display:none"></div>
</div>
<footer>模型 <a href="https://huggingface.co/Kate-lf">Kate-lf</a> · 通过 Inference API 推理 · 首次调用需冷启动约 20 秒</footer>
<script>
const CLS_API = "https://api-inference.huggingface.co/models/Kate-lf/emotion-classification";
const QA_API = "https://api-inference.huggingface.co/models/Kate-lf/rag-qa-base-bert";
function switchTab(id) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
document.getElementById(id).classList.add('active');
event.target.classList.add('active');
}
async function doClassify() {
const input = document.getElementById('clsInput').value.trim();
const btn = document.getElementById('clsBtn');
const box = document.getElementById('clsResult');
if (!input) return;
btn.disabled = true; btn.innerHTML = '<span class="spinner"></span>推理中...';
box.style.display = 'none';
try {
const res = await fetch(CLS_API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ inputs: input }) });
const data = await res.json();
if (data.error) throw new Error(data.error);
const top = data[0][0];
box.className = 'result';
box.innerText = top.label + '(置信度 ' + (top.score * 100).toFixed(1) + '%)';
} catch (e) {
box.className = 'result error';
box.innerText = '模型正在冷启动(约 20 秒),请稍后重试。';
}
box.style.display = 'block';
btn.disabled = false; btn.innerHTML = '开始分类';
}
async function doQA() {
const ctx = document.getElementById('qaCtx').value.trim();
const q = document.getElementById('qaQ').value.trim();
const btn = document.getElementById('qaBtn');
const box = document.getElementById('qaResult');
if (!ctx || !q) return;
btn.disabled = true; btn.innerHTML = '<span class="spinner"></span>推理中...';
box.style.display = 'none';
try {
const res = await fetch(QA_API, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ inputs: { question: q, context: ctx } }) });
const data = await res.json();
if (data.error) throw new Error(data.error);
box.className = 'result';
box.innerText = data.score < 0.1 || !data.answer.trim() ? '上下文不包含答案,无法回答。' : '答案:' + data.answer + '\n置信度:' + (data.score * 100).toFixed(1) + '%';
} catch (e) {
box.className = 'result error';
box.innerText = '模型正在冷启动(约 20 秒),请稍后重试。';
}
box.style.display = 'block';
btn.disabled = false; btn.innerHTML = '提 问';
}
</script>
</body>
</html>
|