update
This commit is contained in:
137
test.html
137
test.html
@@ -230,8 +230,8 @@
|
|||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<h1>Dify 中转服务 — 测试面板</h1>
|
<h1>数字人中转服务 — 测试面板</h1>
|
||||||
<p>快速验证 digital-human-relay 与 Dify 的连通性</p>
|
<p>快速验证 digital-human 与 Dify 的连通性</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Status -->
|
<!-- Status -->
|
||||||
@@ -244,20 +244,41 @@
|
|||||||
|
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>中转服务地址</label>
|
<label>中转服务地址</label>
|
||||||
<input type="text" id="relayUrl" placeholder="http://你的服务器IP:8000" />
|
<input type="text" id="relayUrl" placeholder="http://你的服务器IP:8000" value="http://你的服务器IP:8000" />
|
||||||
<small>填写 digital-human 中转服务的根地址,末尾不要加斜杠</small>
|
<small>填写 digital-human 中转服务的根地址,末尾不要加斜杠</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label>Session ID</label>
|
||||||
|
<input type="text" id="sessionId" placeholder="留空则自动生成" />
|
||||||
|
<small>用于多轮对话上下文,不填会自动生成</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label>Dify API 密钥</label>
|
<label>App ID</label>
|
||||||
<input type="password" id="apiKey" placeholder="app-xxxxxxxx" />
|
<input type="text" id="appId" placeholder="app-xxxxxxxx" />
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label>User</label>
|
||||||
|
<input type="text" id="user" placeholder="user-xxx" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label>测试消息</label>
|
||||||
|
<textarea id="message" placeholder="请输入你想问的问题...">你好,请用一句话介绍自己。</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="field">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" id="streamMode" checked style="width:auto;margin-right:6px;" />
|
||||||
|
流式传输(Streaming)
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="btn-row">
|
<div class="btn-row">
|
||||||
<button class="btn btn-ghost" onclick="testHealth()">健康检查</button>
|
<button class="btn btn-primary" id="sendBtn" onclick="sendMessage()">发送请求</button>
|
||||||
<button class="btn btn-primary" id="sendBtn" onclick="sendMessage()">发送测试请求</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -278,7 +299,11 @@
|
|||||||
|
|
||||||
// ── Helpers ────────────────────────────────────────────────
|
// ── Helpers ────────────────────────────────────────────────
|
||||||
const relayUrl = () => document.getElementById('relayUrl').value.replace(/\/$/, '');
|
const relayUrl = () => document.getElementById('relayUrl').value.replace(/\/$/, '');
|
||||||
const apiKey = () => document.getElementById('apiKey').value.trim();
|
const sessionId = () => document.getElementById('sessionId').value.trim() || crypto.randomUUID();
|
||||||
|
const appId = () => document.getElementById('appId').value.trim();
|
||||||
|
const user = () => document.getElementById('user').value.trim() || 'test-user';
|
||||||
|
const message = () => document.getElementById('message').value.trim();
|
||||||
|
const isStream = () => document.getElementById('streamMode').checked;
|
||||||
|
|
||||||
function setStatus(type, text) {
|
function setStatus(type, text) {
|
||||||
const dot = document.getElementById('statusDot');
|
const dot = document.getElementById('statusDot');
|
||||||
@@ -310,32 +335,13 @@
|
|||||||
document.getElementById('streamingIndicator').style.display = loading ? 'flex' : 'none';
|
document.getElementById('streamingIndicator').style.display = loading ? 'flex' : 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Health Check ───────────────────────────────────────────
|
|
||||||
async function testHealth() {
|
|
||||||
const url = relayUrl();
|
|
||||||
if (!url) { alert('请先填写"中转服务地址"'); return; }
|
|
||||||
clearOutput();
|
|
||||||
setStatus('idle', '检测中…');
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${url}/health`);
|
|
||||||
const data = await res.json().catch(() => ({}));
|
|
||||||
clearOutput();
|
|
||||||
append(`HTTP 状态码: ${res.status}\n`, res.ok ? 'success-text' : 'error-text');
|
|
||||||
append(JSON.stringify(data, null, 2), res.ok ? 'success-text' : 'error-text');
|
|
||||||
setStatus(res.ok ? 'ok' : 'err', res.ok ? `服务正常 — ${data.status || 'ok'}` : `HTTP ${res.status}`);
|
|
||||||
} catch (e) {
|
|
||||||
clearOutput();
|
|
||||||
append(`连接失败:${e.message}`, 'error-text');
|
|
||||||
setStatus('err', '连接失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Send Message ───────────────────────────────────────────
|
// ── Send Message ───────────────────────────────────────────
|
||||||
async function sendMessage() {
|
async function sendMessage() {
|
||||||
const url = relayUrl();
|
const url = relayUrl();
|
||||||
const key = apiKey();
|
|
||||||
if (!url) { alert('请填写"中转服务地址"'); return; }
|
if (!url) { alert('请填写"中转服务地址"'); return; }
|
||||||
if (!key) { alert('请填写"Dify API 密钥"'); return; }
|
|
||||||
|
const msg = message();
|
||||||
|
if (!msg) { alert('请填写"测试消息"'); return; }
|
||||||
|
|
||||||
// abort previous
|
// abort previous
|
||||||
if (abortController) abortController.abort();
|
if (abortController) abortController.abort();
|
||||||
@@ -346,26 +352,69 @@
|
|||||||
setStatus('idle', '请求中…');
|
setStatus('idle', '请求中…');
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
stream: false,
|
messages: [{ content: msg }],
|
||||||
messages: [
|
app_id: appId() || 'test-app',
|
||||||
{ role: 'user', content: '你好,请用一句话介绍自己。' }
|
user: user(),
|
||||||
]
|
session_id: sessionId(),
|
||||||
|
is_stream: isStream(),
|
||||||
};
|
};
|
||||||
|
|
||||||
append(`POST ${url}/v1/chat/completions\n`);
|
append(`POST ${url}/digital-human\n\n`);
|
||||||
append(`Authorization: Bearer ${key.substring(0, 8)}…\n\n`);
|
|
||||||
append(`Request:\n${JSON.stringify(payload, null, 2)}\n\n`);
|
append(`Request:\n${JSON.stringify(payload, null, 2)}\n\n`);
|
||||||
append(`Response:\n`);
|
append(`Response:\n`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Direct to Dify (bypass relay — useful if relay itself has issues)
|
if (isStream()) {
|
||||||
const endpoint = `${url}/v1/chat/completions`;
|
// ── Streaming ───────────────────────────────────────
|
||||||
|
const endpoint = `${url}/digital-human`;
|
||||||
const res = await fetch(endpoint, {
|
const res = await fetch(endpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: { 'Content-Type': 'application/json' },
|
||||||
'Content-Type': 'application/json',
|
body: JSON.stringify(payload),
|
||||||
'Authorization': `Bearer ${key}`,
|
signal: abortController.signal,
|
||||||
},
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const errText = await res.text();
|
||||||
|
append(`[${res.status}] ERROR\n${errText}`, 'error-text');
|
||||||
|
setStatus('err', `HTTP ${res.status}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reader = res.body.getReader();
|
||||||
|
const decoder = new TextDecoder('utf-8');
|
||||||
|
let buffer = '';
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const { done, value } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
|
||||||
|
buffer += decoder.decode(value, { stream: true });
|
||||||
|
const lines = buffer.split('\n');
|
||||||
|
buffer = lines.pop() || '';
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const trimmed = line.trim();
|
||||||
|
if (!trimmed || trimmed === 'data: [DONE]' || trimmed === 'data:') continue;
|
||||||
|
if (trimmed.startsWith('data: ')) {
|
||||||
|
try {
|
||||||
|
const raw = trimmed.slice(6);
|
||||||
|
const chunk = JSON.parse(raw);
|
||||||
|
const content = chunk?.choices?.[0]?.message?.content || '';
|
||||||
|
if (content) append(content, 'success-text');
|
||||||
|
} catch { /* skip malformed */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append('\n\n[DONE]', 'warn-text');
|
||||||
|
setStatus('ok', '流式响应完成');
|
||||||
|
} else {
|
||||||
|
// ── Non-Streaming ────────────────────────────────────
|
||||||
|
const endpoint = `${url}/digital-human`;
|
||||||
|
const res = await fetch(endpoint, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify(payload),
|
body: JSON.stringify(payload),
|
||||||
signal: abortController.signal,
|
signal: abortController.signal,
|
||||||
});
|
});
|
||||||
@@ -381,13 +430,11 @@
|
|||||||
const reply = data.choices[0]?.message?.content || '';
|
const reply = data.choices[0]?.message?.content || '';
|
||||||
append(`回复内容:\n${reply}`, 'success-text');
|
append(`回复内容:\n${reply}`, 'success-text');
|
||||||
setStatus('ok', '请求成功');
|
setStatus('ok', '请求成功');
|
||||||
} else if (res.ok && data.error) {
|
|
||||||
append(`Dify 错误:${JSON.stringify(data.error, null, 2)}`, 'error-text');
|
|
||||||
setStatus('err', 'Dify 返回错误');
|
|
||||||
} else {
|
} else {
|
||||||
append(`原始响应:\n${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}`, 'error-text');
|
append(`原始响应:\n${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}`, 'error-text');
|
||||||
setStatus('err', `HTTP ${res.status}`);
|
setStatus('err', `HTTP ${res.status}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name === 'AbortError') {
|
if (e.name === 'AbortError') {
|
||||||
append('请求已取消', 'warn-text');
|
append('请求已取消', 'warn-text');
|
||||||
|
|||||||
Reference in New Issue
Block a user