diff --git a/digital-human2.py b/old/digital-human2.py similarity index 100% rename from digital-human2.py rename to old/digital-human2.py diff --git a/test.html b/test.html index 5c8b7e3..c1425e4 100644 --- a/test.html +++ b/test.html @@ -230,8 +230,8 @@
-

Dify 中转服务 — 测试面板

-

快速验证 digital-human-relay 与 Dify 的连通性

+

数字人中转服务 — 测试面板

+

快速验证 digital-human 与 Dify 的连通性

@@ -244,20 +244,41 @@
- + 填写 digital-human 中转服务的根地址,末尾不要加斜杠
+
+ + + 用于多轮对话上下文,不填会自动生成 +
+
- - + + +
+
+ +
+
+ + +
+ +
+ +
+
- - +
@@ -277,8 +298,12 @@ let abortController = null; // ── Helpers ──────────────────────────────────────────────── - const relayUrl = () => document.getElementById('relayUrl').value.replace(/\/$/, ''); - const apiKey = () => document.getElementById('apiKey').value.trim(); + const relayUrl = () => document.getElementById('relayUrl').value.replace(/\/$/, ''); + 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) { const dot = document.getElementById('statusDot'); @@ -310,32 +335,13 @@ 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 ─────────────────────────────────────────── async function sendMessage() { const url = relayUrl(); - const key = apiKey(); if (!url) { alert('请填写"中转服务地址"'); return; } - if (!key) { alert('请填写"Dify API 密钥"'); return; } + + const msg = message(); + if (!msg) { alert('请填写"测试消息"'); return; } // abort previous if (abortController) abortController.abort(); @@ -346,47 +352,88 @@ setStatus('idle', '请求中…'); const payload = { - stream: false, - messages: [ - { role: 'user', content: '你好,请用一句话介绍自己。' } - ] + messages: [{ content: msg }], + app_id: appId() || 'test-app', + user: user(), + session_id: sessionId(), + is_stream: isStream(), }; - append(`POST ${url}/v1/chat/completions\n`); - append(`Authorization: Bearer ${key.substring(0, 8)}…\n\n`); + append(`POST ${url}/digital-human\n\n`); append(`Request:\n${JSON.stringify(payload, null, 2)}\n\n`); append(`Response:\n`); try { - // Direct to Dify (bypass relay — useful if relay itself has issues) - const endpoint = `${url}/v1/chat/completions`; - const res = await fetch(endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Authorization': `Bearer ${key}`, - }, - body: JSON.stringify(payload), - signal: abortController.signal, - }); + if (isStream()) { + // ── Streaming ─────────────────────────────────────── + const endpoint = `${url}/digital-human`; + const res = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + signal: abortController.signal, + }); - const raw = await res.text(); - let data; - try { data = JSON.parse(raw); } catch { data = raw; } + if (!res.ok) { + const errText = await res.text(); + append(`[${res.status}] ERROR\n${errText}`, 'error-text'); + setStatus('err', `HTTP ${res.status}`); + return; + } - append(`[${res.status}] `, res.ok ? 'success-text' : 'error-text'); - append(`${res.ok ? 'OK' : 'ERROR'}\n\n`, res.ok ? 'success-text' : 'error-text'); + const reader = res.body.getReader(); + const decoder = new TextDecoder('utf-8'); + let buffer = ''; - if (res.ok && data.choices) { - const reply = data.choices[0]?.message?.content || ''; - append(`回复内容:\n${reply}`, 'success-text'); - setStatus('ok', '请求成功'); - } else if (res.ok && data.error) { - append(`Dify 错误:${JSON.stringify(data.error, null, 2)}`, 'error-text'); - setStatus('err', 'Dify 返回错误'); + 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 { - append(`原始响应:\n${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}`, 'error-text'); - setStatus('err', `HTTP ${res.status}`); + // ── Non-Streaming ──────────────────────────────────── + const endpoint = `${url}/digital-human`; + const res = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload), + signal: abortController.signal, + }); + + const raw = await res.text(); + let data; + try { data = JSON.parse(raw); } catch { data = raw; } + + append(`[${res.status}] `, res.ok ? 'success-text' : 'error-text'); + append(`${res.ok ? 'OK' : 'ERROR'}\n\n`, res.ok ? 'success-text' : 'error-text'); + + if (res.ok && data.choices) { + const reply = data.choices[0]?.message?.content || ''; + append(`回复内容:\n${reply}`, 'success-text'); + setStatus('ok', '请求成功'); + } else { + append(`原始响应:\n${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}`, 'error-text'); + setStatus('err', `HTTP ${res.status}`); + } } } catch (e) { if (e.name === 'AbortError') {